diff options
-rw-r--r-- | .cirrus.tasks.yml | 3 | ||||
-rw-r--r-- | contrib/xml2/xpath.c | 49 | ||||
-rw-r--r-- | contrib/xml2/xslt_proc.c | 10 | ||||
-rw-r--r-- | doc/src/sgml/libpq.sgml | 20 | ||||
-rw-r--r-- | doc/src/sgml/logical-replication.sgml | 42 | ||||
-rw-r--r-- | src/bin/psql/command.c | 7 | ||||
-rw-r--r-- | src/bin/psql/common.c | 35 | ||||
-rw-r--r-- | src/bin/psql/common.h | 1 | ||||
-rw-r--r-- | src/bin/psql/prompt.c | 8 | ||||
-rw-r--r-- | src/include/utils/pg_locale.h | 3 | ||||
-rw-r--r-- | src/interfaces/libpq/exports.txt | 11 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 8 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 1 | ||||
-rw-r--r-- | src/interfaces/libpq/t/006_service.pl | 17 |
14 files changed, 139 insertions, 76 deletions
diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml index 92057006c93..1a366975d82 100644 --- a/.cirrus.tasks.yml +++ b/.cirrus.tasks.yml @@ -938,14 +938,11 @@ task: # - Don't use ccache, the files are uncacheable, polluting ccache's # cache # - Use -fmax-errors, as particularly cpluspluscheck can be very verbose - # - XXX have to disable ICU to avoid errors: - # https://postgr.es/m/20220323002024.f2g6tivduzrktgfa%40alap3.anarazel.de ### always: headers_headerscheck_script: | time ./configure \ ${LINUX_CONFIGURE_FEATURES} \ - --without-icu \ --quiet \ CC="gcc" CXX"=g++" CLANG="clang-16" make -s -j${BUILD_JOBS} clean diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index 11216b9b7f9..4ac291c8251 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -54,7 +54,7 @@ static xmlChar *pgxml_texttoxmlchar(text *textstring); static xpath_workspace *pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt); -static void cleanup_workspace(volatile xpath_workspace *workspace); +static void cleanup_workspace(xpath_workspace *workspace); /* @@ -88,8 +88,8 @@ Datum xml_encode_special_chars(PG_FUNCTION_ARGS) { text *tin = PG_GETARG_TEXT_PP(0); - text *tout; - volatile xmlChar *tt = NULL; + text *volatile tout = NULL; + xmlChar *volatile tt = NULL; PgXmlErrorContext *xmlerrcxt; xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); @@ -111,7 +111,7 @@ xml_encode_special_chars(PG_FUNCTION_ARGS) PG_CATCH(); { if (tt != NULL) - xmlFree((xmlChar *) tt); + xmlFree(tt); pg_xml_done(xmlerrcxt, true); @@ -120,7 +120,7 @@ xml_encode_special_chars(PG_FUNCTION_ARGS) PG_END_TRY(); if (tt != NULL) - xmlFree((xmlChar *) tt); + xmlFree(tt); pg_xml_done(xmlerrcxt, false); @@ -145,11 +145,10 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, xmlChar *plainsep) { volatile xmlBufferPtr buf = NULL; - xmlChar *result; - int i; + xmlChar *volatile result = NULL; PgXmlErrorContext *xmlerrcxt; - /* spin some error handling */ + /* spin up some error handling */ xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); PG_TRY(); @@ -168,7 +167,7 @@ pgxmlNodeSetToText(xmlNodeSetPtr nodeset, } if (nodeset != NULL) { - for (i = 0; i < nodeset->nodeNr; i++) + for (int i = 0; i < nodeset->nodeNr; i++) { if (plainsep != NULL) { @@ -257,8 +256,8 @@ xpath_nodeset(PG_FUNCTION_ARGS) xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2)); xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3)); xmlChar *xpath; - text *xpres; - volatile xpath_workspace *workspace; + text *volatile xpres = NULL; + xpath_workspace *volatile workspace = NULL; PgXmlErrorContext *xmlerrcxt; xpath = pgxml_texttoxmlchar(xpathsupp); @@ -302,8 +301,8 @@ xpath_list(PG_FUNCTION_ARGS) text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2)); xmlChar *xpath; - text *xpres; - volatile xpath_workspace *workspace; + text *volatile xpres = NULL; + xpath_workspace *volatile workspace = NULL; PgXmlErrorContext *xmlerrcxt; xpath = pgxml_texttoxmlchar(xpathsupp); @@ -344,8 +343,8 @@ xpath_string(PG_FUNCTION_ARGS) text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; int32 pathsize; - text *xpres; - volatile xpath_workspace *workspace; + text *volatile xpres = NULL; + xpath_workspace *volatile workspace = NULL; PgXmlErrorContext *xmlerrcxt; pathsize = VARSIZE_ANY_EXHDR(xpathsupp); @@ -398,9 +397,9 @@ xpath_number(PG_FUNCTION_ARGS) text *document = PG_GETARG_TEXT_PP(0); text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; - float4 fRes = 0.0; - bool isNull = false; - volatile xpath_workspace *workspace = NULL; + volatile float4 fRes = 0.0; + volatile bool isNull = false; + xpath_workspace *volatile workspace = NULL; PgXmlErrorContext *xmlerrcxt; xpath = pgxml_texttoxmlchar(xpathsupp); @@ -444,8 +443,8 @@ xpath_bool(PG_FUNCTION_ARGS) text *document = PG_GETARG_TEXT_PP(0); text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; - int bRes; - volatile xpath_workspace *workspace = NULL; + volatile int bRes = 0; + xpath_workspace *volatile workspace = NULL; PgXmlErrorContext *xmlerrcxt; xpath = pgxml_texttoxmlchar(xpathsupp); @@ -518,7 +517,7 @@ pgxml_xpath(text *document, xmlChar *xpath, PgXmlErrorContext *xmlerrcxt) /* Clean up after processing the result of pgxml_xpath() */ static void -cleanup_workspace(volatile xpath_workspace *workspace) +cleanup_workspace(xpath_workspace *workspace) { if (workspace->res) xmlXPathFreeObject(workspace->res); @@ -537,9 +536,9 @@ pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *septag, xmlChar *plainsep) { - volatile xmlChar *xpresstr = NULL; + xmlChar *volatile xpresstr = NULL; + text *volatile xpres = NULL; PgXmlErrorContext *xmlerrcxt; - text *xpres; if (res == NULL) return NULL; @@ -578,7 +577,7 @@ pgxml_result_to_text(xmlXPathObjectPtr res, PG_CATCH(); { if (xpresstr != NULL) - xmlFree((xmlChar *) xpresstr); + xmlFree(xpresstr); pg_xml_done(xmlerrcxt, true); @@ -587,7 +586,7 @@ pgxml_result_to_text(xmlXPathObjectPtr res, PG_END_TRY(); /* Free various storage */ - xmlFree((xmlChar *) xpresstr); + xmlFree(xpresstr); pg_xml_done(xmlerrcxt, false); diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c index c8e7dd45ed5..53550c7dc24 100644 --- a/contrib/xml2/xslt_proc.c +++ b/contrib/xml2/xslt_proc.c @@ -48,7 +48,7 @@ xslt_process(PG_FUNCTION_ARGS) text *doct = PG_GETARG_TEXT_PP(0); text *ssheet = PG_GETARG_TEXT_PP(1); - text *result; + text *volatile result = NULL; text *paramstr; const char **params; PgXmlErrorContext *xmlerrcxt; @@ -58,8 +58,7 @@ xslt_process(PG_FUNCTION_ARGS) volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL; volatile xsltTransformContextPtr xslt_ctxt = NULL; volatile int resstat = -1; - volatile xmlChar *resstr = NULL; - int reslen = 0; + xmlChar *volatile resstr = NULL; if (fcinfo->nargs == 3) { @@ -80,6 +79,7 @@ xslt_process(PG_FUNCTION_ARGS) { xmlDocPtr ssdoc; bool xslt_sec_prefs_error; + int reslen = 0; /* Parse document */ doctree = xmlReadMemory((char *) VARDATA_ANY(doct), @@ -160,7 +160,7 @@ xslt_process(PG_FUNCTION_ARGS) if (doctree != NULL) xmlFreeDoc(doctree); if (resstr != NULL) - xmlFree((xmlChar *) resstr); + xmlFree(resstr); xsltCleanupGlobals(); pg_xml_done(xmlerrcxt, true); @@ -177,7 +177,7 @@ xslt_process(PG_FUNCTION_ARGS) xsltCleanupGlobals(); if (resstr) - xmlFree((xmlChar *) resstr); + xmlFree(resstr); pg_xml_done(xmlerrcxt, false); diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 298c4b38ef9..b2c2cf9eac8 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2740,26 +2740,6 @@ char *PQport(const PGconn *conn); </listitem> </varlistentry> - <varlistentry id="libpq-PQservice"> - <term><function>PQservice</function><indexterm><primary>PQservice</primary></indexterm></term> - - <listitem> - <para> - Returns the service of the active connection. - -<synopsis> -char *PQservice(const PGconn *conn); -</synopsis> - </para> - - <para> - <xref linkend="libpq-PQservice"/> returns <symbol>NULL</symbol> if the - <parameter>conn</parameter> argument is <symbol>NULL</symbol>. - Otherwise, if there was no service provided, it returns an empty string. - </para> - </listitem> - </varlistentry> - <varlistentry id="libpq-PQtty"> <term><function>PQtty</function><indexterm><primary>PQtty</primary></indexterm></term> diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index f317ed9c50e..e26f7f59d4a 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -709,8 +709,8 @@ HINT: To initiate replication, you must manually create the replication slot, e </para> <para> - To confirm that the standby server is indeed ready for failover, follow these - steps to verify that all necessary logical replication slots have been + To confirm that the standby server is indeed ready for failover for a given subscriber, follow these + steps to verify that all the logical replication slots required by that subscriber have been synchronized to the standby server: </para> @@ -764,7 +764,7 @@ HINT: To initiate replication, you must manually create the replication slot, e Check that the logical replication slots identified above exist on the standby server and are ready for failover. <programlisting> -/* standby # */ SELECT slot_name, (synced AND NOT temporary AND NOT conflicting) AS failover_ready +/* standby # */ SELECT slot_name, (synced AND NOT temporary AND invalidation_reason IS NULL) AS failover_ready FROM pg_replication_slots WHERE slot_name IN ('sub1','sub2','sub3', 'pg_16394_sync_16385_7394666715149055164'); @@ -782,10 +782,42 @@ HINT: To initiate replication, you must manually create the replication slot, e <para> If all the slots are present on the standby server and the result (<literal>failover_ready</literal>) of the above SQL query is true, then - existing subscriptions can continue subscribing to publications now on the - new primary server. + existing subscriptions can continue subscribing to publications on the new + primary server. + </para> + + <para> + The first two steps in the above procedure are meant for a + <productname>PostgreSQL</productname> subscriber. It is recommended to run + these steps on each subscriber node, that will be served by the designated + standby after failover, to obtain the complete list of replication + slots. This list can then be verified in Step 3 to ensure failover readiness. + Non-<productname>PostgreSQL</productname> subscribers, on the other hand, may + use their own methods to identify the replication slots used by their + respective subscriptions. + </para> + + <para> + In some cases, such as during a planned failover, it is necessary to confirm + that all subscribers, whether <productname>PostgreSQL</productname> or + non-<productname>PostgreSQL</productname>, will be able to continue + replication after failover to a given standby server. In such cases, use the + following SQL, instead of performing the first two steps above, to identify + which replication slots on the primary need to be synced to the standby that + is intended for promotion. This query returns the relevant replication slots + associated with all the failover-enabled subscriptions. </para> + <para> +<programlisting> +/* primary # */ SELECT array_agg(quote_literal(r.slot_name)) AS slots + FROM pg_replication_slots r + WHERE r.failover AND NOT r.temporary; + slots +------- + {'sub1','sub2','sub3', 'pg_16394_sync_16385_7394666715149055164'} +(1 row) +</programlisting></para> </sect1> <sect1 id="logical-replication-row-filter"> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 9fcd2db8326..0a55901b14e 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -4480,6 +4480,7 @@ SyncVariables(void) { char vbuf[32]; const char *server_version; + char *service_name; /* get stuff from connection */ pset.encoding = PQclientEncoding(pset.db); @@ -4489,12 +4490,16 @@ SyncVariables(void) setFmtEncoding(pset.encoding); SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); - SetVariable(pset.vars, "SERVICE", PQservice(pset.db)); SetVariable(pset.vars, "USER", PQuser(pset.db)); SetVariable(pset.vars, "HOST", PQhost(pset.db)); SetVariable(pset.vars, "PORT", PQport(pset.db)); SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); + service_name = get_conninfo_value("service"); + SetVariable(pset.vars, "SERVICE", service_name); + if (service_name) + pg_free(service_name); + /* this bit should match connection_warnings(): */ /* Try to get full text form of version, might include "devel" etc */ server_version = PQparameterStatus(pset.db, "server_version"); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index d2c0a49c46c..cd329ade12b 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -2531,6 +2531,41 @@ session_username(void) return PQuser(pset.db); } +/* + * Return the value of option for keyword in the current connection. + * + * The caller is responsible for freeing the result value allocated. + */ +char * +get_conninfo_value(const char *keyword) +{ + PQconninfoOption *opts; + PQconninfoOption *serviceopt = NULL; + char *res = NULL; + + if (pset.db == NULL) + return NULL; + + opts = PQconninfo(pset.db); + if (opts == NULL) + return NULL; + + for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt) + { + if (strcmp(opt->keyword, keyword) == 0) + { + serviceopt = opt; + break; + } + } + + /* Take a copy of the value, as it is freed by PQconninfoFree(). */ + if (serviceopt && serviceopt->val != NULL) + res = pg_strdup(serviceopt->val); + PQconninfoFree(opts); + + return res; +} /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 7f1a23de1e8..64762ab9817 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -39,6 +39,7 @@ extern bool SendQuery(const char *query); extern bool is_superuser(void); extern bool standard_strings(void); extern const char *session_username(void); +extern char *get_conninfo_value(const char *keyword); extern void expand_tilde(char **filename); extern void clean_extended_state(void); diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index 3aa7d2d06c8..b08d7328fbf 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -169,8 +169,12 @@ get_prompt(promptStatus_t status, ConditionalStack cstack) break; /* service name */ case 's': - if (pset.db && PQservice(pset.db)) - strlcpy(buf, PQservice(pset.db), sizeof(buf)); + { + const char *service_name = GetVariable(pset.vars, "SERVICE"); + + if (service_name) + strlcpy(buf, service_name, sizeof(buf)); + } break; /* backend pid */ case 'p': diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 44ff60a25b4..1cd7c76a0a7 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -15,6 +15,9 @@ #include "mb/pg_wchar.h" #ifdef USE_ICU +/* only include the C APIs, to avoid errors in cpluspluscheck */ +#undef U_SHOW_CPLUSPLUS_API +#define U_SHOW_CPLUSPLUS_API 0 #include <unicode/ucol.h> #endif diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 0625cf39e9a..dbbae642d76 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -205,9 +205,8 @@ PQcancelFinish 202 PQsocketPoll 203 PQsetChunkedRowsMode 204 PQgetCurrentTimeUSec 205 -PQservice 206 -PQsetAuthDataHook 207 -PQgetAuthDataHook 208 -PQdefaultAuthDataHook 209 -PQfullProtocolVersion 210 -appendPQExpBufferVA 211 +PQsetAuthDataHook 206 +PQgetAuthDataHook 207 +PQdefaultAuthDataHook 208 +PQfullProtocolVersion 209 +appendPQExpBufferVA 210 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 51a9c416584..09eb79812ac 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -7462,14 +7462,6 @@ PQdb(const PGconn *conn) } char * -PQservice(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pgservice; -} - -char * PQuser(const PGconn *conn) { if (!conn) diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 7d3a9df6fd5..af8004f952a 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -400,7 +400,6 @@ extern int PQrequestCancel(PGconn *conn); /* Accessor functions for PGconn objects */ extern char *PQdb(const PGconn *conn); -extern char *PQservice(const PGconn *conn); extern char *PQuser(const PGconn *conn); extern char *PQpass(const PGconn *conn); extern char *PQhost(const PGconn *conn); diff --git a/src/interfaces/libpq/t/006_service.pl b/src/interfaces/libpq/t/006_service.pl index 4fe5adc5c2a..d896558a6cc 100644 --- a/src/interfaces/libpq/t/006_service.pl +++ b/src/interfaces/libpq/t/006_service.pl @@ -47,6 +47,12 @@ my $srvfile_default = "$td/pg_service.conf"; # Missing service file. my $srvfile_missing = "$td/pg_service_missing.conf"; +# Service file with nested "service" defined. +my $srvfile_nested = "$td/pg_service_nested.conf"; +copy($srvfile_valid, $srvfile_nested) + or die "Could not copy $srvfile_valid to $srvfile_nested: $!"; +append_to_file($srvfile_nested, 'service=invalid_srv' . $newline); + # Set the fallback directory lookup of the service file to the temporary # directory of this test. PGSYSCONFDIR is used if the service file # defined in PGSERVICEFILE cannot be found, or when a service file is @@ -146,6 +152,17 @@ local $ENV{PGSERVICEFILE} = "$srvfile_empty"; unlink($srvfile_default); } +# Checks nested service file contents. +{ + local $ENV{PGSERVICEFILE} = $srvfile_nested; + + $dummy_node->connect_fails( + 'service=my_srv', + 'connection with nested service file', + expected_stderr => + qr/nested service specifications not supported in service file/); +} + $node->teardown_node; done_testing(); |