更新说明(2026-03-31)
当前
QueryLab本地实现使用的是Tauri 2 + Rust + Svelte 5 + CodeMirror 6。 这份文档里的 API 设计和元数据 SQL 仍然有效,但编辑器部分最初是按Monaco思路写的。 现在应将这里的“补全策略”理解为 CodeMirror 6 落地参考:
Monaco completion provider->CodeMirror completion sourceeditor model->EditorState/ 文档状态language service-> Rust 元数据命令 + 前端补全整合层
下面继续把你要的 3 块补齐:Tauri Commands API(入参/出参 JSON)、MySQL 元数据 SQL 清单、编辑器补全策略(缓存/刷新/性能)。都按“直接照着写代码”的粒度来。
A) Tauri Commands API 清单(入参/出参)
A.0 通用返回结构(所有 command 统一)
{
"ok": true,
"data": {},
"error": null,
"trace_id": "optional-string"
}失败示例:
{
"ok": false,
"data": null,
"error": { "code": "DB_CONN_FAILED", "message": "连接失败", "detail": "..." },
"trace_id": "..."
}A.1 通用类型定义(前后端共享)
ConnectionInfo
{
"id": "uuid",
"name": "prod-mysql",
"driver": "mysql",
"host": "127.0.0.1",
"port": 3306,
"user": "root",
"password": "encrypted-or-plain-input",
"default_db": "optional",
"ssl": {
"enabled": false,
"mode": "preferred | required | verify_ca | verify_identity",
"ca_path": "optional",
"cert_path": "optional",
"key_path": "optional"
},
"ssh": {
"enabled": false,
"host": "1.2.3.4",
"port": 22,
"user": "ubuntu",
"auth": { "type": "password|key", "password": "optional", "key_path": "optional", "passphrase": "optional" },
"remote_host": "127.0.0.1",
"remote_port": 3306,
"local_port": 0
},
"tags": ["dev", "prod"],
"created_at": 0,
"updated_at": 0
}QueryOptions
{
"database": "optional-db",
"max_rows": 1000,
"timeout_ms": 30000,
"paging": { "enabled": true, "page": 1, "page_size": 200 },
"return_format": "chunked | full",
"include_total": false
}QueryResult(chunked)
{
"query_id": "uuid",
"sets": [
{
"set_index": 0,
"columns": [
{ "name": "id", "type": "BIGINT", "nullable": false },
{ "name": "name", "type": "VARCHAR", "nullable": true }
],
"meta": { "elapsed_ms": 12, "affected_rows": 0, "warning_count": 0 },
"chunks": [
{ "chunk_index": 0, "rows": [[1,"a"],[2,"b"]] }
],
"paging": { "page": 1, "page_size": 200, "has_more": true }
}
]
}A.2 App/Health
app_get_info()
req:{}res.data
{ "version": "1.0.0", "platform": "mac|win|linux", "build": "dev|prod" }A.3 连接管理
conn_list()
req:{}res.data
{ "connections": [/* ConnectionInfo */] }conn_upsert(connection: ConnectionInfo)
req
{ "connection": { /* ConnectionInfo */ } }res.data
{ "id": "uuid" }conn_delete(id: string)
req
{ "id": "uuid" }res.data
{ "deleted": true }conn_test(connection: ConnectionInfo)
req
{ "connection": { /* ConnectionInfo */ } }res.data
{ "latency_ms": 25, "server_version": "8.0.x", "user": "root", "default_db": "..." }conn_open(connection_id: string)
打开连接池并返回运行态句柄。 req
{ "connection_id": "uuid" }res.data
{ "runtime_id": "uuid", "server_version": "8.0.x" }conn_close(runtime_id: string)
req
{ "runtime_id": "uuid" }res.data
{ "closed": true }A.4 Schema / Metadata
meta_list_databases(runtime_id)
req
{ "runtime_id": "uuid" }res.data
{ "databases": ["db1","db2"] }meta_list_tables(runtime_id, database, include_views?)
req
{ "runtime_id": "uuid", "database": "db1", "include_views": true }res.data
{
"tables": [
{ "name": "users", "type": "BASE TABLE", "comment": "", "engine": "InnoDB", "rows_est": 1234 },
{ "name": "v_users", "type": "VIEW", "comment": "" }
]
}meta_get_table_schema(runtime_id, database, table)
res.data
{
"database": "db1",
"table": "users",
"columns": [
{ "name": "id", "type": "BIGINT", "nullable": false, "default": null, "comment": "", "extra": "auto_increment" }
],
"indexes": [
{ "name": "PRIMARY", "unique": true, "columns": ["id"] }
],
"foreign_keys": [
{ "name": "fk_user_team", "columns": ["team_id"], "ref_table": "teams", "ref_columns": ["id"] }
],
"create_sql": "CREATE TABLE ..."
}meta_search_symbols(runtime_id, database, q, limit)
用于补全/搜索:表、列、函数、过程。 req
{ "runtime_id": "uuid", "database": "db1", "q": "us", "limit": 50 }res.data
{
"tables": ["users","user_logs"],
"columns": [{ "table":"users", "name":"user_id" }],
"routines": ["user_stats"]
}A.5 SQL 执行与取消
query_execute(runtime_id, sql, options)
req
{ "runtime_id": "uuid", "sql": "select * from users;", "options": { /* QueryOptions */ } }res.data
{ "query_id": "uuid", "result": { /* QueryResult */ } }query_fetch_more(runtime_id, query_id, set_index, next_page?)
用于分页/继续拉取 chunk。 req
{ "runtime_id": "uuid", "query_id": "uuid", "set_index": 0, "next_page": 2 }query_cancel(runtime_id, query_id)
res.data
{ "canceled": true }实现建议:每次执行生成
query_id,Rust 侧保存 cancel token;mysql 驱动层支持中断/超时就走中断,否则至少能“停止继续拉取+丢弃结果”。
A.6 表数据浏览/编辑
table_select(runtime_id, database, table, where?, order_by?, page, page_size)
req
{
"runtime_id":"uuid","database":"db1","table":"users",
"where":"id > 10","order_by":"id desc","page":1,"page_size":200
}res.data
{
"columns":[{"name":"id","type":"BIGINT"}],
"rows":[[11,"a"],[12,"b"]],
"paging":{"page":1,"page_size":200,"has_more":true}
}table_update_cells(runtime_id, database, table, key, changes)
强烈建议:编辑必须基于“主键/唯一键定位”,否则拒绝更新并提示。 req
{
"runtime_id":"uuid","database":"db1","table":"users",
"key":{"pk_columns":["id"],"pk_values":[11]},
"changes":[{"column":"name","value":"new"}]
}res.data
{ "affected_rows": 1 }table_insert_row(runtime_id, database, table, values)
req
{ "runtime_id":"uuid","database":"db1","table":"users","values":{"name":"x","age":18} }table_delete_row(runtime_id, database, table, key)
req
{ "runtime_id":"uuid","database":"db1","table":"users","key":{"pk_columns":["id"],"pk_values":[11]} }A.7 导入导出(任务型)
export_start(runtime_id, spec)
req
{
"runtime_id":"uuid",
"spec":{
"source":{"type":"table","database":"db1","table":"users"},
"format":"csv|sql|xlsx",
"path":"/abs/path/out.csv",
"options":{"delimiter":",","with_header":true,"encoding":"utf-8","chunk_rows":5000}
}
}res.data
{ "task_id":"uuid" }import_start(runtime_id, spec)
(略,同理,返回 task_id)
task_cancel(task_id)
task_get_status(task_id)
事件(Rust emit → 前端 listen)
task_progress:{task_id, percent, message, processed_rows}task_done:{task_id, path, elapsed_ms}task_error:{task_id, code, message}
A.8 历史/模板
history_add(runtime_id, item)
history_list(connection_id, q?, limit?, offset?)
template_upsert(template)
template_list()
A.9 License/VIP
license_get_status()
res.data
{ "plan":"free|pro", "expires_at":0, "features":["excel_export","explain_viz"] }license_import(license_text_or_path)
license_clear()
B) MySQL 元数据 SQL 清单(可直接封装成 db/mysql/metadata.rs)
说明:以下 SQL 默认使用参数
:db,:table,Rust 侧自己替换成?并 bind。
B.1 服务器信息
SELECT VERSION() AS version;
SELECT CURRENT_USER() AS current_user;
SELECT @@character_set_server AS charset_server, @@collation_server AS collation_server;B.2 数据库列表
SELECT SCHEMA_NAME
FROM information_schema.SCHEMATA
ORDER BY SCHEMA_NAME;B.3 表/视图列表(含类型、注释、引擎、行数估计)
SELECT
TABLE_NAME,
TABLE_TYPE,
IFNULL(TABLE_COMMENT,'') AS TABLE_COMMENT,
IFNULL(ENGINE,'') AS ENGINE,
IFNULL(TABLE_ROWS,0) AS ROWS_EST
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = :db
ORDER BY TABLE_NAME;B.4 列信息(字段、类型、默认、nullable、extra、注释)
SELECT
ORDINAL_POSITION,
COLUMN_NAME,
COLUMN_TYPE,
DATA_TYPE,
IS_NULLABLE,
COLUMN_DEFAULT,
EXTRA,
IFNULL(COLUMN_COMMENT,'') AS COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = :db AND TABLE_NAME = :table
ORDER BY ORDINAL_POSITION;B.5 索引信息
SELECT
INDEX_NAME,
NON_UNIQUE,
SEQ_IN_INDEX,
COLUMN_NAME,
INDEX_TYPE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = :db AND TABLE_NAME = :table
ORDER BY INDEX_NAME, SEQ_IN_INDEX;B.6 外键信息(列映射)
SELECT
kcu.CONSTRAINT_NAME,
kcu.COLUMN_NAME,
kcu.REFERENCED_TABLE_NAME,
kcu.REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE kcu
WHERE kcu.TABLE_SCHEMA = :db
AND kcu.TABLE_NAME = :table
AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY kcu.CONSTRAINT_NAME, kcu.ORDINAL_POSITION;B.7 外键约束(更新/删除规则)
SELECT
rc.CONSTRAINT_NAME,
rc.UPDATE_RULE,
rc.DELETE_RULE
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
WHERE rc.CONSTRAINT_SCHEMA = :db
AND rc.TABLE_NAME = :table
ORDER BY rc.CONSTRAINT_NAME;B.8 建表 SQL
SHOW CREATE TABLE `:db`.`:table`;注意:这里
:db/:table需要用反引号安全包裹并做合法性校验(只允许字母数字下划线等),不要用 bind 参数替换对象名。
B.9 视图定义(可选)
SELECT VIEW_DEFINITION
FROM information_schema.VIEWS
WHERE TABLE_SCHEMA = :db AND TABLE_NAME = :table;B.10 存储过程/函数列表(可选)
SELECT ROUTINE_NAME, ROUTINE_TYPE
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = :db
ORDER BY ROUTINE_TYPE, ROUTINE_NAME;B.11 表主键/唯一键(用于“可编辑”判断)
SELECT
tc.CONSTRAINT_NAME,
tc.CONSTRAINT_TYPE,
kcu.COLUMN_NAME,
kcu.ORDINAL_POSITION
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
AND tc.TABLE_NAME = kcu.TABLE_NAME
AND tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
WHERE tc.CONSTRAINT_SCHEMA = :db
AND tc.TABLE_NAME = :table
AND tc.CONSTRAINT_TYPE IN ('PRIMARY KEY','UNIQUE')
ORDER BY tc.CONSTRAINT_TYPE, tc.CONSTRAINT_NAME, kcu.ORDINAL_POSITION;C) 编辑器补全策略(缓存/刷新/性能)
目标:快、准、不拖 UI。补全分三层:关键字/内置函数(静态) + Schema(缓存) + 上下文(当前 SQL)。
C.1 补全内容来源
1)静态词库(前端内置)
- MySQL Keywords(SELECT/INSERT/UPDATE…)
- 常用内置函数(COUNT/SUM/NOW/JSON_EXTRACT…)
- 片段模板(
sel*→SELECT * FROM ${table} LIMIT 200;)
2)Schema 词库(来自后端 meta)
- databases
- tables/views
- columns(按表)
- routines(可选)
3)上下文感知(前端解析)
从光标位置往前解析:
FROM <here>→ 补表JOIN <here>→ 补表SELECT <here>→ 补列/函数WHERE <here>→ 补列/函数
解析别名:
FROM users u→u.时补 users 的列
上下文解析不要求完整 SQL AST,用轻量规则就能很好用。
C.2 缓存模型(建议)
前端内存缓存(工作台级)
type Cache = {
runtimeId: string
db?: string
lastRefreshAt: number
tables: string[]
columnsByTable: Record<string, string[]>
routines: string[]
}后端缓存(Rust,按 runtime_id + db)
meta_list_tables返回后写入缓存meta_get_table_schema获取列后写入缓存- 设置 TTL(例如 10 分钟)+ 手动刷新按钮
C.3 刷新策略(关键:别卡)
触发点
- 连接打开:只拉 databases(快)
- 用户展开某个 db:拉 tables/views(一次)
- 用户第一次引用某张表(或展开表节点):拉 columns(按需)
- 用户点击刷新:清掉缓存,重新拉当前 db 的 tables(列按需再拉)
预热策略(可选,体验更爽)
进入 db 后后台预热:
- 拉 tables
- 再“最多预热前 N 张常用表”的 columns(比如最近打开过的 10 张)
任何预热都走低优先级任务,能取消。
C.4 Completion Provider 规则(落地建议)
1)识别场景
从 model.getValueInRange() 取光标前 200~500 字符,做:
- 是否在字符串/注释内:是 → 不给 schema 补全(只给关键字)
- token:最后一个词、是否包含
.、是否处于FROM/JOIN/UPDATE/INTO后
2)给建议的优先级
alias.→ 列(最高)FROM/JOIN/UPDATE/INTO→ 表(最高)SELECT/WHERE/ON/ORDER BY/GROUP BY→ 列 + 函数- 其他 → 关键字 + 模板片段
3)避免一次性塞太多
- 默认 limit 200~500 条(按 relevance 排序)
- 表/列太多时:按输入前缀过滤(
q)后再展示 - 必要时调用后端:
meta_search_symbols(q, limit)(比全量拉取更快)
C.5 处理“多库同名表/列”
补全展示 label:
- 表:
users (db1) - 列:
id (users)
- 表:
插入文本保持简洁:
usersid
当用户显式输入
db.table时,允许插入db1.users
C.6 权限/性能/失败兜底
后端 meta 查询失败:
- 前端 fallback 到静态关键字补全
- Schema 区域显示“无权限/连接异常”
大库(上万表):
- 禁止全量 columns 预热
- 强制走
meta_search_symbols(按 q 搜索) - SchemaTree 支持“输入过滤”而不是滚动找
C.7 建议你们做的 2 个“明显爽点”
- 智能生成 SQL:在 SchemaTree 右键 “生成 SELECT/INSERT/UPDATE” 并自动带列清单
- 结果列 → 反向补全:查询结果表格里右键列名 “复制字段名/插入到编辑器”