# 接口交互格式

两个接口：

1. **`POST /sql/execute`** — 同步 JSON：解析 → **方言改写** → 鉴权 → 打库  
2. **`POST /sql/diagnose/stream`** — SSE：诊断过程事件 + 最终完整 `result` JSON  

## 核心概念：两份 SQL

| 字段 | 含义 |
| --- | --- |
| `sql`（submittedSql） | 用户提交的原始 SQL |
| `executedSql` | 后台按方言改写/优化后、真正交给数据库的 SQL |

示例：用户提交 `SELECT * FROM user;`，方言策略补默认分页后，`executedSql` 可能为 `SELECT * FROM user LIMIT 10`。

链路位置：**解析通过之后 → 权限判定之前** 完成改写；权限与打库都针对「将要执行」的对象 / `executedSql`。

- `PARSE_ERROR`：尚未改写，`executedSql` 为 `null`
- 其余状态：应返回 `executedSql`（及可选 `rewrite` 说明）
- 诊断时同时传双 SQL；**`fixedSql` 面向用户编辑器（提交态）**，不要把实验室自动补的 `LIMIT` 写进用户稿，除非用户原本就有

---

## 1. SQL 执行接口

### 请求 `POST /sql/execute`

```http
POST /sql/execute HTTP/1.1
Content-Type: application/json
Authorization: Bearer <token>
```

```json
{
  "sql": "SELECT * FROM user",
  "dialect": "mysql",
  "connectionId": "lab-conn-001"
}
```

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `sql` | string | 用户提交的 SQL |
| `dialect` | string | 方言，决定改写规则 |
| `connectionId` | string | 实验室数据源连接 |

### 响应格式

```json
{
  "requestId": "exec-7f3a",
  "status": "SUCCESS | PARSE_ERROR | PERMISSION_DENIED | EXEC_ERROR",
  "message": "人类可读摘要",
  "sql": "用户提交的原始 SQL",
  "executedSql": "实际打库的 SQL，解析失败时为 null",
  "rewrite": {
    "applied": true,
    "rules": ["default_select_limit"],
    "detail": "未指定 LIMIT，按实验室策略追加 LIMIT 10"
  },
  "diagnosable": false,
  "parsedSummary": {
    "action": "SELECT",
    "tables": ["user"],
    "columns": ["*"]
  },
  "result": null,
  "error": null,
  "permissionGaps": null
}
```

| 字段 | 说明 |
| --- | --- |
| `sql` | 用户提交 SQL |
| `executedSql` | 实际执行 SQL；`PARSE_ERROR` 时为 `null` |
| `rewrite` | 改写元信息；未改写可为 `{"applied":false,"rules":[]}` 或 `null` |
| `parsedSummary` | 建议基于**将执行**的 SQL 对象抽取（改写后） |

### 按 status 的 payload

#### `SUCCESS`

```json
{
  "requestId": "exec-7f3a",
  "status": "SUCCESS",
  "message": "执行成功",
  "sql": "SELECT * FROM user",
  "executedSql": "SELECT * FROM user LIMIT 10",
  "rewrite": {
    "applied": true,
    "rules": ["default_select_limit"],
    "detail": "未指定 LIMIT，按实验室策略追加 LIMIT 10"
  },
  "diagnosable": false,
  "parsedSummary": {
    "action": "SELECT",
    "tables": ["user"],
    "columns": ["*"]
  },
  "result": {
    "columns": ["id", "name"],
    "rows": [[1, "alice"]],
    "rowCount": 1,
    "affectedRows": null
  },
  "error": null,
  "permissionGaps": null
}
```

前端：展示结果集；建议同时展示「实际执行 SQL」，避免用户困惑行数被截断。

#### `PARSE_ERROR`

```json
{
  "requestId": "exec-7f3b",
  "status": "PARSE_ERROR",
  "message": "本地语法解析失败",
  "sql": "SELEC * FROM user",
  "executedSql": null,
  "rewrite": null,
  "diagnosable": true,
  "parsedSummary": null,
  "result": null,
  "error": {
    "code": "PARSE_FAILED",
    "message": "Syntax error at line 1 column 6",
    "position": { "line": 1, "column": 6 }
  },
  "permissionGaps": null
}
```

解析失败不进入改写；诊断只针对 `sql`。

#### `PERMISSION_DENIED`

```json
{
  "requestId": "exec-7f3c",
  "status": "PERMISSION_DENIED",
  "message": "权限不足",
  "sql": "DELETE FROM users WHERE id = 1",
  "executedSql": "DELETE FROM users WHERE id = 1",
  "rewrite": { "applied": false, "rules": [] },
  "diagnosable": false,
  "parsedSummary": {
    "action": "DELETE",
    "tables": ["users"],
    "columns": ["id"]
  },
  "result": null,
  "error": {
    "code": "PERM_DENIED",
    "message": "缺少表 users 的 DELETE 权限"
  },
  "permissionGaps": [
    { "table": "users", "action": "DELETE", "columns": null }
  ]
}
```

前端：直接展示权限明细，**不**调诊断。

#### `EXEC_ERROR`

```json
{
  "requestId": "exec-7f3d",
  "status": "EXEC_ERROR",
  "message": "数据库执行失败",
  "sql": "SELECT nam FROM user",
  "executedSql": "SELECT nam FROM user LIMIT 10",
  "rewrite": {
    "applied": true,
    "rules": ["default_select_limit"],
    "detail": "未指定 LIMIT，按实验室策略追加 LIMIT 10"
  },
  "diagnosable": true,
  "parsedSummary": {
    "action": "SELECT",
    "tables": ["user"],
    "columns": ["nam"]
  },
  "result": null,
  "error": {
    "code": "42S22",
    "message": "Unknown column 'nam' in 'field list'",
    "position": null
  },
  "permissionGaps": null
}
```

诊断请求必须带上 `sql` + `executedSql` + `rewrite`，避免 Agent 把自动 `LIMIT` 误判为用户错误。

### 前端分流

```text
SUCCESS              → 展示 result；可选展示 executedSql
PERMISSION_DENIED    → 展示 error + permissionGaps
PARSE_ERROR          → 展示 error，并 POST /sql/diagnose/stream
EXEC_ERROR + diagnosable=true  → 同上，走诊断 SSE（含双 SQL）
EXEC_ERROR + diagnosable=false → 只展示 error
```

---

## 2. SQL 诊断接口（SSE）

### 请求 `POST /sql/diagnose/stream`

```http
POST /sql/diagnose/stream HTTP/1.1
Content-Type: application/json
Accept: text/event-stream
Authorization: Bearer <token>
```

```json
{
  "requestId": "exec-7f3d",
  "status": "EXEC_ERROR",
  "sql": "SELECT nam FROM user",
  "executedSql": "SELECT nam FROM user LIMIT 10",
  "rewrite": {
    "applied": true,
    "rules": ["default_select_limit"],
    "detail": "未指定 LIMIT，按实验室策略追加 LIMIT 10"
  },
  "dialect": "mysql",
  "connectionId": "lab-conn-001",
  "diagnosable": true,
  "parsedSummary": {
    "action": "SELECT",
    "tables": ["user"],
    "columns": ["nam"]
  },
  "error": {
    "code": "42S22",
    "message": "Unknown column 'nam' in 'field list'",
    "position": null
  },
  "notes": null
}
```

| 字段 | 说明 |
| --- | --- |
| `sql` | 用户提交 SQL |
| `executedSql` | 实际执行 SQL；`PARSE_ERROR` 时可为 `null` |
| `rewrite` | 改写说明，供 Agent 区分实验室策略与用户笔误 |
| `status` / `error` / `parsedSummary` | 同执行失败上下文 |

### SSE 事件类型

| event | 何时 | data |
| --- | --- | --- |
| `meta` | 流开始 | `requestId`、`status` 等 |
| `phase` | 阶段切换 | `classify` / `tool` / `generate` / `validate` |
| `tool` | 工具调用 | `name`、`state`=`start\|end`、`ok` |
| `message` | 可选可读进度 | `delta` 文本增量 |
| `result` | **终态** | 完整诊断 JSON |
| `error` | 失败 | `code`、`message` |
| `done` | 流结束 | `ok` |

### 成功流示例（EXEC + 改写）

```text
event: meta
data: {"requestId":"diag-9c01","execRequestId":"exec-7f3d","status":"EXEC_ERROR"}

event: phase
data: {"phase":"tool"}

event: tool
data: {"name":"get_table_schema","state":"end","ok":true}

event: phase
data: {"phase":"generate"}

event: message
data: {"delta":"列 nam 不存在；实验室已自动追加 LIMIT，修正稿将写回用户提交态 SQL…"}

event: phase
data: {"phase":"validate"}

event: tool
data: {"name":"validate_sql_syntax","state":"end","ok":true}

event: result
data: {"errorCategory":"SEMANTIC","problemSummary":"列 nam 不存在","rootCause":"表 user 无 nam 列；executedSql 中的 LIMIT 10 为实验室默认改写，非用户错误","suggestions":["将 nam 改为实际列名，如 name"],"fixedSql":"SELECT name FROM user","confidence":0.9,"assumptions":[],"usedCapabilities":["builtin:get_table_schema","builtin:validate_sql_syntax"]}

event: done
data: {"ok":true}
```

注意：`fixedSql` 为 `SELECT name FROM user`（提交态），**不含**自动 `LIMIT`；用户再次执行时由后台重新改写。

### `result` 固定 JSON

```json
{
  "errorCategory": "SEMANTIC",
  "problemSummary": "列 nam 不存在",
  "rootCause": "…",
  "suggestions": ["将 nam 改为 name"],
  "fixedSql": "SELECT name FROM user",
  "confidence": 0.9,
  "assumptions": [],
  "usedCapabilities": ["builtin:get_table_schema", "builtin:validate_sql_syntax"]
}
```

| 字段 | 说明 |
| --- | --- |
| `fixedSql` | **面向编辑器的提交态 SQL**；再次 `/sql/execute` 时仍会走改写 |

### 前端消费方式

```text
phase / tool / message  → 更新「诊断中」进度
result                  → 渲染固定诊断面板；一键填入 fixedSql（提交态）
error                   → 诊断失败态
done                    → 结束 loading
```

### 交互原则

- 对用户无多轮对话；过程事件 + 完整 `result`。  
- 始终区分 submitted / executed，避免把实验室改写当成用户语法问题。  
- `fixedSql` 回填编辑器后，再次执行仍经：解析 → 改写 → 权限 → 打库。
