2202E — array_subscript_error(数组下标错误)
速览
2202E 是类别 22 Data Exception 中的 array_subscript_error 条件。目录同时保留 ERRCODE_ARRAY_ELEMENT_ERROR 这一兼容别名,并使用 ERRCODE_ARRAY_SUBSCRIPT_ERROR 作为带条件名的宏。两个宏编码的是同一个 SQLSTATE。
不要把所有看起来越界的表达式都当成错误。PostgreSQL 文档明确说明,读取当前边界之外的数组下标会返回 NULL;提供错误数量的下标同样返回 NULL。数组切片还有独立的历史规则:完全位于边界外的切片可以产生空的零维数组,部分重叠的切片则缩减为重叠部分。
当其他路径校验形状或下标并拒绝它时才会抛出 2202E。核心源码中的这类路径包括数组拼接或构造时维度不兼容、无效切片边界,以及部分带下标赋值检查。诊断时,操作本身和下标数值同样重要。
可执行的代表性案例是 incompatible_array_dimensions。它在 PostgreSQL 18.6 和 10.21 上均通过:不兼容的拼接抛出 2202E,随后同一自动提交连接保持 IDLE,并成功执行有效的后续拼接。下面的 SQL 摘录是共享注册表中的完整有序语句对;测试执行器仍是建表和清理的唯一来源。
| 字段 | 值 |
|---|---|
| SQLSTATE | 2202E |
| 条件名 | array_subscript_error |
| 状态 | 有效 |
| 已知存在于 | 7.4 |
| 锁定快照 | 9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3 |
| 宏 | ERRCODE_ARRAY_ELEMENT_ERROR, ERRCODE_ARRAY_SUBSCRIPT_ERROR |
| 别名 | ERRCODE_ARRAY_ELEMENT_ERROR |
含义与触发路径
18.6 目录中的定义是类别 22 下的 2202E E ERRCODE_ARRAY_SUBSCRIPT_ERROR array_subscript_error。前面的别名行是 2202E E ERRCODE_ARRAY_ELEMENT_ERROR;源码注释解释 SQL99 的 “array element error” 实际上就是数组下标错误。因此,仍使用别名的代码指向同一 SQLSTATE,而不是另一种条件。
数组具有秩、每个维度的长度以及下界。PostgreSQL 不要求数组从 1 开始,所以诊断时应读取实际下界,而不是作这个假设。文档中的 array_ndims、array_dims、array_lower、array_upper 和 cardinality 函数可以查看所需的元数据。需要诊断错误时,应在同一会话中实际调用这些检查函数;它们是检查工具,不代表已经触发了某个特定错误。
核心路径主要包括:
- 数组元素或切片赋值。
arrayfuncs.c会校验下标和切片边界。一维数组可以通过给新元素赋值来扩展,中间位置填入NULL;多维数组不支持这种扩展。给空数组赋切片时必须提供两个边界。因此,赋值访问当前读取边界之外的位置时,应按赋值操作分析,不能从SELECT a[n]的结果推断。 - 数组拼接。 当同秩数组的非拼接维度的长度或下界不同时,
array_cat抛出2202E。代表性案例覆盖的就是这条路径。 - 多维构造。 表达式执行器在用于构造多维数组的非空数组表达式维度不兼容时,也会抛出同一条件。
源码中还存在其他调用方,包括数据类型辅助路径。类别 22 是宽泛的数据异常类别;具体是哪个形状或下标契约失败,要由 2202E 条目、源码函数和报文共同确定。
报文与诊断
可执行摘录如下:
第一条语句的两个二维数组内层维度不同。PostgreSQL 18.6 报告:
PostgreSQL 10.21 的同一案例给出相同的主报文和详细信息文本,历史源码行号是 356。第二条语句返回 {1,2,3,4}。这些报文和详细信息属于拼接路径,不是所有 2202E 调用方都必然使用的措辞。
其他源码确认的模板包括 array subscript out of range、array slice subscript must provide both boundaries(详细信息会解释给空数组赋值的要求),以及 upper bound cannot be less than lower bound。如果驱动程序提供这些字段,请保留 message_detail、message_hint、source_file、source_function 和 source_line;它们往往能区分切片校验与拼接校验。
诊断
先按表达式分类:
- 普通元素读取如
a[999]可以合法返回NULL。在称为服务器错误前,检查数组本身、下标表达式以及存储的边界。 - 切片读取根据文档规则可能返回
NULL、空的零维数组或缩减后的重叠部分。没有错误响应时不要把这些值映射成2202E。 - 赋值、数组构造和拼接会执行校验代码。记录数组秩、维度、下界、提供的下标数量,以及语句是否在修改值。
遇到真实错误时,记录 SQLSTATE、严重级别、主报文、详细信息、提示、语句位置以及关系对象或函数上下文。使用同一个值或源表达式配合 array_ndims、array_dims、array_lower 和 array_upper 对照。拼接时比较每一个非拼接维度和下界;切片时检查两个边界及其顺序;构造时检查每个子数组的形状。
在 PL/pgSQL 中,处理器可以捕获 array_subscript_error 或 SQLSTATE '2202E',但处理器的事务行为取决于代码块。带 EXCEPTION 子句的代码块会让受保护主体在子事务中运行;主体出错后,主体内对持久数据库状态的修改会先回滚,再运行处理器。OTHERS 也有文档规定的排除项,应用要修复数组操作时应使用具体条件。
处理
修复违反形状契约的操作。拼接前统一维度和下界;提供完整且顺序正确的切片边界;只有在确实需要 NULL 填充语义时才使用一维扩展;或者使用形状匹配的子数组重新构造多维值。如果读取返回 NULL 是合法结果,应先按值处理,不要在判断应用是否需要区分“缺少元素”和“存储的 NULL 元素”前就用 COALESCE 掩盖它。
代表性错误在自动提交下运行。失败后连接状态为 IDLE,有效拼接成功。在显式事务中,ERROR 通常会让事务进入中止状态,直到 ROLLBACK 或回滚到保存点;连接本身不一定需要关闭。带异常子句的 PL/pgSQL 代码块可以让受保护主体在子事务中封装失败,主体内的修改会在处理器运行前回滚。应读取客户端的实际事务状态,不要仅凭 2202E 推断连接结局。
版本
目录记录 2202E 存在于锁定的 7.4–8.4.22 pre-9.0 正式源码、9.0.23 至 18.6 的全部正式快照及 19 Beta 3 预览快照。同 tag 的 REL8_1_4 errcodes.sgml 表已经列出 2202E 和条件名 array_subscript_error,因此至少可以确认 8.1.4 已有该条件名。7.0–7.3 仍有候选源码缺口,因此 7.4 观察结果只是存在边界,不是精确引入版本。18.6 固定源码 commit 为 724edf9bde9d356724ad384a2e196edc3c9f80f7;其 errcodes.txt 同时保留别名宏和带条件名的宏。
不兼容维度案例在 PostgreSQL 18.6 和 10.21 上均通过,主报文和详细信息文本相同而源码行号不同。这个跨版本结果不保证所有旧小版本或其他 2202E 调用路径的措辞都不变。
相关
22000 — data_exception 是宽泛的类别 22 代码。22005 — error_in_assignment 是不同的赋值条件,不应替代数组专用的 2202E。22P02 — invalid_text_representation 处理输入文本解析。2202E 也不同于成功的越界读取,后者按数组文档规则返回 NULL。
来源
结构化证据记录在公开证据 JSON中。源码记录固定到 PostgreSQL commit 724edf9bde9d356724ad384a2e196edc3c9f80f7;运行记录保留共享注册表、两个目标版本和 2202E-snippet-registry-final-20260909 运行 ID。
src.errcodes.18.6—errcodes.txtsrc.array-userfuncs.18.6—array_userfuncs.csrc.arrayfuncs.18.6—arrayfuncs.csrc.exec-expr.18.6—execExprInterp.cdoc.array.18与doc.func.18— Arrays 与 Array Functions and Operatorsdoc.transactions.18— Transactions