summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.tasks.yml3
-rw-r--r--contrib/xml2/xpath.c49
-rw-r--r--contrib/xml2/xslt_proc.c10
-rw-r--r--doc/src/sgml/libpq.sgml20
-rw-r--r--doc/src/sgml/logical-replication.sgml42
-rw-r--r--src/backend/utils/adt/xml.c21
-rw-r--r--src/bin/psql/command.c7
-rw-r--r--src/bin/psql/common.c35
-rw-r--r--src/bin/psql/common.h1
-rw-r--r--src/bin/psql/prompt.c8
-rw-r--r--src/bin/psql/tab-complete.in.c20
-rw-r--r--src/include/utils/pg_locale.h3
-rw-r--r--src/interfaces/libpq/exports.txt11
-rw-r--r--src/interfaces/libpq/fe-connect.c8
-rw-r--r--src/interfaces/libpq/libpq-fe.h1
-rw-r--r--src/interfaces/libpq/t/006_service.pl17
16 files changed, 172 insertions, 84 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/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 2bd39b6ac4b..f7b731825fc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -532,7 +532,7 @@ xmltext(PG_FUNCTION_ARGS)
volatile xmlChar *xmlbuf = NULL;
PgXmlErrorContext *xmlerrcxt;
- /* Otherwise, we gotta spin up some error handling. */
+ /* First we gotta spin up some error handling. */
xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
PG_TRY();
@@ -685,7 +685,7 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
volatile xmlBufferPtr buf = NULL;
volatile xmlSaveCtxtPtr ctxt = NULL;
ErrorSaveContext escontext = {T_ErrorSaveContext};
- PgXmlErrorContext *xmlerrcxt;
+ PgXmlErrorContext *volatile xmlerrcxt = NULL;
#endif
if (xmloption_arg != XMLOPTION_DOCUMENT && !indent)
@@ -726,13 +726,18 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
return (text *) data;
}
- /* Otherwise, we gotta spin up some error handling. */
- xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
-
+ /*
+ * Otherwise, we gotta spin up some error handling. Unlike most other
+ * routines in this module, we already have a libxml "doc" structure to
+ * free, so we need to call pg_xml_init() inside the PG_TRY and be
+ * prepared for it to fail (typically due to palloc OOM).
+ */
PG_TRY();
{
size_t decl_len = 0;
+ xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
+
/* The serialized data will go into this buffer. */
buf = xmlBufferCreate();
@@ -863,10 +868,10 @@ xmltotext_with_options(xmltype *data, XmlOptionType xmloption_arg, bool indent)
xmlSaveClose(ctxt);
if (buf)
xmlBufferFree(buf);
- if (doc)
- xmlFreeDoc(doc);
+ xmlFreeDoc(doc);
- pg_xml_done(xmlerrcxt, true);
+ if (xmlerrcxt)
+ pg_xml_done(xmlerrcxt, true);
PG_RE_THROW();
}
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/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 53e7d35fe98..13db6523448 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -4624,6 +4624,26 @@ match_previous_words(int pattern_id,
COMPLETE_WITH("FROM");
}
+ /* Complete "GRANT/REVOKE * ON LARGE OBJECT *" with TO/FROM */
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "LARGE", "OBJECT", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "LARGE", "OBJECT", MatchAny))
+ {
+ if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+ COMPLETE_WITH("TO");
+ else
+ COMPLETE_WITH("FROM");
+ }
+
+ /* Complete "GRANT/REVOKE * ON LARGE OBJECTS" with TO/FROM */
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "LARGE", "OBJECTS") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "LARGE", "OBJECTS"))
+ {
+ if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny))
+ COMPLETE_WITH("TO");
+ else
+ COMPLETE_WITH("FROM");
+ }
+
/* GROUP BY */
else if (TailMatches("FROM", MatchAny, "GROUP"))
COMPLETE_WITH("BY");
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();