sql-splitter-x-4

内容来源:clawhub · 原始地址 · 查看安装指南

原始内容


name: sql-splitter

description: 拆分 SQL 文件为独立文件(存储过程、函数、视图、触发器、表结构、索引、约束),自动分析依赖并生成合并脚本


SQL 文件拆分工具 v3.6.2

将包含多个 SQL 对象的单一文件或目录拆分为独立的 .sql 文件,

并自动分析对象间依赖关系,生成按依赖排序的合并脚本。

v3.4.5 修复 — dbo两段式替换 + DATETIME2排序 + 方括号替换架构优化 + SELECT INTO转换 + DDL分号

  • v3.5.4 新增: 过程体内所有语句(DML+变量赋值)补分号 — 新增_ensure_statement_semicolons()方法(Step 8.6),处理DML和变量赋值的分号补全,同时正确处理跨行INSERT INTO

  • v3.5.3 行为变更: 非类型名方括号→加双引号[aa]"aa"(v3.5.3起加双引号; v3.5.2及以前去[]不加双引号→aa),类型名[int]int仍只去[]不加双引号。修改位置:_convert_bracket_identifiers()新增_replace_bracket回调函数,用TYPE_MAPPINGS键集合区分类型名和普通标识符

  • dbo替换规则(v3.5.6最终版) — 三段式和两段式区别处理:

    • 三段式(已有其他schema名,如HRBI_Stage.[dbo].[xxx]): dbo是SQL Server默认schema,直接删除

    • 两段式(只有dbo,如[dbo].[xxx]dbo.xxx): dbo替换为schema_prefix

    • 关键区分:输入中若有其他schema名存在,dbo是冗余层→删除;若只有dbo,dbo是唯一schema标识→替换为prefix

    • 正则替换顺序(不可颠倒): 1)双点号(\\w+)\\.\\.(\\w+)\\1.\\2 2)三段式引号"\\w+"\\.dbo\\.\"(\\w+)\""$1" 3)三段式裸名(\\w+)\\.dbo\\.(\\w+)"$1\".\"$2\" 4)三段式混合"\\w+\"\\.\\w+\\.(\\w+)→保留 5)两段式引号"dbo\"\\.\"(\\w+)\"prefix.\"$1\" 6)两段式裸名dbo\\.(\\w+)prefix.$1

    • 实现位置:_replace_dbo_prefix()

    • ⚠️ 改完dbo规则后必须跑全量312过程验证

    • 详见 dbo演进史

  • DATETIME2→TIMESTAMP2而非TIMESTAMP(BUG) — TYPE_MAPPINGS的key构建正则alternation时无排序,DATETIME排在DATETIME2前面抢先匹配,DATETIME2被拆成DATETIME+2变成TIMESTAMP2。修复:sorted(keys, key=len, reverse=True)按长度降序排列,DATETIME2(9字符)排在DATETIME(8字符)前面。影响范围:全局_TYPE_NAMES_PATTERN_FULL_TYPE_NAMES_PATTERN、以及_post_convert_table_types_post_convert_generic_types中的两个_bare_type_pattern

  • 方括号替换在token还原后执行导致注释内容被误改(架构BUG)_post_convert_table_types/_post_convert_generic_types中的\\[([^\\]]+)\\]在Step 6.7(token还原后)执行,注释已还原为原始文本,注释中含方括号(如---一次性解决率 是[处理人响应次数)会被误匹配截断。修复:把方括号替换从Step 6.7移到Step 4_convert_bracket_identifiers()中执行——此时注释和字符串已被tokenize保护,方括号只出现在真实SQL代码中,不会被注释内容干扰

  • SELECT INTO #临时表→CTAS创建GTT(新规则) — 达梦不支持SELECT ... INTO #新表 FROM ...建表语法。转换规则:临时表(INTO #xxx/INTO tmp_xxx)→CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT ... FROM ...(CTAS方式);普通表暂不自动转换(与达梦变量赋值语法SELECT expr INTO var FROM形式相同,无法区分)。修改位置:_convert_temp_tables()新增SELECT INTO处理逻辑

  • 过程体内DDL语句结尾加分号(新规则) — 达梦存储过程体内每条DDL(CREATE/ALTER/DROP TABLE/INDEX等)必须以;结尾。新增_ensure_ddl_semicolons()方法(Step 8.5),用状态机扫描过程体:单行DDL如DROP TABLE xxx直接补;,跨行DDL如CREATE GLOBAL TEMPORARY TABLE "tmp_xxx"\n(id INT)\n)行补;,CTAS跨行子查询在遇到下一个语句开头时给上一行补;,同时处理ON PRIMARY文件组语法去掉。312个存储过程中DDL块缺分号数从447降为0

  • 过程体内所有语句(DML+变量赋值)结尾加分号(v3.5.4起) — 新增_ensure_statement_semicolons()方法(Step 8.6)。达梦存储过程体内DML(INSERT/DELETE/UPDATE/SELECT)和变量赋值(:=)也必须以分号结尾。关键:跨行INSERT INTO(table_name后无括号,列定义在下一行)不加分号;单行DELETE FROM...WHERE...补分号;v_xxx := expr补分号;跨行赋值((/+,/结尾)不补分号。312个存储过程全部验证通过。

  • 过程体内DML语句和变量赋值结尾加分号(v3.5.6新增) — 达梦存储过程体内每条DML(INSERT/DELETE/UPDATE/SELECT)和变量赋值(v_xxx := expr)必须以;结尾。新增_ensure_statement_semicolons()方法(Step 8.6),逐行扫描过程体:单行DML如delete FROM xxx WHERE y=1直接补;,跨行INSERT INTO(table_name\n(columns)\nSELECT ...)不加分号,列定义结束后自动补;;变量赋值v_xxx := expr直接补;,跨行赋值(v_xxx := ('v_xxx := expr +)不加分号。312个存储过程中DML/变量赋值缺分号数从数百降为0

  • schema_prefix传参链路断裂split_sql_v21.py_convert_split_output()函数签名有schema_prefix参数,但调用处(第886行)没传,convert调用(第977行)也没传,导致dbo替换永远不生效。修复:调用链全程传递schema_prefix

  • 新增CLI参数--schema-prefix--dm--schema-prefix手动指定dbo替换前缀;--dm--convert-to dm快捷方式。默认从输入文件名自动提取schema_prefix

  • 6条用户规则验证通过:1) []替换规则:[aa]"aa"(去[]加双引号)、[hrbi].[xxx]"hrbi"."xxx"(schema/表名加双引号)、[int]int/[nvarchar]nvarchar(类型名去[]不加双引号,后续做类型映射) 2) dbo替换规则:三段式[HRBI].[dbo].[xxx]"HRBI"."xxx"(删dbo)/两段式[dbo].[xxx]"hrbi_stage"."xxx"(替换为prefix)/裸名dbo.xxxhrbi_stage.xxx(替换为prefix) 3) VARCHAR(n)加CHAR定义 4) DATETIME/DATETIME2→TIMESTAMP 5) NCHAR→CHAR 6) SELECT INTO #临时表→CTAS创建GTT

  • 7条用户规则验证通过:1) []替换规则:[aa]"aa"(去[]加双引号)、[hrbi].[xxx]"hrbi"."xxx"(schema/表名加双引号)、[int]int/[nvarchar]nvarchar(类型名去[]不加双引号,后续做类型映射) 2) dbo替换规则:三段式[HRBI].[dbo].[xxx]"HRBI"."xxx"(删dbo)/两段式[dbo].[xxx]"hrbi_stage"."xxx"(替换为prefix)/裸名dbo.xxxhrbi_stage.xxx(替换为prefix) 3) VARCHAR(n)加CHAR定义 4) DATETIME/DATETIME2→TIMESTAMP 5) NCHAR→CHAR 6) SELECT INTO #临时表→CTAS创建GTT 7) 过程体内DDL语句结尾加分号

  • 312个存储过程转换全部成功

当多个模式有公共前缀时(如DATETIMEDATETIME2),正则alternation|从左到右匹配,公共前缀排在前面会抢先匹配,留下后缀字符。必须按长度降序排列alternation中的模式。同理适用于SMALLDATETIME(13字符)>DATETIMEOFFSET(15字符)>DATETIME2(9字符)>DATETIME(8字符)等所有有前缀关系的类型名。这不仅是dm_converter的问题——任何用|连接多个可变长度keyword的正则都需注意此陷阱。

⚠️ 方括号替换必须在token保护下执行

_convert_bracket_identifiers()(Step 4,token化后/还原前)是方括号替换的唯一正确位置。此时注释和字符串已被替换为__TOKEN_N__占位符,方括号只出现在真实SQL代码中。如果在token还原后(Step 6.7)做方括号替换,注释中的[]也会被误处理——即使[^\]\n]+限制不跨行,注释内不含换行时仍会跨标识符贪婪匹配。

⚠️ schema_prefix必须贯穿调用链

split_sql_file()_convert_split_output()convert_sqlserver_to_dm_with_result()DMConverter.convert()_replace_dbo_prefix()。任何一环漏传schema_prefix,dbo替换就不生效。新增schema_prefix参数到split_sql_file()签名,CLI默认从输入文件名提取。

v3.4.0 新功能 — TRUNCATE→DELETE FROM + TABLE/VIEW结尾加分号 + 所有方言拆分加分号

  • TRUNCATE TABLE → DELETE FROM — 达梦不支持TRUNCATE在存储过程内,自动将 TRUNCATE TABLE xxx 转换为 DELETE FROM xxx

  • TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE结尾加分号 — 拆分后的表、视图等DDL对象结尾自动加 ;,确保达梦可直接执行(新增 _add_ending_semicolon 方法)

  • 所有方言拆分后均加分号 — 之前Oracle/DM方言拆分后不加分号(用/代替),现在统一加分号(split_sql_v21.py已去掉Oracle/DM特殊逻辑)

  • 53个单元测试全部通过(新增9个:4个TRUNCATE转换+5个结尾分号)

  • ⚠️ TRUNCATE→DELETE语义差异:TRUNCATE是DDL(不可回滚、重置自增),DELETE是DML(可回滚、不重置自增)。自动转换后行为不完全等价,但这是达梦过程体内的唯一可行方案。如果需要重置自增列,需在DELETE后手动调用序列重置。

v3.3.0 新功能 — PROCEDURE双重引号修复 + IDENTITY位置修正 + 临时表正则修复 + token碰撞修复

  • 存储过程PROCEDURE双重引号bug修复 — 之前三个正则顺序执行,第一个替换PROCEDURE sp_test(...) 后输出 "sp_test",第三个又匹配到已替换的结果再包引号变成 ""sp_test"" 。修复:合并为一个正则+分支回调,确保每个存储过程声明只被匹配和替换一次

  • IDENTITY自增列位置修正 — 之前 IDENTITY("id",1,1) 被插在表定义的 ) 前面,达梦语法要求紧跟 ) 后面。修复:优先匹配独占一行的 ) ,也兼容单行写法 (id INT IDENTITY(1,1) NOT NULL)

  • #临时表正则修复 — 之前 [^]]* 匹配到 NVARCHAR(100)) 就截断,导致临时表列定义不完整。修复:改用贪婪 (.+) + re.DOTALL 匹配到最后一个 ) 。同时修复 ## 全局临时表先被替换为 #tmp_ 的bug

  • token_map碰撞修复 — Step3重新tokenize时counter从0开始,"v_users" 新占位符覆盖了 'N/A' 原占位符,导致字符串被替换为标识符。修复:新增 start_counter 参数,Step3从原最大key+1开始编号

  • 44个单元测试全部通过

v3.2.x 功能 — PROCEDURE用AS + VARCHAR(n CHAR)对PROC生效 + CAST中nvarchar映射

  • 存储过程PROCEDURE用AS而非IS(v3.2.3) — 达梦存储过程声明用AS,函数用IS,之前PROCEDURE错误用了Oracle风格的IS关键区分:PROCEDURE→AS,FUNCTION→IS

  • 存储过程VARCHAR(n)加CHAR语义(v3.2.2) — DECLARE变量和参数中的VARCHAR(n)VARCHAR(n CHAR),与TABLE转换一致

  • CAST中nvarchar→VARCHAR(n CHAR)(v3.2.2)cast(x as nvarchar(50))CAST(x AS VARCHAR(50 CHAR)),之前nvarchar在CAST中未被映射

  • _post_convert_generic_types增强(v3.2.2) — 新增_bare_type_pattern映射裸类型名(如CAST中nvarchar),之前只映射方括号包裹的类型

  • SET NOCOUNT ON/OFF直接删除(v3.2.1) — 达梦不需要,之前注释保留,现在直接整行删除

  • 462个真实SQL对象端到端测试通过(HRBI_Stage.sql, 7万行)

旧版功能(v3.0/v2.4.x)

  • 存储过程/函数方括号替换(v3.0)_post_convert_generic_types方法,所有对象类型统一做方括号→双引号+dbo替换

  • 双点号..替换(v3.0) — SQL Server的database..object(省略dbo)→达梦database.object

  • 数据类型正则bug修复(v3.0) — 捕获组改为非捕获组(?:...)修复双重映射;suffix [^]][^)]修复贪婪匹配

  • UTF-16自动转换 — SQL Server导出文件常为UTF-16编码,需先用Python转UTF-8再拆分

v2.4.5 功能 — 方括号转双引号 + dbo前缀智能处理 + 精确拆分

  • 方括号→双引号(v3.5.3起) - [schema].[table]"schema"."table"

  • 普通标识符/列名 [aa]"aa" (v3.5.3起加双引号; v3.5.2及以前去[]不加双引号→aa)

  • SQL类型名 [nvarchar]nvarchar(去掉方括号+类型映射,不加双引号)

  • 支持30+种SQL Server类型名识别

  • dbo替换规则(v3.5.6最终版) — 三段式和两段式区别处理:

    • 三段式(已有schema): dbo直接删除。[HRBI_Stage].[dbo].[Users]"HRBI_Stage"."Users" / HRBI_Stage.[dbo].[Users]HRBI_Stage."Users"

    • 两段式(无schema): dbo替换为schema_prefix。[dbo].[Users]"hrbi_stage"."Users" / dbo.Usershrbi_stage.Users

    • 双点号 xxx..yyyxxx.yyy(SQL Server省略dbo的写法)

    • 正则顺序(不可颠倒): 1)双点号→2)三段式引号→3)三段式裸名→4)三段式混合→5)两段式引号→6)两段式裸名

    • ⚠️ 改完dbo规则后必须跑全量312过程验证

  • 支持双引号包裹格式:"dbo"."Users" 和裸名格式:dbo.Users 均可正确匹配

  • 精确拆分增强 - 无明确终止符时的兜底逻辑

  • 新增_find_next_create函数:当找不到;GO终止符时,用下一个CREATE关键字作为对象边界上界

  • 跳过字符串和注释内的CREATE,只匹配真正的CREATE语句开头

  • 所有对象类型(table/view/procedure/function/trigger/index/constraint)均有兜底

  • VARCHAR CHAR语义后处理 - VARCHAR(n)VARCHAR(n CHAR)

  • 修复detokenize中类型名映射绕过CHAR语义的问题

  • 重写达梦数据库转换器 - 完全重写 dm_converter.py

    • token化保护: 字符串/注释替换为占位符后再做正则替换,避免误改字符串内容

    • 按对象类型独立转换: procedure/function/view/trigger/table/index/constraint

    • 40+种数据类型映射, 30+种函数映射

    • 变量语法转换: @var -> var, DECLARE @var -> var, SET @var= -> var:=

    • TRY-CATCH -> EXCEPTION WHEN OTHERS THEN

    • 全局变量转换: @@ROWCOUNT -> SQL%ROWCOUNT

    • 触发器伪表: inserted/deleted -> NEW/OLD

    • 转换结果输出到子目录: output_split_dm/

  • 拆分后转换集成 - split_sql_v21.py 新增 convert_to 参数

    • 拆分完成后自动调用转换器,按对象类型独立转换

    • 生成达梦版合并脚本 merge_all.sql

  • CLI参数 - split_sql_v22.py 新增 --convert-to dm

  • 29个转换单元测试 - test_dm_converter.py 全部通过

  • 修复已知bug:

    • INSERT INTO 不再被误替换为 INTEGERO

    • token_map 合并避免占位符还原丢失

    • content = new_content 遗漏导致变量@替换无效

    • 嵌套括号 VARCHAR(100) 导致参数列表正则截断

    • 终止符 / 不再重复添加

过滤特定对象类型(只提取存储过程等)

拆分默认输出所有对象类型。如果只需要某类对象(如只要存储过程),有两种方式:

方式1:拆分后拷贝(推荐,最简单)


# 先完整拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver



# 再只拷贝目标类型到独立目录

cp output_dir/proc_*.sql /path/to/output_proc/

方式2:拆分后删除不需要的文件


# 保留存储过程,删除其他

cd output_dir && ls -1 | grep -v '^proc_' | xargs rm
  • ⚠️ 拆分→转换→过滤的完整工作流(v3.5.4) — 正确顺序:(1)拆分 split_sql_v21.py input.sql output_split (2)转换 batch_convert.py output_split output_split_dm schema_prefix (3)过滤 cp output_split_dm/proc_*.sql output_final/关键坑:如果先过滤再转换,batch_convert.py 只能转换过滤后的文件,schema_prefix可能不对(文件名变了);如果转换后新旧文件混在同一目录,cp 可能把旧文件也拷进去。必须先转换再过滤,过滤目录必须清空。转换后验证:grep -rl '\bdbo\.' output_split_dm/ 应为0

达梦转换使用方法


# 拆分SQL Server文件并转换为达梦数据库语法

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v22.py input.sql output_dir --dialect sqlserver --convert-to dm



# 仅转换(不拆分)

python3 -c "from dm_converter import convert_sqlserver_to_dm; print(convert_sqlserver_to_dm('SELECT GETDATE()', 'generic'))"

质量报告使用方法


# 单对象报告

from dm_converter import DMConverter

from report_generator import ConversionReportGenerator



converter = DMConverter()

result = converter.convert(sql_content, 'procedure', schema_prefix='hrbi')

report = ConversionReportGenerator.generate_single(result, 'sp_test', 'procedure')

print(report.to_markdown())

print(f'兼容性评分: {report.score}/100')



# 批量报告

batch = ConversionReportGenerator.generate_batch(results, schema_prefix='hrbi')

batch.save_html('report.html')    # 暗色主题HTML

batch.save_json('report.json')    # 结构化JSON

batch.save_markdown('report.md')  # Markdown



# 快速评分(不生成报告)

score = ConversionReportGenerator.quick_score(result)

批量转换(推荐,用脚本文件)

v21不支持--convert-to参数,需分两步:拆分→转换→过滤。详见 拆分+转换完整工作流

⚠️ 拆分≠转换: 拆分产出的是原始SQL Server语法文件,必须经过batch_convert.py转换才能得到达梦语法。只拆分不转换是常见错误。


# 1) 拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver



# 2) 批量转换(写脚本文件方式)

cat > /tmp/batch_convert.py << 'PYEOF'

#!/usr/bin/env python3

import os, sys

sys.path.insert(0, '/Users/a1234/.hermes/skills/sql-splitter/scripts')

from dm_converter import convert_sqlserver_to_dm



src_dir = sys.argv[1] if len(sys.argv) > 1 else 'output_dir'

dm_dir = sys.argv[2] if len(sys.argv) > 2 else src_dir + '_dm'

schema_prefix = sys.argv[3] if len(sys.argv) > 3 else os.path.basename(src_dir).replace('_split','')

os.makedirs(dm_dir, exist_ok=True)



ok = err = 0

err_list = []

for f in sorted(os.listdir(src_dir)):

    if not f.endswith('.sql') or f == 'merge_all.sql': continue

    obj_type = f.split('_')[0]

    type_map = {'proc':'procedure','func':'function','trig':'trigger',

                'view':'view','table':'table','idx':'index','uidx':'index',

                'con':'constraint','seq':'sequence'}

    mapped_type = type_map.get(obj_type, 'generic')

    with open(os.path.join(src_dir, f)) as fh: c = fh.read()

    try:

        converted = convert_sqlserver_to_dm(c, mapped_type, schema_prefix=schema_prefix)

        with open(os.path.join(dm_dir, f), 'w') as fh: fh.write(converted)

        ok += 1

    except Exception as e:

        err += 1; err_list.append(f'{f}: {str(e)[:120]}')



print(f'转换完成: {ok} 成功, {err} 失败')

if err_list:

    for e in err_list[:15]: print(f'  - {e}')

PYEOF



python3 /tmp/batch_convert.py /path/to/output_dir /path/to/output_dir_dm schema_prefix

转换规则

| 类别 | SQL Server | 达梦 |

|------|-----------|------|

| 标识符 | [aa](列名) → "aa"(去[]加双引号)/[hrbi].[xxx](schema/表名) → "hrbi"."xxx"(去[]加双引号)/[nvarchar](类型名) → nvarchar(去[]不加双引号,后续做类型映射) |

| 声明 | CREATE PROCEDURE ... AS | CREATE OR REPLACE PROCEDURE ...(p1 INT) AS |

| 数据类型 | INT/BIT/DATETIME/MONEY/NVARCHAR/VARCHAR/UNIQUEIDENTIFIER | INTEGER/BOOLEAN/TIMESTAMP/DECIMAL(19,4)/VARCHAR(n CHAR)/CHAR(36) |

| 函数 | GETDATE()/ISNULL()/LEN()/CONVERT() | CURRENT_TIMESTAMP/NVL()/LENGTH()/CAST() |

| 变量 | @var / DECLARE @var / SET @var= | var / var / var:= |

| 异常 | BEGIN TRY...END TRY BEGIN CATCH...END CATCH | BEGIN...EXCEPTION WHEN OTHERS THEN...END; |

| 事务 | COMMIT TRANSACTION / ROLLBACK TRANSACTION | COMMIT / ROLLBACK |

| 全局变量 | @@ROWCOUNT / @@ERROR | SQL%ROWCOUNT / SQL%ERROR_CODE |

| 触发器 | inserted/deleted | NEW/OLD |

| 终止符 | GO | / |

| 清表 | TRUNCATE TABLE xxx | DELETE FROM xxx |

| 建临时表 | SELECT * INTO #tmp FROM src | CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT * FROM src |

| DDL结尾 | 过程体DDL无分号 | 自动补分号(单行/跨行/CTAS/ON PRIMARY均处理, 仅PROC/FUNC/TRIG) |

输出目录结构


input_split/ ← 原始拆分结果

├── proc_sp_test.sql

├── table_users.sql

├── view_v_users.sql

└── merge_all.sql



input_split_dm/ ← 达梦转换版本

├── proc_sp_test.sql

├── table_users.sql

├── view_v_users.sql

└── merge_all.sql

支持的对象类型转换

| 对象类型 | 转换策略 |

|---------|---------|

| 存储过程 | CREATE OR REPLACE + AS + 参数@去除 + 参数加括号 + 终止符/ |

| 函数 | CREATE OR REPLACE + RETURN + IS + 终止符/ |

| 视图 | CREATE OR REPLACE + SCHEMABINDING去除 |

| 触发器 | CREATE OR REPLACE + inserted/deleted->NEW/OLD + 终止符/ |

| 表 | IDENTITY保留 + 表选项去除 + 类型映射 |

| 索引 | CLUSTERED/NONCLUSTERED去除 + INCLUDE去除 |

| 约束 | WITH NOCHECK去除 |

转换器核心设计要点(开发调试血泪史)

  • token化保护: 字符串/注释替换为占位符后再做正则替换,避免误改字符串内容

  • token_map合并: Step2对象类型转换后重新tokenize时,必须合并旧token_map,否则__TOKEN_0__等占位符还原丢失

  • 变量@前缀: 在token还原后再做,且用_tokenize_strings_only只保护字符串(不保护注释,注释里@变量也要转)

  • content = new_content 不可省略: re.sub后必须更新content变量,否则后续替换基于旧文本

  • 嵌套括号: VARCHAR(100)中的)会截断[^)]*,参数列表匹配需用(\([^)]*(?:\([^)]*\)[^)]*)*\))匹配嵌套

  • 数据类型上下文: 前缀需包含DECLARE\s+,否则DECLARE @v DATETIME中的DATETIME不会被转换

  • INSERT INTO误匹配: INSERT INT被匹配为前缀\n+列名INSERT+类型INT,需在数据类型替换中排除SQL关键字作为列名

  • ⚠️ 多正则顺序执行重复匹配陷阱(v3.3.0修复): _convert_procedure中三个re.sub顺序处理同类模式(有括号参数/无括号参数/无参数存储过程),第一个替换后的结果被后续正则再次匹配,导致引号叠加""sp_test""绝不可用多个re.sub顺序处理同一token的不同形式——必须用单一正则+分支回调,确保每个模式只被匹配一次。详见 v3.3.0修复记录

  • ⚠️ 添加新转换规则的流程: 在dm_converter中添加新规则(如TRUNCATE→DELETE)的标准流程:(1)在_convert_statements或新增专用方法中实现逻辑 (2)在convert() Step4方法链中调用 (3)在test_dm_converter.py添加测试(至少3个:基本转换+大小写+上下文) (4)跑全部测试确认无回归 (5)更新SKILL.md转换规则表和更新日志 (6)更新wiki页面

  • ⚠️ 不用delegate_task做代码审查: dm_converter.py有2265行,delegate_task子任务逐行审查会超时(600s)。正确做法:主agent直接用search_files+read_file定位关键函数,逐条验证规则覆盖,手动补缺失。delegate_task适合独立可并行的任务,不适合需要大量file I/O的逐行审查

  • ⚠️ IDENTITY插入位置陷阱(v3.3.0修复): SQL中)出现在很多上下文(列类型VARCHAR(100)、函数调用、表定义结束)。匹配表结束的)必须用上下文锚定(独占一行^(\s*)\)(\s*$)或紧跟;/换行),不能简单匹配第一个)后行尾——会匹配到列定义中的嵌套)。详见 v3.3.0修复记录

  • ⚠️ token_map占位符key碰撞(v3.3.0修复): Step3重新tokenize时counter从0开始,新占位符__TOKEN_0__覆盖了Step1中同key的原始内容,导致字符串'N/A'被还原为标识符"v_users"。修复:_tokenize新增start_counter参数,Step3传入max(已存在key)+1任何生成占位符的系统重新运行时,必须从已存在key的最大值+1开始。详见 v3.3.0修复记录

  • ⚠️ 方括号替换规则变更(v3.5.3): _convert_bracket_identifiers()从v3.5.3起区分类型名和非类型名:非类型名[aa]"aa"(加双引号),类型名[int]int(不加双引号,后续做类型映射)。判断逻辑:取方括号内首段token,与TYPE_MAPPINGS键集合(小写)匹配。注意[int identity]这种含后缀的方括号内容,只有首段int参与类型名判断,整个内容会被保留为裸名(int identity不加双引号)——这是正确行为,因为这种写法只出现在列定义中,后续类型映射会处理。v3.5.2及以前所有方括号内容都不加双引号(→裸名),需要额外_post_convert_generic_types步骤加双引号

  • ⚠️ _quote_name先split再去方括号(v3.3.0修复): _quote_name先检查整体[...body...]格式,但[dbo].[PROC_xxx][开头]结尾被误当成单个方括号标识符,去首尾后变成dbo].[PROC_xxx当输入可能是schema.name格式时,必须先split('.')再逐段去方括号/引号,绝不能先对整体做去除外层处理。详见 v3.3.0修复记录

  • ⚠️ ##全局临时表替换顺序(v3.3.0修复): re.sub(r'#(\w+)',...)##GlobalTemp只替换第二个#变成#tmp_。修复:先替换##gtmp_再替换#tmp_。详见 v3.3.0修复记录

  • ⚠️ TRUNCATE TABLE在达梦存储过程内不支持(v3.4.0): 达梦不支持在存储过程内使用TRUNCATE TABLE,_convert_truncate自动将TRUNCATE TABLE xxxDELETE FROM xxx。注意:DELETE FROM没有TRUNCATE的重置IDENTITY/不写日志等语义差异,但达梦存储过程内只能用DELETE

  • ⚠️ _ensure_ddl_semicolons状态机实现要点(v3.4.5): 达梦存储过程体内DDL必须以;结尾。用状态机扫描过程体:(1)单行DDL如DROP TABLE xxx直接补; (2)跨行建表用括号深度跟踪,在)行补; (3)CTAS跨行子查询遇到下一个语句开头时回补; (4)ON PRIMARY文件组语法去掉后补;关键:此步骤仅对PROCEDURE/FUNCTION/TRIGGER执行,TABLE/VIEW等独立对象由Step9处理。312个存储过程DDL缺分号从447降为0

  • ⚠️ SELECT INTO #临时表达梦不支持(v3.4.5): 达梦不支持SELECT ... INTO #新表 FROM ...建表语法。_convert_temp_tables自动将临时表的SELECT INTO转为CTAS:CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT ... FROM ...非临时表的SELECT INTO暂不自动转换——因为与达梦变量赋值语法SELECT expr INTO var FROM ...形式相同,无法程序化区分。用户需手动处理非临时表的SELECT INTO(改为先CREATE TABLE再INSERT INTO ... SELECT)

  • ⚠️ delegate_task不适合逐行代码审查(v3.4.5): 2265行dm_converter.py用delegate_task子任务逐行审查600s超时。正确做法:主agent直接用search_files+read_file定位关键函数,逐条验证规则覆盖,手动补缺失。delegate_task适合独立可并行的任务,不适合大量file I/O的审查

  • ⚠️ TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE结尾必须加分号(v3.4.0): 之前只有PROCEDURE/FUNCTION/TRIGGER有_add_terminator/终止符,TABLE/VIEW等DDL对象结尾没有统一加分号。新增_add_ending_semicolon确保这些对象以;结尾。拆分阶段(split_sql_v21.py)也去掉了Oracle/DM不加分号的特殊逻辑,所有方言统一加分号

  • ⚠️ 函数参数链路断裂是隐蔽BUG(v3.4.5): _convert_split_output()签名有schema_prefix参数但调用处没传,convert_sqlserver_to_dm_with_result()也没传——函数链路每环漏传,整个功能静默失效。教训:新增参数到函数签名时,必须grep所有调用点确保传参,否则功能"存在但不生效"的BUG极难发现

  • ⚠️ "完全拆分出X"≠"拆分所有":当用户说"完全拆分出存储过程"时,意思是只提取存储过程,不是拆分全部对象类型。应使用过滤方式(见上方"过滤特定对象类型"章节),先拆分再按前缀筛选,而非默认全量输出后让用户自己找。

  • ⚠️ patch工具缩进陷阱(严重,已反复触发): patch工具修改Python缩进时极易出错:(1) else块内代码被放到块外 (2) if子块和if本身同缩进 (3) 修复脚本的缩进也可能不对(17空格vs16空格的1位偏差导致整个if块变成else子块)。终极方案:涉及Python方法体修改时,不要用patch,用Python脚本替换整个方法(find方法定义起始→find return content结束→拼接新方法体)。每次修改后必须用python3 -m py_compile file.py验证。仅靠lint不够——py_compile才能发现缩进导致的SyntaxError/IndentationError。详见 v2.4.5设计记录

  • ⚠️ Python缓存陷阱: 修改.py后pytest可能运行旧的__pycache__/*.pyc。修改后必须find . -name '*.pyc' -deletePYTHONDONTWRITEBYTECODE=1 python3 -m pytest ...。否则改了代码但测试结果不变,误导调试方向

  • ⚠️ write_file不能写代码文件: Hermes的write_file工具会给内容添加NNN|行号前缀,导致Python文件损坏。代码文件只能用patch工具或terminal的python脚本修改。详见 v2.4.3修复记录

  • ⚠️ Python脚本嵌套字符串修改dm_converter: execute_code中用字符串拼接修改dm_converter.py会因缩进/引号嵌套报IndentationError。正确做法:写独立.py脚本文件到/tmp/再用terminal执行。步骤:(1)write_file写patch脚本到/tmp (2)terminal运行python3 /tmp/patch_xxx.py (3)py_compile验证 (4)跑测试。这是patch工具和terminal python脚本的补充方案——当patch工具做复杂多位置修改时,脚本文件更可控

  • ⚠️ detokenize类型名映射陷阱: 方括号包裹的类型名[nvarchar]在token保护下不会被Step4类型映射匹配,必须在detokenize还原时同时做映射+去掉方括号,否则变成nvarchar(100)但已过Step4不再映射。详见 v2.4.5设计记录

  • ⚠️ _convert_data_types捕获组偏移陷阱(v3.0修复): 正则(\[?(TYPE_PATTERN)\]?)中TYPE_PATTERN本身是(INT|VARCHAR|...)捕获组,导致type_name是group(3)而内部type是group(4),suffix本应在group(4)却变成了group(5)。症状:INT映射成INTEGERINTVARCHAR映射成VARCHARVARCHAR。修复:改(TYPE_PATTERN)(?:TYPE_PATTERN)非捕获组

  • ⚠️ _convert_data_types suffix贪婪匹配陷阱(v3.0修复): suffix正则(\([^]]*...) used [^]] (match non-]) instead of [^)] (match non-))。[^]]*matches everything up to a]which rarely appears in SQL, so(100) 后的所有内容全被吞进suffix。症状:第一个类型后面所有列定义都被当作suffix,后续列的类型映射全部失效。修复:[^]][^)]`

  • ⚠️ procedure/function方括号不替换(v3.0修复): _post_convert_table_types只对TABLE/VIEW做方括号→双引号+类型映射+dbo替换,PROCEDURE/FUNCTION走的是_replace_dbo_prefix只处理双引号格式的dbo。但procedure原始SQL是[dbo].[xxx]方括号格式,dbo替换匹配不到。修复:新增_post_convert_generic_types对所有非TABLE/VIEW类型做方括号→双引号+类型映射+dbo替换

  • ⚠️ 存储过程VARCHAR(n)缺少CHAR语义(v3.2.2修复): 之前_post_convert_generic_types注释写"不做 VARCHAR(n) -> VARCHAR(n CHAR) (过程体内变量声明不需要)",但用户要求存储过程中的VARCHAR也必须加CHAR语义,与TABLE一致。修复:(1)在_post_convert_generic_types中新增VARCHAR(n) → VARCHAR(n CHAR)替换正则 (2)DECLARE变量/参数也会被加CHAR语义

  • ⚠️ CAST中nvarchar未映射(v3.2.2修复): _post_convert_generic_types原来只有_bracket_type_pattern(匹配方括号包裹的类型如[nvarchar]),但SQL Server过程体中cast(x as nvarchar(50))nvarchar是裸名无方括号,不匹配。修复:新增_bare_type_pattern(?<=\s)前缀匹配裸类型名。注意:_bare_type_pattern必须用lookbehind (?<=\s)避免匹配列名(列名在逗号/括号后不会有空格前缀)

  • ⚠️ PROCEDURE三正则顺序执行导致双重引号(v3.3.0修复): _convert_procedure中三个re.sub顺序执行,第一个_format_bracket_params替换后输出PROCEDURE "sp_test" (...),第三个_fmt_no_param_proc的正则PROC\s+(.+?)\s+AS又匹配到了这个结果,把"sp_test" (...)当成name再包引号变成""sp_test""。修复:合并为单一正则+分支回调_format_proc,确保每个存储过程声明只被匹配和替换一次。教训:多个正则顺序替换同一类语法时,后面的正则会匹配前面替换的结果——必须用单一正则或标记已替换区域

  • ⚠️ SET NOCOUNT ON/OFF带分号不匹配(v3.2.1修复): _convert_statements正则^\s*SET NOCOUNT ON\s*$不匹配SET NOCOUNT ON;(行末带分号),导致过程体内部的SET NOCOUNT ON未被转换。修复:正则加\s*;?兼容分号。同时用户要求直接删除而非注释保留,所以SET NOCOUNT ON/SET NOCOUNT OFF映射为空字符串,正则加\n?吃掉换行不留空行

  • ⚠️ PROCEDURE三正则顺序执行导致双重引号(v3.3.0修复): _convert_procedure中三个re.sub顺序执行,第一个替换后输出PROCEDURE "sp_test" (...),第三个正则又匹配到把"sp_test"(...)当name再包引号变成""sp_test""。修复:合并为单一正则+分支回调。教训:多个正则顺序替换同一类语法时,后面的会匹配前面替换的结果——必须用单一正则或标记已替换区域

  • ⚠️ IDENTITY子句插入位置(v3.3.0修复): 达梦语法CREATE TABLE "name" (...) IDENTITY("col", 1, 1)。之前正则匹配到VARCHAR(100 CHAR)行末的)把IDENTITY插在了)前面。修复:优先匹配独占一行的^(\s*)\)(\s*$),fallback到行尾)教训:表定义中列类型括号里的)和表结束)在正则中难以区分——要求结束括号独占一行或用锚点精确匹配

  • ⚠️ 临时表正则嵌套括号截断(v3.3.0修复): [^)]*NVARCHAR(100))就截断。修复:改用贪婪(.+)+re.DOTALL匹配到最后一个)教训:匹配"最后一个右括号"时,排除式模式不可靠,改用贪婪+DOTALL

  • ⚠️ ##全局临时表替换顺序(v3.3.0修复): re.sub(r'#(\w+)',...)##GlobalTemp只替换第二个#变成#tmp_。修复:先替换##gtmp_再替换#tmp_教训:替换含##的标识符时必须先处理双#再处理单#

  • ⚠️ token_map碰撞(v3.3.0修复): Step1 tokenize'N/A'__TOKEN_0__,Step3重新tokenize"v_users"又分配__TOKEN_0__覆盖原值。修复:_tokenize新增start_counter参数,Step3从原最大key+1开始。教训:pipeline中多次tokenize必须保证key不碰撞——传起始偏移量

  • ⚠️ _quote_name处理[dbo].xxx: [dbo].[PROC_xxx]整体被误当单个方括号标识符,剥首尾括号变成dbo].[PROC_xxx。修复:先按.拆分再逐段去方括号/引号。教训:处理含.的标识符时,必须先拆分再清理每段括号

  • ⚠️ SET NOCOUNT ON 在过程体内部不转换: 转换器只处理紧跟 AS 后的 SET NOCOUNT ON(转为注释)。如果 SET NOCOUNT ON 出现在过程体中间(如第7行),不会被转换,残留到输出中。达梦不支持该语句,需手动注释或删除。实测462对象中2个存此问题(0.43%),属已知边界case

  • ⚠️ git push分支对齐: 本地git可能在master分支提交,但GitHub仓库HEAD分支可能是main。push到master不更新GitHub默认展示的main分支,导致网页看不到最新代码。修正:git remote show origin确认HEAD分支 → git checkout main && git merge master && git push origin main

  • ⚠️ GitHub API上传大文件超时: dm_converter.py(88KB+)通过GitHub Contents API上传时,base64后请求体巨大,curl经常超时(300s+)。推荐方式:直接git add && git commit && git push,比API逐文件PUT快得多且更可靠。之前memory记录"api.github.com可达但github.com被墙"已过时——2026-06-14实测git push可正常工作。仅在git push完全不通时才fallback到API上传

  • ⚠️ UTF-16编码SQL文件: SSMS导出的SQL脚本常为UTF-16编码(带BOM),拆分前必须先转UTF-8,否则内容被当成二进制乱码。转换命令: python3 -c "open('out.sql','w',encoding='utf-8').write(open('in.sql',encoding='utf-16').read())" 详见 v3.0修复与UTF-16转换记录

  • ⚠️ DATE类型映射重复陷阱: 当存储过程参数类型为DATE时(无方括号包裹),detokenize的类型映射可能在Step4已经替换过一次DATE→DATE(因为DATE在达梦也是合法类型名),但如果正则边界不够精确,会把DATE后面的换行/空白也吃进去,导致相邻关键字拼接,如DATE\nAS变成DATEDATE\nASDATEDATEAS。根因:类型映射正则的后缀锚点需用\b(?=\s|,|\)|$)精确截断,不能贪婪吃进换行符。每次修改类型映射正则后,必须跑test_dm_converter.py验证

  • ⚠️ dbo前缀正则陷阱: detokenize后方括号变成双引号,正则必须同时匹配"dbo".dbo.两种格式。三段式必须在两段式之前处理,否则schema.dbo.object中的dbo.object先被两段式误匹配。详见 v2.4.5设计记录

运行转换测试


cd ~/.openclaw/skills/sql-splitter/scripts

python3 -m pytest test_dm_converter.py -v

发布到 clawhub.ai


# ⚠️ 必须用绝对路径,不能用相对路径`.`

clawhub publish /absolute/path/to/skill-dir --slug sql-splitter --version X.Y.Z

# 错误: clawhub publish .  → "Error: SKILL.md required" (即使SKILL.md明明存在)

# 正确: clawhub publish /absolute/path/to/skill-dir

# ⚠️ 版本号冲突:clawhub不允许覆盖已发布版本,必须升版本号(如3.2.2→3.2.3)重新发布

# ⚠️ 如果publish成功但随后改了SKILL.md,再次publish同一版本号会报"already exists",必须升版

### ⚠️ GitHub 推送可能失败

macOS环境下github.com经常网络不通("Failed to connect to github.com port 443")。clawhub发布不依赖GitHub,可以独立使用。如果需要同步GitHub,稍后重试或检查网络。

### ⚠️ 商业化路径已废弃

v3.6.0起移除了所有License/产品化相关代码(keygen.py、license_verifier.py、RSA密钥对)。sql-splitter现在是纯开源免费工具,无功能限制。references/下的产品化规划文档也已删除。

注意:clawhub上可能存在同slug不同owner的技能(如@fish1981bimmer/sql-splitter@kingaiwork/sql-splitter),clawhub inspect可能报AMBIGUOUS_SKILL_SLUG错误。此时用clawhub install @fish1981bimmer/sql-splitter指定owner。

发布到 GitHub


cd /Users/a1234/.hermes/skills/sql-splitter

git add -A && git commit -m "vX.Y.Z: 变更说明"

# ⚠️ 确认远程主分支名!git remote show origin 查看HEAD branch

# 如果远程HEAD是main但本地在master上提交,push到master不会更新GitHub默认展示的main

# 修正: git checkout main && git merge master && git push origin main

git push origin main

支持的 SQL 方言

  • MySQL

  • PostgreSQL

  • Oracle

  • SQL Server

  • 达梦 (DM)

  • 通用 (Generic)

v2.2.1 功能

  • GUI 界面 - 提供图形化界面进行 SQL 文件拆分操作

  • 断点续传 - 支持记录处理进度,中断后可以继续处理

  • 批量并行处理 - 支持同时处理多个 SQL 文件,提升处理速度

  • 结果预览和对比 - 可视化查看拆分结果,支持与原始文件对比

  • 配置文件管理 - 保存和加载常用配置,支持导入导出

  • 详细错误处理 - 结构化错误信息,包含错误类型、上下文和修复建议

  • Dry-run 预览模式 - 预览拆分结果而不实际创建文件

  • 安全修复 - pickle反序列化漏洞修复,检查点改用JSON序列化

支持的 SQL 对象类型

| 类型 | 前缀 | 说明 |

|------|------|------|

| 存储过程 | proc_ | CREATE PROCEDURE |

| 函数 | func_ | CREATE FUNCTION |

| 视图 | view_ | CREATE VIEW |

| 触发器 | trig_ | CREATE TRIGGER |

| 表结构 | table_ | CREATE TABLE |

| 包 | pkg_ | CREATE PACKAGE |

| 索引 | idx_ | CREATE INDEX |

| 唯一索引 | uidx_ | CREATE UNIQUE INDEX |

| 约束 | con_ | ALTER TABLE ADD CONSTRAINT |

| 序列 | seq_ | CREATE SEQUENCE |

| 同义词 | syn_ | CREATE SYNONYM (Oracle) |

| 事件 | evt_ | CREATE EVENT (MySQL) |

| 物化视图 | mv_ | CREATE MATERIALIZED VIEW (PostgreSQL) |

| 类型 | type_ | CREATE TYPE |

v2.0 核心改进

边界检测重写

  • 使用 BEGIN...END 深度匹配确定存储过程/函数/触发器边界

  • 支持 IF...THEN...END IF、CASE...END CASE、LOOP...END LOOP 嵌套

  • 不再依赖"下一个 CREATE 位置"做上界,正确处理过程体内的嵌套 CREATE 语句

  • Oracle/DM: 通过 / 终止符定位;SQL Server: 通过 GO 定位

  • PostgreSQL: 支持 $$...$$ 包裹语法

  • 字符串和注释内的分号/关键字不会干扰边界检测

依赖分析改进

  • 函数调用检测改为限定上下文模式(:= 赋值、WHERE/HAVING 子句等),大幅减少误报

  • SQL 关键字过滤表扩展到 150+ 个,涵盖内置函数、控制流、聚合等

  • 自引用自动排除

  • 循环依赖不再报错,按类型优先级追加

合并脚本方言适配

  • Oracle/DM: @@filename + SET DEFINE OFF

  • SQL Server: :r filename + GO

  • PostgreSQL: \i filename + ON_ERROR_STOP

  • MySQL: source filename

  • 通用: 注释方式

架构优化

  • 提取 common.py 共享模块:SQLDialect 枚举、对象前缀、类型优先级、关键字表

  • dependency_analyzer.py 不再重复定义枚举,直接引用 common

  • 拆分后自动调用依赖分析,生成 merge_all.sql

  • 新增 37 个单元测试

使用方法

GUI 模式(推荐)


python3 ~/.openclaw/skills/sql-splitter/scripts/gui.py

单文件拆分


# 推荐: 用 v21 (CLI稳定, 支持所有拆分功能)

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py <input.sql> [output_dir] --dialect sqlserver



# v22 目前在无GUI环境会 ImportError (SQLSplitterGUI 依赖 tkinter)

# 如需使用, 确保系统有 tkinter: apt install python3-tk / brew install python-tk

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v22.py <input.sql> [output_dir] 2>/dev/null || \

  python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py <input.sql> [output_dir]

拆分后转达梦(两步法)


# v21 不支持 --convert-to 参数, 需分两步:

# 1) 拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver

# 2) 批量转换(用 dm_converter 直接调用)

# ⚠️ 注意: 不要用 python3 -c "复杂多行脚本",安全扫描会拦截

# 推荐写临时脚本文件再运行:

python3 /tmp/batch_convert.py  # 脚本内容见 scripts/batch_convert.py

批量转换脚本scripts/batch_convert.py — 用法: python3 scripts/batch_convert.py [src_dir] [dm_dir] [schema_prefix]

  • 自动按文件名前缀(proce→procedure, view→view, table→table等)识别对象类型

  • 遍历目录逐文件调用 convert_sqlserver_to_dm()

  • 默认参数: src_dir=HRBI_Stage_split, schema_prefix=HRBI_Stage

UTF-16 编码文件处理


# SQL Server 导出的 .sql 文件常为 UTF-16 编码, 需先转 UTF-8:

python3 -c "

with open('input.sql','r',encoding='utf-16') as f: content=f.read()

with open('input_utf8.sql','w',encoding='utf-8') as f: f.write(content)

print(f'Converted: {len(content.splitlines())} lines')

"

# 然后用 input_utf8.sql 做拆分

批量拆分(目录)


python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --batch <目录路径> [输出目录]

批量拆分(多个文件)


python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --batch "file1.sql,file2.sql,file3.sql" [输出目录]

指定方言


python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --dialect oracle input.sql

支持的方言:mysql, postgresql, oracle, sqlserver, dm, generic

不生成合并脚本


python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --no-merge input.sql

预览结果


python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --preview input.sql output_dir

检查点管理


# 列出所有检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --list



# 查看恢复进度

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --resume input.sql



# 清理旧检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --clear --days 7



# 删除检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --delete input.sql

配置管理


# 列出所有配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --list



# 保存配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --save --name oracle --dialect oracle



# 加载配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --load --name oracle



# 导出配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --export --name oracle --export-path oracle_config.json



# 导入配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --import --import-path oracle_config.json --name oracle

参数说明

| 参数 | 说明 |

|------|------|

| input.sql | 要拆分的 SQL 文件路径(单文件模式必需) |

| --batch | 批量模式标志 |

| --dialect | 指定 SQL 方言 |

| --no-merge | 不生成依赖排序的合并脚本 |

| -q, --quiet | 静默模式 |

| output_dir | 输出目录(可选,默认:原文件名_split) |

运行测试


cd ~/.openclaw/skills/sql-splitter/scripts

python3 -m pytest test_dm_converter.py -v

端到端质量验证(大文件转换后)

转换完成后,建议跑10项质量检查确认残留SQL Server语法:


DM_DIR="输出目录_dm"

echo "1. 残留方括号:        $(grep -rl '\[.*\]' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "2. 残留dbo.:          $(grep -rl '\bdbo\.' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "3. 残留@@变量:        $(grep -rl '@@[A-Z]' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "4. 残留SET NOCOUNT ON:$(grep -rl 'SET NOCOUNT ON' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "5. 残留GO终止符:      $(grep -rwl '^GO$' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "6. 残留GETDATE():     $(grep -rl 'GETDATE()' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "7. 残留ISNULL:        $(grep -rl '\bISNULL(' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "8. 双重映射(INTEGERINT等): $(grep -rl 'INTEGERINT\|VARCHARVARCHAR' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "9. 双点号残留:        $(grep -rl '\.\.' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "10.CREATE OR REPLACE数:$(grep -rl 'CREATE OR REPLACE' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

所有计数应为0(除了第10项和第4项可能有少量边界case残留需手动处理)。

输出示例

假设输入文件 myapp.sql 包含:

  • users

  • 视图 v_users(依赖 users)

  • 存储过程 sp_update(依赖 users)

输出:


myapp_split/

├── table_users.sql

├── view_v_users.sql

├── proc_sp_update.sql

└── merge_all.sql          ← 按依赖排序的合并脚本

merge_all.sql 内容(以 Oracle 为例):


-- [1/3] table: users

@@table_users.sql



-- [2/3] view: v_users  -- depends on: users

@@view_v_users.sql



-- [3/3] procedure: sp_update  -- depends on: users

@@proc_sp_update.sql

文件结构


sql-splitter/

├── SKILL.md ← 本文档
├── SKILL.md ← 本文档
├── references/

│   ├── dm-converter-design.md ← 达梦转换器设计要点

│   ├── dm-converter-v243-fixes.md ← v2.4.3 修复记录

│   ├── dm-converter-v246-fixes.md ← v2.4.6 修复记录(捕获组偏移+suffix贪婪+procedure方括号)

│   ├── dm-converter-v30-fixes.md ← v3.0 修复记录(含HRBI_Stage真实项目验证)

│   ├── dm-converter-v322-fixes.md ← v3.2.2 修复记录(PROC VARCHAR CHAR + CAST nvarchar映射)
│   ├── dm-converter-v323-fixes.md ← v3.2.3 修复记录(PROCEDURE用AS而非IS)
│   ├── dm-converter-v330-fixes.md ← v3.3.0 修复记录(双重引号+IDENTITY位置+临时表正则+token碰撞+方括号处理)│   ├── dm-converter-v330-fixes.md ← v3.3.0 修复记录(双重引号+IDENTITY位置+临时表正则+token碰撞+方括号处理)

│   ├── dm-converter-v345-fixes.md ← v3.4.5 修复记录(dbo两段式+DATETIME2排序+方括号架构+schema_prefix传参)

│   ├── dm-converter-v353-fixes.md ← v3.5.3 修复记录(方括号替换规则变更:非类型名加双引号)

│   ├── dm-converter-v340-fixes.md ← v3.4.0 修复记录

│   ├── dm-converter-v245-bracket-dbo-split.md ← v2.4.5 方括号+dbo设计记录

│   ├── split-convert-workflow-20260627.md ← 拆分+转换完整工作流 + 用户反馈理解教训
│   ├── document-maintenance.md ← 文档维护规范(删除功能时的清理清单+重复条目预防)

└── scripts/

    ├── common.py ← 共享模块(枚举、常量、工具函数)

    ├── split_sql.py ← v2.0 主拆分脚本

    ├── split_sql_v21.py ← v2.1 主拆分脚本(带错误处理+转换集成)

    ├── split_sql_v22.py ← v2.2 主拆分脚本(集成所有新功能+功能守卫)

    ├── dm_converter.py ← 达梦数据库转换器 v3.4.5

    ├── report_generator.py ← 转换质量报告生成器(兼容性评分+风险+HTML/MD/JSON)

    ├── dependency_analyzer.py ← 依赖分析器

    ├── error_handler.py ← 错误处理模块

    ├── gui.py ← GUI 界面(tkinter)

    ├── checkpoint.py ← 断点续传模块

    ├── batch_processor.py ← 批量并行处理模块

    ├── result_previewer.py ← 结果预览和对比模块

    ├── batch_convert.py ← 批量达梦转换脚本(拆分后调用)

    ├── config_manager.py ← 配置文件管理模块

    ├── test_sql_splitter.py ← 拆分单元测试(37个)

    ├── test_v21_features.py ← v2.1 功能测试

    ├── test_dm_converter.py ← 达梦转换单元测试(53个)

    └── test_v22_features.py ← v2.2 功能测试

达梦转换器已知问题(v2.4.3)

v2.4.3 修复了 9 个核心 BUG(DATEADD参数重排、SELECT INTO、IF/WHILE控制流、PRINT等),40个测试全部通过。详见 v2.4.3修复记录

仍需手动调整的项目:

  • STRING_AGG→LISTAGG 缺少 WITHIN GROUP 子句

  • STUFF→OVERLAY、REPLICATE→RPAD 语义不完全对等

  • 临时表 #temp → GTT/普通表

  • EXEC/EXECUTE 动态SQL → EXECUTE IMMEDIATE

  • RAISERROR → RAISE_APPLICATION_ERROR

  • TOP n → ROWNUM/FETCH FIRST

  • MERGE/游标/WITH(NOLOCK)/IF EXISTS 等差异

v2.4.4 已修复的映射:

  • VARCHAR(n) → VARCHAR(n CHAR):达梦VARCHAR默认BYTE语义,必须加CHAR才等效SQL Server的字符语义

  • UNIQUEIDENTIFIER → CHAR(36):达梦用CHAR(36)而非VARCHAR(36),UUID是定长

v2.4.1 新功能 — 拆分自动加 OR REPLACE

  • 视图和存储过程自动添加 OR REPLACE — 拆分时对 procedure/function/view/trigger 四类对象,自动将 CREATE 转为 CREATE OR REPLACE

  • 达梦和 Oracle 环境下对象已存在时需要 OR REPLACE,否则会报错

  • 已有 OR REPLACE 的语句不会重复添加

  • 所有方言均生效(不仅限于 DM/Oracle)

  • 实现在 split_sql_v21.py 的 obj_content 提取后、写入文件前

注意事项

  • 使用正则+深度匹配识别 SQL 对象边界,对极复杂嵌套语法可能有局限

  • 默认 UTF-8 编码,遇到编码问题自动 replace

  • 建议先备份原文件

  • 批量模式会自动创建以原文件名命名的子目录

  • 自动检测 SQL 方言,也可手动指定

  • 同名文件自动追加序号(如 proc_sp_init_2.sql

常见问题

拆分结果不正确(多个对象混在一个文件中)

症状:拆分后生成的文件包含多个 SQL 对象,而不是每个对象一个文件。

原因:原始 SQL 文件中的对象缺少分号结束符。sql-splitter 依赖分号来确定对象的结束位置。

解决方案:为每个 SQL 语句添加分号。例如:


-- 错误:缺少分号

Create table a(

  Id int,

  Name varchar(10)

)



Create table b(

  Id int,

  Name varchar(10)

)



-- 正确:添加分号

Create table a(

  Id int,

  Name varchar(10)

);



Create table b(

  Id int,

  Name varchar(10)

);

快速修复方法


# 使用 sed 为每个 CREATE 语句后的空行添加分号

sed -i '' '/^Create /,/^)/s/)$/);/' input.sql

视图未被识别

症状:拆分后没有生成视图文件,或视图被识别为其他对象类型。

原因:视图语法不规范,缺少 AS 关键字。

解决方案:修正视图语法,添加 AS 关键字。例如:


-- 错误:缺少 AS

create view v_a

(

select * from dual

);



-- 正确:添加 AS

CREATE VIEW v_a AS

SELECT * FROM dual;

存储过程/函数未被正确拆分

症状:多个存储过程混在一个文件中,或产生重复文件。

原因:存储过程语法不规范,缺少 AS/BEGIN 关键字或分隔符。

解决方案:根据数据库类型修正语法:

SQL Server


-- 错误:缺少 AS 和 GO

create proc p_a

(

select * from dual

);

create proc p_b

(

select * from dual

);



-- 正确:添加 AS 和 GO

CREATE PROCEDURE p_a

AS

BEGIN

    SELECT * FROM dual;

END

GO



CREATE PROCEDURE p_b

AS

BEGIN

    SELECT * FROM dual;

END

GO

Oracle/达梦


-- 错误:缺少 IS/AS 和 /

CREATE PROCEDURE p_a

BEGIN

    SELECT * FROM dual;

END



-- 正确:添加 IS/AS 和 /

CREATE OR REPLACE PROCEDURE p_a AS

BEGIN

    SELECT * FROM dual;

END;

/

MySQL


-- 错误:缺少 DELIMITER

CREATE PROCEDURE p_a()

BEGIN

    SELECT * FROM dual;

END



-- 正确:使用 DELIMITER

DELIMITER //

CREATE PROCEDURE p_a()

BEGIN

    SELECT * FROM dual;

END //

DELIMITER ;

产生重复文件

症状:拆分后生成多个内容相同或相似的文件(如 proc_p_a.sqlproc_p_a_2.sql)。

原因:对象边界检测失败,通常由以下原因导致:

  • 对象之间缺少分隔符(分号、GO、/ 等)

  • 对象语法不规范(缺少 AS、BEGIN 等)

  • 嵌套对象语法错误

解决方案

  1. 检查并修正原始 SQL 文件的语法

  2. 确保每个对象之间有正确的分隔符

  3. 使用 --dialect 参数明确指定数据库类型

  4. 对于复杂情况,考虑手动拆分或使用数据库工具导出

预检查清单

在运行 sql-splitter 之前,建议检查以下内容:

  • 每个 SQL 语句都有分号结束符

  • 视图包含 AS 关键字

  • 存储过程/函数包含 AS/BEGIN 关键字

  • SQL Server 对象之间有 GO 分隔符

  • Oracle/达梦 对象末尾有 / 终止符

  • MySQL 存储过程使用 DELIMITER

  • 对象名称没有特殊字符或保留字冲突

  • 文件编码为 UTF-8

文档维护规范

  • 功能描述按版本倒序排列:最新版本(v3.6.2)在最前,旧版本(v2.2.1等)在后

  • 更新日志必须严格按版本号降序 — 如v3.6.2→v3.6.0→v3.5.6→v3.5.0→v3.4.5→...→v1.0.0。每次新增版本后,用grep '^### v' SKILL.md检查排序是否正确

  • 避免重复章节:同一功能(如达梦转换)只在一个版本章节下详细描述,其他地方引用即可

  • 标题中的版本号必须与 clawhub 发布版本一致

  • 更新日志保留完整历史,但主体部分只展开最新版和次新版

  • clawhub版本号冲突时:发布后如果又改了内容,必须升版本号(如3.2.2→3.2.3)重新发布,clawhub不允许覆盖已发布版本

更新日志

v3.6.0 (2026-07-04)

  • 移除 License/产品化相关代码 — 删除 keygen.py、license_verifier.py、private_key.pem、public_key.pem、issued_licenses.json,License 管理功能暂不实施
  • 移除产品化规划文档 — 删除 references/ 下的 sql-splitter-productization.md、sql-splitter-v350-productization.md、sql-splitter-commercialization.md
  • 清理 SKILL.md 重复条目 — 删除"商业化产品化"章节、"License管理"章节、"发布前检查清单"、重复的 dbo 规则/用户规则验证条目
  • 纯开源免费工具 — sql-splitter 不再有功能限制,所有用户可使用全部功能

v3.5.6 (2026-07-04)

  • 补丁: SKILL.md 修复 — 修复 clawhub 发布后 SKILL.md 被 patch 工具引入的反斜杠转义问题,清理重复条目

v3.5.0 (2026-06-21)

  • report_generator.py — 转换质量报告生成器(兼容性评分0-100 + 风险分级 + HTML/MD/JSON输出 + 7个测试)

  • gui.py重写 — 完整tkinter GUI(文件选择→拆分→转换→质量报告)

  • pip打包 — pyproject.toml + src/sql_splitter/ + sql-splitter CLI入口

v3.4.5 (2026-06-26)

  • dbo两段式无prefix时去掉dbodbo.xxx无schema_prefix时直接去掉dbo变成xxx,不再保留

  • DATETIME2→TIMESTAMP排序BUG — 正则alternation按长度降序排列,避免DATETIME抢先匹配DATETIME2

  • 方括号替换移到Step4 — 从Step 6.7(token还原后)移到Step 4(token化后),注释/字符串已被保护不会误改

  • schema_prefix参数贯穿调用链 — split_sql_file→_convert_split_output→convert全链传参

  • 新增CLI --schema-prefix和--dm参数

  • 312个存储过程转换验证通过(HRBI_Stage=54, HRBI_DW=157, HRBI_DM=101)

  • 详见 v3.4.5修复记录(含dbo演进史)

v3.4.0 (2026-06-20)

  • TRUNCATE TABLE → DELETE FROM — 达梦不支持TRUNCATE在存储过程内,自动将TRUNCATE TABLE xxx转换为DELETE FROM xxx

  • TABLE/VIEW结尾加分号 — TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE转换后结尾自动加;

  • 所有方言拆分后均加分号 — 之前Oracle/DM不加分号,现在统一加

  • 53个单元测试全部通过(新增9个:4个TRUNCATE转换+5个结尾分号)

v3.3.0 (2026-06-19)

  • PROCEDURE双重引号bug修复 — 三个顺序正则导致已替换结果被再次匹配,引号叠加成""sp_test""。合并为单一正则+分支回调

  • IDENTITY位置修正IDENTITY("id",1,1))前移到)后,符合达梦语法。同时兼容单行和多行表定义

  • #临时表正则修复 — 列定义中的)截断匹配,改用贪婪匹配。##全局临时表不再被替换为#tmp_

  • token_map碰撞修复 — Step3重新tokenize占位符key覆盖原key,字符串'N/A'变成标识符"v_users"。新增start_counter参数避免碰撞

v3.2.4 (2026-06-14)

  • 更新日志排序修正 — 所有版本严格按版本号降序排列

  • 旧版日志精简 — 去掉重复子项展开,保持简洁

v3.2.3 (2026-06-14)

  • 存储过程PROCEDURE用AS而非IS — 达梦存储过程声明用AS,函数用IS,之前PROCEDURE也用了IS是错误

v3.2.2 (2026-06-14)

  • 存储过程VARCHAR(n)加CHAR语义 — DECLARE变量和参数中的VARCHAR(n)VARCHAR(n CHAR),与TABLE转换一致

  • CAST中nvarchar→VARCHAR(n CHAR)cast(x as nvarchar(50))CAST(x AS VARCHAR(50 CHAR)),之前nvarchar未映射

  • _post_convert_generic_types增强 — 新增裸类型名映射(via _bare_type_pattern),之前只映射方括号包裹的类型

  • 44个单元测试全部通过(含4个新增PROCEDURE类型映射测试)

  • 462个真实SQL对象端到端测试通过(HRBI_Stage.sql, 7万行)

v3.2.1 (2026-06-14)

  • SET NOCOUNT ON/OFF直接删除 — 之前注释保留,用户要求直接去掉(达梦不需要)

  • SET NOCOUNT ON;带分号不匹配 — 正则加\s*;?兼容行末分号,之前只匹配无分号的SET NOCOUNT ON

  • 批量转换脚本 — 新增scripts/batch_convert.py,写脚本文件而非python3 -c内联(安全扫描会拦截后者)

v3.1.0 (2026-06-13)

  • PROCEDURE参数加括号 - CREATE PROC name @p1 INT ASCREATE OR REPLACE PROCEDURE name (p1 INT) AS

  • OR REPLACE兼容 - 正则匹配 CREATE OR REPLACE PROC(拆分阶段已加 OR REPLACE 的情况)

  • AS保留 - 存储过程的AS关键字保留为AS(达梦PROCEDURE用AS,函数用IS)

  • GO;兼容 - GO; 也被替换为 /(之前只匹配纯 GO 行)

  • DATE不再双重映射 - _post_convert_generic_types 中对 DATE 类型直接返回 DATE

  • VARCHAR(max)/NVARCHAR(max) → VARCHAR(4096 CHAR) - 之前映射为TEXT,改为VARCHAR(4096 CHAR)

  • VARCHAR2类型映射 - varchar2 → VARCHAR2VARCHAR2(max) → VARCHAR2(4096 CHAR)

v3.0.0 (2026-06-13)

  • 修复3个dm_converter核心BUG:

    • 捕获组偏移: _convert_data_types正则TYPE_PATTERN用了捕获组,导致group偏移,类型双重映射(INTINTEGERINT)。改非捕获组(?:...)

    • suffix贪婪匹配: [^]]*应为[^)]*,导致VARCHAR(100)后所有列定义被吞进suffix,后续类型映射失效

    • procedure方括号不替换: 新增_post_convert_generic_types方法,所有对象类型都做方括号→双引号+类型映射+dbo替换

  • 方括号替换对所有对象类型生效 - PROCEDURE/FUNCTION/TRIGGER 也做 [xxx]"xxx" + dbo替换

  • 双点号..替换 - SQL Server的database..object(省略dbo schema)→达梦database.object

  • UTF-16编码支持 - SSMS导出脚本转UTF-8后拆分

  • 40个单元测试全部通过

  • 462个真实SQL对象端到端测试通过(HRBI_Stage.sql, 7万行)

  • 详见 v3.0修复记录 | v2.5.1修复记录

v2.5.0 (2026-05-31)

  • 变量命名规范 - DECLARE局部变量自动加v_前缀, 参数保持原名, 符合达梦开发规范

  • 多变量DECLARE - DECLARE @v1 INT, @v2 VARCHAR(100) 正确拆分为多行独立声明

  • 类型映射修正 - bit->BOOLEAN, tinyint->SMALLINT (达梦无TINYINT)

  • dbo前缀替换扩展 - 存储过程/函数中的dbo.也被替换为schema前缀

  • 存储过程参数格式化 - 参数换行缩进, 加括号, DECIMAL(18,2)等括号内逗号不被误拆

  • 类型映射修复 - [datetime] DEFAULT等DEFAULT后缀场景也能正确映射

  • SELECT INTO变量名 - 与DECLARE声明保持一致, 自动加v_前缀

v2.4.5 (2026-06-08)

  • 方括号→双引号 - [schema].[table]"schema"."table",类型名[nvarchar]nvarchar(去掉方括号并做类型映射)

  • dbo前缀智能处理 - 三段式[HRBI].[dbo].[Users]"HRBI"."Users"(删dbo);两段式[dbo].[Users]hrbi_stage."Users"(用文件名替换)

  • 精确拆分增强 - 无;/GO终止符时,用下一个CREATE关键字作为对象边界兜底

  • VARCHAR CHAR语义后处理 - 修复detokenize类型映射绕过CHAR语义的问题

  • schema_prefix自动传递 - 从源文件名自动提取前缀传给dm_converter

  • 40个测试全部通过

v2.4.4 (2026-06-07)

  • 数据类型映射调整 - 按达梦最佳实践修正

    • VARCHAR(n) → VARCHAR(n CHAR):达梦VARCHAR默认BYTE语义,必须加CHAR才等效SQL Server的字符语义

    • UNIQUEIDENTIFIER → CHAR(36):达梦用CHAR(36)而非VARCHAR(36),UUID是定长

  • 40个测试全部通过

v2.4.3 (2026-06-06)

  • 达梦转换器BUG修复 - 9个失败测试全部修复,40/40通过

    • BIT→BOOLEAN, TINYINT→SMALLINT 类型映射修正

    • NVARCHAR(n) → VARCHAR(n CHAR) 达梦字符语义转换

    • SET NOCOUNT ON注释格式修正

    • DATEADD专用转换方法(参数重排:DATEADD(day,n,date) → date + INTERVAL 'n' DAY)

    • SELECT赋值区分有无FROM(有FROM→SELECT INTO,无FROM→:=)

    • IF...BEGIN...END → IF...THEN...END IF 控制流转换

    • WHILE...BEGIN...END → WHILE...LOOP...END LOOP 控制流转换

    • PRINT → DBMS_OUTPUT.PUT_LINE 转换(+号连接改||)

  • 详见 v2.4.3修复记录 | 缩进调试技巧 | 7万行实战

v2.4.1 (2026-05-30)

  • 拆分自动加 OR REPLACE - 对 procedure/function/view/trigger 四类对象,自动将 CREATE 转为 CREATE OR REPLACE

    • 已有 OR REPLACE 的语句不重复添加

    • 所有方言均生效

v2.4.0 (2026-05-23)

  • 重写达梦数据库转换器 - 完全重写 dm_converter.py

    • token化保护: 字符串/注释替换为占位符后再做正则替换

    • 按对象类型独立转换: procedure/function/view/trigger/table/index/constraint

    • 40+种数据类型映射, 30+种函数映射

    • 变量语法转换: @var -> var, DECLARE @var -> var, SET @var= -> var:=

    • TRY-CATCH -> EXCEPTION WHEN OTHERS THEN

    • 全局变量转换: @@ROWCOUNT -> SQL%ROWCOUNT

    • 触发器伪表: inserted/deleted -> NEW/OLD

    • 转换结果输出到子目录: output_split_dm/

  • 拆分后转换集成 - split_sql_v21.py 新增 convert_to 参数

    • 拆分完成后自动调用转换器,按对象类型独立转换

    • 生成达梦版合并脚本 merge_all.sql

  • 29个转换单元测试 - test_dm_converter.py 全部通过

  • 详见 v2.4.0修复记录

v2.2.1 (2026-05-01)

  • 安全修复 - 修复 pickle 反序列化漏洞,替换为 JSON + 数据验证

  • 新增安全文档 - 添加 SECURITY.md

  • 新增依赖管理 - 添加 requirements.txt

v2.2.0 (2026-04-27)

  • 新增 GUI 界面 - 提供图形化界面进行 SQL 文件拆分操作

  • 新增断点续传功能 - 支持记录处理进度,中断后可以继续处理

  • 新增批量并行处理 - 支持同时处理多个 SQL 文件,提升处理速度

  • 新增结果预览和对比 - 可视化查看拆分结果,支持与原始文件对比

  • 新增配置文件管理 - 保存和加载常用配置,支持导入导出

v1.1.0 (2026-04-13)

  • 新增索引支持:CREATE INDEX, CREATE UNIQUE INDEX

  • 新增约束支持:ALTER TABLE ADD CONSTRAINT

  • 所有 6 种方言均支持索引/约束识别

v1.0.0

  • 初始版本