news 2026/9/14 15:36:48

Bytebase SQL 审核规则扩展实战:将 Schema Walk-Through 校验内置为可配置的 BUILTIN_WALK_THROUGH_CHECK 规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bytebase SQL 审核规则扩展实战:将 Schema Walk-Through 校验内置为可配置的 BUILTIN_WALK_THROUGH_CHECK 规则

Bytebase SQL 审核规则扩展实战:将 Schema Walk-Through 校验内置为可配置的 BUILTIN_WALK_THROUGH_CHECK 规则

【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase

Bytebase 的 SQL 审核(SQL Review)在提交 SQL 前会自动做 Schema 模拟执行(Walk-Through)校验,提前发现引用不存在表、列、索引等问题。本文以仓库内的实现计划文档docs/plans/2026-02-04-builtin-walk-through-check.md为主线,完整还原把这一隐式校验改造为一条"用户可在 UI 中看到、可配置错误级别"的内置审核规则的全过程:从 proto 枚举扩展、内置规则注册、审核调用点改造,到前端 schema 与五种语言 i18n 文案的补齐与构建验证。读完本文,你将掌握 Bytebase SQL Review 内置规则从后端到前端、从 proto 到文案的完整落地链路,以及引擎差异化规则注册的实现方法。

背景:为什么要把 Walk-Through 变成一条"可见"的审核规则

在本次改造之前,Bytebase 的 Schema Walk-Through(模拟执行校验)虽然已经在 SQL 审核流程中生效,但它是一条"隐形"的检查:它不作为一个可配置的审核规则条目存在,用户无法在 SQL 审核规则界面上看到它,也就无法调整它出问题时的提示级别(Error / Warning)。

本次实现计划的 Goal 写得非常明确:

Model the existing WalkThrough schema validation as a builtin SQL review rule so users can see it in the UI and configure its error level.

即:把现有 WalkThrough Schema 校验建模为一条内置 SQL 审核规则,让用户在 UI 中看到它,并能配置它的错误级别。

整体架构(来自原文档)概括为四步:

  1. 在 proto 中新增BUILTIN_WALK_THROUGH_CHECK枚举值;
  2. 在 5 个受支持的引擎上将其注册为内置规则;
  3. SQLReviewCheck的现有 WalkThrough 调用点接线,让配置的级别覆盖 advice 状态;
  4. WalkThrough 函数本身保持不变。

涉及的技术栈为 Protobuf、Go、TypeScript、Vue,且不需要任何组件层面的改动。

Walk-Through 校验的底层原理

在深入实现细节前,先理解被改造对象本身。Walk-Through 的核心实现位于backend/plugin/schema包,通过一个按引擎注册的函数表对外暴露:

walkThroughs = make(map[storepb.Engine]walkThrough) walkThroughsWithContext = make(map[storepb.Engine]walkThroughWithContext)

见 schema.go。对外入口WalkThrough/WalkThroughWithContext根据引擎查找已注册的实现:

func WalkThrough(engine storepb.Engine, d *model.DatabaseMetadata, ast []base.AST) *storepb.Advice { return WalkThroughWithContext(context.Background(), engine, WalkThroughContext{}, d, ast) } func WalkThroughWithContext(ctx context.Context, engine storepb.Engine, wtCtx WalkThroughContext, d *model.DatabaseMetadata, ast []base.AST) *storepb.Advice { if f, ok := walkThroughsWithContext[engine]; ok { return f(ctx, wtCtx, d, ast) } f, ok := walkThroughs[engine] if !ok { return nil } return f(d, ast) }

见 schema.go。也就是说,没有为某引擎注册实现时,Walk-Through 会静默返回nil(不产生 advice)。

WalkThroughContext携带会话级上下文,其中RawSQL是原始 SQL 文本,供 omni 目录(catalog)执行时使用:

type WalkThroughContext struct { SessionUser string // RawSQL is the original SQL text to be executed. RawSQL string }

见 schema.go。

以 MySQL 系列为例,实现位于 mysql/walk_through.go,在init()中注册了 MySQL、MariaDB、OceanBase 三个引擎:

func init() { schema.RegisterWalkThroughWithContext(storepb.Engine_MYSQL, WalkThroughWithContext) schema.RegisterWalkThroughWithContext(storepb.Engine_MARIADB, WalkThroughWithContext) schema.RegisterWalkThroughWithContext(storepb.Engine_OCEANBASE, WalkThroughWithContext) }

其核心流程(文件头注释)是:

  1. 创建 omni MySQL catalog;
  2. catalog.LoadMetadata安装当前数据库 Schema 快照,失败的 object 用占位符顶替;
  3. catalog.Exec(userSQL)在内存中执行用户 DDL;
  4. 将执行错误映射为*storepb.Advice
  5. 把更新后的 catalog 转换回DatabaseMetadata,供后续规则使用。

其中RawSQL == ""时直接返回nil(跳过校验);执行前还会先做一轮预检查(precheckMySQLOmniWalkThrough/precheckMySQLRawWalkThrough),预检查的 advice 优先级高于执行期 advice。模拟执行失败会返回带DDLSimulationFailed错误码的 advice。

这也是为什么该内置规则带有"可配置级别"的诉求——Walk-Through 一旦命中(如引用了不存在的列),默认返回的是Advice_ERROR级别,通过内置规则改造后,用户便可以把该检查降级为 WARNING,或保持为 ERROR。

Task 1:扩展 proto 枚举并重新生成代码

第一步是为规则类型枚举新增值。原始计划要求修改proto/store/store/review_config.proto:161,在BUILTIN_PRIOR_BACKUP_CHECK = 109;之后追加BUILTIN_WALK_THROUGH_CHECK = 110;。当前仓库的 proto 文件 review_config.proto 中,该值已经落地,且后续还接续了STATEMENT_DISALLOW_TRUNCATE = 111BUILTIN_STATEMENT_MAXIMUM_SQL_SIZE = 112

ADVICE_ONLINE_MIGRATION = 108; BUILTIN_PRIOR_BACKUP_CHECK = 109; BUILTIN_WALK_THROUGH_CHECK = 110; STATEMENT_DISALLOW_TRUNCATE = 111; BUILTIN_STATEMENT_MAXIMUM_SQL_SIZE = 112; }

该枚举位于SQLReviewRuleType枚举中。修改后需要重新生成代码:

cd proto && buf generate

生成后可以用如下命令验证生成产物中是否包含新常量:

grep -r "BUILTIN_WALK_THROUGH_CHECK" backend/generated-go/store/ | head -5

当前仓库的生成产物 review_config.pb.go 中已经存在 4 处该常量引用,说明生成步骤已通过验证。

Task 2:重构 GetBuiltinRules 支持按引擎差异化注册

第二步是注册内置规则。改造前的GetBuiltinRules只能无差别地为引擎附加规则,而 walk-through 规则适用的引擎集合(MySQL、MariaDB、TiDB、Postgres、OceanBase)与 prior-backup 规则(MySQL、Postgres、TiDB、MSSQL、Oracle)并不相同,因此需要重构为"按引擎分别追加"的结构。

当前仓库 builtin_rules.go 中,该函数已经按计划落地:先无条件注册BUILTIN_STATEMENT_MAXIMUM_SQL_SIZE(带MaxSheetCheckSize数字载荷),再用两个独立的switch分别按引擎集合注册两条内置规则:

func GetBuiltinRules(engine storepb.Engine) []*storepb.SQLReviewRule { rules := []*storepb.SQLReviewRule{ { Type: storepb.SQLReviewRule_BUILTIN_STATEMENT_MAXIMUM_SQL_SIZE, Level: storepb.SQLReviewRule_WARNING, Engine: engine, Payload: &storepb.SQLReviewRule_NumberPayload{ NumberPayload: &storepb.SQLReviewRule_NumberRulePayload{ Number: common.MaxSheetCheckSize, }, }, }, } switch engine { case storepb.Engine_MYSQL, storepb.Engine_MARIADB, storepb.Engine_POSTGRES, storepb.Engine_TIDB, storepb.Engine_MSSQL, storepb.Engine_ORACLE: rules = append(rules, &storepb.SQLReviewRule{ Type: storepb.SQLReviewRule_BUILTIN_PRIOR_BACKUP_CHECK, Level: storepb.SQLReviewRule_WARNING, Engine: engine, }) default: } switch engine { case storepb.Engine_MYSQL, storepb.Engine_MARIADB, storepb.Engine_TIDB, storepb.Engine_POSTGRES, storepb.Engine_OCEANBASE: rules = append(rules, &storepb.SQLReviewRule{ Type: storepb.SQLReviewRule_BUILTIN_WALK_THROUGH_CHECK, Level: storepb.SQLReviewRule_WARNING, Engine: engine, }) default: } return rules }

注意一个细节:计划文档中的示例代码把新规则默认级别写为ERROR,而仓库实际落地的默认级别是WARNING。这是因为在SQLReviewCheck中,内置规则只在用户配置没有显式覆盖时才被追加(见下文的NoAppendBuiltin逻辑),若默认即为 ERROR 会改变既有用户的审核行为,因此实际实现选择了更保守的 WARNING。这也是阅读实现计划时应以最终代码为准的典型示例

注册完成后可验证编译:

go build ./backend/plugin/advisor/...

Task 3:在 SQLReviewCheck 调用点接线可配置级别

第三步是核心改造:把 Walk-Through 从一段独立的硬编码逻辑,改为"按规则查找并应用配置级别"。

改造前(计划文档中的原始代码)在sql_review.go:122-131附近,Walk-Through 仅在!builtinOnly && FinalMetadata != nil时触发,且用一个引擎switch限定引擎集合,命中的 advice 直接原样返回:

if !builtinOnly && checkContext.FinalMetadata != nil { switch checkContext.DBType { case storepb.Engine_TIDB, storepb.Engine_MYSQL, storepb.Engine_MARIADB, storepb.Engine_POSTGRES, storepb.Engine_OCEANBASE: if advice := schema.WalkThrough(checkContext.DBType, checkContext.FinalMetadata, asts); advice != nil { return []*storepb.Advice{advice}, nil } default: // Other database types don't need walkthrough } }

改造后(计划文档给出的目标代码),SQLReviewCheck当前实现位于 sql_review.go,已演进为基于WalkThroughWithContext的版本:

if checkContext.FinalMetadata != nil { walkThroughContext := schema.WalkThroughContext{ SessionUser: checkContext.SessionUser, RawSQL: statements, } if advice := schema.WalkThroughWithContext(ctx, checkContext.DBType, walkThroughContext, checkContext.FinalMetadata, asts); advice != nil { for _, rule := range ruleList { if rule.Engine == checkContext.DBType && rule.Type == storepb.SQLReviewRule_BUILTIN_WALK_THROUGH_CHECK { if status, err := NewStatusBySQLReviewRuleLevel(rule.Level); err == nil { advice.Status = status } return []*storepb.Advice{advice}, nil } } } }

核心变化与计划完全一致:

  • 去掉了!builtinOnly守卫:只要ruleList中存在该规则就执行(作为内置规则它几乎总是存在);
  • 去掉了引擎switch:规则本身的Engine字段已经决定了它是否出现在该引擎的ruleList中,不再需要二次判断;
  • 按规则查找并覆盖级别:命中 advice 后,用规则配置的Level通过NewStatusBySQLReviewRuleLevel转换为Advice_Status覆盖advice.Status
  • 保留早返回行为:一旦产生 walk-through advice 立即返回,不再继续跑其余审核规则。

级别转换函数定义在 advisor.go:

func NewStatusBySQLReviewRuleLevel(level storepb.SQLReviewRule_Level) (storepb.Advice_Status, error) { switch level { case storepb.SQLReviewRule_ERROR: return storepb.Advice_ERROR, nil case storepb.SQLReviewRule_WARNING: return storepb.Advice_WARNING, nil default: return storepb.Advice_STATUS_UNSPECIFIED, errors.Errorf("unexpected rule level type: %s", level) } }

同时,SQLReviewCheck入口处的内置规则追加逻辑(sql_review.go)保证了"配置覆盖优先":当用户配置里已存在同类型、同引擎的规则时,跳过内置版本,否则才追加GetBuiltinRules(checkContext.DBType)返回的内置规则——这正是BUILTIN_WALK_THROUGH_CHECK的默认级别(WARNING)能被用户显式覆盖为 ERROR 的机制基础:

if !checkContext.NoAppendBuiltin { // Append builtin rules only if the user hasn't overridden them in their config. for _, r := range GetBuiltinRules(checkContext.DBType) { overridden := false for _, userRule := range ruleList { if userRule.Type == r.Type && userRule.Engine == r.Engine { overridden = true break } } if overridden { continue } ruleList = append(ruleList, r) } }

改造完成后依次验证编译与静态检查:

go build ./backend/plugin/advisor/... golangci-lint run --allow-parallel-runners backend/plugin/advisor/...

Task 4:前端规则 Schema 注册

后端注册完成后,前端需要让该规则在 SQL 审核规则界面中可展示、可配置。这一步修改 sql-review-schema.yaml。

计划要求在文件末尾(当时的 2029 行附近,位于engine: ORACLE之后)追加 5 条引擎条目。当前仓库中这些条目已经存在(sql-review-schema.yaml),紧随BUILTIN_PRIOR_BACKUP_CHECK的 6 条条目之后:

- type: BUILTIN_WALK_THROUGH_CHECK category: BUILTIN engine: MYSQL - type: BUILTIN_WALK_THROUGH_CHECK category: BUILTIN engine: MARIADB - type: BUILTIN_WALK_THROUGH_CHECK category: BUILTIN engine: TIDB - type: BUILTIN_WALK_THROUGH_CHECK category: BUILTIN engine: POSTGRES - type: BUILTIN_WALK_THROUGH_CHECK category: BUILTIN engine: OCEANBASE

与内置规则的引擎集合严格一致(MySQL、MariaDB、TiDB、Postgres、OceanBase)。注意此处的category: BUILTIN意味着它是平台预置规则,用户可直接在规则列表中看到并调整级别,但不能修改其参数载荷。

此外,proto 生成的前端类型声明 review_config_service_pb.d.ts 中也已同步包含BUILTIN_WALK_THROUGH_CHECK = 110枚举值。

Task 5:五种语言的 i18n 文案

为了让规则名称与描述在不同语言环境下正确显示,需要在全部 5 个 locale 文件中追加文案。计划要求修改frontend/src/locales/sql-review/下的en-US.jsonzh-CN.jsones-ES.jsonja-JP.jsonvi-VN.json五个文件(当时都在第 48 行附近,紧跟BUILTIN_PRIOR_BACKUP_CHECK条目之后)。

当前仓库中这些条目均已落地,例如 en-US.json:

"BUILTIN_WALK_THROUGH_CHECK": { "description": "Validates SQL statements against the current database schema by simulating their execution. Detects issues like referencing non-existent tables, columns, or indexes.", "title": "Schema walk-through validation" },

计划文档给出的各语言完整文案(可直接对照使用):

en-US(英语):

"BUILTIN_WALK_THROUGH_CHECK": { "title": "Schema walk-through validation", "description": "Validates SQL statements against the current database schema by simulating their execution. Detects issues like referencing non-existent tables, columns, or indexes." },

zh-CN(简体中文):

"BUILTIN_WALK_THROUGH_CHECK": { "title": "Schema 模拟执行检查", "description": "通过模拟执行来验证 SQL 语句与当前数据库 Schema 的一致性。检测引用不存在的表、列或索引等问题。" },

es-ES(西班牙语):

"BUILTIN_WALK_THROUGH_CHECK": { "title": "Validación de recorrido del esquema", "description": "Valida las sentencias SQL contra el esquema de base de datos actual simulando su ejecución. Detecta problemas como hacer referencia a tablas, columnas o índices inexistentes." },

ja-JP(日语):

"BUILTIN_WALK_THROUGH_CHECK": { "title": "スキーマウォークスルー検証", "description": "SQL文の実行をシミュレートして、現在のデータベーススキーマとの整合性を検証します。存在しないテーブル、カラム、インデックスの参照などの問題を検出します。" },

vi-VN(越南语):

"BUILTIN_WALK_THROUGH_CHECK": { "title": "Kiểm tra mô phỏng schema", "description": "Xác thực các câu lệnh SQL với schema cơ sở dữ liệu hiện tại bằng cách mô phỏng việc thực thi. Phát hiện các vấn đề như tham chiếu đến bảng, cột hoặc chỉ mục không tồn tại." },

说明:上表为计划文档原文(含 JSON 的\u转义形式);仓库中实际落地文案存在少量措辞迭代(如西语标题改为 "Validación de recorrido del esquema"),以仓库当前文件为准。

Task 6-7:前端与后端构建验证

完成代码改动后,需要依次通过前后端两轮验证。

前端检查与类型检查:

pnpm --dir frontend check pnpm --dir frontend type-check

其中check会跑仓库自带的 frontend 结构/规范门禁脚本(见frontend/scripts/下的check-react-layering.mjscheck-ui-guideline.mjs等),确保改动符合仓库的 React 分层与 UI 规范;type-check验证 TypeScript 类型,包括sql-review-schema.yaml的条目与review_config_service_pb.d.ts中的枚举值是否一致。

全量后端构建:

go build -ldflags "-w -s" -p=16 -o ./bytebase-build/bytebase ./backend/bin/server/main.go

这一步会在bytebase-build/下产出完整的 bytebase 服务端二进制,验证整个后端(含 proto 生成代码、advisor 注册、sql_review.go 改动)可整体编译。

Task 8:提交信息

所有改动验证通过后,按计划文档要求提交,提交信息为:

feat: make WalkThrough a configurable builtin SQL review rule

实现要点回顾与仓库印证

本次改造虽然只涉及一个枚举值、一个注册函数、一处调用点和若干前端文案,但它完整演示了 Bytebase SQL Review 内置规则的标准扩展路径,可以从以下四层理解整个机制:

  1. 协议层Type枚举(review_config.proto)定义规则类型,Engine字段(engine = 9)声明规则适用引擎,Level字段(level = 2)声明默认级别;经buf generate同步到 Go 与 TypeScript 两端。
  2. 注册层GetBuiltinRules(builtin_rules.go)按引擎返回内置规则集,SQLReviewCheck在用户未覆盖时自动追加(sql_review.go)。
  3. 执行层:Walk-Through 通过schema包的注册表按引擎分发(schema.go),各引擎实现(如 mysql/walk_through.go)在内存 catalog 中模拟执行用户 DDL;SQLReviewCheck命中后按规则级别改写 advice 状态(sql_review.go)。
  4. 展示层sql-review-schema.yaml声明规则与引擎的展示组合,locale JSON 提供多语言标题与描述,共同驱动 SQL 审核规则界面渲染。

需要注意的是,从源码结构与最终落地的代码看,这条规则的默认级别最终定为WARNING(而非计划文档示例代码中的ERROR),说明"方案文档给出方向、实际落地以兼容性为准"是这类内置规则改造的常态。读者若在自己分支上实施类似改造,建议同样把默认级别设置得保守一些,避免对存量用户的审核结果造成意外影响。

【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 15:36:02

TC275 UDS Bootloader开发实战:硬件适配与车规级可靠性设计

1. 这不是一份“教程”,而是一份TC275 UDS Bootloader开发现场实录我第一次在Infineon TC275上跑通UDS Bootloader时,烧了三块PCB,重刷了十七次Flash,最后发现卡在一条没被手册重点标注的寄存器配置上——不是代码逻辑错&#xff…

作者头像 李华
网站建设 2026/9/14 15:35:54

51单片机心率计仿真:Proteus+Keil5闭环调试指南

简介:本资源是一套基于51单片机的心率脉搏计完整嵌入式开发包,面向电子类初学者、单片机课程设计学生及健康监测方向实践者,解决心率信号采集、滤波处理、峰值识别与实时显示等核心问题。压缩包共18个文件,含C语言主程序&#xff…

作者头像 李华
网站建设 2026/9/14 15:34:48

小爱音箱接入 ChatGPT:MiGPT 部署与使用指南

小爱音箱接入 ChatGPT:MiGPT 部署与使用指南 【免费下载链接】mi-gpt 🏠 将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt MiGPT 的作用是把家里的小爱音箱接入 Ch…

作者头像 李华
网站建设 2026/9/14 15:34:42

Spring Boot + Vue宠物管理系统实战:从设计到部署

做宠物管理系统这个项目,最早是给学生准备的一套Spring Boot Vue前后端分离实战案例。市面上这套题目的源码一搜一大把,但真正能一次跑起来的很少,要么数据库表结构缺胳膊少腿,要么前端依赖装不上,要么文档就一句“下…

作者头像 李华
网站建设 2026/9/14 15:33:05

Spring IOC源码学习:从声明式事务入口拆解代理机制

Spring IOC 源码学习,我从声明式事务的入口点开始拆做了几年 Java 后端,Spring 天天在用,但真正下决心啃一次源码,还是因为被线上一个事务失效的问题折磨到头皮发麻。业务方法明明标了Transactional,异常也抛了&#x…

作者头像 李华