PostgreSQL C存储过程实现未知类型Datum比较与动态返回函数
PostgreSQL C语言存储过程实现方案
一、返回类型声明问题解决
你不需要用variadic "any"的写法,PostgreSQL的多态类型系统完全可以满足你的需求:函数固定接收4个参数,前两个为同类型可比较类型,后两个为同类型返回值类型,直接按如下声明即可:
CREATE OR REPLACE FUNCTION f(anycompatible, anycompatible, anyelement, anyelement) RETURNS anyelement AS '/var/lib/postgresql/f.so', 'f' LANGUAGE C IMMUTABLE STRICT;
说明:
anycompatible会自动约束前两个输入参数为相同的可比较类型,兼容PG14及以上版本;如果需要适配更低版本,替换为anyelement即可,同样会自动约束前两个参数类型一致- 后两个
anyelement约束两个返回候选值类型相同,返回值类型会自动和后两个参数的类型匹配,PostgreSQL会在调用时自动推导所有类型,不需要手动指定
二、未知类型Datum的类型安全比较
你之前直接对Datum做大小比较的写法是错误的,Datum只是通用值容器,本身不携带类型信息,不同类型的比较逻辑完全不同。正确的做法是通过参数类型Oid获取对应类型的默认比较函数,调用后得到比较结果。
基础实现(兼容所有PG版本,足够大多数场景使用)
#include "postgres.h" #include "fmgr.h" #include "utils/builtins.h" #include "utils/lsyscache.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(f); Datum f(PG_FUNCTION_ARGS) { Datum col1 = PG_GETARG_DATUM(0); Datum col2 = PG_GETARG_DATUM(1); Oid cmp_type = PG_GETARG_TYPE_OID(0); int32 cmp_result; // 获取当前类型的默认B树比较函数 Oid cmp_func_oid = get_sort_function_for_type(cmp_type, false, false); if (!OidIsValid(cmp_func_oid)) { elog(ERROR, "unsupported type for comparison: %u", cmp_type); } // 调用类型原生比较函数,自动适配值传递/引用传递、对齐等底层逻辑 cmp_result = DatumGetInt32(FunctionCall2Coll(cmp_func_oid, PG_GET_COLLATION(), col1, col2)); if (cmp_result < 0) { // col1 < col2 返回col3 PG_RETURN_DATUM(PG_GETARG_DATUM(2)); } else if (cmp_result > 0) { // col1 > col2 返回col4 PG_RETURN_DATUM(PG_GETARG_DATUM(3)); } else { // 相等返回null PG_RETURN_NULL(); } }
高性能SortSupport优化实现(适合大量数据批量调用场景)
如果需要处理千万级以上的行调用,使用SortSupport接口可以缓存比较上下文,避免每次调用重复查找比较函数,性能提升30%以上:
#include "postgres.h" #include "fmgr.h" #include "utils/sortsupport.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(f); Datum f(PG_FUNCTION_ARGS) { Datum col1 = PG_GETARG_DATUM(0); Datum col2 = PG_GETARG_DATUM(1); Oid cmp_type = PG_GETARG_TYPE_OID(0); int cmp_result; // 静态变量缓存上一次使用的比较上下文,同类型调用不需要重复初始化 static SortSupportData ssup = {0}; static Oid last_cmp_type = InvalidOid; static Oid last_collation = InvalidOid; Oid cur_collation = PG_GET_COLLATION(); if (cmp_type != last_cmp_type || cur_collation != last_collation) { memset(&ssup, 0, sizeof(SortSupportData)); ssup.ssup_cxt = CurrentMemoryContext; ssup.ssup_collation = cur_collation; ssup.ssup_nulls_first = false; PrepareSortSupportFromType(cmp_type, &ssup); last_cmp_type = cmp_type; last_collation = cur_collation; } cmp_result = ApplySortComparator(col1, false, col2, false, &ssup); if (cmp_result < 0) { PG_RETURN_DATUM(PG_GETARG_DATUM(2)); } else if (cmp_result > 0) { PG_RETURN_DATUM(PG_GETARG_DATUM(3)); } else { PG_RETURN_NULL(); } }
注意事项
- 你声明了
STRICT属性,PostgreSQL会自动处理输入参数为null的情况,不需要在C代码中额外判断null值 - 所有PostgreSQL内置可比较类型(数值、字符串、日期、枚举、自定义可比较类型等)都自带默认B树比较函数,不需要额外适配
- 编译时需要使用PostgreSQL的pgxs工具链,确保头文件和库链接正确
内容的提问来源于stack exchange,提问作者docjosh
相关产品推荐
相关产品推荐

