You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中使用SCROLL游标时出现unexpected plan node type:41错误排查

PostgreSQL游标滚动问题:"游标无法向后滚动"与"unexpected plan node type: 41"

问题背景

我有一段检查refcursor是否存在数据的逻辑,多数函数中运行正常,但在特定函数里执行时,PostgreSQL会抛出**"游标无法向后滚动"**的错误。

基础逻辑代码如下:

MOVE FORWARD 1 FROM ref;
IF FOUND THEN
  has_rows := TRUE;
END IF;

-- 如果无数据则抛出错误
IF NOT has_rows THEN
  RAISE EXCEPTION 'no data available';
ELSE
  MOVE BACKWARD 1 FROM ref; -- 找到数据后回退到游标起始位置以返回所有行
END IF;

为解决滚动问题,我给游标添加了SCROLL选项,结果又出现新错误:"ERROR: unexpected plan node type: 41"。这个问题仅出现在该特定函数中,其他使用相同逻辑的函数均正常运行。公开资料中未找到该错误的相关信息,仅在PostgreSQL源码中发现该错误与查询计划创建有关。

为复现问题,我编写了测试函数:

CREATE OR REPLACE FUNCTION pg_temp.test_cursor()
 RETURNS refcursor
 LANGUAGE plpgsql
AS $$

DECLARE ref REFCURSOR;
has_rows BOOLEAN := FALSE;

BEGIN

ref := 'results_refcursor';
OPEN ref scroll FOR

SELECT 1 AS m;

  MOVE FORWARD 1 FROM ref;
  IF FOUND THEN
    has_rows := TRUE;
  END IF;

  -- 如果无数据则抛出错误
  IF NOT has_rows THEN
    RAISE EXCEPTION 'no data available';
  ELSE
  MOVE BACKWARD 1 FROM ref; -- 找到数据后回退到游标起始位置以返回所有行
  END IF;

RETURN ref;

END; $$;

使用的PostgreSQL版本:

PostgreSQL 15.0 on x86_64-amazon-linux-gnu, compiled by gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4), 64-bit

注:必须使用游标实现,无法替换该方案。

解决方案

1. 替换MOVE BACKWARD逻辑

出现"unexpected plan node type: 41"错误,是因为PostgreSQL 15对部分简单查询(如SELECT 1)使用SCROLL游标并执行向后移动时,查询计划生成会出现异常。可以换一种方式检查数据,同时避免回退游标:

DECLARE
  ref REFCURSOR;
  has_rows BOOLEAN := FALSE;
  temp_val INT; -- 对应查询列的类型
BEGIN
  ref := 'results_refcursor';
  OPEN ref FOR SELECT 1 AS m;

  -- 先获取第一行数据
  FETCH NEXT FROM ref INTO temp_val;
  IF FOUND THEN
    has_rows := TRUE;
    -- 重新创建游标,将已获取的行和剩余数据合并
    CLOSE ref;
    OPEN ref SCROLL FOR
      SELECT temp_val AS m
      UNION ALL
      SELECT 1 AS m; -- 替换为原查询语句
  ELSE
    RAISE EXCEPTION 'no data available';
  END IF;

  RETURN ref;
END;

2. 显式声明SCROLL游标

不在OPEN时添加SCROLL,而是在声明游标时直接指定SCROLL属性:

DECLARE
  ref SCROLL REFCURSOR; -- 显式声明为可滚动游标
  has_rows BOOLEAN := FALSE;
BEGIN
  ref := 'results_refcursor';
  OPEN ref FOR SELECT 1 AS m;

  MOVE FORWARD 1 FROM ref;
  IF FOUND THEN
    has_rows := TRUE;
  END IF;

  IF NOT has_rows THEN
    RAISE EXCEPTION 'no data available';
  ELSE
    MOVE BACKWARD 1 FROM ref;
  END IF;

  RETURN ref;
END;

3. 升级PostgreSQL版本

该错误大概率是PostgreSQL 15.0的版本bug,后续小版本(如15.1及以上)已修复该问题,建议升级到对应分支的最新版本。


内容的提问来源于stack exchange,提问作者DatabaseShouter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:04:52