PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_recvlogical.c File Reference
#include "postgres_fe.h"
#include <dirent.h>
#include <limits.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common/file_perm.h"
#include "common/logging.h"
#include "fe_utils/option_utils.h"
#include "getopt_long.h"
#include "libpq-fe.h"
#include "libpq/pqsignal.h"
#include "libpq/protocol.h"
#include "pqexpbuffer.h"
#include "streamutil.h"
Include dependency graph for pg_recvlogical.c:

Go to the source code of this file.

Macros

#define RECONNECT_SLEEP_TIME   5
 

Enumerations

enum  StreamStopReason { STREAM_STOP_NONE , STREAM_STOP_END_OF_WAL , STREAM_STOP_KEEPALIVE , STREAM_STOP_SIGNAL }
 

Functions

static void usage (void)
 
static void StreamLogicalLog (void)
 
static bool flushAndSendFeedback (PGconn *conn, TimestampTz *now)
 
static void prepareToTerminate (PGconn *conn, XLogRecPtr endpos, StreamStopReason reason, XLogRecPtr lsn)
 
static bool sendFeedback (PGconn *conn, TimestampTz now, bool force, bool replyRequested)
 
static void disconnect_atexit (void)
 
static void OutputFsync (TimestampTz now)
 
static void sigexit_handler (SIGNAL_ARGS)
 
static void sighup_handler (SIGNAL_ARGS)
 
int main (int argc, char **argv)
 

Variables

static charoutfile = NULL
 
static int verbose = 0
 
static bool two_phase = false
 
static bool failover = false
 
static int noloop = 0
 
static int standby_message_timeout = 10 * 1000
 
static int fsync_interval = 10 * 1000
 
static XLogRecPtr startpos = InvalidXLogRecPtr
 
static XLogRecPtr endpos = InvalidXLogRecPtr
 
static bool do_create_slot = false
 
static bool slot_exists_ok = false
 
static bool do_start_slot = false
 
static bool do_drop_slot = false
 
static charreplication_slot = NULL
 
static char ** options
 
static size_t noptions = 0
 
static const charplugin = "test_decoding"
 
static int outfd = -1
 
static volatile sig_atomic_t time_to_abort = false
 
static volatile sig_atomic_t stop_reason = STREAM_STOP_NONE
 
static volatile sig_atomic_t output_reopen = false
 
static bool output_isfile
 
static TimestampTz output_last_fsync = -1
 
static bool output_needs_fsync = false
 
static XLogRecPtr output_written_lsn = InvalidXLogRecPtr
 
static XLogRecPtr output_fsync_lsn = InvalidXLogRecPtr
 

Macro Definition Documentation

◆ RECONNECT_SLEEP_TIME

#define RECONNECT_SLEEP_TIME   5

Definition at line 32 of file pg_recvlogical.c.

Enumeration Type Documentation

◆ StreamStopReason

Enumerator
STREAM_STOP_NONE 
STREAM_STOP_END_OF_WAL 
STREAM_STOP_KEEPALIVE 
STREAM_STOP_SIGNAL 

Definition at line 34 of file pg_recvlogical.c.

35{
StreamStopReason
@ STREAM_STOP_KEEPALIVE
@ STREAM_STOP_END_OF_WAL
@ STREAM_STOP_NONE
@ STREAM_STOP_SIGNAL

Function Documentation

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 181 of file pg_recvlogical.c.

182{
183 if (conn != NULL)
184 PQfinish(conn);
185}
void PQfinish(PGconn *conn)
static int fb(int x)
PGconn * conn
Definition streamutil.c:52

References conn, fb(), and PQfinish().

Referenced by main().

◆ flushAndSendFeedback()

static bool flushAndSendFeedback ( PGconn conn,
TimestampTz now 
)
static

Definition at line 1058 of file pg_recvlogical.c.

1059{
1060 /* flush data to disk, so that we send a recent flush pointer */
1061 OutputFsync(*now);
1063 if (!sendFeedback(conn, *now, true, false))
1064 return false;
1065
1066 return true;
1067}
Datum now(PG_FUNCTION_ARGS)
Definition timestamp.c:1613
static bool sendFeedback(PGconn *conn, TimestampTz now, bool force, bool replyRequested)
static void OutputFsync(TimestampTz now)
TimestampTz feGetCurrentTimestamp(void)
Definition streamutil.c:830

References conn, feGetCurrentTimestamp(), now(), OutputFsync(), and sendFeedback().

Referenced by prepareToTerminate(), and StreamLogicalLog().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 696 of file pg_recvlogical.c.

697{
698 static struct option long_options[] = {
699/* general options */
700 {"file", required_argument, NULL, 'f'},
701 {"fsync-interval", required_argument, NULL, 'F'},
702 {"no-loop", no_argument, NULL, 'n'},
703 {"enable-failover", no_argument, NULL, 5},
704 {"enable-two-phase", no_argument, NULL, 't'},
705 {"two-phase", no_argument, NULL, 't'}, /* deprecated */
706 {"verbose", no_argument, NULL, 'v'},
707 {"version", no_argument, NULL, 'V'},
708 {"help", no_argument, NULL, '?'},
709/* connection options */
710 {"dbname", required_argument, NULL, 'd'},
711 {"host", required_argument, NULL, 'h'},
712 {"port", required_argument, NULL, 'p'},
713 {"username", required_argument, NULL, 'U'},
714 {"no-password", no_argument, NULL, 'w'},
715 {"password", no_argument, NULL, 'W'},
716/* replication options */
717 {"startpos", required_argument, NULL, 'I'},
718 {"endpos", required_argument, NULL, 'E'},
719 {"option", required_argument, NULL, 'o'},
720 {"plugin", required_argument, NULL, 'P'},
721 {"status-interval", required_argument, NULL, 's'},
722 {"slot", required_argument, NULL, 'S'},
723/* action */
724 {"create-slot", no_argument, NULL, 1},
725 {"start", no_argument, NULL, 2},
726 {"drop-slot", no_argument, NULL, 3},
727 {"if-not-exists", no_argument, NULL, 4},
728 {NULL, 0, NULL, 0}
729 };
730 int c;
731 int option_index;
732 uint32 hi,
733 lo;
734 char *db_name;
735
736 pg_logging_init(argv[0]);
737 progname = get_progname(argv[0]);
738 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
739
740 if (argc > 1)
741 {
742 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
743 {
744 usage();
745 exit(0);
746 }
747 else if (strcmp(argv[1], "-V") == 0 ||
748 strcmp(argv[1], "--version") == 0)
749 {
750 puts("pg_recvlogical (PostgreSQL) " PG_VERSION);
751 exit(0);
752 }
753 }
754
755 while ((c = getopt_long(argc, argv, "E:f:F:ntvd:h:p:U:wWI:o:P:s:S:",
756 long_options, &option_index)) != -1)
757 {
758 switch (c)
759 {
760/* general options */
761 case 'f':
763 break;
764 case 'F':
765 if (!option_parse_int(optarg, "-F/--fsync-interval", 0,
766 INT_MAX / 1000,
768 exit(1);
769 fsync_interval *= 1000;
770 break;
771 case 'n':
772 noloop = 1;
773 break;
774 case 't':
775 two_phase = true;
776 break;
777 case 'v':
778 verbose++;
779 break;
780 case 5:
781 failover = true;
782 break;
783/* connection options */
784 case 'd':
786 break;
787 case 'h':
789 break;
790 case 'p':
792 break;
793 case 'U':
795 break;
796 case 'w':
797 dbgetpassword = -1;
798 break;
799 case 'W':
800 dbgetpassword = 1;
801 break;
802/* replication options */
803 case 'I':
804 if (sscanf(optarg, "%X/%08X", &hi, &lo) != 2)
805 pg_fatal("could not parse start position \"%s\"", optarg);
806 startpos = ((uint64) hi) << 32 | lo;
807 break;
808 case 'E':
809 if (sscanf(optarg, "%X/%08X", &hi, &lo) != 2)
810 pg_fatal("could not parse end position \"%s\"", optarg);
811 endpos = ((uint64) hi) << 32 | lo;
812 break;
813 case 'o':
814 {
815 char *data = pg_strdup(optarg);
816 char *val = strchr(data, '=');
817
818 if (val != NULL)
819 {
820 /* remove =; separate data from val */
821 *val = '\0';
822 val++;
823 }
824
825 noptions += 1;
827
828 options[(noptions - 1) * 2] = data;
829 options[(noptions - 1) * 2 + 1] = val;
830 }
831
832 break;
833 case 'P':
835 break;
836 case 's':
837 if (!option_parse_int(optarg, "-s/--status-interval", 0,
838 INT_MAX / 1000,
840 exit(1);
842 break;
843 case 'S':
845 break;
846/* action */
847 case 1:
848 do_create_slot = true;
849 break;
850 case 2:
851 do_start_slot = true;
852 break;
853 case 3:
854 do_drop_slot = true;
855 break;
856 case 4:
857 slot_exists_ok = true;
858 break;
859
860 default:
861 /* getopt_long already emitted a complaint */
862 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
863 exit(1);
864 }
865 }
866
867 /*
868 * Any non-option arguments?
869 */
870 if (optind < argc)
871 {
872 pg_log_error("too many command-line arguments (first is \"%s\")",
873 argv[optind]);
874 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
875 exit(1);
876 }
877
878 /*
879 * Required arguments
880 */
881 if (replication_slot == NULL)
882 {
883 pg_log_error("no slot specified");
884 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
885 exit(1);
886 }
887
888 if (do_start_slot && outfile == NULL)
889 {
890 pg_log_error("no target file specified");
891 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
892 exit(1);
893 }
894
895 if (!do_drop_slot && dbname == NULL)
896 {
897 pg_log_error("no database specified");
898 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
899 exit(1);
900 }
901
903 {
904 pg_log_error("at least one action needs to be specified");
905 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
906 exit(1);
907 }
908
910 {
911 pg_log_error("cannot use --create-slot or --start together with --drop-slot");
912 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
913 exit(1);
914 }
915
917 {
918 pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
919 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
920 exit(1);
921 }
922
924 {
925 pg_log_error("--endpos may only be specified with --start");
926 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
927 exit(1);
928 }
929
930 if (!do_create_slot)
931 {
932 if (two_phase)
933 {
934 pg_log_error("%s may only be specified with --create-slot", "--enable-two-phase");
935 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
936 exit(1);
937 }
938
939 if (failover)
940 {
941 pg_log_error("%s may only be specified with --create-slot", "--enable-failover");
942 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
943 exit(1);
944 }
945 }
946
947 /*
948 * Obtain a connection to server. Notably, if we need a password, we want
949 * to collect it from the user immediately.
950 */
952 if (!conn)
953 /* Error message already written in GetConnection() */
954 exit(1);
956
957 /*
958 * Trap signals. (Don't do this until after the initial password prompt,
959 * if one is needed, in GetConnection.)
960 */
961#ifndef WIN32
965#endif
966
967 /*
968 * Run IDENTIFY_SYSTEM to check the connection type for each action.
969 * --create-slot and --start actions require a database-specific
970 * replication connection because they handle logical replication slots.
971 * --drop-slot can remove replication slots from any replication
972 * connection without this restriction.
973 */
974 if (!RunIdentifySystem(conn, NULL, NULL, NULL, &db_name))
975 exit(1);
976
977 if (!do_drop_slot && db_name == NULL)
978 pg_fatal("could not establish database-specific replication connection");
979
980 /*
981 * Set umask so that directories/files are created with the same
982 * permissions as directories/files in the source data directory.
983 *
984 * pg_mode_mask is set to owner-only by default and then updated in
985 * GetConnection() where we get the mode from the server-side with
986 * RetrieveDataDirCreatePerm() and then call SetDataDirectoryCreatePerm().
987 */
989
990 /* Drop a replication slot. */
991 if (do_drop_slot)
992 {
993 if (verbose)
994 pg_log_info("dropping replication slot \"%s\"", replication_slot);
995
997 exit(1);
998 }
999
1000 /* Create a replication slot. */
1001 if (do_create_slot)
1002 {
1003 if (verbose)
1004 pg_log_info("creating replication slot \"%s\"", replication_slot);
1005
1007 false, false, slot_exists_ok, two_phase,
1008 failover))
1009 exit(1);
1011 }
1012
1013 if (!do_start_slot)
1014 exit(0);
1015
1016 /* Stream loop */
1017 while (true)
1018 {
1020 if (time_to_abort)
1021 {
1022 /*
1023 * We've been Ctrl-C'ed or reached an exit limit condition. That's
1024 * not an error, so exit without an errorcode.
1025 */
1026 exit(0);
1027 }
1028
1029 /*
1030 * Ensure all written data is flushed to disk before exiting or
1031 * starting a new replication.
1032 */
1033 if (outfd != -1)
1035
1036 if (noloop)
1037 {
1038 pg_fatal("disconnected");
1039 }
1040 else
1041 {
1042 /* translator: check source for value for %d */
1043 pg_log_info("disconnected; waiting %d seconds to try again",
1046 }
1047 }
1048}
#define PG_TEXTDOMAIN(domain)
Definition c.h:1343
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
#define pg_realloc_array(pointer, type, count)
Definition fe_memutils.h:74
int pg_mode_mask
Definition file_perm.c:25
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
long val
Definition informix.c:689
void pg_logging_init(const char *argv0)
Definition logging.c:85
#define pg_log_error(...)
Definition logging.h:108
#define pg_log_error_hint(...)
Definition logging.h:114
#define pg_log_info(...)
Definition logging.h:126
const char * progname
Definition main.c:44
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
#define pg_fatal(...)
const void * data
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
static int verbose
static int noloop
static bool do_start_slot
static void sighup_handler(SIGNAL_ARGS)
static bool two_phase
static void StreamLogicalLog(void)
static XLogRecPtr endpos
static bool failover
static const char * plugin
static volatile sig_atomic_t time_to_abort
static bool slot_exists_ok
static int fsync_interval
static bool do_drop_slot
static char * replication_slot
static void sigexit_handler(SIGNAL_ARGS)
static int outfd
static size_t noptions
static char * outfile
static void usage(void)
static XLogRecPtr startpos
#define RECONNECT_SLEEP_TIME
static bool do_create_slot
static void disconnect_atexit(void)
static int standby_message_timeout
#define pqsignal
Definition port.h:548
const char * get_progname(const char *argv0)
Definition path.c:669
char * c
void pg_usleep(long microsec)
Definition signal.c:53
int dbgetpassword
Definition streamutil.c:50
PGconn * GetConnection(void)
Definition streamutil.c:60
char * dbhost
Definition streamutil.c:46
char * dbport
Definition streamutil.c:48
char * dbname
Definition streamutil.c:49
bool RunIdentifySystem(PGconn *conn, char **sysid, TimeLineID *starttli, XLogRecPtr *startpos, char **db_name)
Definition streamutil.c:409
char * dbuser
Definition streamutil.c:47
static void CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
Definition walsender.c:1265
static void DropReplicationSlot(DropReplicationSlotCmd *cmd)
Definition walsender.c:1479
#define SIGHUP
Definition win32_port.h:158
#define XLogRecPtrIsValid(r)
Definition xlogdefs.h:29
#define InvalidXLogRecPtr
Definition xlogdefs.h:28

References conn, CreateReplicationSlot(), data, dbgetpassword, dbhost, dbname, dbport, dbuser, disconnect_atexit(), do_create_slot, do_drop_slot, do_start_slot, DropReplicationSlot(), endpos, failover, fb(), feGetCurrentTimestamp(), fsync_interval, get_progname(), GetConnection(), getopt_long(), InvalidXLogRecPtr, no_argument, noloop, noptions, optarg, optind, option_parse_int(), outfd, outfile, OutputFsync(), pg_fatal, pg_log_error, pg_log_error_hint, pg_log_info, pg_logging_init(), pg_mode_mask, pg_realloc_array, pg_strdup(), PG_TEXTDOMAIN, pg_usleep(), plugin, pqsignal, progname, RECONNECT_SLEEP_TIME, replication_slot, required_argument, RunIdentifySystem(), set_pglocale_pgservice(), sigexit_handler(), SIGHUP, sighup_handler(), slot_exists_ok, standby_message_timeout, startpos, StreamLogicalLog(), time_to_abort, two_phase, usage(), val, verbose, and XLogRecPtrIsValid.

◆ OutputFsync()

static void OutputFsync ( TimestampTz  now)
static

Definition at line 188 of file pg_recvlogical.c.

189{
191
193
194 /*
195 * Save the last flushed position as the replication start point. On
196 * reconnect, replication resumes from there to avoid re-sending flushed
197 * data.
198 */
200
201 if (fsync_interval <= 0)
202 return;
203
205 return;
206
207 output_needs_fsync = false;
208
209 /* can only fsync if it's a regular file */
210 if (!output_isfile)
211 return;
212
213 if (fsync(outfd) != 0)
214 pg_fatal("could not fsync file \"%s\": %m", outfile);
215}
static bool output_needs_fsync
static bool output_isfile
static XLogRecPtr output_written_lsn
static TimestampTz output_last_fsync
static XLogRecPtr output_fsync_lsn
#define fsync(fd)
Definition win32_port.h:83

References fsync, fsync_interval, now(), outfd, outfile, output_fsync_lsn, output_isfile, output_last_fsync, output_needs_fsync, output_written_lsn, pg_fatal, and startpos.

Referenced by flushAndSendFeedback(), main(), and StreamLogicalLog().

◆ prepareToTerminate()

static void prepareToTerminate ( PGconn conn,
XLogRecPtr  endpos,
StreamStopReason  reason,
XLogRecPtr  lsn 
)
static

Definition at line 1074 of file pg_recvlogical.c.

1076{
1077 /*
1078 * If pg_recvlogical is terminated by a signal, we can reach here without
1079 * sending final feedback. In that case, send feedback once more before
1080 * sending CopyDone so the replication slot can advance far enough to
1081 * reduce the chance of resending duplicate data when pg_recvlogical is
1082 * restarted.
1083 *
1084 * This is still only a best-effort attempt. Depending on when the signal
1085 * arrives, the receiver may have written decoded output that the server
1086 * cannot yet safely treat as confirmed, so a later restart can still see
1087 * duplicate data.
1088 *
1089 * For other termination cases, such as STREAM_STOP_KEEPALIVE and
1090 * STREAM_STOP_END_OF_WAL, feedback has already been sent before reaching
1091 * here, so there is no need to call flushAndSendFeedback() again.
1092 */
1093 if (reason == STREAM_STOP_SIGNAL)
1094 {
1096
1098 }
1099
1101 (void) PQflush(conn);
1102
1103 if (verbose)
1104 {
1105 switch (reason)
1106 {
1107 case STREAM_STOP_SIGNAL:
1108 pg_log_info("received interrupt signal, exiting");
1109 break;
1111 pg_log_info("end position %X/%08X reached by keepalive",
1113 break;
1116 pg_log_info("end position %X/%08X reached by WAL record at %X/%08X",
1118 break;
1119 case STREAM_STOP_NONE:
1120 Assert(false);
1121 break;
1122 }
1123 }
1124}
#define Assert(condition)
Definition c.h:1002
int64 TimestampTz
Definition timestamp.h:39
int PQflush(PGconn *conn)
Definition fe-exec.c:4036
int PQputCopyEnd(PGconn *conn, const char *errormsg)
Definition fe-exec.c:2766
static bool flushAndSendFeedback(PGconn *conn, TimestampTz *now)
#define LSN_FORMAT_ARGS(lsn)
Definition xlogdefs.h:47

References Assert, conn, endpos, fb(), feGetCurrentTimestamp(), flushAndSendFeedback(), LSN_FORMAT_ARGS, now(), pg_log_info, PQflush(), PQputCopyEnd(), STREAM_STOP_END_OF_WAL, STREAM_STOP_KEEPALIVE, STREAM_STOP_NONE, STREAM_STOP_SIGNAL, verbose, and XLogRecPtrIsValid.

Referenced by StreamLogicalLog().

◆ sendFeedback()

static bool sendFeedback ( PGconn conn,
TimestampTz  now,
bool  force,
bool  replyRequested 
)
static

Definition at line 129 of file pg_recvlogical.c.

130{
133
134 char replybuf[1 + 8 + 8 + 8 + 8 + 1];
135 int len = 0;
136
137 /*
138 * we normally don't want to send superfluous feedback, but if it's
139 * because of a timeout we need to, otherwise wal_sender_timeout will kill
140 * us.
141 */
142 if (!force &&
145 return true;
146
147 if (verbose)
148 pg_log_info("confirming write up to %X/%08X, flush to %X/%08X (slot %s)",
152
154 len += 1;
156 len += 8;
158 len += 8;
160 len += 8;
161 fe_sendint64(now, &replybuf[len]); /* sendTime */
162 len += 8;
163 replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */
164 len += 1;
165
169
170 if (PQputCopyData(conn, replybuf, len) <= 0 || PQflush(conn))
171 {
172 pg_log_error("could not send feedback packet: %s",
174 return false;
175 }
176
177 return true;
178}
char * PQerrorMessage(const PGconn *conn)
int PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
Definition fe-exec.c:2712
const void size_t len
#define PqReplMsg_StandbyStatusUpdate
Definition protocol.h:84
void fe_sendint64(int64 i, char *buf)
Definition streamutil.c:884
uint64 XLogRecPtr
Definition xlogdefs.h:21

References conn, fb(), fe_sendint64(), InvalidXLogRecPtr, len, LSN_FORMAT_ARGS, now(), output_fsync_lsn, output_written_lsn, pg_log_error, pg_log_info, PQerrorMessage(), PQflush(), PQputCopyData(), PqReplMsg_StandbyStatusUpdate, replication_slot, startpos, and verbose.

Referenced by flushAndSendFeedback(), and StreamLogicalLog().

◆ sigexit_handler()

static void sigexit_handler ( SIGNAL_ARGS  )
static

Definition at line 678 of file pg_recvlogical.c.

679{
681 time_to_abort = true;
682}
static volatile sig_atomic_t stop_reason

References stop_reason, STREAM_STOP_SIGNAL, and time_to_abort.

Referenced by main().

◆ sighup_handler()

static void sighup_handler ( SIGNAL_ARGS  )
static

Definition at line 688 of file pg_recvlogical.c.

689{
690 output_reopen = true;
691}
static volatile sig_atomic_t output_reopen

References output_reopen.

Referenced by main().

◆ StreamLogicalLog()

static void StreamLogicalLog ( void  )
static

Definition at line 221 of file pg_recvlogical.c.

222{
223 PGresult *res;
224 char *copybuf = NULL;
226 PQExpBuffer query;
228
230
231 /*
232 * Connect in replication mode to the server
233 */
234 if (!conn)
236 if (!conn)
237 /* Error message already written in GetConnection() */
238 return;
239
240 /*
241 * Start the replication
242 */
243 if (verbose)
244 pg_log_info("starting log streaming at %X/%08X (slot %s)",
247
248 /* Initiate the replication stream at specified location */
249 query = createPQExpBuffer();
250 appendPQExpBufferStr(query, "START_REPLICATION SLOT ");
252 appendPQExpBuffer(query, " LOGICAL %X/%08X", LSN_FORMAT_ARGS(startpos));
253
254 /* print options if there are any */
255 if (noptions)
256 appendPQExpBufferStr(query, " (");
257
258 for (size_t i = 0; i < noptions; i++)
259 {
260 /* separator */
261 if (i > 0)
262 appendPQExpBufferStr(query, ", ");
263
264 /* write option name */
265 AppendQuotedIdentifier(query, options[i * 2]);
266
267 /* write option value if specified */
268 if (options[i * 2 + 1] != NULL)
269 {
270 appendPQExpBufferChar(query, ' ');
271 AppendQuotedLiteral(query, options[i * 2 + 1]);
272 }
273 }
274
275 if (noptions)
276 appendPQExpBufferChar(query, ')');
277
278 res = PQexec(conn, query->data);
280 {
281 pg_log_error("could not send replication command \"%s\": %s",
282 query->data, PQresultErrorMessage(res));
283 PQclear(res);
284 goto error;
285 }
286 PQclear(res);
287 resetPQExpBuffer(query);
288
289 if (verbose)
290 pg_log_info("streaming initiated");
291
292 while (!time_to_abort)
293 {
294 int r;
295 size_t bytes_left;
296 size_t bytes_written;
298 size_t hdr_len;
299
301
302 if (copybuf != NULL)
303 {
305 copybuf = NULL;
306 }
307
308 /*
309 * Potentially send a status message to the primary.
310 */
312
313 if (outfd != -1 &&
317
318 if (standby_message_timeout > 0 &&
321 {
322 /* Time to send feedback! */
323 if (!sendFeedback(conn, now, true, false))
324 goto error;
325
327 }
328
329 /* got SIGHUP, close output file */
330 if (outfd != -1 && output_reopen && strcmp(outfile, "-") != 0)
331 {
334 close(outfd);
335 outfd = -1;
336 }
337 output_reopen = false;
338
339 /* open the output file, if not open yet */
340 if (outfd == -1)
341 {
342 struct stat statbuf;
343
344 if (strcmp(outfile, "-") == 0)
345 outfd = fileno(stdout);
346 else
349 if (outfd == -1)
350 {
351 pg_log_error("could not open log file \"%s\": %m", outfile);
352 goto error;
353 }
354
355 if (fstat(outfd, &statbuf) != 0)
356 {
357 pg_log_error("could not stat file \"%s\": %m", outfile);
358 goto error;
359 }
360
361 output_isfile = S_ISREG(statbuf.st_mode) && !isatty(outfd);
362 }
363
364 r = PQgetCopyData(conn, &copybuf, 1);
365 if (r == 0)
366 {
367 /*
368 * In async mode, and no data available. We block on reading but
369 * not more than the specified timeout, so that we can send a
370 * response back to the client.
371 */
375 struct timeval timeout;
376 struct timeval *timeoutptr = NULL;
377
378 if (PQsocket(conn) < 0)
379 {
380 pg_log_error("invalid socket: %s", PQerrorMessage(conn));
381 goto error;
382 }
383
386
387 /* Compute when we need to wakeup to send a keepalive message. */
390 ((int64) 1000);
391
392 /* Compute when we need to wakeup to fsync the output file. */
395 ((int64) 1000);
396
397 /* Now compute when to wakeup. */
398 if (message_target > 0 || fsync_target > 0)
399 {
401 long secs;
402 int usecs;
403
405
408
411 &secs,
412 &usecs);
413 if (secs <= 0)
414 timeout.tv_sec = 1; /* Always sleep at least 1 sec */
415 else
416 timeout.tv_sec = secs;
417 timeout.tv_usec = usecs;
419 }
420
422 if (r == 0 || (r < 0 && errno == EINTR))
423 {
424 /*
425 * Got a timeout or signal. Continue the loop and either
426 * deliver a status packet to the server or just go back into
427 * blocking.
428 */
429 continue;
430 }
431 else if (r < 0)
432 {
433 pg_log_error("%s() failed: %m", "select");
434 goto error;
435 }
436
437 /* Else there is actually data on the socket */
438 if (PQconsumeInput(conn) == 0)
439 {
440 pg_log_error("could not receive data from WAL stream: %s",
442 goto error;
443 }
444 continue;
445 }
446
447 /* End of copy stream */
448 if (r == -1)
449 break;
450
451 /* Failure while reading the copy stream */
452 if (r == -2)
453 {
454 pg_log_error("could not read COPY data: %s",
456 goto error;
457 }
458
459 /* Check the message type. */
461 {
462 int pos;
463 bool replyRequested;
465 bool endposReached = false;
466
467 /*
468 * Parse the keepalive message, enclosed in the CopyData message.
469 * We just check if the server requested a reply, and ignore the
470 * rest.
471 */
472 pos = 1; /* skip msgtype PqReplMsg_Keepalive */
473 walEnd = fe_recvint64(&copybuf[pos]);
475
476 pos += 8; /* read walEnd */
477
478 pos += 8; /* skip sendTime */
479
480 if (r < pos + 1)
481 {
482 pg_log_error("streaming header too small: %d", r);
483 goto error;
484 }
485 replyRequested = copybuf[pos];
486
488 {
489 /*
490 * If there's nothing to read on the socket until a keepalive
491 * we know that the server has nothing to send us; and if
492 * walEnd has passed endpos, we know nothing else can have
493 * committed before endpos. So we can bail out now.
494 */
495 endposReached = true;
496 }
497
498 /* Send a reply, if necessary */
500 {
502 goto error;
504 }
505
506 if (endposReached)
507 {
509 time_to_abort = true;
510 break;
511 }
512
513 continue;
514 }
515 else if (copybuf[0] != PqReplMsg_WALData)
516 {
517 pg_log_error("unrecognized streaming header: \"%c\"",
518 copybuf[0]);
519 goto error;
520 }
521
522 /*
523 * Read the header of the WALData message, enclosed in the CopyData
524 * message. We only need the WAL location field (dataStart), the rest
525 * of the header is ignored.
526 */
527 hdr_len = 1; /* msgtype PqReplMsg_WALData */
528 hdr_len += 8; /* dataStart */
529 hdr_len += 8; /* walEnd */
530 hdr_len += 8; /* sendTime */
531 if (r < hdr_len + 1)
532 {
533 pg_log_error("streaming header too small: %d", r);
534 goto error;
535 }
536
537 /* Extract WAL location for this block */
539
541 {
542 /*
543 * We've read past our endpoint, so prepare to go away being
544 * cautious about what happens to our output data.
545 */
547 goto error;
549 time_to_abort = true;
550 break;
551 }
552
554
555 bytes_left = r - hdr_len;
556 bytes_written = 0;
557
558 /* signal that a fsync is needed */
559 output_needs_fsync = true;
560
561 while (bytes_left)
562 {
563 ssize_t ret;
564
565 ret = write(outfd,
567 bytes_left);
568
569 if (ret < 0)
570 {
571 pg_log_error("could not write %zu bytes to log file \"%s\": %m",
573 goto error;
574 }
575
576 /* Write was successful, advance our position */
577 bytes_written += ret;
578 bytes_left -= ret;
579 }
580
581 if (write(outfd, "\n", 1) != 1)
582 {
583 pg_log_error("could not write %zu bytes to log file \"%s\": %m",
584 (size_t) 1, outfile);
585 goto error;
586 }
587
589 {
590 /* endpos was exactly the record we just processed, we're done */
592 goto error;
594 time_to_abort = true;
595 break;
596 }
597 }
598
599 /* Clean up connection state if stream has been aborted */
600 if (time_to_abort)
602
603 res = PQgetResult(conn);
604 if (PQresultStatus(res) == PGRES_COPY_OUT)
605 {
606 PQclear(res);
607
608 /*
609 * We're doing a client-initiated clean exit and have sent CopyDone to
610 * the server. Drain any messages, so we don't miss a last-minute
611 * ErrorResponse. The walsender stops generating WALData records once
612 * it sees CopyDone, so expect this to finish quickly. After CopyDone,
613 * it's too late for sendFeedback(), even if this were to take a long
614 * time. Hence, use synchronous-mode PQgetCopyData().
615 */
616 while (1)
617 {
618 int r;
619
620 if (copybuf != NULL)
621 {
623 copybuf = NULL;
624 }
625 r = PQgetCopyData(conn, &copybuf, 0);
626 if (r == -1)
627 break;
628 if (r == -2)
629 {
630 pg_log_error("could not read COPY data: %s",
632 time_to_abort = false; /* unclean exit */
633 goto error;
634 }
635 }
636
637 res = PQgetResult(conn);
638 }
640 {
641 pg_log_error("unexpected termination of replication stream: %s",
643 PQclear(res);
644 goto error;
645 }
646 PQclear(res);
647
648 if (outfd != -1 && strcmp(outfile, "-") != 0)
649 {
651
652 OutputFsync(t);
653 if (close(outfd) != 0)
654 pg_log_error("could not close file \"%s\": %m", outfile);
655 }
656 outfd = -1;
657error:
658 if (copybuf != NULL)
659 {
661 copybuf = NULL;
662 }
663 destroyPQExpBuffer(query);
664 PQfinish(conn);
665 conn = NULL;
666}
#define Max(x, y)
Definition c.h:1125
int64_t int64
Definition c.h:680
#define PG_BINARY
Definition c.h:1431
int PQsocket(const PGconn *conn)
void PQfreemem(void *ptr)
Definition fe-exec.c:4068
int PQconsumeInput(PGconn *conn)
Definition fe-exec.c:2001
PGresult * PQexec(PGconn *conn, const char *query)
Definition fe-exec.c:2279
int PQgetCopyData(PGconn *conn, char **buffer, int async)
Definition fe-exec.c:2833
int pg_file_create_mode
Definition file_perm.c:19
#define close(a)
Definition win32.h:12
#define write(a, b, c)
Definition win32.h:14
int i
Definition isn.c:77
#define PQresultErrorMessage
#define PQgetResult
#define PQclear
#define PQresultStatus
@ PGRES_COPY_BOTH
Definition libpq-fe.h:143
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PGRES_COPY_OUT
Definition libpq-fe.h:137
static void prepareToTerminate(PGconn *conn, XLogRecPtr endpos, StreamStopReason reason, XLogRecPtr lsn)
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void destroyPQExpBuffer(PQExpBuffer str)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
#define PqReplMsg_WALData
Definition protocol.h:77
#define PqReplMsg_Keepalive
Definition protocol.h:75
static void error(void)
int64 fe_recvint64(char *buf)
Definition streamutil.c:895
void feTimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition streamutil.c:849
bool feTimestampDifferenceExceeds(TimestampTz start_time, TimestampTz stop_time, int msec)
Definition streamutil.c:871
#define AppendQuotedLiteral(b, s)
Definition streamutil.h:48
#define AppendQuotedIdentifier(b, s)
Definition streamutil.h:47
static StringInfo copybuf
Definition tablesync.c:129
#define EINTR
Definition win32_port.h:378
#define fstat
Definition win32_port.h:73
#define S_ISREG(m)
Definition win32_port.h:335
#define select(n, r, w, e, timeout)
Definition win32_port.h:517

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), AppendQuotedIdentifier, AppendQuotedLiteral, close, conn, copybuf, createPQExpBuffer(), PQExpBufferData::data, destroyPQExpBuffer(), EINTR, endpos, error(), fb(), fe_recvint64(), feGetCurrentTimestamp(), feTimestampDifference(), feTimestampDifferenceExceeds(), flushAndSendFeedback(), fstat, fsync_interval, GetConnection(), i, InvalidXLogRecPtr, LSN_FORMAT_ARGS, Max, noptions, now(), outfd, outfile, output_isfile, output_last_fsync, output_needs_fsync, output_reopen, output_written_lsn, OutputFsync(), PG_BINARY, pg_file_create_mode, pg_log_error, pg_log_info, PGRES_COMMAND_OK, PGRES_COPY_BOTH, PGRES_COPY_OUT, PQclear, PQconsumeInput(), PQerrorMessage(), PQexec(), PQfinish(), PQfreemem(), PQgetCopyData(), PQgetResult, PqReplMsg_Keepalive, PqReplMsg_WALData, PQresultErrorMessage, PQresultStatus, PQsocket(), prepareToTerminate(), replication_slot, resetPQExpBuffer(), S_ISREG, select, sendFeedback(), standby_message_timeout, startpos, stop_reason, STREAM_STOP_END_OF_WAL, STREAM_STOP_KEEPALIVE, time_to_abort, verbose, write, and XLogRecPtrIsValid.

Referenced by main().

◆ usage()

static void usage ( void  )
static

Definition at line 82 of file pg_recvlogical.c.

83{
84 printf(_("%s controls PostgreSQL logical decoding streams.\n\n"),
85 progname);
86 printf(_("Usage:\n"));
87 printf(_(" %s [OPTION]...\n"), progname);
88 printf(_("\nAction to be performed:\n"));
89 printf(_(" --create-slot create a new replication slot (for the slot's name see --slot)\n"));
90 printf(_(" --drop-slot drop the replication slot (for the slot's name see --slot)\n"));
91 printf(_(" --start start streaming in a replication slot (for the slot's name see --slot)\n"));
92 printf(_("\nOptions:\n"));
93 printf(_(" --enable-failover enable replication slot synchronization to standby servers when\n"
94 " creating a replication slot\n"));
95 printf(_(" -E, --endpos=LSN exit after receiving the specified LSN\n"));
96 printf(_(" -f, --file=FILE receive log into this file, - for stdout\n"));
97 printf(_(" -F --fsync-interval=SECS\n"
98 " time between fsyncs to the output file (default: %d)\n"), (fsync_interval / 1000));
99 printf(_(" --if-not-exists do not error if slot already exists when creating a slot\n"));
100 printf(_(" -I, --startpos=LSN where in an existing slot should the streaming start\n"));
101 printf(_(" -n, --no-loop do not loop on connection lost\n"));
102 printf(_(" -o, --option=NAME[=VALUE]\n"
103 " pass option NAME with optional value VALUE to the\n"
104 " output plugin\n"));
105 printf(_(" -P, --plugin=PLUGIN use output plugin PLUGIN (default: %s)\n"), plugin);
106 printf(_(" -s, --status-interval=SECS\n"
107 " time between status packets sent to server (default: %d)\n"), (standby_message_timeout / 1000));
108 printf(_(" -S, --slot=SLOTNAME name of the logical replication slot\n"));
109 printf(_(" -t, --enable-two-phase enable decoding of prepared transactions when creating a slot\n"));
110 printf(_(" --two-phase (same as --enable-two-phase, deprecated)\n"));
111 printf(_(" -v, --verbose output verbose messages\n"));
112 printf(_(" -V, --version output version information, then exit\n"));
113 printf(_(" -?, --help show this help, then exit\n"));
114 printf(_("\nConnection options:\n"));
115 printf(_(" -d, --dbname=DBNAME database to connect to\n"));
116 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
117 printf(_(" -p, --port=PORT database server port number\n"));
118 printf(_(" -U, --username=NAME connect as specified database user\n"));
119 printf(_(" -w, --no-password never prompt for password\n"));
120 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
121 printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
122 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
123}
#define _(x)
Definition elog.c:96
#define printf(...)
Definition port.h:267

References _, fb(), fsync_interval, plugin, printf, progname, and standby_message_timeout.

Referenced by main().

Variable Documentation

◆ do_create_slot

bool do_create_slot = false
static

Definition at line 52 of file pg_recvlogical.c.

Referenced by main().

◆ do_drop_slot

bool do_drop_slot = false
static

Definition at line 55 of file pg_recvlogical.c.

Referenced by main().

◆ do_start_slot

bool do_start_slot = false
static

Definition at line 54 of file pg_recvlogical.c.

Referenced by main().

◆ endpos

XLogRecPtr endpos = InvalidXLogRecPtr
static

Definition at line 51 of file pg_recvlogical.c.

Referenced by main(), prepareToTerminate(), and StreamLogicalLog().

◆ failover

◆ fsync_interval

int fsync_interval = 10 * 1000
static

Definition at line 49 of file pg_recvlogical.c.

Referenced by main(), OutputFsync(), StreamLogicalLog(), and usage().

◆ noloop

int noloop = 0
static

Definition at line 47 of file pg_recvlogical.c.

Referenced by main().

◆ noptions

◆ options

char** options
static

Definition at line 59 of file pg_recvlogical.c.

◆ outfd

int outfd = -1
static

Definition at line 64 of file pg_recvlogical.c.

Referenced by main(), OutputFsync(), and StreamLogicalLog().

◆ outfile

◆ output_fsync_lsn

XLogRecPtr output_fsync_lsn = InvalidXLogRecPtr
static

Definition at line 72 of file pg_recvlogical.c.

Referenced by OutputFsync(), and sendFeedback().

◆ output_isfile

bool output_isfile
static

Definition at line 68 of file pg_recvlogical.c.

Referenced by OutputFsync(), and StreamLogicalLog().

◆ output_last_fsync

TimestampTz output_last_fsync = -1
static

Definition at line 69 of file pg_recvlogical.c.

Referenced by OutputFsync(), and StreamLogicalLog().

◆ output_needs_fsync

bool output_needs_fsync = false
static

Definition at line 70 of file pg_recvlogical.c.

Referenced by OutputFsync(), and StreamLogicalLog().

◆ output_reopen

volatile sig_atomic_t output_reopen = false
static

Definition at line 67 of file pg_recvlogical.c.

Referenced by sighup_handler(), and StreamLogicalLog().

◆ output_written_lsn

XLogRecPtr output_written_lsn = InvalidXLogRecPtr
static

Definition at line 71 of file pg_recvlogical.c.

Referenced by OutputFsync(), sendFeedback(), and StreamLogicalLog().

◆ plugin

◆ replication_slot

char* replication_slot = NULL
static

Definition at line 56 of file pg_recvlogical.c.

Referenced by main(), sendFeedback(), and StreamLogicalLog().

◆ slot_exists_ok

bool slot_exists_ok = false
static

Definition at line 53 of file pg_recvlogical.c.

Referenced by main().

◆ standby_message_timeout

int standby_message_timeout = 10 * 1000
static

Definition at line 48 of file pg_recvlogical.c.

Referenced by main(), StreamLogicalLog(), and usage().

◆ startpos

◆ stop_reason

volatile sig_atomic_t stop_reason = STREAM_STOP_NONE
static

Definition at line 66 of file pg_recvlogical.c.

Referenced by sigexit_handler(), and StreamLogicalLog().

◆ time_to_abort

volatile sig_atomic_t time_to_abort = false
static

Definition at line 65 of file pg_recvlogical.c.

Referenced by main(), sigexit_handler(), and StreamLogicalLog().

◆ two_phase

◆ verbose

int verbose = 0
static

Definition at line 44 of file pg_recvlogical.c.

Referenced by main(), prepareToTerminate(), sendFeedback(), and StreamLogicalLog().