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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 04:09:02