【GaussDB】排查报错"ERROR: Plpgsql estate is null"
背景
客户在 GaussDB 中执行一个上万行的匿名块时,遇到了 ERROR: Plpgsql estate is null 报错。通过手动加日志点位二分法,定位到报错 SQL 与子函数调用相关。单独调用子函数不会报错,因此需要进一步分析。
分析
数据库版本
GaussDB 506.0 SPC0500 集中式
业务背景
该匿名块是应急业务脚本,用于在生产环境出现问题时执行完整业务逻辑。脚本内容从各存储过程复制展平,为减少重复代码,使用了子函数封装公共逻辑。从业务角度看,这种写法是合理的。
技术排查
首先,定位报错位置。虽然可以通过加日志或使用 DBE_UTILITY.FORMAT_ERROR_BACKTRACE() 获取堆栈,但客户已找到出错 SQL,因此直接分析其特征。
简化用例
CREATE TABLE t_test_subfunction (a INT, b INT);
CREATE TYPE ty_test_subfunction IS (a INT, b INT);
CREATE TYPE tyt_test_subfunction IS TABLE OF ty_test_subfunction;
DECLARE
l_tyt_test_subfunction tyt_test_subfunction;
FUNCTION subfunc (x IN INT)
RETURN INT AS
BEGIN
RETURN 1;
END;
BEGIN
l_tyt_test_subfunction := tyt_test_subfunction();
l_tyt_test_subfunction.extend;
l_tyt_test_subfunction(1) := ty_test_subfunction(1,1);
-- 下面这条语句报错 ERROR: Plpgsql estate is null
INSERT INTO t_test_subfunction
SELECT subfunc(a), b FROM unnest_table(l_tyt_test_subfunction);
END;
/
排查步骤
- 子函数独立性:将子函数创建为独立函数后,不再报错 → 问题与子函数相关。
- 数据源影响:将
unnest_table替换为实际表后,不再报错 → 问题与unnest_table相关。 - 子函数位置:将子函数从 SELECT 列表移至 WHERE 条件,仍然报错 → 与子函数位置无关。
- 语句类型:将 INSERT 改为 SELECT INTO,仍然报错 → 与数据修改无关。
- SQL 上下文:将子函数调用改为简单变量赋值(不含 SQL),不再报错 → 问题仅出现在 SQL 中。
- 参数类型:将子函数入参从列改为常量或变量,不再报错 → 问题与入参为列时相关。
- 多表关联:再 JOIN 一个实际表,将子函数入参改为该表的列,仍然报错 → 只要存在
unnest_table,子函数入参为列就会触发问题。 - SRF 函数扩展:移除
unnest_table,改为查询dbe_perf.statement,仍然报错 → 问题不仅限于unnest_table,所有 SRF 函数都会触发。
结论
问题场景明确:在使用 SRF 函数的 SQL 中,如果调用了子函数且子函数入参包含列,则会报错 ERROR: Plpgsql estate is null。
最小化复现用例
DECLARE
l INT;
FUNCTION subfunc (x IN INT) RETURN INT AS
BEGIN
RETURN 1;
END;
BEGIN
SELECT count(1) INTO l
FROM dbe_perf.statement
WHERE subfunc(node_id) = 1;
END;
/
执行效果:
gaussdb=> DECLARE
gaussdb-> l INT;
gaussdb-> FUNCTION subfunc (x IN INT) RETURN INT AS
gaussdb-> BEGIN
gaussdb$> RETURN 1;
gaussdb$> END;
gaussdb$> BEGIN
gaussdb$> SELECT count(1) INTO l
gaussdb$> FROM dbe_perf.statement
gaussdb$> WHERE subfunc(node_id) = 1;
gaussdb$> END;
gaussdb$> /
ERROR: Plpgsql estate is null.
CONTEXT: SQL statement "select count(1) from dbe_perf.statement
where subfunc(node_id)=1"
PL/pgSQL function inline_code_block line 8 at SQL statement
gaussdb=>
官方文档说明
参考官方文档:GaussDB 子程序限制
文档中提到:
嵌套子程序不支持重载、不支持使用 SETOF。
该描述存在歧义:是子程序不支持返回 SETOF,还是使用子程序的 SQL 中不能使用 SETOF?
与华为的沟通预期
我做过产品,完全知道这会是什么样的结果:
- 认为报错是设计如此,文档已有提醒。
- 承认文档描述不清,后续完善。
- 承认报错信息不友好,后续版本改进。
- 如需支持此功能,需提需求,但版本规划可能靠后。
由于数据库版本已确定,非严重问题不会轻易变更版本,因此客户只能自行修改 SQL。
总结
通过多轮测试,精准排查出 GaussDB 子函数的一个功能缺陷(暂无法确认为 BUG)。本文的排查思路希望能为读者提供参考和启发。
