跳到主要内容

伪类型

伪类型(pseudo-type)不是可以直接用于表列的数据类型,而是 PostgreSQL 用来描述函数参数、返回值、过程或内部对象的特殊类型。它们让函数能够表达“任意类型”“一行记录”或“无返回值”等接口语义。

常见伪类型​

伪类型含义或用途
anyelement、anyarray与调用时具体类型关联的多态接口(旧式多态类型)
anycompatible、anycompatiblearray允许多个参数按兼容类型统一(较新的多态接口)
record任意复合记录,返回结构通常需由调用者提供
void函数不返回有意义的值
trigger触发器函数的返回类型
event_trigger事件触发器函数的返回类型
internalPostgreSQL 内部调用约定,不能作为普通 SQL 数据传入
cstring内部 C 字符串表示,不是表列文本

完整清单和可用位置随 PostgreSQL 版本变化,应以目标版本的 CREATE FUNCTION 文档为准。

多态函数​

多态伪类型让一个函数适配多种具体数据类型。例如数组相关函数可以对不同元素类型工作。函数声明中的类型约束会在调用时统一解析:

CREATE FUNCTION first_item(anyarray)
RETURNS anyelement
LANGUAGE sql
AS $$ SELECT $1[1] $$;

SELECT first_item(ARRAY[10, 20, 30]);
SELECT first_item(ARRAY['a', 'b']);

旧式 anyelement 家族要求相关参数推导出同一具体类型;anycompatible 家族允许 PostgreSQL 找到共同兼容类型。设计多态函数时要测试空数组、未知字面量和隐式转换。

record 返回值​

返回 record 的函数没有固定的列定义,调用者通常需要提供列清单:

CREATE FUNCTION pair_values()
RETURNS record
LANGUAGE sql
AS $$ SELECT 1, 'ok' $$;

SELECT * FROM pair_values() AS p(number integer, status text);

如果返回结构稳定,优先定义命名组合类型或 RETURNS TABLE,让依赖方获得更好的类型检查和文档。record 适合动态查询结果,但会增加调用复杂度。

trigger、event_trigger 和 void​

触发器函数必须声明 RETURNS trigger,返回 NEW、OLD 或 NULL 的规则由触发器类型决定:

CREATE FUNCTION set_updated_at()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated_at := clock_timestamp();
RETURN NEW;
END;
$$;

void 函数可以执行副作用但不返回值。调用方仍需考虑事务边界、异常和幂等性;void 不意味着操作一定异步或不可失败。

为什么不能建表列​

伪类型缺少稳定的存储布局或需要调用上下文才能确定具体类型,因此不能直接用于普通列:

-- 不可行:record 不是可直接存储的列类型
-- CREATE TABLE t (value record);

需要保存任意结构时,根据目标选择 jsonb、bytea、组合类型、数组或专门的表结构,而不是把伪类型当成“万能列类型”。

权限和安全​

声明为 internal 的函数通常只供数据库内部或扩展使用,普通用户不应直接调用。多态函数和动态 record 查询也不能绕过权限检查;安全定义、search_path 和动态 SQL 仍需显式设计。

常见误区​

  • 试图把 record、trigger 或 anyelement 用作表列。
  • 返回 record 却不提供稳定的列定义,让客户端无法可靠映射。
  • 把 anyelement 当成运行时反射系统,忽略类型推导和隐式转换规则。
  • 直接暴露 internal 函数或依赖未公开的内部接口。
  • 用伪类型逃避真正的数据建模,而不是选择 jsonb、组合类型或关联表。