static void pg_output_begin(LogicalDecodingContext* ctx, DecodingJsonData* data, ReorderBufferTXN* txn, bool last_write) { OutputPluginPrepareWrite(ctx, last_write); appendStringInfo( ctx->out, "{\"type\":\"transaction.begin\",\"xid\":\"%u\",\"committed\":\"%s\"}", txn->xid, timestamptz_to_str(txn->commit_time) ); OutputPluginWrite(ctx, last_write); }
static void pg_decode_commit_txn(LogicalDecodingContext* ctx, ReorderBufferTXN* txn, XLogRecPtr commit_lsn) { OutputPluginPrepareWrite(ctx, true); appendStringInfo( ctx->out, "{\"type\":\"transaction.commit\",\"xid\":\"%u\",\"committed\":\"%s\"}", txn->xid, timestamptz_to_str(txn->commit_time) ); OutputPluginWrite(ctx, true); }
int write_frame(LogicalDecodingContext *ctx, plugin_state *state) { int err = 0; bytea *output = NULL; check(err, try_writing(&output, &write_avro_binary, &state->frame_value)); OutputPluginPrepareWrite(ctx, true); appendBinaryStringInfo(ctx->out, VARDATA(output), VARSIZE(output) - VARHDRSZ); OutputPluginWrite(ctx, true); pfree(output); return err; }
/* COMMIT callback */ static void decoder_raw_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, XLogRecPtr commit_lsn) { DecoderRawData *data = ctx->output_plugin_private; if (!data->isLocal) { XTM_INFO("Send commit of transaction %u to replica\n", txn->xid); OutputPluginPrepareWrite(ctx, true); appendStringInfoString(ctx->out, "COMMIT;"); OutputPluginWrite(ctx, true); } else { XTM_INFO("Skip commit of transaction %u\n", txn->xid); } }
static void decoder_raw_begin_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn) { DecoderRawData *data = ctx->output_plugin_private; Assert(lastXid != txn->xid); lastXid = txn->xid; if (MMIsLocalTransaction(txn->xid)) { XTM_INFO("Skip local transaction %u\n", txn->xid); data->isLocal = true; } else { OutputPluginPrepareWrite(ctx, true); XTM_INFO("Send transaction %u to replica\n", txn->xid); appendStringInfoString(ctx->out, "BEGIN;"); OutputPluginWrite(ctx, true); data->isLocal = false; } }
static void pg_decode_change(LogicalDecodingContext* ctx, ReorderBufferTXN* txn, Relation relation, ReorderBufferChange* change) { DecodingJsonData* data; Form_pg_class class_form; TupleDesc tupdesc; HeapTuple tuple; MemoryContext old; data = ctx->output_plugin_private; data->xact_wrote_changes = true; class_form = RelationGetForm(relation); tupdesc = RelationGetDescr(relation); old = MemoryContextSwitchTo(data->context); OutputPluginPrepareWrite(ctx, true); appendStringInfoString(ctx->out, "{\"type\":\"table\""); appendStringInfo( ctx->out, ",\"schema\":\"%s\"", get_namespace_name( get_rel_namespace( RelationGetRelid(relation) ) ) ); appendStringInfo(ctx->out, ",\"name\":\"%s\"", NameStr(class_form->relname)); appendStringInfo( ctx->out, ",\"change\":\"%s\"", change->action == REORDER_BUFFER_CHANGE_INSERT ? "INSERT" : change->action == REORDER_BUFFER_CHANGE_UPDATE ? "UPDATE" : change->action == REORDER_BUFFER_CHANGE_DELETE ? "DELETE" : "FIXME" ); if (change->action == REORDER_BUFFER_CHANGE_UPDATE || change->action == REORDER_BUFFER_CHANGE_DELETE) { appendStringInfoString(ctx->out, ",\"key\":{"); RelationGetIndexList(relation); if (OidIsValid(relation->rd_replidindex)) { int i; Relation index = index_open(relation->rd_replidindex, ShareLock); tuple = change->data.tp.oldtuple ? &change->data.tp.oldtuple->tuple : &change->data.tp.newtuple->tuple; for (i = 0; i < index->rd_index->indnatts; i++) { int j = index->rd_index->indkey.values[i]; Form_pg_attribute attr = tupdesc->attrs[j - 1]; if (i > 0) appendStringInfoChar(ctx->out, ','); appendStringInfo(ctx->out, "\"%s\":", NameStr(attr->attname)); print_value(ctx->out, tupdesc, tuple, j - 1); } index_close(index, NoLock); } else { appendStringInfoString(ctx->out, "\"***FIXME***\""); } appendStringInfoChar(ctx->out, '}'); } if (change->action == REORDER_BUFFER_CHANGE_UPDATE || change->action == REORDER_BUFFER_CHANGE_INSERT) { appendStringInfoString(ctx->out, ",\"data\":{"); tuple_to_stringinfo(ctx->out, tupdesc, &change->data.tp.newtuple->tuple, false); appendStringInfoChar(ctx->out, '}'); } appendStringInfoChar(ctx->out, '}'); MemoryContextSwitchTo(old); MemoryContextReset(data->context); OutputPluginWrite(ctx, true); }
/* * Callback for individual changed tuples */ static void decoder_raw_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change) { DecoderRawData *data; MemoryContext old; char replident = relation->rd_rel->relreplident; bool is_rel_non_selective; data = ctx->output_plugin_private; if (data->isLocal) { XTM_INFO("Skip action %d in transaction %u\n", change->action, txn->xid); return; } XTM_INFO("Send action %d in transaction %u to replica\n", change->action, txn->xid); /* Avoid leaking memory by using and resetting our own context */ old = MemoryContextSwitchTo(data->context); /* * Determine if relation is selective enough for WHERE clause generation * in UPDATE and DELETE cases. A non-selective relation uses REPLICA * IDENTITY set as NOTHING, or DEFAULT without an available replica * identity index. */ RelationGetIndexList(relation); is_rel_non_selective = (replident == REPLICA_IDENTITY_NOTHING || (replident == REPLICA_IDENTITY_DEFAULT && !OidIsValid(relation->rd_replidindex))); /* Decode entry depending on its type */ switch (change->action) { case REORDER_BUFFER_CHANGE_INSERT: if (change->data.tp.newtuple != NULL) { OutputPluginPrepareWrite(ctx, true); decoder_raw_insert(ctx->out, relation, &change->data.tp.newtuple->tuple); OutputPluginWrite(ctx, true); } break; case REORDER_BUFFER_CHANGE_UPDATE: if (!is_rel_non_selective) { HeapTuple oldtuple = change->data.tp.oldtuple != NULL ? &change->data.tp.oldtuple->tuple : NULL; HeapTuple newtuple = change->data.tp.newtuple != NULL ? &change->data.tp.newtuple->tuple : NULL; OutputPluginPrepareWrite(ctx, true); decoder_raw_update(ctx->out, relation, oldtuple, newtuple); OutputPluginWrite(ctx, true); } break; case REORDER_BUFFER_CHANGE_DELETE: if (!is_rel_non_selective) { OutputPluginPrepareWrite(ctx, true); decoder_raw_delete(ctx->out, relation, &change->data.tp.oldtuple->tuple); OutputPluginWrite(ctx, true); } break; default: /* Should not come here */ Assert(0); break; } MemoryContextSwitchTo(old); MemoryContextReset(data->context); }