static void pg_decode_change(LogicalDecodingContext *ctx,
ReorderBufferTXN *txn, Relation rel,
ReorderBufferChange *change);
+static void pg_decode_message(LogicalDecodingContext *ctx,
+ ReorderBufferTXN *txn, XLogRecPtr message_lsn,
+ bool transactional, Size sz,
+ const char *message);
void
_PG_init(void)
cb->change_cb = pg_decode_change;
cb->commit_cb = pg_decode_commit_txn;
cb->shutdown_cb = pg_decode_shutdown;
+ cb->message_cb = pg_decode_message;
}
OutputPluginWrite(ctx, true);
}
+
+static void
+pg_decode_message(LogicalDecodingContext *ctx,
+ ReorderBufferTXN *txn, XLogRecPtr lsn,
+ bool transactional, Size sz,
+ const char *message)
+{
+ OutputPluginPrepareWrite(ctx, true);
+ appendStringInfo(ctx->out, "message: lsn: %X/%X transactional: %d sz: %zu content:",
+ (uint32)(lsn >> 32), (uint32)lsn, transactional, sz);
+ appendBinaryStringInfo(ctx->out, message, sz);
+ OutputPluginWrite(ctx, true);
+}
break;
case XLOG_STANDBY_LOCK:
break;
+ case XLOG_STANDBY_MESSAGE:
+ {
+ xl_standby_message *message = (xl_standby_message *) buf->record_data;
+ if (message->transactional &&
+ !SnapBuildProcessChange(builder, r->xl_xid, buf->origptr))
+ break;
+ else if(!message->transactional &&
+ SnapBuildXactNeedsSkip(builder, buf->origptr))
+ break;
+
+ ReorderBufferQueueMessage(ctx->reorder, r->xl_xid, buf->endptr,
+ message->transactional, message->size, message->message);
+ break;
+ }
default:
elog(ERROR, "unexpected RM_STANDBY_ID record type: %u", info);
}
XLogRecPtr commit_lsn);
static void change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
Relation relation, ReorderBufferChange *change);
+static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+ XLogRecPtr message_lsn, bool transactional, Size sz,
+ const char *message);
static void LoadOutputPlugin(OutputPluginCallbacks *callbacks, char *plugin);
ctx->reorder->begin = begin_cb_wrapper;
ctx->reorder->apply_change = change_cb_wrapper;
ctx->reorder->commit = commit_cb_wrapper;
+ ctx->reorder->message = message_cb_wrapper;
ctx->out = makeStringInfo();
ctx->prepare_write = prepare_write;
error_context_stack = errcallback.previous;
}
+static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+ XLogRecPtr message_lsn,
+ bool transactional, Size sz,
+ const char *message)
+{
+ LogicalDecodingContext *ctx = cache->private_data;
+ LogicalErrorCallbackState state;
+ ErrorContextCallback errcallback;
+
+ if (ctx->callbacks.message_cb == NULL)
+ return;
+
+ /* Push callback + info on the error context stack */
+ state.ctx = ctx;
+ state.callback_name = "message";
+ state.report_location = message_lsn; /* beginning of commit record */
+ errcallback.callback = output_plugin_error_callback;
+ errcallback.arg = (void *) &state;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
+
+ /* set output state */
+ ctx->accept_writes = true;
+ ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
+ ctx->write_location = message_lsn;
+
+ /* do the actual work: call callback */
+ ctx->callbacks.message_cb(ctx, txn, message_lsn, transactional,
+ sz, message);
+
+ /* Pop the error context stack */
+ error_context_stack = errcallback.previous;
+}
+
/*
* Set the required catalog xmin horizon for historic snapshots in the current
* replication slot.
#include "funcapi.h"
#include "miscadmin.h"
+#include "access/xact.h"
+
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "replication/logicalfuncs.h"
#include "storage/fd.h"
+#include "storage/standby.h"
/* private date for writing out data */
typedef struct DecodingOutputState
return ret;
}
+
+
+/*
+ * SQL function returning the changestream in binary, only peeking ahead.
+ */
+Datum
+pg_logical_send_message_bytea(PG_FUNCTION_ARGS)
+{
+ bool transactional = PG_GETARG_BOOL(0);
+ bytea *data = PG_GETARG_BYTEA_PP(1);
+ XLogRecPtr lsn;
+
+ lsn = LogStandbyMessage(VARDATA_ANY(data), VARSIZE_ANY_EXHDR(data), transactional);
+ PG_RETURN_LSN(lsn);
+}
+
+Datum
+pg_logical_send_message_text(PG_FUNCTION_ARGS)
+{
+ /* bytea and text are compatible */
+ return pg_logical_send_message_bytea(fcinfo);
+}
change->data.tp.oldtuple = NULL;
}
break;
+ case REORDER_BUFFER_CHANGE_MESSAGE:
+ if (change->data.msg.message != NULL)
+ pfree(change->data.msg.message);
+ change->data.msg.message = NULL;
+ break;
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
if (change->data.snapshot)
{
ReorderBufferCheckSerializeTXN(rb, txn);
}
+void
+ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn,
+ bool transactional, Size sz, const char *msg)
+{
+ ReorderBufferTXN *txn = NULL;
+
+ if (xid != InvalidTransactionId)
+ txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
+
+ if (transactional)
+ {
+ ReorderBufferChange *change;
+
+ Assert(xid != InvalidTransactionId);
+ Assert(txn != NULL);
+
+ change = ReorderBufferGetChange(rb);
+ change->action = REORDER_BUFFER_CHANGE_MESSAGE;
+ change->data.msg.transactional = true;
+ change->data.msg.sz = sz;
+ change->data.msg.message = palloc(sz);
+ memcpy(change->data.msg.message, msg, sz);
+
+ ReorderBufferQueueChange(rb, xid, lsn, change);
+ }
+ else
+ {
+ rb->message(rb, txn, lsn, transactional, sz, msg);
+ }
+}
+
+
static void
AssertTXNLsnOrder(ReorderBuffer *rb)
{
}
RelationClose(relation);
break;
+ case REORDER_BUFFER_CHANGE_MESSAGE:
+ rb->message(rb, txn, change->lsn,
+ change->data.msg.transactional,
+ change->data.msg.sz,
+ change->data.msg.message);
+ break;
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
/* get rid of the old */
TeardownHistoricSnapshot(false);
}
break;
}
+ case REORDER_BUFFER_CHANGE_MESSAGE:
+ {
+ char *data;
+
+ sz += change->data.msg.sz;
+ ReorderBufferSerializeReserve(rb, sz);
+
+ data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
+ memcpy(data, change->data.msg.message,
+ change->data.msg.sz);
+ break;
+ }
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
{
Snapshot snap;
data += len;
}
break;
+ case REORDER_BUFFER_CHANGE_MESSAGE:
+ {
+ Size len = change->data.msg.sz;
+ change->data.msg.message = palloc(len);
+ memcpy(change->data.msg.message, data, len);
+
+ data += len;
+ }
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
{
Snapshot oldsnap;
ProcArrayApplyRecoveryInfo(&running);
}
+ else if (info == XLOG_STANDBY_MESSAGE)
+ {
+ /* Only interesting to logical decoding. Check decode.c */
+ }
else
elog(PANIC, "standby_redo: unknown op code %u", info);
}
*/
(void) GetTopTransactionId();
}
+
+XLogRecPtr
+LogStandbyMessage(const char *message, size_t size, bool transactional)
+{
+ xl_standby_message xlrec;
+ XLogRecData rdata[2];
+
+ /*
+ * Force xid to be allocated if we're sending a transactional message.
+ */
+ if (transactional)
+ GetCurrentTransactionId();
+
+ xlrec.size = size;
+ xlrec.transactional = transactional;
+
+ rdata[0].data = (char *) &xlrec;
+ rdata[0].len = SizeOfStandbyMessage;
+ rdata[0].buffer = InvalidBuffer;
+ rdata[0].next = &rdata[1];
+
+ rdata[1].data = (char *) message;
+ rdata[1].len = size;
+ rdata[1].buffer = InvalidBuffer;
+ rdata[1].next = NULL;
+
+ return XLogInsert(RM_STANDBY_ID, XLOG_STANDBY_MESSAGE, rdata);
+}
DESCR("peek at changes from replication slot");
DATA(insert OID = 3785 ( pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ ));
DESCR("peek at binary changes from replication slot");
+DATA(insert OID = 3791 ( pg_logical_send_message PGNSP PGUID 12 1 0 0 0 f f f f f f v 2 0 3220 "16 25" _null_ _null_ _null_ _null_ pg_logical_send_message_text _null_ _null_ _null_ ));
+DESCR("send a textual message");
+DATA(insert OID = 3792 ( pg_logical_send_message PGNSP PGUID 12 1 0 0 0 f f f f f f v 2 0 3220 "16 17" _null_ _null_ _null_ _null_ pg_logical_send_message_bytea _null_ _null_ _null_ ));
+DESCR("send a binary message");
DATA(insert OID = 6022 ( sequence_local_alloc PGNSP PGUID 12 1 0 0 0 f f f f t f s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ sequence_local_alloc _null_ _null_ _null_ ));
DESCR("Local SequenceAM allocation");
extern Datum pg_logical_slot_peek_changes(PG_FUNCTION_ARGS);
extern Datum pg_logical_slot_peek_binary_changes(PG_FUNCTION_ARGS);
+extern Datum pg_logical_send_message_bytea(PG_FUNCTION_ARGS);
+extern Datum pg_logical_send_message_text(PG_FUNCTION_ARGS);
#endif
struct LogicalDecodingContext *
);
+/*
+ * Called for messages sent by C code.
+ */
+typedef void (*LogicalDecodeMessageCB) (
+ struct LogicalDecodingContext *,
+ ReorderBufferTXN *txn,
+ XLogRecPtr message_lsn,
+ bool transactional, Size sz,
+ const char *message);
+
/*
* Output plugin callbacks
*/
LogicalDecodeChangeCB change_cb;
LogicalDecodeCommitCB commit_cb;
LogicalDecodeShutdownCB shutdown_cb;
+ LogicalDecodeMessageCB message_cb;
} OutputPluginCallbacks;
void OutputPluginPrepareWrite(struct LogicalDecodingContext *ctx, bool last_write);
REORDER_BUFFER_CHANGE_INSERT,
REORDER_BUFFER_CHANGE_UPDATE,
REORDER_BUFFER_CHANGE_DELETE,
+ REORDER_BUFFER_CHANGE_MESSAGE,
REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT,
REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID,
REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID
ReorderBufferTupleBuf *newtuple;
} tp;
+ struct
+ {
+ size_t sz;
+ bool transactional;
+ char *message;
+ } msg;
+
/* New snapshot, set when action == *_INTERNAL_SNAPSHOT */
Snapshot snapshot;
ReorderBufferTXN *txn,
XLogRecPtr commit_lsn);
+/* message callback signature */
+typedef void (*ReorderBufferMessageCB) (
+ ReorderBuffer *rb,
+ ReorderBufferTXN *txn,
+ XLogRecPtr message_lsn,
+ bool transactional, Size sz,
+ const char *message);
+
struct ReorderBuffer
{
/*
ReorderBufferBeginCB begin;
ReorderBufferApplyChangeCB apply_change;
ReorderBufferCommitCB commit;
+ ReorderBufferMessageCB message;
/*
* Pointer that will be passed untouched to the callbacks.
void ReorderBufferReturnChange(ReorderBuffer *, ReorderBufferChange *);
void ReorderBufferQueueChange(ReorderBuffer *, TransactionId, XLogRecPtr lsn, ReorderBufferChange *);
+void ReorderBufferQueueMessage(ReorderBuffer *, TransactionId, XLogRecPtr lsn,
+ bool transactional, Size sz, const char *msg);
void ReorderBufferCommit(ReorderBuffer *, TransactionId,
XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
TimestampTz commit_time, RepNodeId origin_id, XLogRecPtr origin_lsn);
*/
#define XLOG_STANDBY_LOCK 0x00
#define XLOG_RUNNING_XACTS 0x10
+#define XLOG_STANDBY_MESSAGE 0x20
typedef struct xl_standby_locks
{
#define MinSizeOfXactRunningXacts offsetof(xl_running_xacts, xids)
+typedef struct xl_standby_message
+{
+ size_t size;
+ bool transactional;
+ char message[FLEXIBLE_ARRAY_MEMBER];
+} xl_standby_message;
+
+#define SizeOfStandbyMessage (offsetof(xl_standby_message, message))
/* Recovery handlers for the Standby Rmgr (RM_STANDBY_ID) */
extern void standby_redo(XLogRecPtr lsn, XLogRecord *record);
extern void LogAccessExclusiveLockPrepare(void);
extern XLogRecPtr LogStandbySnapshot(void);
+extern XLogRecPtr LogStandbyMessage(const char *message, size_t size, bool transactional);
#endif /* STANDBY_H */