Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * libpqwalreceiver.c
4 : *
5 : * This file contains the libpq-specific parts of walreceiver. It's
6 : * loaded as a dynamic module to avoid linking the main server binary with
7 : * libpq.
8 : *
9 : * Portions Copyright (c) 2010-2020, PostgreSQL Global Development Group
10 : *
11 : *
12 : * IDENTIFICATION
13 : * src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 : #include "postgres.h"
18 :
19 : #include <unistd.h>
20 : #include <sys/time.h>
21 :
22 : #include "access/xlog.h"
23 : #include "catalog/pg_type.h"
24 : #include "common/connect.h"
25 : #include "funcapi.h"
26 : #include "libpq-fe.h"
27 : #include "mb/pg_wchar.h"
28 : #include "miscadmin.h"
29 : #include "pgstat.h"
30 : #include "pqexpbuffer.h"
31 : #include "replication/walreceiver.h"
32 : #include "utils/builtins.h"
33 : #include "utils/memutils.h"
34 : #include "utils/pg_lsn.h"
35 : #include "utils/tuplestore.h"
36 :
37 300 : PG_MODULE_MAGIC;
38 :
39 : void _PG_init(void);
40 :
41 : struct WalReceiverConn
42 : {
43 : /* Current connection to the primary, if any */
44 : PGconn *streamConn;
45 : /* Used to remember if the connection is logical or physical */
46 : bool logical;
47 : /* Buffer for currently read records */
48 : char *recvBuf;
49 : };
50 :
51 : /* Prototypes for interface functions */
52 : static WalReceiverConn *libpqrcv_connect(const char *conninfo,
53 : bool logical, const char *appname,
54 : char **err);
55 : static void libpqrcv_check_conninfo(const char *conninfo);
56 : static char *libpqrcv_get_conninfo(WalReceiverConn *conn);
57 : static void libpqrcv_get_senderinfo(WalReceiverConn *conn,
58 : char **sender_host, int *sender_port);
59 : static char *libpqrcv_identify_system(WalReceiverConn *conn,
60 : TimeLineID *primary_tli);
61 : static int libpqrcv_server_version(WalReceiverConn *conn);
62 : static void libpqrcv_readtimelinehistoryfile(WalReceiverConn *conn,
63 : TimeLineID tli, char **filename,
64 : char **content, int *len);
65 : static bool libpqrcv_startstreaming(WalReceiverConn *conn,
66 : const WalRcvStreamOptions *options);
67 : static void libpqrcv_endstreaming(WalReceiverConn *conn,
68 : TimeLineID *next_tli);
69 : static int libpqrcv_receive(WalReceiverConn *conn, char **buffer,
70 : pgsocket *wait_fd);
71 : static void libpqrcv_send(WalReceiverConn *conn, const char *buffer,
72 : int nbytes);
73 : static char *libpqrcv_create_slot(WalReceiverConn *conn,
74 : const char *slotname,
75 : bool temporary,
76 : CRSSnapshotAction snapshot_action,
77 : XLogRecPtr *lsn);
78 : static pid_t libpqrcv_get_backend_pid(WalReceiverConn *conn);
79 : static WalRcvExecResult *libpqrcv_exec(WalReceiverConn *conn,
80 : const char *query,
81 : const int nRetTypes,
82 : const Oid *retTypes);
83 : static void libpqrcv_disconnect(WalReceiverConn *conn);
84 :
85 : static WalReceiverFunctionsType PQWalReceiverFunctions = {
86 : libpqrcv_connect,
87 : libpqrcv_check_conninfo,
88 : libpqrcv_get_conninfo,
89 : libpqrcv_get_senderinfo,
90 : libpqrcv_identify_system,
91 : libpqrcv_server_version,
92 : libpqrcv_readtimelinehistoryfile,
93 : libpqrcv_startstreaming,
94 : libpqrcv_endstreaming,
95 : libpqrcv_receive,
96 : libpqrcv_send,
97 : libpqrcv_create_slot,
98 : libpqrcv_get_backend_pid,
99 : libpqrcv_exec,
100 : libpqrcv_disconnect
101 : };
102 :
103 : /* Prototypes for private functions */
104 : static PGresult *libpqrcv_PQexec(PGconn *streamConn, const char *query);
105 : static PGresult *libpqrcv_PQgetResult(PGconn *streamConn);
106 : static char *stringlist_to_identifierstr(PGconn *conn, List *strings);
107 :
108 : /*
109 : * Module initialization function
110 : */
111 : void
112 300 : _PG_init(void)
113 : {
114 300 : if (WalReceiverFunctions != NULL)
115 0 : elog(ERROR, "libpqwalreceiver already loaded");
116 300 : WalReceiverFunctions = &PQWalReceiverFunctions;
117 300 : }
118 :
119 : /*
120 : * Establish the connection to the primary server for XLOG streaming
121 : *
122 : * Returns NULL on error and fills the err with palloc'ed error message.
123 : */
124 : static WalReceiverConn *
125 298 : libpqrcv_connect(const char *conninfo, bool logical, const char *appname,
126 : char **err)
127 : {
128 : WalReceiverConn *conn;
129 : PostgresPollingStatusType status;
130 : const char *keys[5];
131 : const char *vals[5];
132 298 : int i = 0;
133 :
134 : /*
135 : * We use the expand_dbname parameter to process the connection string (or
136 : * URI), and pass some extra options.
137 : */
138 298 : keys[i] = "dbname";
139 298 : vals[i] = conninfo;
140 298 : keys[++i] = "replication";
141 298 : vals[i] = logical ? "database" : "true";
142 298 : if (!logical)
143 : {
144 : /*
145 : * The database name is ignored by the server in replication mode, but
146 : * specify "replication" for .pgpass lookup.
147 : */
148 0 : keys[++i] = "dbname";
149 0 : vals[i] = "replication";
150 : }
151 298 : keys[++i] = "fallback_application_name";
152 298 : vals[i] = appname;
153 298 : if (logical)
154 : {
155 298 : keys[++i] = "client_encoding";
156 298 : vals[i] = GetDatabaseEncodingName();
157 : }
158 298 : keys[++i] = NULL;
159 298 : vals[i] = NULL;
160 :
161 298 : Assert(i < sizeof(keys));
162 :
163 298 : conn = palloc0(sizeof(WalReceiverConn));
164 298 : conn->streamConn = PQconnectStartParams(keys, vals,
165 : /* expand_dbname = */ true);
166 298 : if (PQstatus(conn->streamConn) == CONNECTION_BAD)
167 : {
168 0 : *err = pchomp(PQerrorMessage(conn->streamConn));
169 0 : return NULL;
170 : }
171 :
172 : /*
173 : * Poll connection until we have OK or FAILED status.
174 : *
175 : * Per spec for PQconnectPoll, first wait till socket is write-ready.
176 : */
177 298 : status = PGRES_POLLING_WRITING;
178 : do
179 : {
180 : int io_flag;
181 : int rc;
182 :
183 806 : if (status == PGRES_POLLING_READING)
184 298 : io_flag = WL_SOCKET_READABLE;
185 : #ifdef WIN32
186 : /* Windows needs a different test while waiting for connection-made */
187 : else if (PQstatus(conn->streamConn) == CONNECTION_STARTED)
188 : io_flag = WL_SOCKET_CONNECTED;
189 : #endif
190 : else
191 508 : io_flag = WL_SOCKET_WRITEABLE;
192 :
193 806 : rc = WaitLatchOrSocket(MyLatch,
194 : WL_EXIT_ON_PM_DEATH | WL_LATCH_SET | io_flag,
195 806 : PQsocket(conn->streamConn),
196 : 0,
197 : WAIT_EVENT_LIBPQWALRECEIVER_CONNECT);
198 :
199 : /* Interrupted? */
200 806 : if (rc & WL_LATCH_SET)
201 : {
202 210 : ResetLatch(MyLatch);
203 210 : ProcessWalRcvInterrupts();
204 : }
205 :
206 : /* If socket is ready, advance the libpq state machine */
207 806 : if (rc & io_flag)
208 596 : status = PQconnectPoll(conn->streamConn);
209 806 : } while (status != PGRES_POLLING_OK && status != PGRES_POLLING_FAILED);
210 :
211 298 : if (PQstatus(conn->streamConn) != CONNECTION_OK)
212 : {
213 8 : *err = pchomp(PQerrorMessage(conn->streamConn));
214 8 : return NULL;
215 : }
216 :
217 290 : if (logical)
218 : {
219 : PGresult *res;
220 :
221 290 : res = libpqrcv_PQexec(conn->streamConn,
222 : ALWAYS_SECURE_SEARCH_PATH_SQL);
223 290 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
224 : {
225 0 : PQclear(res);
226 0 : ereport(ERROR,
227 : (errmsg("could not clear search path: %s",
228 : pchomp(PQerrorMessage(conn->streamConn)))));
229 : }
230 290 : PQclear(res);
231 : }
232 :
233 290 : conn->logical = logical;
234 :
235 290 : return conn;
236 : }
237 :
238 : /*
239 : * Validate connection info string (just try to parse it)
240 : */
241 : static void
242 60 : libpqrcv_check_conninfo(const char *conninfo)
243 : {
244 60 : PQconninfoOption *opts = NULL;
245 60 : char *err = NULL;
246 :
247 60 : opts = PQconninfoParse(conninfo, &err);
248 60 : if (opts == NULL)
249 0 : ereport(ERROR,
250 : (errcode(ERRCODE_SYNTAX_ERROR),
251 : errmsg("invalid connection string syntax: %s", err)));
252 :
253 60 : PQconninfoFree(opts);
254 60 : }
255 :
256 : /*
257 : * Return a user-displayable conninfo string. Any security-sensitive fields
258 : * are obfuscated.
259 : */
260 : static char *
261 0 : libpqrcv_get_conninfo(WalReceiverConn *conn)
262 : {
263 : PQconninfoOption *conn_opts;
264 : PQconninfoOption *conn_opt;
265 : PQExpBufferData buf;
266 : char *retval;
267 :
268 0 : Assert(conn->streamConn != NULL);
269 :
270 0 : initPQExpBuffer(&buf);
271 0 : conn_opts = PQconninfo(conn->streamConn);
272 :
273 0 : if (conn_opts == NULL)
274 0 : ereport(ERROR,
275 : (errmsg("could not parse connection string: %s",
276 : _("out of memory"))));
277 :
278 : /* build a clean connection string from pieces */
279 0 : for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
280 : {
281 : bool obfuscate;
282 :
283 : /* Skip debug and empty options */
284 0 : if (strchr(conn_opt->dispchar, 'D') ||
285 0 : conn_opt->val == NULL ||
286 0 : conn_opt->val[0] == '\0')
287 0 : continue;
288 :
289 : /* Obfuscate security-sensitive options */
290 0 : obfuscate = strchr(conn_opt->dispchar, '*') != NULL;
291 :
292 0 : appendPQExpBuffer(&buf, "%s%s=%s",
293 0 : buf.len == 0 ? "" : " ",
294 : conn_opt->keyword,
295 : obfuscate ? "********" : conn_opt->val);
296 : }
297 :
298 0 : PQconninfoFree(conn_opts);
299 :
300 0 : retval = PQExpBufferDataBroken(buf) ? NULL : pstrdup(buf.data);
301 0 : termPQExpBuffer(&buf);
302 0 : return retval;
303 : }
304 :
305 : /*
306 : * Provides information of sender this WAL receiver is connected to.
307 : */
308 : static void
309 0 : libpqrcv_get_senderinfo(WalReceiverConn *conn, char **sender_host,
310 : int *sender_port)
311 : {
312 0 : char *ret = NULL;
313 :
314 0 : *sender_host = NULL;
315 0 : *sender_port = 0;
316 :
317 0 : Assert(conn->streamConn != NULL);
318 :
319 0 : ret = PQhost(conn->streamConn);
320 0 : if (ret && strlen(ret) != 0)
321 0 : *sender_host = pstrdup(ret);
322 :
323 0 : ret = PQport(conn->streamConn);
324 0 : if (ret && strlen(ret) != 0)
325 0 : *sender_port = atoi(ret);
326 0 : }
327 :
328 : /*
329 : * Check that primary's system identifier matches ours, and fetch the current
330 : * timeline ID of the primary.
331 : */
332 : static char *
333 76 : libpqrcv_identify_system(WalReceiverConn *conn, TimeLineID *primary_tli)
334 : {
335 : PGresult *res;
336 : char *primary_sysid;
337 :
338 : /*
339 : * Get the system identifier and timeline ID as a DataRow message from the
340 : * primary server.
341 : */
342 76 : res = libpqrcv_PQexec(conn->streamConn, "IDENTIFY_SYSTEM");
343 76 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
344 : {
345 0 : PQclear(res);
346 0 : ereport(ERROR,
347 : (errmsg("could not receive database system identifier and timeline ID from "
348 : "the primary server: %s",
349 : pchomp(PQerrorMessage(conn->streamConn)))));
350 : }
351 76 : if (PQnfields(res) < 3 || PQntuples(res) != 1)
352 : {
353 0 : int ntuples = PQntuples(res);
354 0 : int nfields = PQnfields(res);
355 :
356 0 : PQclear(res);
357 0 : ereport(ERROR,
358 : (errmsg("invalid response from primary server"),
359 : errdetail("Could not identify system: got %d rows and %d fields, expected %d rows and %d or more fields.",
360 : ntuples, nfields, 3, 1)));
361 : }
362 76 : primary_sysid = pstrdup(PQgetvalue(res, 0, 0));
363 76 : *primary_tli = pg_strtoint32(PQgetvalue(res, 0, 1));
364 76 : PQclear(res);
365 :
366 76 : return primary_sysid;
367 : }
368 :
369 : /*
370 : * Thin wrapper around libpq to obtain server version.
371 : */
372 : static int
373 324 : libpqrcv_server_version(WalReceiverConn *conn)
374 : {
375 324 : return PQserverVersion(conn->streamConn);
376 : }
377 :
378 : /*
379 : * Start streaming WAL data from given streaming options.
380 : *
381 : * Returns true if we switched successfully to copy-both mode. False
382 : * means the server received the command and executed it successfully, but
383 : * didn't switch to copy-mode. That means that there was no WAL on the
384 : * requested timeline and starting point, because the server switched to
385 : * another timeline at or before the requested starting point. On failure,
386 : * throws an ERROR.
387 : */
388 : static bool
389 198 : libpqrcv_startstreaming(WalReceiverConn *conn,
390 : const WalRcvStreamOptions *options)
391 : {
392 : StringInfoData cmd;
393 : PGresult *res;
394 :
395 198 : Assert(options->logical == conn->logical);
396 198 : Assert(options->slotname || !options->logical);
397 :
398 198 : initStringInfo(&cmd);
399 :
400 : /* Build the command. */
401 198 : appendStringInfoString(&cmd, "START_REPLICATION");
402 198 : if (options->slotname != NULL)
403 198 : appendStringInfo(&cmd, " SLOT \"%s\"",
404 : options->slotname);
405 :
406 198 : if (options->logical)
407 198 : appendStringInfoString(&cmd, " LOGICAL");
408 :
409 396 : appendStringInfo(&cmd, " %X/%X",
410 198 : (uint32) (options->startpoint >> 32),
411 198 : (uint32) options->startpoint);
412 :
413 : /*
414 : * Additional options are different depending on if we are doing logical
415 : * or physical replication.
416 : */
417 198 : if (options->logical)
418 : {
419 : char *pubnames_str;
420 : List *pubnames;
421 : char *pubnames_literal;
422 :
423 198 : appendStringInfoString(&cmd, " (");
424 :
425 198 : appendStringInfo(&cmd, "proto_version '%u'",
426 : options->proto.logical.proto_version);
427 :
428 226 : if (options->proto.logical.streaming &&
429 28 : PQserverVersion(conn->streamConn) >= 140000)
430 28 : appendStringInfoString(&cmd, ", streaming 'on'");
431 :
432 198 : pubnames = options->proto.logical.publication_names;
433 198 : pubnames_str = stringlist_to_identifierstr(conn->streamConn, pubnames);
434 198 : if (!pubnames_str)
435 0 : ereport(ERROR,
436 : (errmsg("could not start WAL streaming: %s",
437 : pchomp(PQerrorMessage(conn->streamConn)))));
438 198 : pubnames_literal = PQescapeLiteral(conn->streamConn, pubnames_str,
439 : strlen(pubnames_str));
440 198 : if (!pubnames_literal)
441 0 : ereport(ERROR,
442 : (errmsg("could not start WAL streaming: %s",
443 : pchomp(PQerrorMessage(conn->streamConn)))));
444 198 : appendStringInfo(&cmd, ", publication_names %s", pubnames_literal);
445 198 : PQfreemem(pubnames_literal);
446 198 : pfree(pubnames_str);
447 :
448 208 : if (options->proto.logical.binary &&
449 10 : PQserverVersion(conn->streamConn) >= 140000)
450 10 : appendStringInfoString(&cmd, ", binary 'true'");
451 :
452 198 : appendStringInfoChar(&cmd, ')');
453 : }
454 : else
455 0 : appendStringInfo(&cmd, " TIMELINE %u",
456 : options->proto.physical.startpointTLI);
457 :
458 : /* Start streaming. */
459 198 : res = libpqrcv_PQexec(conn->streamConn, cmd.data);
460 198 : pfree(cmd.data);
461 :
462 198 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
463 : {
464 0 : PQclear(res);
465 0 : return false;
466 : }
467 198 : else if (PQresultStatus(res) != PGRES_COPY_BOTH)
468 : {
469 0 : PQclear(res);
470 0 : ereport(ERROR,
471 : (errmsg("could not start WAL streaming: %s",
472 : pchomp(PQerrorMessage(conn->streamConn)))));
473 : }
474 198 : PQclear(res);
475 198 : return true;
476 : }
477 :
478 : /*
479 : * Stop streaming WAL data. Returns the next timeline's ID in *next_tli, as
480 : * reported by the server, or 0 if it did not report it.
481 : */
482 : static void
483 126 : libpqrcv_endstreaming(WalReceiverConn *conn, TimeLineID *next_tli)
484 : {
485 : PGresult *res;
486 :
487 : /*
488 : * Send copy-end message. As in libpqrcv_PQexec, this could theoretically
489 : * block, but the risk seems small.
490 : */
491 248 : if (PQputCopyEnd(conn->streamConn, NULL) <= 0 ||
492 122 : PQflush(conn->streamConn))
493 4 : ereport(ERROR,
494 : (errmsg("could not send end-of-streaming message to primary: %s",
495 : pchomp(PQerrorMessage(conn->streamConn)))));
496 :
497 122 : *next_tli = 0;
498 :
499 : /*
500 : * After COPY is finished, we should receive a result set indicating the
501 : * next timeline's ID, or just CommandComplete if the server was shut
502 : * down.
503 : *
504 : * If we had not yet received CopyDone from the backend, PGRES_COPY_OUT is
505 : * also possible in case we aborted the copy in mid-stream.
506 : */
507 122 : res = libpqrcv_PQgetResult(conn->streamConn);
508 122 : if (PQresultStatus(res) == PGRES_TUPLES_OK)
509 : {
510 : /*
511 : * Read the next timeline's ID. The server also sends the timeline's
512 : * starting point, but it is ignored.
513 : */
514 0 : if (PQnfields(res) < 2 || PQntuples(res) != 1)
515 0 : ereport(ERROR,
516 : (errmsg("unexpected result set after end-of-streaming")));
517 0 : *next_tli = pg_strtoint32(PQgetvalue(res, 0, 0));
518 0 : PQclear(res);
519 :
520 : /* the result set should be followed by CommandComplete */
521 0 : res = libpqrcv_PQgetResult(conn->streamConn);
522 : }
523 122 : else if (PQresultStatus(res) == PGRES_COPY_OUT)
524 : {
525 122 : PQclear(res);
526 :
527 : /* End the copy */
528 122 : if (PQendcopy(conn->streamConn))
529 0 : ereport(ERROR,
530 : (errmsg("error while shutting down streaming COPY: %s",
531 : pchomp(PQerrorMessage(conn->streamConn)))));
532 :
533 : /* CommandComplete should follow */
534 122 : res = libpqrcv_PQgetResult(conn->streamConn);
535 : }
536 :
537 122 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
538 0 : ereport(ERROR,
539 : (errmsg("error reading result of streaming command: %s",
540 : pchomp(PQerrorMessage(conn->streamConn)))));
541 122 : PQclear(res);
542 :
543 : /* Verify that there are no more results */
544 122 : res = libpqrcv_PQgetResult(conn->streamConn);
545 122 : if (res != NULL)
546 0 : ereport(ERROR,
547 : (errmsg("unexpected result after CommandComplete: %s",
548 : pchomp(PQerrorMessage(conn->streamConn)))));
549 122 : }
550 :
551 : /*
552 : * Fetch the timeline history file for 'tli' from primary.
553 : */
554 : static void
555 0 : libpqrcv_readtimelinehistoryfile(WalReceiverConn *conn,
556 : TimeLineID tli, char **filename,
557 : char **content, int *len)
558 : {
559 : PGresult *res;
560 : char cmd[64];
561 :
562 0 : Assert(!conn->logical);
563 :
564 : /*
565 : * Request the primary to send over the history file for given timeline.
566 : */
567 0 : snprintf(cmd, sizeof(cmd), "TIMELINE_HISTORY %u", tli);
568 0 : res = libpqrcv_PQexec(conn->streamConn, cmd);
569 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
570 : {
571 0 : PQclear(res);
572 0 : ereport(ERROR,
573 : (errmsg("could not receive timeline history file from "
574 : "the primary server: %s",
575 : pchomp(PQerrorMessage(conn->streamConn)))));
576 : }
577 0 : if (PQnfields(res) != 2 || PQntuples(res) != 1)
578 : {
579 0 : int ntuples = PQntuples(res);
580 0 : int nfields = PQnfields(res);
581 :
582 0 : PQclear(res);
583 0 : ereport(ERROR,
584 : (errmsg("invalid response from primary server"),
585 : errdetail("Expected 1 tuple with 2 fields, got %d tuples with %d fields.",
586 : ntuples, nfields)));
587 : }
588 0 : *filename = pstrdup(PQgetvalue(res, 0, 0));
589 :
590 0 : *len = PQgetlength(res, 0, 1);
591 0 : *content = palloc(*len);
592 0 : memcpy(*content, PQgetvalue(res, 0, 1), *len);
593 0 : PQclear(res);
594 0 : }
595 :
596 : /*
597 : * Send a query and wait for the results by using the asynchronous libpq
598 : * functions and socket readiness events.
599 : *
600 : * We must not use the regular blocking libpq functions like PQexec()
601 : * since they are uninterruptible by signals on some platforms, such as
602 : * Windows.
603 : *
604 : * The function is modeled on PQexec() in libpq, but only implements
605 : * those parts that are in use in the walreceiver api.
606 : *
607 : * May return NULL, rather than an error result, on failure.
608 : */
609 : static PGresult *
610 1460 : libpqrcv_PQexec(PGconn *streamConn, const char *query)
611 : {
612 1460 : PGresult *lastResult = NULL;
613 :
614 : /*
615 : * PQexec() silently discards any prior query results on the connection.
616 : * This is not required for this function as it's expected that the caller
617 : * (which is this library in all cases) will behave correctly and we don't
618 : * have to be backwards compatible with old libpq.
619 : */
620 :
621 : /*
622 : * Submit the query. Since we don't use non-blocking mode, this could
623 : * theoretically block. In practice, since we don't send very long query
624 : * strings, the risk seems negligible.
625 : */
626 1460 : if (!PQsendQuery(streamConn, query))
627 0 : return NULL;
628 :
629 : for (;;)
630 : {
631 : /* Wait for, and collect, the next PGresult. */
632 : PGresult *result;
633 :
634 2596 : result = libpqrcv_PQgetResult(streamConn);
635 2596 : if (result == NULL)
636 1136 : break; /* query is complete, or failure */
637 :
638 : /*
639 : * Emulate PQexec()'s behavior of returning the last result when there
640 : * are many. We are fine with returning just last error message.
641 : */
642 1460 : PQclear(lastResult);
643 1460 : lastResult = result;
644 :
645 2920 : if (PQresultStatus(lastResult) == PGRES_COPY_IN ||
646 2794 : PQresultStatus(lastResult) == PGRES_COPY_OUT ||
647 2470 : PQresultStatus(lastResult) == PGRES_COPY_BOTH ||
648 1136 : PQstatus(streamConn) == CONNECTION_BAD)
649 : break;
650 1136 : }
651 :
652 1460 : return lastResult;
653 : }
654 :
655 : /*
656 : * Perform the equivalent of PQgetResult(), but watch for interrupts.
657 : */
658 : static PGresult *
659 3222 : libpqrcv_PQgetResult(PGconn *streamConn)
660 : {
661 : /*
662 : * Collect data until PQgetResult is ready to get the result without
663 : * blocking.
664 : */
665 7904 : while (PQisBusy(streamConn))
666 : {
667 : int rc;
668 :
669 : /*
670 : * We don't need to break down the sleep into smaller increments,
671 : * since we'll get interrupted by signals and can handle any
672 : * interrupts here.
673 : */
674 1464 : rc = WaitLatchOrSocket(MyLatch,
675 : WL_EXIT_ON_PM_DEATH | WL_SOCKET_READABLE |
676 : WL_LATCH_SET,
677 : PQsocket(streamConn),
678 : 0,
679 : WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE);
680 :
681 : /* Interrupted? */
682 1464 : if (rc & WL_LATCH_SET)
683 : {
684 0 : ResetLatch(MyLatch);
685 0 : ProcessWalRcvInterrupts();
686 : }
687 :
688 : /* Consume whatever data is available from the socket */
689 1464 : if (PQconsumeInput(streamConn) == 0)
690 : {
691 : /* trouble; return NULL */
692 4 : return NULL;
693 : }
694 : }
695 :
696 : /* Now we can collect and return the next PGresult */
697 3218 : return PQgetResult(streamConn);
698 : }
699 :
700 : /*
701 : * Disconnect connection to primary, if any.
702 : */
703 : static void
704 290 : libpqrcv_disconnect(WalReceiverConn *conn)
705 : {
706 290 : PQfinish(conn->streamConn);
707 290 : if (conn->recvBuf != NULL)
708 8 : PQfreemem(conn->recvBuf);
709 290 : pfree(conn);
710 290 : }
711 :
712 : /*
713 : * Receive a message available from XLOG stream.
714 : *
715 : * Returns:
716 : *
717 : * If data was received, returns the length of the data. *buffer is set to
718 : * point to a buffer holding the received message. The buffer is only valid
719 : * until the next libpqrcv_* call.
720 : *
721 : * If no data was available immediately, returns 0, and *wait_fd is set to a
722 : * socket descriptor which can be waited on before trying again.
723 : *
724 : * -1 if the server ended the COPY.
725 : *
726 : * ereports on error.
727 : */
728 : static int
729 345616 : libpqrcv_receive(WalReceiverConn *conn, char **buffer,
730 : pgsocket *wait_fd)
731 : {
732 : int rawlen;
733 :
734 345616 : if (conn->recvBuf != NULL)
735 283720 : PQfreemem(conn->recvBuf);
736 345616 : conn->recvBuf = NULL;
737 :
738 : /* Try to receive a CopyData message */
739 345616 : rawlen = PQgetCopyData(conn->streamConn, &conn->recvBuf, 1);
740 345616 : if (rawlen == 0)
741 : {
742 : /* Try consuming some data. */
743 177244 : if (PQconsumeInput(conn->streamConn) == 0)
744 12 : ereport(ERROR,
745 : (errmsg("could not receive data from WAL stream: %s",
746 : pchomp(PQerrorMessage(conn->streamConn)))));
747 :
748 : /* Now that we've consumed some input, try again */
749 177232 : rawlen = PQgetCopyData(conn->streamConn, &conn->recvBuf, 1);
750 177232 : if (rawlen == 0)
751 : {
752 : /* Tell caller to try again when our socket is ready. */
753 61746 : *wait_fd = PQsocket(conn->streamConn);
754 61746 : return 0;
755 : }
756 : }
757 283858 : if (rawlen == -1) /* end-of-streaming or error */
758 : {
759 : PGresult *res;
760 :
761 130 : res = libpqrcv_PQgetResult(conn->streamConn);
762 130 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
763 : {
764 130 : PQclear(res);
765 :
766 : /* Verify that there are no more results. */
767 130 : res = libpqrcv_PQgetResult(conn->streamConn);
768 130 : if (res != NULL)
769 : {
770 0 : PQclear(res);
771 :
772 : /*
773 : * If the other side closed the connection orderly (otherwise
774 : * we'd seen an error, or PGRES_COPY_IN) don't report an error
775 : * here, but let callers deal with it.
776 : */
777 0 : if (PQstatus(conn->streamConn) == CONNECTION_BAD)
778 0 : return -1;
779 :
780 0 : ereport(ERROR,
781 : (errmsg("unexpected result after CommandComplete: %s",
782 : PQerrorMessage(conn->streamConn))));
783 : }
784 :
785 130 : return -1;
786 : }
787 0 : else if (PQresultStatus(res) == PGRES_COPY_IN)
788 : {
789 0 : PQclear(res);
790 0 : return -1;
791 : }
792 : else
793 : {
794 0 : PQclear(res);
795 0 : ereport(ERROR,
796 : (errmsg("could not receive data from WAL stream: %s",
797 : pchomp(PQerrorMessage(conn->streamConn)))));
798 : }
799 : }
800 283728 : if (rawlen < -1)
801 0 : ereport(ERROR,
802 : (errmsg("could not receive data from WAL stream: %s",
803 : pchomp(PQerrorMessage(conn->streamConn)))));
804 :
805 : /* Return received messages to caller */
806 283728 : *buffer = conn->recvBuf;
807 283728 : return rawlen;
808 : }
809 :
810 : /*
811 : * Send a message to XLOG stream.
812 : *
813 : * ereports on error.
814 : */
815 : static void
816 33422 : libpqrcv_send(WalReceiverConn *conn, const char *buffer, int nbytes)
817 : {
818 66844 : if (PQputCopyData(conn->streamConn, buffer, nbytes) <= 0 ||
819 33422 : PQflush(conn->streamConn))
820 2 : ereport(ERROR,
821 : (errmsg("could not send data to WAL stream: %s",
822 : pchomp(PQerrorMessage(conn->streamConn)))));
823 33420 : }
824 :
825 : /*
826 : * Create new replication slot.
827 : * Returns the name of the exported snapshot for logical slot or NULL for
828 : * physical slot.
829 : */
830 : static char *
831 182 : libpqrcv_create_slot(WalReceiverConn *conn, const char *slotname,
832 : bool temporary, CRSSnapshotAction snapshot_action,
833 : XLogRecPtr *lsn)
834 : {
835 : PGresult *res;
836 : StringInfoData cmd;
837 : char *snapshot;
838 :
839 182 : initStringInfo(&cmd);
840 :
841 182 : appendStringInfo(&cmd, "CREATE_REPLICATION_SLOT \"%s\"", slotname);
842 :
843 182 : if (temporary)
844 126 : appendStringInfoString(&cmd, " TEMPORARY");
845 :
846 182 : if (conn->logical)
847 : {
848 182 : appendStringInfoString(&cmd, " LOGICAL pgoutput");
849 182 : switch (snapshot_action)
850 : {
851 : case CRS_EXPORT_SNAPSHOT:
852 0 : appendStringInfoString(&cmd, " EXPORT_SNAPSHOT");
853 0 : break;
854 : case CRS_NOEXPORT_SNAPSHOT:
855 56 : appendStringInfoString(&cmd, " NOEXPORT_SNAPSHOT");
856 56 : break;
857 : case CRS_USE_SNAPSHOT:
858 126 : appendStringInfoString(&cmd, " USE_SNAPSHOT");
859 126 : break;
860 : }
861 : }
862 : else
863 : {
864 0 : appendStringInfoString(&cmd, " PHYSICAL RESERVE_WAL");
865 : }
866 :
867 182 : res = libpqrcv_PQexec(conn->streamConn, cmd.data);
868 182 : pfree(cmd.data);
869 :
870 182 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
871 : {
872 0 : PQclear(res);
873 0 : ereport(ERROR,
874 : (errmsg("could not create replication slot \"%s\": %s",
875 : slotname, pchomp(PQerrorMessage(conn->streamConn)))));
876 : }
877 :
878 182 : if (lsn)
879 126 : *lsn = DatumGetLSN(DirectFunctionCall1Coll(pg_lsn_in, InvalidOid,
880 : CStringGetDatum(PQgetvalue(res, 0, 1))));
881 :
882 182 : if (!PQgetisnull(res, 0, 2))
883 0 : snapshot = pstrdup(PQgetvalue(res, 0, 2));
884 : else
885 182 : snapshot = NULL;
886 :
887 182 : PQclear(res);
888 :
889 182 : return snapshot;
890 : }
891 :
892 : /*
893 : * Return PID of remote backend process.
894 : */
895 : static pid_t
896 0 : libpqrcv_get_backend_pid(WalReceiverConn *conn)
897 : {
898 0 : return PQbackendPID(conn->streamConn);
899 : }
900 :
901 : /*
902 : * Convert tuple query result to tuplestore.
903 : */
904 : static void
905 322 : libpqrcv_processTuples(PGresult *pgres, WalRcvExecResult *walres,
906 : const int nRetTypes, const Oid *retTypes)
907 : {
908 : int tupn;
909 : int coln;
910 322 : int nfields = PQnfields(pgres);
911 : HeapTuple tuple;
912 : AttInMetadata *attinmeta;
913 : MemoryContext rowcontext;
914 : MemoryContext oldcontext;
915 :
916 : /* Make sure we got expected number of fields. */
917 322 : if (nfields != nRetTypes)
918 0 : ereport(ERROR,
919 : (errmsg("invalid query response"),
920 : errdetail("Expected %d fields, got %d fields.",
921 : nRetTypes, nfields)));
922 :
923 322 : walres->tuplestore = tuplestore_begin_heap(true, false, work_mem);
924 :
925 : /* Create tuple descriptor corresponding to expected result. */
926 322 : walres->tupledesc = CreateTemplateTupleDesc(nRetTypes);
927 1344 : for (coln = 0; coln < nRetTypes; coln++)
928 2044 : TupleDescInitEntry(walres->tupledesc, (AttrNumber) coln + 1,
929 2044 : PQfname(pgres, coln), retTypes[coln], -1, 0);
930 322 : attinmeta = TupleDescGetAttInMetadata(walres->tupledesc);
931 :
932 : /* No point in doing more here if there were no tuples returned. */
933 322 : if (PQntuples(pgres) == 0)
934 324 : return;
935 :
936 : /* Create temporary context for local allocations. */
937 320 : rowcontext = AllocSetContextCreate(CurrentMemoryContext,
938 : "libpqrcv query result context",
939 : ALLOCSET_DEFAULT_SIZES);
940 :
941 : /* Process returned rows. */
942 810 : for (tupn = 0; tupn < PQntuples(pgres); tupn++)
943 : {
944 : char *cstrs[MaxTupleAttributeNumber];
945 :
946 490 : ProcessWalRcvInterrupts();
947 :
948 : /* Do the allocations in temporary context. */
949 490 : oldcontext = MemoryContextSwitchTo(rowcontext);
950 :
951 : /*
952 : * Fill cstrs with null-terminated strings of column values.
953 : */
954 2044 : for (coln = 0; coln < nfields; coln++)
955 : {
956 1554 : if (PQgetisnull(pgres, tupn, coln))
957 36 : cstrs[coln] = NULL;
958 : else
959 1518 : cstrs[coln] = PQgetvalue(pgres, tupn, coln);
960 : }
961 :
962 : /* Convert row to a tuple, and add it to the tuplestore */
963 490 : tuple = BuildTupleFromCStrings(attinmeta, cstrs);
964 490 : tuplestore_puttuple(walres->tuplestore, tuple);
965 :
966 : /* Clean up */
967 490 : MemoryContextSwitchTo(oldcontext);
968 490 : MemoryContextReset(rowcontext);
969 : }
970 :
971 320 : MemoryContextDelete(rowcontext);
972 : }
973 :
974 : /*
975 : * Public interface for sending generic queries (and commands).
976 : *
977 : * This can only be called from process connected to database.
978 : */
979 : static WalRcvExecResult *
980 714 : libpqrcv_exec(WalReceiverConn *conn, const char *query,
981 : const int nRetTypes, const Oid *retTypes)
982 : {
983 714 : PGresult *pgres = NULL;
984 714 : WalRcvExecResult *walres = palloc0(sizeof(WalRcvExecResult));
985 :
986 714 : if (MyDatabaseId == InvalidOid)
987 0 : ereport(ERROR,
988 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
989 : errmsg("the query interface requires a database connection")));
990 :
991 714 : pgres = libpqrcv_PQexec(conn->streamConn, query);
992 :
993 714 : switch (PQresultStatus(pgres))
994 : {
995 : case PGRES_SINGLE_TUPLE:
996 : case PGRES_TUPLES_OK:
997 322 : walres->status = WALRCV_OK_TUPLES;
998 322 : libpqrcv_processTuples(pgres, walres, nRetTypes, retTypes);
999 322 : break;
1000 :
1001 : case PGRES_COPY_IN:
1002 0 : walres->status = WALRCV_OK_COPY_IN;
1003 0 : break;
1004 :
1005 : case PGRES_COPY_OUT:
1006 126 : walres->status = WALRCV_OK_COPY_OUT;
1007 126 : break;
1008 :
1009 : case PGRES_COPY_BOTH:
1010 0 : walres->status = WALRCV_OK_COPY_BOTH;
1011 0 : break;
1012 :
1013 : case PGRES_COMMAND_OK:
1014 266 : walres->status = WALRCV_OK_COMMAND;
1015 266 : break;
1016 :
1017 : /* Empty query is considered error. */
1018 : case PGRES_EMPTY_QUERY:
1019 0 : walres->status = WALRCV_ERROR;
1020 0 : walres->err = _("empty query");
1021 0 : break;
1022 :
1023 : case PGRES_NONFATAL_ERROR:
1024 : case PGRES_FATAL_ERROR:
1025 : case PGRES_BAD_RESPONSE:
1026 0 : walres->status = WALRCV_ERROR;
1027 0 : walres->err = pchomp(PQerrorMessage(conn->streamConn));
1028 0 : break;
1029 : }
1030 :
1031 714 : PQclear(pgres);
1032 :
1033 714 : return walres;
1034 : }
1035 :
1036 : /*
1037 : * Given a List of strings, return it as single comma separated
1038 : * string, quoting identifiers as needed.
1039 : *
1040 : * This is essentially the reverse of SplitIdentifierString.
1041 : *
1042 : * The caller should free the result.
1043 : */
1044 : static char *
1045 198 : stringlist_to_identifierstr(PGconn *conn, List *strings)
1046 : {
1047 : ListCell *lc;
1048 : StringInfoData res;
1049 198 : bool first = true;
1050 :
1051 198 : initStringInfo(&res);
1052 :
1053 416 : foreach(lc, strings)
1054 : {
1055 218 : char *val = strVal(lfirst(lc));
1056 : char *val_escaped;
1057 :
1058 218 : if (first)
1059 198 : first = false;
1060 : else
1061 20 : appendStringInfoChar(&res, ',');
1062 :
1063 218 : val_escaped = PQescapeIdentifier(conn, val, strlen(val));
1064 218 : if (!val_escaped)
1065 : {
1066 0 : free(res.data);
1067 0 : return NULL;
1068 : }
1069 218 : appendStringInfoString(&res, val_escaped);
1070 218 : PQfreemem(val_escaped);
1071 : }
1072 :
1073 198 : return res.data;
1074 : }
|