41 .
name =
"libpqwalreceiver",
57 bool replication,
bool logical,
59 const char *appname,
char **
err);
64 char **sender_host,
int *sender_port);
73 char **content,
int *
len);
128 elog(
ERROR,
"libpqwalreceiver already loaded");
174 Assert(replication || !logical);
178 keys[++
i] =
"replication";
179 vals[
i] = logical ?
"database" :
"true";
186 keys[++
i] =
"client_encoding";
198 options_val =
psprintf(
"%s -c datestyle=ISO -c intervalstyle=postgres -c extra_float_digits=3",
199 (opt ==
NULL) ?
"" : opt);
200 keys[++
i] =
"options";
211 keys[++
i] =
"dbname";
212 vals[
i] =
"replication";
216 keys[++
i] =
"fallback_application_name";
229 "received message via replication");
246 errmsg(
"password is required"),
247 errdetail(
"Non-superuser cannot connect if the server does not request a password."),
248 errhint(
"Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
255 if (!replication || logical)
272 conn->logical = logical;
338 errmsg(
"password is required"),
339 errdetail(
"Non-superusers must provide a password in the connection string.")));
366 errmsg(
"could not parse connection string: %s",
367 _(
"out of memory"))));
384 buf.len == 0 ?
"" :
" ",
411 if (ret &&
strlen(ret) != 0)
415 if (ret &&
strlen(ret) != 0)
416 *sender_port =
atoi(ret);
439 errmsg(
"could not receive database system identifier and timeline ID from "
440 "the primary server: %s",
450 errmsg(
"invalid response from primary server"),
451 errdetail(
"Could not identify system: got %d rows and %d fields, expected %d rows and %d or more fields.",
511 if (
strcmp(opt->keyword, keyword) == 0 && opt->val &&
548#define appendQuotedIdentifier(b, s) appendQuotedString(b, s, '"')
549#define appendQuotedLiteral(b, s) appendQuotedString(b, s, '\'')
552 * Start streaming WAL data from given streaming options.
554 * Returns true if we switched successfully to copy-both mode. False
555 * means the server received the command and executed it successfully, but
556 * didn't switch to copy-mode. That means that there was no WAL on the
557 * requested timeline and starting point, because the server switched to
558 * another timeline at or before the requested starting point. On failure,
562libpqrcv_startstreaming(WalReceiverConn *conn,
563 const WalRcvStreamOptions *options)
568 Assert(options->logical == conn->logical);
569 Assert(options->slotname || !options->logical);
571 initStringInfo(&cmd);
573 /* Build the command. */
574 appendStringInfoString(&cmd, "START_REPLICATION");
575 if (options->slotname != NULL)
577 appendStringInfoString(&cmd, " SLOT ");
578 appendQuotedIdentifier(&cmd, options->slotname);
581 if (options->logical)
582 appendStringInfoString(&cmd, " LOGICAL");
584 appendStringInfo(&cmd, " %X/%08X", LSN_FORMAT_ARGS(options->startpoint));
587 * Additional options are different depending on if we are doing logical
588 * or physical replication.
590 if (options->logical)
595 appendStringInfoString(&cmd, " (");
597 appendStringInfo(&cmd, "proto_version '%u'",
598 options->proto.logical.proto_version);
600 if (options->proto.logical.streaming_str)
602 appendStringInfoString(&cmd, ", streaming ");
603 appendQuotedLiteral(&cmd, options->proto.logical.streaming_str);
606 if (options->proto.logical.twophase &&
607 PQserverVersion(conn->streamConn) >= 150000)
608 appendStringInfoString(&cmd, ", two_phase 'on'");
610 if (options->proto.logical.origin &&
611 PQserverVersion(conn->streamConn) >= 160000)
613 appendStringInfoString(&cmd, ", origin ");
614 appendQuotedLiteral(&cmd, options->proto.logical.origin);
617 pubnames = options->proto.logical.publication_names;
618 pubnames_str = stringlist_to_identifierstr(pubnames);
619 appendStringInfoString(&cmd, ", publication_names ");
620 appendQuotedLiteral(&cmd, pubnames_str);
623 if (options->proto.logical.binary &&
624 PQserverVersion(conn->streamConn) >= 140000)
625 appendStringInfoString(&cmd, ", binary 'true'");
627 appendStringInfoChar(&cmd, ')');
630 appendStringInfo(&cmd, " TIMELINE %u",
631 options->proto.physical.startpointTLI);
633 /* Start streaming. */
634 res = libpqsrv_exec(conn->streamConn,
636 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
639 if (PQresultStatus(res) == PGRES_COMMAND_OK)
644 else if (PQresultStatus(res) != PGRES_COPY_BOTH)
646 (errcode(ERRCODE_PROTOCOL_VIOLATION),
647 errmsg("could not start WAL streaming: %s",
648 pchomp(PQerrorMessage(conn->streamConn)))));
654 * Stop streaming WAL data. Returns the next timeline's ID in *next_tli, as
655 * reported by the server, or 0 if it did not report it.
658libpqrcv_endstreaming(WalReceiverConn *conn, TimeLineID *next_tli)
663 * Send copy-end message. As in libpqsrv_exec, this could theoretically
664 * block, but the risk seems small.
666 if (PQputCopyEnd(conn->streamConn, NULL) <= 0 ||
667 PQflush(conn->streamConn))
669 (errcode(ERRCODE_CONNECTION_FAILURE),
670 errmsg("could not send end-of-streaming message to primary: %s",
671 pchomp(PQerrorMessage(conn->streamConn)))));
676 * After COPY is finished, we should receive a result set indicating the
677 * next timeline's ID, or just CommandComplete if the server was shut
680 * If we had not yet received CopyDone from the backend, PGRES_COPY_OUT is
681 * also possible in case we aborted the copy in mid-stream.
683 res = libpqsrv_get_result(conn->streamConn,
684 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
685 if (PQresultStatus(res) == PGRES_TUPLES_OK)
688 * Read the next timeline's ID. The server also sends the timeline's
689 * starting point, but it is ignored.
691 if (PQnfields(res) < 2 || PQntuples(res) != 1)
693 (errcode(ERRCODE_PROTOCOL_VIOLATION),
694 errmsg("unexpected result set after end-of-streaming")));
695 *next_tli = pg_strtoint32(PQgetvalue(res, 0, 0));
698 /* the result set should be followed by CommandComplete */
699 res = libpqsrv_get_result(conn->streamConn,
700 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
702 else if (PQresultStatus(res) == PGRES_COPY_OUT)
707 if (PQendcopy(conn->streamConn))
709 (errcode(ERRCODE_CONNECTION_FAILURE),
710 errmsg("error while shutting down streaming COPY: %s",
711 pchomp(PQerrorMessage(conn->streamConn)))));
713 /* CommandComplete should follow */
714 res = libpqsrv_get_result(conn->streamConn,
715 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
718 if (PQresultStatus(res) != PGRES_COMMAND_OK)
720 (errcode(ERRCODE_PROTOCOL_VIOLATION),
721 errmsg("error reading result of streaming command: %s",
722 pchomp(PQerrorMessage(conn->streamConn)))));
725 /* Verify that there are no more results */
726 res = libpqsrv_get_result(conn->streamConn,
727 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
730 (errcode(ERRCODE_PROTOCOL_VIOLATION),
731 errmsg("unexpected result after CommandComplete: %s",
732 pchomp(PQerrorMessage(conn->streamConn)))));
736 * Fetch the timeline history file for 'tli' from primary.
739libpqrcv_readtimelinehistoryfile(WalReceiverConn *conn,
740 TimeLineID tli, char **filename,
741 char **content, int *len)
746 Assert(!conn->logical);
749 * Request the primary to send over the history file for given timeline.
751 snprintf(cmd, sizeof(cmd), "TIMELINE_HISTORY %u", tli);
752 res = libpqsrv_exec(conn->streamConn,
754 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
755 if (PQresultStatus(res) != PGRES_TUPLES_OK)
757 (errcode(ERRCODE_PROTOCOL_VIOLATION),
758 errmsg("could not receive timeline history file from "
759 "the primary server: %s",
760 pchomp(PQerrorMessage(conn->streamConn)))));
761 if (PQnfields(res) != 2 || PQntuples(res) != 1)
763 (errcode(ERRCODE_PROTOCOL_VIOLATION),
764 errmsg("invalid response from primary server"),
765 errdetail("Expected 1 tuple with 2 fields, got %d tuples with %d fields.",
766 PQntuples(res), PQnfields(res))));
767 *filename = pstrdup(PQgetvalue(res, 0, 0));
769 *len = PQgetlength(res, 0, 1);
770 *content = palloc(*len);
771 memcpy(*content, PQgetvalue(res, 0, 1), *len);
776 * Disconnect connection to primary, if any.
779libpqrcv_disconnect(WalReceiverConn *conn)
781 libpqsrv_disconnect(conn->streamConn);
782 PQfreemem(conn->recvBuf);
787 * Receive a message available from XLOG stream.
791 * If data was received, returns the length of the data. *buffer is set to
792 * point to a buffer holding the received message. The buffer is only valid
793 * until the next libpqrcv_* call.
795 * If no data was available immediately, returns 0, and *wait_fd is set to a
796 * socket descriptor which can be waited on before trying again.
798 * -1 if the server ended the COPY.
803libpqrcv_receive(WalReceiverConn *conn, char **buffer,
808 PQfreemem(conn->recvBuf);
809 conn->recvBuf = NULL;
811 /* Try to receive a CopyData message */
812 rawlen = PQgetCopyData(conn->streamConn, &conn->recvBuf, 1);
815 /* Try consuming some data. */
816 if (PQconsumeInput(conn->streamConn) == 0)
818 (errcode(ERRCODE_CONNECTION_FAILURE),
819 errmsg("could not receive data from WAL stream: %s",
820 pchomp(PQerrorMessage(conn->streamConn)))));
822 /* Now that we've consumed some input, try again */
823 rawlen = PQgetCopyData(conn->streamConn, &conn->recvBuf, 1);
826 /* Tell caller to try again when our socket is ready. */
827 *wait_fd = PQsocket(conn->streamConn);
831 if (rawlen == -1) /* end-of-streaming or error */
835 res = libpqsrv_get_result(conn->streamConn,
836 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
837 if (PQresultStatus(res) == PGRES_COMMAND_OK)
841 /* Verify that there are no more results. */
842 res = libpqsrv_get_result(conn->streamConn,
843 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
849 * If the other side closed the connection orderly (otherwise
850 * we'd seen an error, or PGRES_COPY_IN) don't report an error
851 * here, but let callers deal with it.
853 if (PQstatus(conn->streamConn) == CONNECTION_BAD)
857 (errcode(ERRCODE_PROTOCOL_VIOLATION),
858 errmsg("unexpected result after CommandComplete: %s",
859 PQerrorMessage(conn->streamConn))));
864 else if (PQresultStatus(res) == PGRES_COPY_IN)
871 (errcode(ERRCODE_PROTOCOL_VIOLATION),
872 errmsg("could not receive data from WAL stream: %s",
873 pchomp(PQerrorMessage(conn->streamConn)))));
877 (errcode(ERRCODE_PROTOCOL_VIOLATION),
878 errmsg("could not receive data from WAL stream: %s",
879 pchomp(PQerrorMessage(conn->streamConn)))));
881 /* Return received messages to caller */
882 *buffer = conn->recvBuf;
887 * Send a message to XLOG stream.
892libpqrcv_send(WalReceiverConn *conn, const char *buffer, int nbytes)
894 if (PQputCopyData(conn->streamConn, buffer, nbytes) <= 0 ||
895 PQflush(conn->streamConn))
897 (errcode(ERRCODE_CONNECTION_FAILURE),
898 errmsg("could not send data to WAL stream: %s",
899 pchomp(PQerrorMessage(conn->streamConn)))));
903 * Create new replication slot.
904 * Returns the name of the exported snapshot for logical slot or NULL for
908libpqrcv_create_slot(WalReceiverConn *conn, const char *slotname,
909 bool temporary, bool two_phase, bool failover,
910 CRSSnapshotAction snapshot_action, XLogRecPtr *lsn)
915 int use_new_options_syntax;
917 use_new_options_syntax = (PQserverVersion(conn->streamConn) >= 150000);
919 initStringInfo(&cmd);
921 appendStringInfoString(&cmd, "CREATE_REPLICATION_SLOT ");
922 appendQuotedIdentifier(&cmd, slotname);
925 appendStringInfoString(&cmd, " TEMPORARY");
929 appendStringInfoString(&cmd, " LOGICAL pgoutput ");
930 if (use_new_options_syntax)
931 appendStringInfoChar(&cmd, '(');
934 appendStringInfoString(&cmd, "TWO_PHASE");
935 if (use_new_options_syntax)
936 appendStringInfoString(&cmd, ", ");
938 appendStringInfoChar(&cmd, ' ');
943 appendStringInfoString(&cmd, "FAILOVER");
944 if (use_new_options_syntax)
945 appendStringInfoString(&cmd, ", ");
947 appendStringInfoChar(&cmd, ' ');
950 if (use_new_options_syntax)
952 switch (snapshot_action)
954 case CRS_EXPORT_SNAPSHOT:
955 appendStringInfoString(&cmd, "SNAPSHOT 'export'");
957 case CRS_NOEXPORT_SNAPSHOT:
958 appendStringInfoString(&cmd, "SNAPSHOT 'nothing'");
960 case CRS_USE_SNAPSHOT:
961 appendStringInfoString(&cmd, "SNAPSHOT 'use'");
967 switch (snapshot_action)
969 case CRS_EXPORT_SNAPSHOT:
970 appendStringInfoString(&cmd, "EXPORT_SNAPSHOT");
972 case CRS_NOEXPORT_SNAPSHOT:
973 appendStringInfoString(&cmd, "NOEXPORT_SNAPSHOT");
975 case CRS_USE_SNAPSHOT:
976 appendStringInfoString(&cmd, "USE_SNAPSHOT");
981 if (use_new_options_syntax)
982 appendStringInfoChar(&cmd, ')');
986 if (use_new_options_syntax)
987 appendStringInfoString(&cmd, " PHYSICAL (RESERVE_WAL)");
989 appendStringInfoString(&cmd, " PHYSICAL RESERVE_WAL");
992 res = libpqsrv_exec(conn->streamConn,
994 WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
997 if (PQresultStatus(res) != PGRES_TUPLES_OK)
999 (errcode(ERRCODE_PROTOCOL_VIOLATION),
1000 errmsg("could not create replication slot \"%s\": %s",
1052 errmsg(
"could not alter replication slot \"%s\": %s",
1086 errmsg(
"invalid query response"),
1087 errdetail(
"Expected %d fields, got %d fields.",
1106 "libpqrcv query result context",
1158 errmsg(
"the query interface requires a database connection")));
1192 walres->err =
_(
"empty query");
1198 walres->err =
_(
"unexpected pipeline mode");
#define Assert(condition)
#define ALWAYS_SECURE_SEARCH_PATH_SQL
int errcode(int sqlerrcode)
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define MAKE_SQLSTATE(ch1, ch2, ch3, ch4, ch5)
#define ereport(elevel,...)
void err(int eval, const char *fmt,...)
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
int PQserverVersion(const PGconn *conn)
char * PQport(const PGconn *conn)
char * PQhost(const PGconn *conn)
int PQconnectionUsedPassword(const PGconn *conn)
PQconninfoOption * PQconninfo(PGconn *conn)
void PQconninfoFree(PQconninfoOption *connOptions)
PQconninfoOption * PQconninfoParse(const char *conninfo, char **errmsg)
#define ERRCODE_PROTOCOL_VIOLATION
ConnStatusType PQstatus(const PGconn *conn)
PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
int PQbackendPID(const PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
void PQfreemem(void *ptr)
#define palloc0_object(type)
Datum DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
#define PG_MODULE_MAGIC_EXT(...)
#define MaxTupleAttributeNumber
static PGresult * libpqsrv_exec(PGconn *conn, const char *query, uint32 wait_event_info)
static void libpqsrv_connect_complete(PGconn *conn, uint32 wait_event_info)
static void libpqsrv_notice_receiver(void *arg, const PGresult *res)
static void libpqsrv_disconnect(PGconn *conn)
static PGconn * libpqsrv_connect_params_start(const char *const *keywords, const char *const *values, int expand_dbname)
#define PQresultErrorField
static WalReceiverConn * libpqrcv_connect(const char *conninfo, bool replication, bool logical, bool must_use_password, const char *appname, char **err)
static void libpqrcv_readtimelinehistoryfile(WalReceiverConn *conn, TimeLineID tli, char **filename, char **content, int *len)
static pid_t libpqrcv_get_backend_pid(WalReceiverConn *conn)
static void libpqrcv_send(WalReceiverConn *conn, const char *buffer, int nbytes)
static void libpqrcv_disconnect(WalReceiverConn *conn)
static char * libpqrcv_identify_system(WalReceiverConn *conn, TimeLineID *primary_tli)
static char * libpqrcv_create_slot(WalReceiverConn *conn, const char *slotname, bool temporary, bool two_phase, bool failover, CRSSnapshotAction snapshot_action, XLogRecPtr *lsn)
static WalRcvExecResult * libpqrcv_exec(WalReceiverConn *conn, const char *query, const int nRetTypes, const Oid *retTypes)
static void libpqrcv_endstreaming(WalReceiverConn *conn, TimeLineID *next_tli)
static void libpqrcv_get_senderinfo(WalReceiverConn *conn, char **sender_host, int *sender_port)
static int libpqrcv_server_version(WalReceiverConn *conn)
static void libpqrcv_check_conninfo(const char *conninfo, bool must_use_password)
static bool libpqrcv_startstreaming(WalReceiverConn *conn, const WalRcvStreamOptions *options)
static int libpqrcv_receive(WalReceiverConn *conn, char **buffer, pgsocket *wait_fd)
static char * stringlist_to_identifierstr(List *strings)
static void libpqrcv_alter_slot(WalReceiverConn *conn, const char *slotname, const bool *failover, const bool *two_phase)
static WalReceiverFunctionsType PQWalReceiverFunctions
#define appendQuotedIdentifier(b, s)
static char * libpqrcv_get_option_from_conninfo(const char *connInfo, const char *keyword)
static char * libpqrcv_get_conninfo(WalReceiverConn *conn)
static void libpqrcv_processTuples(PGresult *pgres, WalRcvExecResult *walres, const int nRetTypes, const Oid *retTypes)
static char * libpqrcv_get_dbname_from_conninfo(const char *connInfo)
static void appendQuotedString(StringInfo buf, const char *str, char quote)
const char * GetDatabaseEncodingName(void)
void MemoryContextReset(MemoryContext context)
char * pstrdup(const char *in)
void pfree(void *pointer)
char * pchomp(const char *in)
MemoryContext CurrentMemoryContext
void MemoryContextDelete(MemoryContext context)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define CHECK_FOR_INTERRUPTS()
int32 pg_strtoint32(const char *s)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
static AmcheckOptions opts
Datum pg_lsn_in(PG_FUNCTION_ARGS)
static XLogRecPtr DatumGetLSN(Datum X)
static char buf[DEFAULT_XLOG_SEG_SIZE]
static Datum CStringGetDatum(const char *X)
void initPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void termPQExpBuffer(PQExpBuffer str)
#define PQExpBufferDataBroken(buf)
char * psprintf(const char *fmt,...)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void appendStringInfoChar(StringInfo str, char ch)
void initStringInfo(StringInfo str)
walrcv_connect_fn walrcv_connect
TupleDesc CreateTemplateTupleDesc(int natts)
void TupleDescFinalize(TupleDesc tupdesc)
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
WalReceiverFunctionsType * WalReceiverFunctions