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

如何在PostgreSQL的C函数中操作Tuple及嵌套复合类型?

嘿,刚好之前折腾过PostgreSQL的C扩展开发,Tuple操作这块确实官方文档例子少得可怜,我给你直接上实操代码和核心要点,包你能快速上手!

一、读取Tuple(含嵌套复合类型)

PostgreSQL里的行数据都是以HeapTuple结构体传递的,读取字段主要靠heap_getattr函数。如果是嵌套复合类型,它的字段值本质是一个打包后的HeapTupleHeader,提取出来后就能当成普通Tuple处理。

#include "postgres.h"
#include "fmgr.h"
#include "executor/executor.h"
#include "utils/heaptuple.h"
#include "utils/tuple.h"
#include "utils/typcache.h"

PG_MODULE_MAGIC;

// 触发器中读取Tuple(含嵌套复合类型)的示例函数
PG_FUNCTION_INFO_V1(read_nested_tuple);

Datum read_nested_tuple(PG_FUNCTION_ARGS) {
    // 从触发器上下文获取Tuple和元数据
    TriggerData *trigdata = (TriggerData *) fcinfo->context;
    HeapTuple target_tuple = trigdata->tg_new;
    TupleDesc tup_desc = trigdata->tg_relation->rd_att;

    // 1. 读取普通text字段:比如表中的name字段
    Datum name_datum = heap_getattr(target_tuple, Anum_mytable_name, tup_desc, NULL);
    char *name_str = TextDatumGetCString(name_datum);
    elog(INFO, "读取到普通字段name: %s", name_str);

    // 2. 读取嵌套复合类型字段:比如address(自定义类型address_type,包含street、city)
    Datum addr_datum = heap_getattr(target_tuple, Anum_mytable_address, tup_desc, NULL);
    HeapTupleHeader addr_header = DatumGetHeapTupleHeader(addr_datum);
    // 通过类型缓存获取嵌套类型的元数据
    TypeCacheEntry *addr_typcache = lookup_type_cache(get_typid_by_name("address_type"), TYPECACHE_TUPLE_DESC);
    TupleDesc addr_tup_desc = addr_typcache->tupDesc;

    // 读取嵌套类型的street字段
    Datum street_datum = heap_getattr(HeapTupleFromHeader(addr_header), Anum_address_type_street, addr_tup_desc, NULL);
    char *street_str = TextDatumGetCString(street_datum);
    elog(INFO, "读取到嵌套字段street: %s", street_str);

    // 读取嵌套类型的city字段
    Datum city_datum = heap_getattr(HeapTupleFromHeader(addr_header), Anum_address_type_city, addr_tup_desc, NULL);
    char *city_str = TextDatumGetCString(city_datum);
    elog(INFO, "读取到嵌套字段city: %s", city_str);

    PG_RETURN_VOID();
}
二、更新Tuple(含嵌套字段修改)

更新Tuple不能直接修改原结构体,得先把原Tuple的字段值提取出来,修改后再重新构建新Tuple。嵌套字段的修改逻辑和主Tuple一致,只是多了一层“拆包-修改-打包”的过程。

// 触发器中更新Tuple(含嵌套字段)的示例函数
PG_FUNCTION_INFO_V1(update_nested_tuple);

Datum update_nested_tuple(PG_FUNCTION_ARGS) {
    TriggerData *trigdata = (TriggerData *) fcinfo->context;
    HeapTuple old_tuple = trigdata->tg_old;
    TupleDesc tup_desc = trigdata->tg_relation->rd_att;
    Datum new_values[Natts_mytable];
    bool is_null[Natts_mytable];

    // 先把原Tuple的所有字段值和null状态复制到数组中
    heap_deform_tuple(old_tuple, tup_desc, new_values, is_null);

    // 1. 修改普通字段:更新name的值
    new_values[Anum_mytable_name - 1] = CStringGetTextDatum("更新后的名字");
    is_null[Anum_mytable_name - 1] = false;

    // 2. 修改嵌套复合类型字段:更新address的city
    // 先拆包原嵌套Tuple
    HeapTupleHeader old_addr_header = DatumGetHeapTupleHeader(new_values[Anum_mytable_address - 1]);
    HeapTuple old_addr_tuple = HeapTupleFromHeader(old_addr_header);
    TypeCacheEntry *addr_typcache = lookup_type_cache(get_typid_by_name("address_type"), TYPECACHE_TUPLE_DESC);
    TupleDesc addr_tup_desc = addr_typcache->tupDesc;
    Datum addr_new_values[Natts_address_type];
    bool addr_is_null[Natts_address_type];

    // 提取嵌套Tuple的字段值
    heap_deform_tuple(old_addr_tuple, addr_tup_desc, addr_new_values, addr_is_null);
    // 修改city字段
    addr_new_values[Anum_address_type_city - 1] = CStringGetTextDatum("新城市");
    addr_is_null[Anum_address_type_city - 1] = false;

    // 重新打包嵌套Tuple
    HeapTuple new_addr_tuple = heap_form_tuple(addr_tup_desc, addr_new_values, addr_is_null);
    new_values[Anum_mytable_address - 1] = HeapTupleGetDatum(new_addr_tuple);
    is_null[Anum_mytable_address - 1] = false;

    // 构建新的主Tuple并返回
    HeapTuple new_tuple = heap_form_tuple(tup_desc, new_values, is_null);
    trigdata->tg_new = new_tuple;
    PG_RETURN_DATUM(HeapTupleGetDatum(new_tuple));
}
三、创建全新的Tuple(含嵌套复合类型)

创建Tuple需要先构建每个字段的Datum值,嵌套类型要先创建好对应的Tuple,再把它打包成Datum传入主Tuple的字段数组。

// 创建含嵌套复合类型的新Tuple示例函数
PG_FUNCTION_INFO_V1(create_nested_tuple);

Datum create_nested_tuple(PG_FUNCTION_ARGS) {
    // 1. 先创建嵌套的address Tuple
    TypeCacheEntry *addr_typcache = lookup_type_cache(get_typid_by_name("address_type"), TYPECACHE_TUPLE_DESC);
    TupleDesc addr_tup_desc = addr_typcache->tupDesc;
    Datum addr_values[Natts_address_type];
    bool addr_is_null[Natts_address_type] = {false}; // 所有字段非空

    addr_values[Anum_address_type_street - 1] = CStringGetTextDatum("456 Oak Ave");
    addr_values[Anum_address_type_city - 1] = CStringGetTextDatum("Los Angeles");
    HeapTuple addr_tuple = heap_form_tuple(addr_tup_desc, addr_values, addr_is_null);
    Datum addr_datum = HeapTupleGetDatum(addr_tuple);

    // 2. 创建主Tuple(假设表mytable有name和address两个字段)
    Relation target_rel = heap_open(get_relname_relid("mytable"), AccessShareLock);
    TupleDesc main_tup_desc = target_rel->rd_att;
    Datum main_values[Natts_mytable];
    bool main_is_null[Natts_mytable] = {false};

    main_values[Anum_mytable_name - 1] = CStringGetTextDatum("Alice");
    main_values[Anum_mytable_address - 1] = addr_datum;
    HeapTuple main_tuple = heap_form_tuple(main_tup_desc, main_values, main_is_null);

    // 可选:将新Tuple插入到表中
    heap_insert(target_rel, main_tuple);
    heap_close(target_rel, AccessShareLock);

    PG_RETURN_DATUM(HeapTupleGetDatum(main_tuple));
}
关键注意事项
  • 必须包含核心头文件:postgres.h、fmgr.h、utils/heaptuple.h、utils/typcache.h这些是操作Tuple的基础
  • 用lookup_type_cache获取复合类型的元数据,比手动构建更安全,还能避免重复计算
  • Datum类型转换要对应:比如text类型用TextDatumGetCString和CStringGetTextDatum,其他类型有对应的转换宏
  • 触发器函数要先检查fcinfo->context是否为TriggerData类型,避免非法访问崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:59:08