拓冰建站拓冰建站
首页 / 资讯中心 / 正文

dbx 桌面端 Schema Diff 源码指南:src/lib 领域布局、元数据加载护栏与破坏性 DDL 部署安全

dbx 桌面端 Schema Diff 源码指南src/lib 领域布局、元数据加载护栏与破坏性 DDL 部署安全【免费下载链接】dbx15MB轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. Supports MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server and more.项目地址: https://gitcode.com/t8y2/dbxdbx15MB 级轻量跨平台数据库客户端在前端apps/desktop下维护了一套完整的关系型 Schema 对比Schema Diff与安全部署实现。本文以 apps/desktop/src/lib/README.md 为骨架结合schema/、sql/等源码目录、Rust 核心端与回归测试系统讲解src/lib的领域化组织方式、Schema Diff 元数据加载的护栏设计尤其是视图 DDL-only 加载与 MySQL 5.7 错误 1143 的规避以及 Schema Diff 部署阶段必须保持的安全不变量。读完你可以掌握该仓库的模块划分约定、Schema Diff 从元数据加载到生成部署 SQL 再到服务端二次拦截的完整链路并理解破坏性 DDL 为何需要前端分类 服务端确认双重防线。一、src/lib按产品/运行时领域组织的桌面端前端库src/lib是 dbx 桌面端Tauri Vue/TypeScript前端的核心库目录其组织原则是按产品/运行时领域product/runtime domain划分实现模块必须放进对应领域文件夹而不是在根目录新增散落文件。目录职责backendTauri、HTTP、平台、存储与传输桥接common与 DBX 具体功能无关的通用工具app、tabs、sidebar、connection应用外壳、导航与连接 UI 状态辅助database、metadata、schema、table关系库元数据、能力capabilities、DDL 与表对象辅助sql、sql/semantic、editor、query、history、savedSqlSQL 编辑、执行、诊断、历史与已存 SQL 行为dataGrid结果集/网格渲染、编辑、预览、分页与导出ai、mcpAI 助手与 MCP 配置辅助redis、mongo、elasticsearch、etcd、kv、mq、nacos、zookeeper、webdav非关系型或服务特有辅助diagram、document、export、imports被多个组件共享的功能特有工具从实际目录列表看该约定已被严格贯彻除fieldMappingPresets.ts等少量根级文件外src/lib下共有 40 个领域子目录含dataGrid、mongo、redis、elasticsearch、nacos、mqtt、webdav、globalSearch、startup、updates、driverStore等并且测试目录src/lib/__tests__与领域文件夹一一镜像如__tests__/schema/、__tests__/sql/。README 特别强调移动模块时必须同步更新运行时导入runtime imports与同目录测试避免过期的根级别名stale root-level aliases残留。1.1 关于 SQL 片段模板的约定sql领域有一条明确约束见 apps/desktop/src/lib/sql/sqlSnippetTemplates.ts固定的 SQL 补全语句及其数据库方言变体集中在sql/sqlSnippetTemplates.ts行数限制row-limiting各族的正确性需与 crates/dbx-sql/src/sql_dialect/capabilities.rs 以及权威方言文档交叉核对绝不能用生成的默认体替换用户自定义的内置片段体user-customized built-in body。源码实现印证了这条约定。sqlSnippetTemplates.ts中内置sel、ins、upd、cte、join、case、ct、ex、nex、at、ci等 11 个通用片段并通过BUILTIN_SQL_SNIPPET_RULES注册按数据库类型动态构建的函数buildSelectSnippetBody、buildUpdateSnippetBody、buildAlterTableAddColumnSnippetBody。关键判定在resolveSqlSnippetBodyForDatabaseL171-L175export function resolveSqlSnippetBodyForDatabase(snippet: SqlSnippet, databaseType?: DatabaseType): string { const rule BUILTIN_SQL_SNIPPET_RULES.get(snippet.id); if (!rule || snippet.body ! rule.defaultBody) return snippet.body; return rule.buildBody(databaseType); }即只有当片段体仍是出厂默认值时才按当前数据库方言重写一旦用户改动过就原样返回用户内容——这与 README绝不覆盖用户自定义体的约定完全一致。行数限制的方言差异由 sqlDialectSelectLimit.ts 承载默认SELECT *片段行数DEFAULT_SELECT_ROW_LIMIT 100快捷操作select first N默认DEFAULT_SQL_SHORTCUT_SELECT_LIMIT 10。SelectLimitStyle覆盖limit | top | first | fetch-first | rows | rownum六种风格例如 Oracle/OceanBase-Oracle/达梦/Oscar 用WHERE ROWNUM NSQL Server/Access 用SELECT TOP NDB2 用FETCH FIRST N ROWS ONLYFirebird 用ROWS N未知 JDBC 方言则退回最通用的LIMIT。Rust 侧 capabilities.rs 的pagination_strategy与之一致Oracle 在TablePreview/BoundedRead上下文走Rownum注释说明 12c 引入FETCH FIRST前需兼容 11g 基线DB2 走Db2FetchFirstSQL Server 走SqlServerTop其余默认LimitOffset。二、Schema Diff 元数据加载护栏视图只读 DDL绝不探测列元数据src/lib/README.md把 Schema 对比Schema Diff的第一条硬性规则定义为元数据加载护栏Metadata GuardrailsSchema 对比必须在加载元数据之前过滤掉被禁用的对象类型。表table按对比选项加载列与关系型元数据视图view只加载 DDL。这条护栏的实现落在两个文件schema/schemaDiffTableFilter.ts —— 集中维护视图识别与对象/名称过滤schema/schemaDiffMetadataLoad.ts —— 保存 DDL-only 加载计划。2.1 视图识别与对象过滤schemaDiffTableFilter.tsisSchemaDiffView将table_type统一为VIEW含MATERIALIZED VIEW后判断L45-L47export function isSchemaDiffView(table: PickTableInfo, table_type): boolean { return table.table_type.toUpperCase().replace(/\s/g, _).includes(VIEW); }filterSchemaDiffTables的过滤顺序是先按对象类型选项tables/views过滤再叠加 include/exclude 正则tableIncludePattern/tableExcludePattern且支持显式勾选selectedTables。当 include 与 exclude 同时命中时以tableFilterPriorityinclude裁决大小写不敏感匹配通过ignoreTableNameCase控制。显式勾选路径还会借助reconcileSchemaDiffTableMappings按源→目标映射选定目标表避免把未勾选的目标表误判为待删除对象测试用例does not include unselected target tables that would otherwise look like drops覆盖了这一点。2.2 视图 DDL-only 加载计划schemaDiffMetadataLoad.tsschemaDiffMetadataLoadPlanL38-L56是护栏的核心export function schemaDiffMetadataLoadPlan(isView: boolean, options): SchemaDiffMetadataLoadPlan { if (isView) { return { columns: false, indexes: false, foreignKeys: false, triggers: false, ddl: shouldFetchSchemaDiffDdl(true, options) }; } return { columns: options.tables, indexes: options.tables (options.indexes || options.primaryKeys || options.uniqueKeys), foreignKeys: options.tables options.foreignKeys, triggers: options.tables options.triggers, ddl: shouldFetchSchemaDiffDdl(false, options), }; }对视图列、索引、外键、触发器四类元数据 API 一律不调用只取 DDL且仅在视图对比开启时取。对表按tables开关控制列加载索引/外键/触发器分别受indexes||primaryKeys||uniqueKeys、foreignKeys、triggers门控。为什么要做这个限制README 给出了一个非常具体的真实案例MySQL 5.7 在校验SQL SECURITY DEFINER视图时若定义者definer账号已不存在列发现column discovery阶段可能直接报错 1143尽管此时SHOW CREATE VIEW仍能成功。也就是说探测视图的列元数据可能因权限/账号失效而整体失败而只读 DDL 是唯一稳定可用的路径。因此视图识别被集中在schemaDiffTableFilter.tsDDL-only 计划保存在schemaDiffMetadataLoad.ts二者共同保证绝不为了视图去调 column/index/fk/trigger 元数据 API。loadSchemaDetails在运行时执行该计划先由isViewOrMaterializedView判定对象类型VIEW/MATERIALIZED_VIEW再按加载计划并发拉取并通过onProgress逐表上报{current, total, objectName}进度。表级 DDL、列、索引、外键、触发器五路请求共享一个并发限流器。2.3 自适应并发控制Adaptive Metadata ConcurrencyschemaDiffMetadataConcurrencyL15-L24对不同数据库采用不同并发度数据库表数量并发度MySQL / MariaDB≤ 30 张表4MySQL / MariaDB 30 张表2其他默认任意6MySQL 系在超大 Schema 下主动降并发2避免大量元数据请求压垮服务器。并发原语由createConcurrencyLimiter信号量式 acquire/release与mapWithConcurrency保序、首个错误即停提供mapWithConcurrency会保证结果数组按输入顺序排列出错时抛出首个异常并停止调度新任务。测试 apps/desktop/src/lib/tests/schema/schemaDiffMetadataLoad.spec.ts 逐条验证了上述行为MySQL 30 张表并发 4、31 张表并发 2、Postgres 恒为 6视图在views:true时只取 DDL、views:false时 DDL 也不取表在关闭 indexes 但开启 primaryKeys/uniqueKeys 时仍加载索引mapWithConcurrency最大并发 ≤ 2 且输出顺序保持worker 抛错后仅执行到出错项即终止。三、Schema Diff 部署安全从分组不变量到服务端二次拦截README 的第三节定义了 Schema Diff 审查与执行必须保持的四条不变量invariants。这些不变量在前端schemaDiff.ts、Rust 核心dbx-core与 Tauri 命令层均有对应实现构成前端 UI 分类 服务端强制确认的双保险。3.1 不变量一审查分组保留原始顶层对象层级Review groups preserve the original top-level object hierarchy. A modified table with dropped or rebuilt indexes, columns, foreign keys, or triggers appears once in the delete group at table level, while final SQL review lists the exact destructive operations.即表存在于两侧与表内子对象有增删是两回事。groupDiffObjectsschemaDiff.ts L1265-L1360采用 DBeaver 结构对比的 presence-based 语义只有单侧存在的对象进入 create/delete 组双侧都存在的表永远归类为modify其子对象列/索引/外键/触发器的增删改挂在表节点下钻展示绝不允许因为某些列或索引不同就把整表重新分桶到 delete/create。同时被修改的索引、外键在部署时先删后建所以前端把index.type modified与foreignKey.type modified的 UI 操作类型降级为delete见convertToSchemaDiffObjectsL648-L673让用户能明确看到删除风险。测试surfaces a modified index as delete risk because deployment drops it first、counts each selectable destructive child under its table context以及 issue 7225 回归用例都锁定了这一语义。3.2 不变量二表与其嵌套差异是同一个选择单元A table and its nested differences are one selection unit because the backend returns one aggregatedsyncSqlblock per table. Clearing a nested object must clear the table so hidden sibling DDL cannot execute.后端按表返回一条聚合的syncSql因此前端必须把表 其嵌套子差异视为原子选择单元。SchemaDiffObject携带parentId/children组成树setSchemaDiffObjectSelectedL1111-L1129在取消子对象时同步联动父表反之亦然——清除任一嵌套对象都必须连带清除整表否则未勾选的兄弟 DDL 仍可能随聚合syncSql一起执行。更精细的依赖联动由setSchemaDiffObjectSelectedWithDependenciesL1132-L1225实现取消added列时级联取消AFTER 该列的新列、引用该列的新索引/外键删除某列时自动勾选引用它的removed/modified索引与外键保证 DROP 顺序可执行勾选新增索引/外键时自动勾选其引用的新增列。测试index selection keeps added-column and AFTER dependencies visible and selected与clearing an index preserves independently selected column changes分别验证了依赖联动与清索引不影响独立勾选的列改动。选择结果最终由selectSchemaDiffInput投影回结构化 diff剔除未选中子对象、置空syncSql保证发送给后端的始终是用户所见即所得的精确指令。3.3 不变量三最终部署 SQL 必须二次扫描破坏性语句Final deploy SQL is scanned again after user edits. AnyDROP,TRUNCATE, orALTER ... DROPoperation must be listed in the final confirmation.用户可能在审查界面手工编辑最终 SQL因此部署前必须对最终 SQL 再次扫描。前端扫描器是detectDestructiveSchemaDiffStatementsschemaDiff.ts L549-L576它先调用splitSqlStatementRanges按数据库类型切分语句再用stripSqlCommentsAndStringLiterals剥掉注释与字符串字面量然后逐条识别顶层DROP含TEMPORARY、MATERIALIZED VIEW、FOREIGN TABLE、EVENT TRIGGER、USER MAPPING、OWNED BY等TRUNCATEALTER ... DROP COLUMN/CONSTRAINT/INDEX/KEY/PRIMARY KEY/FOREIGN KEY/PARTITION/DEFAULT。剥注释与字面量是关键细节——测试detects destructive schema diff statements without comment or string false positives证明-- DROP TABLE audit_log、SELECT DROP INDEX idx_fake、ALTER TABLE DROP INDEX audit ADD COLUMN note text都不会被误判。UI 侧由schemaDiffReviewAlert(destructiveStatementCount, compatibilityWarningCount)决定是否展示destructive优先或compatibility警示横幅。3.4 不变量四核心部署端点拒绝未确认的破坏性 SQLThe core deploy endpoint rejects destructive Schema Diff SQL unless the request carries explicit destructive confirmation. UI classification is never the only safety boundary.这条是安全架构的基石UI 的分类永远不是唯一安全边界。Rust 核心端在 crates/dbx-core/src/query/mod.rs 的execute_schema_diff_deployL4233 起中强制执行按数据库类型切分语句openGauss 还会探测 A-mode 兼容性以保留CREATE PACKAGE块用is_destructive_schema_diff_statementL4165-L4176做服务端判定——同样先剥注释与字面量再按 token 判断DROP/TRUNCATE或ALTER ... DROP若destructive_statement_count 0 !destructive_confirmed直接返回RolledBack状态与blocked: destructive_confirmation_required一条语句都不执行executed_count: 0。match tokens.first().copied() { Some(DROP | TRUNCATE) true, Some(ALTER) tokens.iter().skip(1).any(|token| *token DROP), _ false, }destructive_confirmed参数经 Tauri 命令execute_script_with_2pcsrc-tauri/src/commands/query.rs L424-L443传入默认unwrap_or(false)——即未显式传 true 一律视为未确认。Rust 侧还自带单测mod.rs L7062 起与 Tauri 层集成测试execute_script_with_2pc_blocks_unconfirmed_destructive_sql断言未确认的破坏性 SQL 返回destructive_confirmation_required、确认后才放行执行。部署本身以单连接单事务方式执行execute_statements_in_transactionPostgres/SQLite/SQL Server/ClickHouse 等具备事务路径的池可整体回滚MySQL/Oracle 的 DDL 隐式提交无法保证原子性时失败返回mixed并附executed_count提示用户部分效果可能已持久化见pool_kind_has_transactional_pathL4191-L4222。3.5 部署目标限定与回滚部署目标 Schema 的确定也有专门规则schemaDiffDeployTargetSchemaschemaDiff.ts L353-L363——若未显式指定 schema则对 MySQL 系mysql/doris/starrocks/goldendb/sundb/databend/gbase回退使用目标数据库名作为限定符SQLite 等则返回 undefined测试uses mysql target database as schema diff deploy qualifier验证。此外enableRollback选项可生成rollbackSyncSql与rollbackCompletenesscomplete/incomplete对无法完整回滚的对象如丢失的 sequence 等通过missingRollbackObjects明确列出。四、Schema Diff 的对比选项与执行流程源码补充schemaDiffOptions.ts定义了对比选项树。目前以 PostgreSQL 系为最完整实现postgres/opengauss共用POSTGRES_SCHEMA_DIFF_OPTIONSmysql/sqlserver在注释中标记为后续扩展未配置时回退到 Postgres 选项集表级tables默认勾选子项primaryKeys、foreignKeys、uniqueKeys、checks、exclusions均默认勾选对象级views、functions、indexes、sequences、triggers、rules、owners行为级cascadeDelete、sequenceLastValues、compareColumnOrder、ignoreTableNameCase、ignoreColumnNameCase、detectRenames、detectTableRenames、enableRollback多数默认关闭。执行进度由schemaDiffProgress.ts定义七个阶段loading-table-lists → loading-source-details → loading-target-details → (loading-extra-objects) → comparing → generating → complete。额外的extra objects阶段sequences/rules/owners 及 PG 函数快路径仅对 Postgres 系且在对应选项开启时进入例程routines对比则通过schemaDiffRoutineObjectTypesIntersection判断源/目标方言是否存在交集的例程类型如同为 MySQL 时才走listFunctions。表映射辅助schemaDiffTableMapping.ts支持显式手工映射manual、同名自动映射automatic与未匹配unmatched并保证显式映射优先、目标表只能被消费一次、大小写不敏感下仅当候选唯一时自动匹配、冲突目标被拒绝conflictSource提示、交换源/目标swap时防御性丢弃畸形重复映射。五、回归测试覆盖三处测试仓库清单README 明确列出了 Schema Diff 两组护栏的回归测试落点均已在仓库中核实元数据护栏回归apps/desktop/src/lib/tests/schema/schemaDiffTableFilter.spec.ts视图识别、对象类型过滤、正则叠加、显式选择与 apps/desktop/src/lib/tests/schema/schemaDiffMetadataLoad.spec.ts并发自适应、DDL-only 计划、进度上报、错误传播、extra-object 门控、进度阶段跳转部署安全回归packages/app-tests/schemaDiff.test.ts16 个用例覆盖分组语义、删除风险表面化、选择投影、依赖联动、破坏性语句检测与警示优先级、crates/dbx-core/src/query/mod.rsRust 侧破坏性判定单测与 src-tauri/src/commands/query.rsTauri 命令层 2PC 集成测试。六、小结Schema Diff 安全部署的完整链路从源码可以梳理出 dbx Schema Diff 从对比到部署的完整调用链过滤schemaDiffTableFilter.ts先按对象类型与名称正则过滤出源/目标表视图识别集中在单一入口加载schemaDiffMetadataLoad.ts按schemaDiffMetadataLoadPlan执行——表加载列/索引/外键/触发器/DDL视图仅取 DDL并以自适应并发控制保护 MySQL 大 Schema对比schemaDiffOptions.ts提供对比选项schemaDiffProgress.ts驱动七阶段进度Postgres 系额外加载 sequences/rules/owners审查schemaDiff.ts将差异转换为树形SchemaDiffObject按 presence-based 语义分组modify/create/delete/none子对象选择与父表原子联动最终 SQL 由detectDestructiveSchemaDiffStatements二次扫描执行Tauri 命令execute_script_with_2pc调用 Rust 端execute_schema_diff_deploy服务端再次独立判定破坏性语句未显式确认destructive_confirmedfalse时整批拒绝确认后以单连接事务执行并返回结构化状态committed/rolled_back/mixed。这条链路的设计要点可以概括为三句话视图永远走 DDL-only 路径以规避 MySQL definer 失效导致的 1143 错误前端分组必须保持存在性优先语义且子对象与整表共享同一个选择单元UI 分类只是提示真正的破坏性 DDL 拦截发生在 Rust 核心端且以显式布尔确认为唯一放行条件。相关源码路径速查apps/desktop/src/lib/README.md本文骨架文档apps/desktop/src/lib/schema/schemaDiffTableFilter.ts视图识别与表过滤apps/desktop/src/lib/schema/schemaDiffMetadataLoad.tsDDL-only 加载计划与并发控制apps/desktop/src/lib/schema/schemaDiff.ts对象分组、选择投影、破坏性语句检测apps/desktop/src/lib/schema/schemaDiffOptions.ts对比选项树apps/desktop/src/lib/schema/schemaDiffTableMapping.ts源/目标表映射apps/desktop/src/lib/sql/sqlSnippetTemplates.ts 与 apps/desktop/src/lib/sql/sqlDialectSelectLimit.ts方言化 SQL 片段crates/dbx-sql/src/sql_dialect/capabilities.rs分页/行数限制方言能力crates/dbx-core/src/query/mod.rs服务端破坏性判定与单事务部署src-tauri/src/commands/query.rsTauri 命令入口packages/app-tests/schemaDiff.test.ts前端回归apps/desktop/src/lib/tests/schema/模块级回归【免费下载链接】dbx15MB轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. Supports MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server and more.项目地址: https://gitcode.com/t8y2/dbx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门