PostgreSQL 18 中文参考文档(未翻译部分保留英文)🌐 切换到英文原版 →

47.6. 逻辑解码输出插件开发 #

47.6.1. 插件初始化函数
47.6.2. 功能与运行约束
47.6.3. 输出模式
47.6.4. 输出插件回调函数详解
47.6.5. 生成输出的辅助函数

在 PostgreSQL 源码树的 contrib/test_decoding 子目录下包含了一个官方的完整示例输出插件。

47.6.1. 插件初始化函数 #

输出插件以动态共享库(.so / .dylib / .dll)的形式提供,系统根据插件名称在标准动态库搜索路径中定位。插件必须导出一个名为 _PG_output_plugin_init 的入口函数。该函数接收一个结构体指针,开发者需在其中填入用于处理具体事件的回调函数指针:

typedef struct OutputPluginCallbacks
{
    LogicalDecodeStartupCB startup_cb;
    LogicalDecodeBeginCB begin_cb;
    LogicalDecodeChangeCB change_cb;
    LogicalDecodeTruncateCB truncate_cb;
    LogicalDecodeCommitCB commit_cb;
    LogicalDecodeMessageCB message_cb;
    LogicalDecodeFilterByOriginCB filter_by_origin_cb;
    LogicalDecodeShutdownCB shutdown_cb;
    LogicalDecodeFilterPrepareCB filter_prepare_cb;
    LogicalDecodeBeginPrepareCB begin_prepare_cb;
    LogicalDecodePrepareCB prepare_cb;
    LogicalDecodeCommitPreparedCB commit_prepared_cb;
    LogicalDecodeRollbackPreparedCB rollback_prepared_cb;
    LogicalDecodeStreamStartCB stream_start_cb;
    LogicalDecodeStreamStopCB stream_stop_cb;
    LogicalDecodeStreamAbortCB stream_abort_cb;
    LogicalDecodeStreamPrepareCB stream_prepare_cb;
    LogicalDecodeStreamCommitCB stream_commit_cb;
    LogicalDecodeStreamChangeCB stream_change_cb;
    LogicalDecodeStreamMessageCB stream_message_cb;
    LogicalDecodeStreamTruncateCB stream_truncate_cb;
} OutputPluginCallbacks;

typedef void (*LogicalOutputPluginInit) (struct OutputPluginCallbacks *cb);

其中 begin_cb、change_cb 与 commit_cb 为强制必须实现的回调;startup_cb、truncate_cb、message_cb、filter_by_origin_cb 与 shutdown_cb 为可选回调。若未设置 truncate_cb,解码流将直接忽略 TRUNCATE 动作。

若插件希望支持对执行中的大事务进行流式传输,则必须实现 stream_start_cb、stream_stop_cb、stream_abort_cb、stream_commit_cb 与 stream_change_cb 回调;stream_message_cb 与 stream_truncate_cb 为可选。若同时支持两阶段提交,还必须实现 stream_prepare_cb。

若插件希望支持两阶段提交,必须实现 begin_prepare_cb、prepare_cb、commit_prepared_cb 与 rollback_prepared_cb,而 filter_prepare_cb 为可选。

47.6.2. 功能与运行约束 #

为了解码、格式化并输出变更,输出插件可以使用服务端后台进程的大部分常规基础设施,包括调用数据类型的输出函数。允许以只读方式访问关系,但仅限于由 initdb 在 pg_catalog 模式下创建的关系,或者通过以下命令显式标记为用户系统目录表的关系:

ALTER TABLE user_catalog_table SET (user_catalog_table = true);
CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);

在输出插件中访问系统目录表或用户目录表时,必须严格使用 systable_* 扫描接口,使用 heap_* 扫描接口会报错退出。此外,严禁在插件中执行任何会导致分配事务 ID 的写操作,这包括向数据表写入数据、执行 DDL 变更以及调用 pg_current_xact_id()。

47.6.3. 输出模式 #

输出插件回调可以向消费者传递几乎任意格式的数据。若插件仅输出当前数据库字符集编码下的纯文本字符串,可以在启动回调中将 OutputPluginOptions.output_type 设置为 OUTPUT_PLUGIN_TEXTUAL_OUTPUT 而非 OUTPUT_PLUGIN_BINARY_OUTPUT。在此模式下所有数据必须与数据库编码完全一致,以便直接作为 text 类型处理。

47.6.4. 输出插件回调函数详解 #

输出插件通过一组标准回调函数接收发生的数据变更。

并发事务严格按照其真实的提交物理顺序进行解码。在 begin 与 commit 回调之间,仅会传递属于该具体事务的变更数据。显式回滚或隐式异常中止的事务永远不会被解码。保存点(Savepoints)按照执行顺序被自动合并到所属的主事务中。

Note

只有已安全刷写到磁盘的事务才会被逻辑解码。当 synchronous_commit 设为 off 时,紧随事务执行后的 pg_logical_slot_get_changes() 可能不会立即看到刚刚提交的事务。

47.6.4.1. 启动回调 #

每当创建复制槽或请求流式传输变更时,可选的 startup_cb 回调均会被触发调用:

typedef void (*LogicalDecodeStartupCB) (struct LogicalDecodingContext *ctx,
                                        OutputPluginOptions *options,
                                        bool is_init);

当复制槽初次创建时 is_init 为 true,其余情况为 false。options 允许插件设置运行参数,包括输出格式模式与是否接收堆重写变更(receive_rewrites)。启动回调还应校验传入的插件选项,并可利用 ctx->output_plugin_private 保存插件的专属私有状态。

47.6.4.2. 关闭清理回调 #

当活跃的复制槽断开或停止流式传输时,可选的 shutdown_cb 回调会被调用,用于释放插件分配的私有资源。这并不代表复制槽被删除,仅代表会话流传输终止。

typedef void (*LogicalDecodeShutdownCB) (struct LogicalDecodingContext *ctx);

47.6.4.3. 事务开始回调 #

当解码出一个已提交事务的开始边界时,调用必须的 begin_cb 回调:

typedef void (*LogicalDecodeBeginCB) (struct LogicalDecodingContext *ctx,
                                      ReorderBufferTXN *txn);

参数 txn 包含了事务的元数据,如提交时间戳与其 XID。

47.6.4.4. 事务提交回调 #

当事务提交被解码完成时调用必须的 commit_cb 回调。在此之前,该事务内所有涉及的行变更均已通过 change_cb 派发完毕:

typedef void (*LogicalDecodeCommitCB) (struct LogicalDecodingContext *ctx,
                                       ReorderBufferTXN *txn,
                                       XLogRecPtr commit_lsn);

47.6.4.5. 行级变更回调 #

对于事务内的每一条单行数据修改(无论是 INSERT、UPDATE 还是 DELETE),均会触发必须的 change_cb 回调:

typedef void (*LogicalDecodeChangeCB) (struct LogicalDecodingContext *ctx,
                                       ReorderBufferTXN *txn,
                                       Relation relation,
                                       ReorderBufferChange *change);

Note

逻辑解码仅能提取非不记录日志(Unlogged,参见 UNLOGGED)且非临时表(Temporary,参见 TEMPORARY or TEMP)的用户自定义表的变更。

47.6.4.6. 表截断回调 #

当执行 TRUNCATE 命令时调用可选的 truncate_cb 回调:

typedef void (*LogicalDecodeTruncateCB) (struct LogicalDecodingContext *ctx,
                                         ReorderBufferTXN *txn,
                                         int nrelations,
                                         Relation relations[],
                                         ReorderBufferChange *change);

由于存在外键级联关联的多张表截断必须原子同步生效,该回调接收关系数组指针。详细语法说明请参考 TRUNCATE。

47.6.4.7. 复制源过滤回调 #

可选的 filter_by_origin_cb 回调用于在解码最前端核验特定复制源 origin_id 回放的数据是否需要被插件接收:

typedef bool (*LogicalDecodeFilterByOriginCB) (struct LogicalDecodingContext *ctx,
                                               RepOriginId origin_id);

若返回 true,代表来自该节点的变更与当前插件无关,系统会将其直接过滤丢弃,从而避免多活与级联架构中的数据循环回环;若返回 false,则正常派发给后续回调处理。通过此回调过滤的效率远高于在输出插件中逐行判断。

47.6.4.8. 通用消息回调 #

当解码出通过逻辑解码消息机制写入的自定义负载时,触发可选的 message_cb 回调:

typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
                                        ReorderBufferTXN *txn,
                                        XLogRecPtr message_lsn,
                                        bool transactional,
                                        const char *prefix,
                                        Size message_size,
                                        const char *message);

prefix 用于标识消息所属插件或命名空间,message 为长度为 message_size 的二进制或文本负载。

47.6.4.9. 预备事务过滤回调 #

可选的 filter_prepare_cb 回调用于判定两阶段提交事务是应该在此预备阶段提前解码,还是推迟到最终 COMMIT PREPARED 时再作为普通单阶段事务解码:

typedef bool (*LogicalDecodeFilterPrepareCB) (struct LogicalDecodingContext *ctx,
                                              TransactionId xid,
                                              const char *gid);

若返回 true 表示跳过此预备阶段的解码;返回 false 则在此阶段解码。若未定义该回调,系统默认对所有两阶段事务均执行两阶段完整解码。

47.6.4.10. 预备事务开始回调 #

当解码出预备事务的起始标记时调用必须的 begin_prepare_cb:

typedef void (*LogicalDecodeBeginPrepareCB) (struct LogicalDecodingContext *ctx,
                                             ReorderBufferTXN *txn);

47.6.4.11. 预备事务确认回调 #

当两阶段事务已完成预备且其涉及的所有行变更已派发完毕时,调用必须的 prepare_cb 回调:

typedef void (*LogicalDecodePrepareCB) (struct LogicalDecodingContext *ctx,
                                        ReorderBufferTXN *txn,
                                        XLogRecPtr prepare_lsn);

47.6.4.12. 预备事务提交回调 #

当解码出 COMMIT PREPARED 时调用必须的 commit_prepared_cb 回调:

typedef void (*LogicalDecodeCommitPreparedCB) (struct LogicalDecodingContext *ctx,
                                               ReorderBufferTXN *txn,
                                               XLogRecPtr commit_lsn);

47.6.4.13. 预备事务回滚回调 #

当解码出 ROLLBACK PREPARED 时调用必须的 rollback_prepared_cb 回调:

typedef void (*LogicalDecodeRollbackPreparedCB) (struct LogicalDecodingContext *ctx,
                                                 ReorderBufferTXN *txn,
                                                 XLogRecPtr prepare_end_lsn,
                                                 TimestampTz prepare_time);

47.6.4.14. 流式分块开始回调 #

当为正在执行的大事务开启一个流式变更分块时调用:

typedef void (*LogicalDecodeStreamStartCB) (struct LogicalDecodingContext *ctx,
                                            ReorderBufferTXN *txn);

47.6.4.15. 流式分块结束回调 #

当关闭一个流式变更分块时调用:

typedef void (*LogicalDecodeStreamStopCB) (struct LogicalDecodingContext *ctx,
                                           ReorderBufferTXN *txn);

47.6.4.16. 流式事务中止回调 #

当中止之前已分批流式传输的事务时调用:

typedef void (*LogicalDecodeStreamAbortCB) (struct LogicalDecodingContext *ctx,
                                            ReorderBufferTXN *txn,
                                            XLogRecPtr abort_lsn);

47.6.4.17. 流式事务预备回调 #

当作为两阶段提交的一部分对流式事务进行预备时调用:

typedef void (*LogicalDecodeStreamPrepareCB) (struct LogicalDecodingContext *ctx,
                                              ReorderBufferTXN *txn,
                                              XLogRecPtr prepare_lsn);

47.6.4.18. 流式事务提交回调 #

当提交之前已分批流式传输的事务时调用:

typedef void (*LogicalDecodeStreamCommitCB) (struct LogicalDecodingContext *ctx,
                                             ReorderBufferTXN *txn,
                                             XLogRecPtr commit_lsn);

47.6.4.19. 流式变更回调 #

在流式变更分块内部发送具体单行修改时调用:

typedef void (*LogicalDecodeStreamChangeCB) (struct LogicalDecodingContext *ctx,
                                             ReorderBufferTXN *txn,
                                             Relation relation,
                                             ReorderBufferChange *change);

47.6.4.20. 流式消息回调 #

在流式分块内部发送通用消息时调用:

typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx,
                                              ReorderBufferTXN *txn,
                                              XLogRecPtr message_lsn,
                                              bool transactional,
                                              const char *prefix,
                                              Size message_size,
                                              const char *message);

47.6.4.21. 流式截断回调 #

在流式分块内部发送 TRUNCATE 命令时调用:

typedef void (*LogicalDecodeStreamTruncateCB) (struct LogicalDecodingContext *ctx,
                                               ReorderBufferTXN *txn,
                                               int nrelations,
                                               Relation relations[],
                                               ReorderBufferChange *change);

47.6.5. 生成输出的辅助函数 #

输出插件在 begin_cb、commit_cb 或 change_cb 等回调函数内部,通过向 ctx->out 中的 StringInfo 缓冲区写入数据来实际生成输出。在写入前需调用 OutputPluginPrepareWrite(ctx, last_write),写入完毕后调用 OutputPluginWrite(ctx, last_write) 完成发送:

OutputPluginPrepareWrite(ctx, true);
appendStringInfo(ctx->out, "BEGIN %u", txn->xid);
OutputPluginWrite(ctx, true);