Skip to content

fix(db): 清空表后自动刷新表数据页签(TRUNCATE 与全表 DELETE 的数据级失效通知) - #413

Merged
feigeCode merged 1 commit into
feigeCode:mainfrom
ekovovo:fix/truncate-table-data-refresh
Oct 16, 2026
Merged

feigeCode merged 1 commit into
feigeCode:mainfrom
ekovovo:fix/truncate-table-data-refresh

Conversation

@ekovovo

@ekovovo ekovovo commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

问题背景(Bug)

右键点击表选择「清空表」执行成功后,已打开的表数据页签中旧数据依然显示,用户误以为清空操作失败,只有手动刷新才能看到空表。

根因分析

排查定位到两个相互叠加的缺陷:

缺陷 1(主因):执行成功后无任何刷新通知 handle_truncate_table 执行成功后仅弹出 toast 提示,没有向 UI 层广播任何数据变更事件。表数据页签(DataGrid)不监听 ConnectionDataEvent::SchemaChanged ,其内部的查询结果缓存不会失效,旧数据一直渲染在界面上。

缺陷 2:SQLite/DuckDB 的清空语句绕过了失效管线 SQLite / DuckDB 不支持 TRUNCATE ,右键清空生成的 SQL 是 DELETE FROM

(无 WHERE 子句)。而 DdlInvalidator 的候选预筛只认 TRUNCATE 等 DDL 关键字, DELETE 属于 DML 被直接跳过——这类语句连「结构变更树刷新」都不会触发,彻底绕开了缓存失效管线。

修复方案:数据级 / 结构级失效双通道

核心设计原则: 整表清空只影响数据,不影响表结构 。因此不滥用既有的 SchemaChanged (那会触发昂贵的元数据缓存失效与整树刷新),而是新增一条轻量的数据级通知通道:
SQL 执行成功
→ DdlInvalidator 识别 TRUNCATE / 无 WHERE 的 DELETE FROM
→ SchemaInvalidationScope { table, data_only: true } (数据级 scope)
→ ConnectionDataEvent::TableDataCleared(四元组精确标识)
→ DataGrid 精确匹配后仅重载自己的数据

1. SQL 识别层(ddl_invalidator.rs)

新增 DdlEvent::DeleteAllRows 事件,三层识别策略:

  • 廉价预筛 : is_full_table_delete() 仅对以 DELETE FROM 开头且不含 WHERE 的语句进入解析,混合脚本场景几乎零开销;
  • AST 精确解析 :基于 sqlparser 检查 delete.selection.is_none() 判定无条件删除,并从 FromTable 枚举(0.62 的 WithFromKeyword / WithoutKeyword )提取目标表名;
  • 字符串 fallback :AST 解析失败时退化到字符串匹配。
    数据级事件不做元数据缓存失效(表结构未变),仅传递数据变更语义。

2. 失效计划层(cache_manager.rs)

SchemaInvalidationScope 新增 table: Option 与 data_only: bool 字段。 from_event 将 TruncateTable / DeleteAllRows 映射为 data_only: true 的 scope,其余事件保持结构级语义不变。

3. 事件广播层(manager.rs、sql_result_tab.rs、sql_editor_view.rs)

ConnectionDataEvent 新增 TableDataCleared { connection_id, database, schema, table } 变体。 三条执行链路全覆盖 ,统一按 scope 分流广播(数据级 → TableDataCleared ,结构级 → SchemaChanged ):

  • 非流式执行( execute_with_session_internal )
  • 流式执行( StreamingExecutionRequest::run() / execute_streaming_cancellable )
  • 手动事务提交( sql_editor_view 的 commit / rollback 分支)

4. 消费端(data_grid.rs 及各监听点)

  • DataGrid :表数据页签订阅 GlobalConnectionNotifier ,收到 TableDataCleared 后按连接/数据库/模式/表四元组精确匹配,命中则守卫加载状态后重载第 1 页数据。无关表格零开销(事件不匹配即忽略),无全库级刷新的惊群效应;
  • 树视图 / 首页 / 个人云同步 :显式忽略该数据级事件(树结构不受影响,数据清空不触发云同步),穷尽 match 保证未来新增变体时所有监听点都能被编译器检查到。

设计考量

  • 性能 :预筛函数是纯字符串前缀检查,非 DML 删除语句零解析成本; TableDataCleared 携带表级精确坐标,只有打开目标表的页签才会刷新,不做连接级广播扩散;数据级事件不触发元数据缓存失效,避免不必要的重查询。
  • 可扩展性 : data_only scope 是通用机制,未来行级变更通知(如大批量 INSERT/UPDATE 后刷新)可复用同一通道; SchemaInvalidationScope 返回 Vec 为多语句脚本的多表失效预留了空间。
  • 正确性 :带 WHERE 的条件删除不会被误判; DELETE FROM t ORDER BY/LIMIT 等变体在 AST 层正确处理;Clone 出的 DataGrid 副本由 new() 重新绑定订阅,不产生重复监听。
  • 跨数据库 :PostgreSQL/MySQL 走 TRUNCATE 路径,SQLite/DuckDB 走 DELETE 路径,两条路径统一收敛到同一个数据级事件。

@feigeCode
feigeCode merged commit 553f3ff into feigeCode:main Oct 16, 2026
8 checks passed
@feigeCode feigeCode mentioned this pull request Oct 19, 2026
2 of 4 tasks
ekovovo pushed a commit to ekovovo/navop that referenced this pull request Oct 19, 2026
- main/Cargo.toml、Cargo.lock:0.20.2 -> 0.20.3
- CHANGELOG.md:新增 v0.20.3 双语发布说明

本版自 v0.20.2 以来已合入 main 的变更:
- 终端按行 / 按屏滚动回看历史输出(feigeCode#409)
- Auto 模式 SQL 编辑器粘性会话,跨执行保留会话变量(feigeCode#312)
- 笔记 Markdown 大纲面板与代码字体设置(feigeCode#406)
- Redis:启用 TLS 修复、集群汇总全部 master、空库切换崩溃修复(feigeCode#414、feigeCode#416)
- SFTP:上传可立即取消、同连接 3 条并发车道、目录符号链接处理(feigeCode#417、feigeCode#418)
- 清空表刷新数据页签、结果集保存限定库名、剪贴板 JSON 原文复制(feigeCode#412、feigeCode#413)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants