【GaussDB】排查报错“ERROR: Plpgsql estate is null“

【GaussDB】排查报错"ERROR: Plpgsql estate is null"

背景

客户在 GaussDB 中执行一个上万行的匿名块时,遇到了 ERROR: Plpgsql estate is null 报错。通过手动加日志点位二分法,定位到报错 SQL 与子函数调用相关。单独调用子函数不会报错,因此需要进一步分析。

分析

数据库版本

GaussDB 506.0 SPC0500 集中式

业务背景

该匿名块是应急业务脚本,用于在生产环境出现问题时执行完整业务逻辑。脚本内容从各存储过程复制展平,为减少重复代码,使用了子函数封装公共逻辑。从业务角度看,这种写法是合理的。

技术排查

首先,定位报错位置。虽然可以通过加日志或使用 DBE_UTILITY.FORMAT_ERROR_BACKTRACE() 获取堆栈,但客户已找到出错 SQL,因此直接分析其特征。

简化用例
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;
/
排查步骤
  1. 子函数独立性:将子函数创建为独立函数后,不再报错 → 问题与子函数相关。
  2. 数据源影响 :将 unnest_table 替换为实际表后,不再报错 → 问题与 unnest_table 相关。
  3. 子函数位置:将子函数从 SELECT 列表移至 WHERE 条件,仍然报错 → 与子函数位置无关。
  4. 语句类型:将 INSERT 改为 SELECT INTO,仍然报错 → 与数据修改无关。
  5. SQL 上下文:将子函数调用改为简单变量赋值(不含 SQL),不再报错 → 问题仅出现在 SQL 中。
  6. 参数类型:将子函数入参从列改为常量或变量,不再报错 → 问题与入参为列时相关。
  7. 多表关联 :再 JOIN 一个实际表,将子函数入参改为该表的列,仍然报错 → 只要存在 unnest_table,子函数入参为列就会触发问题。
  8. SRF 函数扩展 :移除 unnest_table,改为查询 dbe_perf.statement,仍然报错 → 问题不仅限于 unnest_table,所有 SRF 函数都会触发。
结论

问题场景明确:在使用 SRF 函数的 SQL 中,如果调用了子函数且子函数入参包含列,则会报错 ERROR: Plpgsql estate is null

最小化复现用例
sql 复制代码
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;
/

执行效果:

sql 复制代码
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?

与华为的沟通预期

我做过产品,完全知道这会是什么样的结果:

  1. 认为报错是设计如此,文档已有提醒。
  2. 承认文档描述不清,后续完善。
  3. 承认报错信息不友好,后续版本改进。
  4. 如需支持此功能,需提需求,但版本规划可能靠后。

由于数据库版本已确定,非严重问题不会轻易变更版本,因此客户只能自行修改 SQL。

总结

通过多轮测试,精准排查出 GaussDB 子函数的一个功能缺陷(暂无法确认为 BUG)。本文的排查思路希望能为读者提供参考和启发。


相关推荐
DarkAthena11 小时前
【DuckDB】一条 SQL 同时连接 Oracle 和 GaussDB
oracle·gaussdb·duckdb
Gauss松鼠会2 天前
GaussDB(DWS) 日常维护命令
服务器·数据库·postgresql·性能优化·gaussdb·经验总结
Gauss松鼠会2 天前
GaussDB(DWS) GUC参数修改、查看
java·数据库·sql·数据库开发·gaussdb
UtopianCoding2 天前
数据库语法对比详细规则
数据库·mysql·gaussdb
Gauss松鼠会3 天前
GaussDB(DWS) 资源监控Topsql
java·网络·数据库·算法·oracle·性能优化·gaussdb
Gauss松鼠会4 天前
【GaussDB】基于SpringBoot实现操作GaussDB(DWS)的项目实战
java·数据库·经验分享·spring boot·后端·sql·gaussdb
Gauss松鼠会4 天前
【GaussDB】GaussDB 常见问题及解决方案汇总
java·数据库·算法·性能优化·gaussdb·经验总结
DarkAthena9 天前
【GaussDB】讨论下for update游标加锁的设计
gaussdb
Gauss松鼠会15 天前
【GaussDB】数据加密方式:函数加密、透明加密与全密态
数据库·sql·oracle·gaussdb·经验总结·命令总结