DeltaScope:离线优先的 SQL 审计与迁移风险检查工具

为什么需要 SQL 审计?

数据库变更(DDL/DML)是生产事故的高发区。一条缺少 WHERE 的 DELETE、一个没有 LIMIT 的 UPDATE、一次破坏性的 ALTER TABLE,都可能导致不可逆的数据丢失或服务中断。SQL 错误在执行前捕获成本极低,执行后修复代价极高。

DeltaScope 正是为解决这个问题而生的工具。

DeltaScope 是什么?

DeltaScope 是一个离线优先的 SQL 审计与迁移风险检查工具,支持 MySQL、TiDB、PostgreSQL 三种数据库方言。它为 DBA、应用工程师、CI 流水线和 AI Agent 提供了统一的 SQL 变更审查能力。

核心设计理念:所有规则无需数据库连接即可工作,元数据感知规则在没有快照时优雅降级。

审计实战:看看 DeltaScope 能抓出什么问题

MySQL:CREATE TABLE 全方位审计

下面这条 CREATE TABLE 几乎踩遍了所有坑——保留字命名、MyISAM 引擎、latin1 字符集、外键、TEXT/JSON/BIT/TIMESTAMP 禁用类型、复合主键、重复索引……一条语句触发 20+ 条发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `select` (
`select` INT,
id INT,
very_long_column_name_over_sixty_four_characters_for_rule_coverage_0001 VARCHAR(16),
bad_varchar VARCHAR(32),
bad_float DOUBLE,
bad_text TEXT,
bad_json JSON,
bad_bit BIT(1),
bad_ts TIMESTAMP,
bad_char CHAR(4),
bad_charset VARCHAR(16) CHARACTER SET latin1,
bad_collation VARCHAR(16) COLLATE latin1_swedish_ci,
no_default INT NOT NULL,
nullable_col INT DEFAULT 0,
CONSTRAINT badpk PRIMARY KEY (id, bad_varchar),
CONSTRAINT baduk UNIQUE KEY (bad_varchar),
CONSTRAINT badfk FOREIGN KEY (id) REFERENCES parent(id),
CONSTRAINT badcheck CHECK (id > 0),
UNIQUE KEY baduniq (bad_varchar),
KEY badidx (bad_varchar, no_default),
KEY baddup (bad_varchar),
KEY badleft (bad_varchar),
FULLTEXT KEY badfull (bad_text)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
ROW_FORMAT=COMPACT AUTO_INCREMENT=9 COMMENT='comment too long';

DeltaScope 输出(节选关键发现):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Verdict: reject
Statements: 1
Blockers: 9
Warnings: 12
Notices: 0

Statement 1: CREATE TABLE
- [blocker] ddl.table.name.keyword.forbid: table name "select" is a reserved keyword
- [blocker] ddl.table.engine.allowlist: engine MyISAM is not in allowlist [InnoDB]
- [blocker] ddl.table.charset.allowlist: charset latin1 is not in allowlist [utf8mb4]
- [blocker] ddl.table.foreign_key.forbid: foreign keys are forbidden
- [blocker] ddl.table.primary_key.not_null.require: primary key column "bad_varchar" is nullable
- [blocker] ddl.column.name.keyword.forbid: column name "select" is a reserved keyword
- [blocker] ddl.column.name.max_length: column name exceeds 64 characters
- [blocker] ddl.column.charset.allowlist: column charset latin1 is not in allowlist [utf8mb4]
- [blocker] ddl.column.varchar.max_length: VARCHAR(32) exceeds max length limit
- [warning] ddl.column.blob_text.forbid: TEXT type is forbidden
- [warning] ddl.column.json.forbid: JSON type is forbidden
- [warning] ddl.column.bit.forbid: BIT type is forbidden
- [warning] ddl.column.timestamp.forbid: TIMESTAMP type is forbidden
- [warning] ddl.column.float_double.forbid: DOUBLE type is forbidden
- [warning] ddl.column.comment.require: column must have a comment
- [warning] ddl.column.default.require: column "no_default" should define a default value
- [warning] ddl.table.audit_columns.require: missing audit columns (created_at, updated_at)
- [warning] ddl.table.comment.max_length: table comment exceeds max length limit
- [warning] ddl.index.duplicate.forbid: index "baddup" duplicates "badleft" on (bad_varchar)
- [warning] ddl.index.redundant_left_prefix.forbid: index is left-prefix redundant
- [warning] ddl.constraint.primary_key.name.prefix.require: constraint "badpk" missing prefix "pk_"

一条 CREATE TABLE,9 个 blocker + 12 个 warning,覆盖了命名规范、引擎/字符集、列类型限制、主键语义、索引冗余、约束命名、审计字段等 7 大类规则。这就是 DeltaScope 的能力——不只是检查”有没有注释”,而是对 DDL 做全方位治理。

MySQL:ALTER TABLE 破坏性变更

1
deltascope audit --sql "alter table users drop column email"
1
2
3
4
5
6
7
8
Verdict: reject
Statements: 1
Blockers: 1
Warnings: 0
Notices: 0

Statement 1: ALTER TABLE
- [blocker] ddl.alter.drop_column.forbid: dropping a column is a destructive operation

DROP COLUMN 是不可逆操作,直接触发 blocker,判定结果为 reject。

TiDB:CREATE TABLE 主键规范

1
2
3
4
CREATE TABLE bad_pk_probe (
id INT NOT NULL,
PRIMARY KEY (id)
) COMMENT='x';
1
deltascope audit --dialect tidb --file ./bad_pk.sql
1
2
3
4
5
6
7
8
9
10
Verdict: reject
Statements: 1
Blockers: 3
Warnings: 0
Notices: 0

Statement 1: CREATE TABLE
- [blocker] ddl.table.primary_key.bigint.require: primary key column "id" must be BIGINT
- [blocker] ddl.table.primary_key.unsigned.require: primary key column "id" must be UNSIGNED
- [blocker] ddl.table.primary_key.auto_increment.require: primary key column "id" must be AUTO_INCREMENT

主键用 INT 而非 BIGINT UNSIGNED AUTO_INCREMENT,一次抓出三个问题。这在 TiDB 场景下尤其重要——不合规的主键设计会导致分布式场景下的性能和兼容性问题。

TiDB:ALTER TABLE 多条变更合并建议

1
2
ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2);
ALTER TABLE orders ADD COLUMN note VARCHAR(255);
1
deltascope audit --dialect tidb --file ./multi_alter.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
Verdict: review
Statements: 2
Blockers: 0
Warnings: 3
Notices: 0

Statement 1: ALTER TABLE
- [warning] ddl.column.comment.require: column "discount" must have a comment

Statement 2: ALTER TABLE
- [warning] ddl.column.comment.require: column "note" must have a comment

- [warning] ddl.alter.merge.tidb.require: multiple ALTER TABLE on "orders" should be merged into a single statement

TiDB 对同一张表的多次 ALTER 应该合并为一条语句,避免多次 DDL 导致的在线DDL开销。DeltaScope 不仅检查单条语句,还能跨语句给出合并建议。

PostgreSQL:CREATE TABLE 全方位审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE "select" (
id INTEGER,
"select" INTEGER,
very_long_column_name_over_sixty_four_characters_for_rule_coverage_0001 VARCHAR(16),
bad_varchar VARCHAR(32),
bad_float DOUBLE PRECISION,
bad_text TEXT,
bad_json JSON,
bad_bit BIT(1),
bad_ts TIMESTAMP,
bad_char CHAR(4),
no_default INTEGER NOT NULL,
nullable_col INTEGER DEFAULT 0,
CONSTRAINT badpk PRIMARY KEY (id, bad_varchar),
CONSTRAINT baduk UNIQUE (bad_varchar),
CONSTRAINT badcheck CHECK (id > 0)
);
1
deltascope audit --dialect postgresql --file ./bad_pg_table.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Verdict: reject
Statements: 1
Blockers: 4
Warnings: 8
Notices: 0

Statement 1: CREATE TABLE
- [blocker] ddl.table.name.keyword.forbid: table name "select" is a reserved keyword
- [blocker] ddl.column.name.keyword.forbid: column name "select" is a reserved keyword
- [blocker] ddl.column.name.max_length: column name exceeds 64 characters
- [blocker] ddl.table.columns.min_count: table has fewer than 12 columns
- [warning] ddl.column.blob_text.forbid: TEXT type is forbidden
- [warning] ddl.column.comment.require: column must have a comment
- [warning] ddl.column.default.require: column "no_default" should define a default value
- [warning] ddl.column.not_null.require: column "nullable_col" should be NOT NULL
- [warning] ddl.table.audit_columns.require: missing audit columns (created_at, updated_at)
- [warning] ddl.table.comment.require: table must have a comment
- [warning] ddl.constraint.primary_key.name.prefix.require: constraint "badpk" missing prefix "pk_"
- [warning] ddl.constraint.unique_key.name.prefix.require: constraint "baduk" missing prefix "uk_"

注意 PostgreSQL 没有 ENGINE/CHARSET/ROW_FORMAT 概念,DeltaScope 会自动跳过这些 MySQL 专属规则,只检查 PG 适用的规则。

PostgreSQL:ALTER TABLE 方言专属风险

PostgreSQL 的 ALTER TABLE 有一些 MySQL 不存在的特殊风险,DeltaScope 针对性地提供了专属规则:

DROP COLUMN——不会回收磁盘空间:

1
deltascope audit --dialect postgresql --sql "alter table users drop column legacy_name"
1
2
3
4
5
6
7
8
Verdict: reject
Statements: 1
Blockers: 1
Warnings: 0
Notices: 0

Statement 1: ALTER TABLE
- [blocker] ddl.pg.alter.drop_column.advisory: dropping a column in PostgreSQL does not reclaim disk space; consider creating a new table without the column instead

PostgreSQL 的 DROP COLUMN 只是标记列为不可见,不会释放磁盘空间。对于大表,正确的做法是新建表并迁移数据。

ADD COLUMN NOT NULL 无 DEFAULT——全表重写:

1
deltascope audit --dialect postgresql --sql "alter table users add column bio text not null"
1
2
3
4
5
6
7
8
Verdict: review
Statements: 1
Blockers: 0
Warnings: 1
Notices: 0

Statement 1: ALTER TABLE
- [warning] ddl.pg.alter.add_column.non_null_no_default.warn: adding a NOT NULL column without a DEFAULT requires a full table rewrite in PostgreSQL

在 PostgreSQL 11 之前,ADD COLUMN NOT NULL 没有 DEFAULT 会导致全表重写(ACCESS EXCLUSIVE 锁),对大表来说是灾难性操作。加上 DEFAULT 后可以避免:

1
2
-- 安全写法
ALTER TABLE users ADD COLUMN bio TEXT NOT NULL DEFAULT '';

SET DATA TYPE——可能触发重写:

1
deltascope audit --dialect postgresql --sql "alter table users alter column name type jsonb using to_jsonb(name)"
1
2
3
4
5
6
7
8
Verdict: review
Statements: 1
Blockers: 0
Warnings: 1
Notices: 0

Statement 1: ALTER TABLE
- [warning] ddl.pg.alter.set_data_type.rewrite.warn: changing column data type may require a full table rewrite

修改列的数据类型可能触发全表重写,DeltaScope 会标记这类高风险操作。

DML 防护:没有 WHERE 的 DELETE

1
deltascope audit --sql "delete from users"
1
2
3
4
5
6
7
8
Verdict: reject
Statements: 1
Blockers: 1
Warnings: 0
Notices: 0

Statement 1: DELETE
- [blocker] dml.where.require: UPDATE and DELETE statements must include a WHERE clause

加上 WHERE 后通过:

1
deltascope audit --sql "delete from users where id = 1"
1
2
3
4
5
Verdict: pass
Statements: 1
Blockers: 0
Warnings: 0
Notices: 0

多语句迁移文件审计

实际场景中,迁移文件通常包含多条语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
-- migrations/20260328_add_discount.sql
CREATE TABLE products (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'created time',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'updated time',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='product catalog';

ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2);

DELETE FROM audit_log WHERE created_at < '2020-01-01';
1
deltascope audit --file ./migrations/20260328_add_discount.sql --format json

JSON 输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{
"verdict": "review",
"summary": { "statements": 3, "blockers": 0, "warnings": 4, "notices": 0 },
"explanation": {
"summary": "Audit produced 4 finding(s) across 3 statement(s)",
"reasons": [
"column `id` must have a comment",
"column `name` must have a comment",
"column `price` must have a comment",
"column `discount` must have a comment"
]
},
"statements": [
{
"index": 0,
"kind": "ddl",
"raw_sql": "CREATE TABLE products (...)",
"findings": [
{
"rule_id": "ddl.column.comment.require",
"level": "warning",
"message": "column `id` must have a comment",
"suggestion": "Add a COMMENT clause to column `id`",
"location": { "line": 2, "column": 3 }
}
]
},
{
"index": 1,
"kind": "ddl",
"raw_sql": "ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2)",
"findings": [
{
"rule_id": "ddl.column.comment.require",
"level": "warning",
"message": "column `discount` must have a comment",
"suggestion": "Add a COMMENT clause to column `discount`",
"location": { "line": 1, "column": 38 }
}
]
},
{
"index": 2,
"kind": "dml",
"raw_sql": "DELETE FROM audit_log WHERE created_at < '2020-01-01'"
}
]
}

3 条语句,4 个 warning,全部指向注释缺失。第 3 条 DELETE 因为有 WHERE 子句,顺利通过。

DML 影响行数估算

在元数据感知模式下,DeltaScope 还能估算 DML 的影响范围:

1
2
deltascope audit --sql "delete from users where id = 42" \
--host 127.0.0.1 --port 3306 --user root --ask-password --schema app
1
2
3
4
5
6
7
8
9
10
11
{
"impact": {
"estimated_rows": 1,
"estimated_ratio": 0.0001,
"risk_level": "low",
"confidence": "high",
"source": "metadata",
"reason_codes": ["pk_equality"],
"notes": ["refined with table statistics"]
}
}

基于主键等值条件 + 表统计信息,精确估算影响 1 行,风险等级 low。

核心特性

DDL 治理

  • CREATE TABLE 检查:标识符规范、注释要求、主键约束(BIGINT UNSIGNED AUTO_INCREMENT)、审计字段、字符集/排序规则、索引规范、表选项
  • ALTER TABLE 检查:破坏性操作检测(DROP COLUMN、CHANGE COLUMN 等)、兼容性验证、存在性校验、合并建议、PostgreSQL 特有的全表重写风险提示
  • 对象生命周期:CREATE VIEW、DROP TABLE、TRUNCATE TABLE 检查,以及 PostgreSQL 特有的扩展、类型、域、物化视图、序列、Schema、DCL 检查

DML 防护

  • WHERE 子句强制要求
  • LIMIT / ORDER BY 控制
  • 子查询与 JOIN 条件检查
  • 批量 INSERT 模式检测
  • 对象黑名单
  • 受影响行数保守估算(离线模式基于 SQL 形状,元数据模式基于表统计信息,PostgreSQL 可使用 EXPLAIN)

多方言支持

通过 --dialect 标志切换 MySQL、TiDB、PostgreSQL,自动检测方言不匹配(如 PostgreSQL 语法搭配 MySQL 方言)。不同方言有专属规则,如 PostgreSQL 的全表重写检测、TiDB 的主键规范。

严重等级与判定

严重等级 含义
blocker 禁止执行
warning 需要审查
notice 信息提示

判定结果:pass(通过)、review(需审查)、reject(拒绝),基于最高严重等级自动确定。

可配置策略

通过 YAML 文件(deltascope.yaml)启用/禁用规则、覆盖严重等级、设置参数,同一配置适用于所有使用场景:

1
2
3
4
5
6
rules:
dml.where.require:
enabled: true
level: blocker
params:
required: true

快速开始

安装

macOS(Homebrew 推荐):

1
2
brew tap Fanduzi/deltascope
brew install --cask deltascope

通用安装脚本:

1
curl -fsSL https://raw.githubusercontent.com/Fanduzi/DeltaScope/main/install.sh | sh

指定版本:

1
2
curl -fsSL https://raw.githubusercontent.com/Fanduzi/DeltaScope/v0.60.0/install.sh | \
DELTASCOPE_VERSION=v0.60.0 sh

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 审计一条危险的 DML
deltascope audit --sql "delete from users"

# 审计 CREATE TABLE
deltascope audit --sql "create table tbl_users (...)"

# 从文件审计
deltascope audit --file ./migrations/20260328_add_column.sql

# PostgreSQL 方言
deltascope audit --dialect postgresql --sql "create table orders (...)"

# TiDB 方言
deltascope audit --dialect tidb --sql "alter table users drop column email"

# JSON 输出(CI 场景)
deltascope audit --sql "..." --format json --fail-on warning

CI/CD 集成

DeltaScope 原生支持多种 CI 输出格式:

格式 用途
github-actions GitHub Actions PR 注释
sarif GitHub Code Scanning
gitlab-codequality GitLab Code Quality 报告

GitHub Actions 示例

DeltaScope 提供了开箱即用的 Composite Action,可直接在 PR 流程中拦截危险 SQL:

1
2
3
4
- uses: Fanduzi/DeltaScope@main
with:
sql-file: ./migrations.sql
fail-on: warning

SARIF 输出

1
deltascope audit --file ./migrations.sql --format sarif > deltascope.sarif

GitLab Code Quality

1
deltascope audit --dialect postgresql --file ./migrations/add_index.sql --format gitlab-codequality

元数据感知模式

离线模式覆盖所有规则,元数据感知模式通过连接数据库获取 information_schema(MySQL/TiDB)或 pg_catalog(PostgreSQL)来增强检查能力:

  • 表/索引存在性验证
  • 索引重复检测
  • 行大小限制检查
  • DML 影响行数精确估算
1
2
deltascope audit --sql "alter table orders add index idx_status (status)" \
--host 127.0.0.1 --port 3306 --user root --ask-password --schema app

AI Agent 集成

MCP Server

无需安装,直接通过 npx 运行:

1
claude mcp add --scope user deltascope -- npx -y @fanduzi/deltascope-mcp

MCP Server 暴露 audit_sqldescribe_rulelist_rulesget_capabilities 四个工具,AI Agent 可实时审计 SQL 变更。

AI Agent Skill

支持 Claude Code、Codex、Cursor 等 40+ AI Agent:

1
npx skills add deltascope

HTTP 服务

1
deltascope-server -listen 127.0.0.1:8083

提供 /healthz/versionPOST /v1/audit 接口,支持 API Key 认证、速率限制、Prometheus 指标。

Go 库集成

1
2
3
4
5
6
import "github.com/Fanduzi/DeltaScope/pkg/deltascope"

result, err := deltascope.Audit(ctx, deltascope.Request{
SQL: "delete from users",
Dialect: deltascope.DialectMySQL,
})

pkg/deltascope 提供稳定的公共 API,内部包可独立演进。

架构设计

DeltaScope 采用整洁架构 / 六边形架构,遵循严格的依赖规则:

  • 领域层internal/domain/)不依赖基础设施层
  • 一条审计管线,四种接入方式:CLI、HTTP、MCP、Go 库共享 Parse → Extract → Enrich → Evaluate → Report 流水线
  • 离线优先保证:每条规则离线可用,元数据感知规则无快照时静默跳过

适用场景

角色 场景
DBA 审查工单中的 SQL 变更,自动拦截高风险操作
应用工程师 本地开发时检查迁移脚本,提前发现问题
SRE/DevOps CI 流水线中自动审查 SQL 文件,阻断危险变更
AI Agent 通过 MCP 或 Skill 实时审计生成的 SQL

总结

DeltaScope 通过离线优先的设计,让 SQL 审计不再依赖数据库连接,可以在任何环节(本地开发、代码审查、CI 流水线、AI 辅助)轻松集成。从上面的实战例子可以看到,无论是 MySQL 的注释规范、TiDB 的主键约束、还是 PostgreSQL 的全表重写风险,DeltaScope 都能精准识别并给出明确的修复建议。如果你正在寻找一个轻量、可配置、多方言支持的 SQL 变更审查工具,不妨试试 DeltaScope。

项目地址:https://github.com/Fanduzi/DeltaScope
许可证:Apache 2.0

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2026 Fan() All Rights Reserved.

访客数 : | 访问量 :