如何在Liquibase中声明并运行匿名PL/pgSQL块?执行时出现SQL语法异常求助
解决PostgreSQL+Liquibase执行匿名PL/pgSQL块的语法异常问题
我来帮你搞定这个问题!你遇到的SQL语法错误,核心是匿名PL/pgSQL块的写法不符合PostgreSQL规范,再加上Liquibase的配置没适配这种复杂语句,导致解析失败。下面一步步给你修正:
1. 修正SQL文件的PL/pgSQL语法
PostgreSQL的匿名PL/pgSQL块必须用DO $$ ... $$包裹,而且变量声明要统一放在DECLARE段内,同时赋值要用规范的:=语法。把你的027-rename-psps.sql改成下面这样:
DO $$ DECLARE -- 声明游标和变量,全部放在DECLARE段内 cur_rename CURSOR (modulekeyold varchar) FOR SELECT * FROM payment_module_config WHERE module_key = modulekeyold; counter INTEGER := 0; commitnum INTEGER := 100; -- 显式声明记录变量的类型,避免隐式类型问题 recordvar payment_module_config%ROWTYPE; BEGIN FOR recordvar IN cur_rename('WirecardPaymentService_1.0') LOOP RAISE NOTICE 'Value: %', recordvar; UPDATE payment_module_config SET module_key='GetNet V1' WHERE id = recordvar.id; counter := counter + 1; IF (counter % commitnum = 0) THEN RAISE NOTICE '>>> counter: %', counter; COMMIT; END IF; END LOOP; COMMIT; END $$;
为什么原来的写法错了?
- 没有用
DO $$ ... $$包裹:PostgreSQL不允许直接执行裸的PL/pgSQL块,必须通过DO语句触发执行 - 变量位置错误:
counter和commitnum必须放在DECLARE关键字下方,不能单独写在外面 - 赋值语法不规范:PostgreSQL中PL/pgSQL的变量赋值推荐用
:=,虽然部分版本兼容=,但规范写法能避免潜在问题 - 记录变量未显式声明:显式指定
payment_module_config%ROWTYPE让代码更清晰,也能避免隐式转换错误
2. 调整Liquibase的变更日志配置
你的Liquibase配置里,splitStatements="true"会把SQL文件里的分号当作语句分隔符,导致把完整的PL/pgSQL块拆成多个无效语句,必须关闭这个配置。修改changeSet里的sqlFile标签:
<changeSet author="wamk" id="20210909102601"> <preConditions onFail="MARK_RAN"> <dbms type="postgresql" /> </preConditions> <sqlFile path="027-rename-psps.sql" relativeToChangelogFile="true" endDelimiter=";" dbms="PostgreSQL" encoding="utf8" splitStatements="false"/> <!-- 关键:关闭语句拆分 --> </changeSet>
配置调整的原因
整个DO $$ ... $$块是一个完整的PL/pgSQL语句,内部的分号是块内的语句分隔符,不是整个SQL语句的结束符。如果splitStatements="true",Liquibase会错误地把块内的每个分号都当作独立语句的结束,导致解析失败。
做完这两步调整后,重新执行Liquibase变更,应该就能正常运行了!
内容的提问来源于stack exchange,提问作者shadow0wolf




