From bf371b501580cfa300c5198cc11ac67acc7631c3 Mon Sep 17 00:00:00 2001 From: Badrul Chowdhury Date: Mon, 25 Sep 2017 13:42:44 -0700 Subject: [PATCH] =?UTF-8?q?Introducing=20pgwire=20v3.1:=201.=20adds=20supp?= =?UTF-8?q?ort=20for=20backend=20(BE)=20to=20accept=20optional=20parameter?= =?UTF-8?q?s,=20ie=20parameters=20that=20have=20=E2=80=9C=5Fpq=5F=E2=80=9D?= =?UTF-8?q?=20as=20a=20proper=20prefix=20in=20their=20names=202.=20creates?= =?UTF-8?q?=20data=20structure=20for=20passing=20in=20additional=20paramet?= =?UTF-8?q?ers=20in=20FE,=20adding=20command=20line=20support=20is=20out?= =?UTF-8?q?=20of=20scope=20of=20this=20item=203.=20enhances=20FE->BE=20pro?= =?UTF-8?q?tocol=20negotiation:=20adds=20support=20for=20newer=20FE=20to?= =?UTF-8?q?=20connect=20to=20older=20BE=20while=20maintaining=20back-compa?= =?UTF-8?q?tibility,=20ie=20same=20FE=20<->=20BE,=20older=20FE=20to=20newe?= =?UTF-8?q?r=20BE=20work=20as=20before?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/postmaster/postmaster.c | 112 ++++++++++++++++++++--- src/backend/utils/misc/guc.c | 22 ++++- src/include/postmaster/postmaster.h | 7 ++ 20 files changed, 372 insertions(+), 40 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 95180b2ef5..21d77333be 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -103,6 +103,7 @@ #include "lib/ilist.h" #include "libpq/auth.h" #include "libpq/libpq.h" +#include "libpq/pqformat.h" #include "libpq/pqsignal.h" #include "miscadmin.h" #include "pg_getopt.h" @@ -567,6 +568,77 @@ HANDLE PostmasterHandle; #endif /* + * Initiate protocol negotiation phase; + * protocol negotiation is only supported for pgwire version 3.x, x>0. + * + * Ensure that the packet write buffer is flushed. + */ +int +NegotiateServerProtocol(Port *port) +{ + if (whereToSendOutput != DestRemote || + PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) + return -1; + + int sendStatus = 0; + + /* NegotiateServerProtocol packet structure + * + * [ 'Y' | msgLength | min_version | max_version | param_list_len | list of param names ] + */ + + sendStatus = SendServerProtocolVersionMessage(port); + + /* Ensure that the message buffer is flushed */ + pq_flush(); + + return sendStatus; +} + +/* + * Construct and send a ServerProtocolVersion message. + * + * Message contains: + * 1. minimum, maximum versions supported by the BE, + * 2. number of parameters that were honored by the BE from startup packet, + * 3. a list of strings consisting of the parameter names accepted by BE. + */ +int +SendServerProtocolVersionMessage(Port *port) +{ + StringInfoData buf; + + /* PG message type*/ + pq_beginmessage(&buf, 'Y'); + + /* Protocol version numbers */ + pq_sendint(&buf, PG_PROTOCOL_EARLIEST, sizeof(int32)); /* min */ + pq_sendint(&buf, PG_PROTOCOL_LATEST, sizeof(int32)); /* max */ + + /* Length of parameter list; parameter list consists of (key, value) pairs */ + pq_sendint(&buf, list_length(port->guc_options) / 2, sizeof(int32)); + + ListCell *gucopts = list_head(port->guc_options); + while (gucopts) + { + char *name; + + /* First comes key, which we need. */ + name = lfirst(gucopts); + gucopts = lnext(gucopts); + + /* Then comes value, which we don't need. */ + gucopts = lnext(gucopts); + + pq_sendstring(&buf, name); + } + + pq_endmessage(&buf); + + return 0; +} + +/* * Postmaster main entry point */ void @@ -2050,20 +2122,6 @@ retry1: */ FrontendProtocol = proto; - /* Check we can handle the protocol the frontend is using. */ - - if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || - PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) || - (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) && - PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))) - ereport(FATAL, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u", - PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto), - PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST), - PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST), - PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))); - /* * Now fetch parameters out of startup packet and save them into the Port * structure. All data structures attached to the Port struct must be @@ -2145,9 +2203,35 @@ retry1: ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid startup packet layout: expected terminator as last byte"))); + + /* + * Need to negotiate pgwire protocol if + * 1. FE version is not the same as BE version + * 2. FE version is not 3.0 + */ + if (FrontendProtocol != PG_PROTOCOL_LATEST + && FrontendProtocol != PG_PROTOCOL(3, 0)) + { + /* Negotiate parameters after all the error-checking is done */ + if (NegotiateServerProtocol(port)) + return STATUS_ERROR; + } } else { + /* Check we can handle the protocol the frontend is using. */ + if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) || + PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) || + (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) && + PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))) + ereport(FATAL, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u", + PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto), + PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST), + PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST), + PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))); + /* * Get the parameters from the old-style, fixed-width-fields startup * packet as C strings. The packet destination was cleared first so a diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 246fea8693..69441f3f86 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3961,6 +3961,7 @@ static int GUCNestLevel = 0; /* 1 when in main transaction */ static int guc_var_compare(const void *a, const void *b); static int guc_name_compare(const char *namea, const char *nameb); +static bool is_optional(const char *guc_name); static void InitializeGUCOptionsFromEnvironment(void); static void InitializeOneGUCOption(struct config_generic *gconf); static void push_old_value(struct config_generic *gconf, GucAction action); @@ -4416,7 +4417,7 @@ find_option(const char *name, bool create_placeholders, int elevel) /* * Check if the name is qualified, and if so, add a placeholder. */ - if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL) + if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL || is_optional(name)) return add_placeholder_variable(name, elevel); } @@ -4467,6 +4468,25 @@ guc_name_compare(const char *namea, const char *nameb) return 0; } +/* + * A GUC variable is deemed optional if the name contains "_pq_" as a proper prefix. + * + * It takes the whole struct as input in case we want to move away from name-based + * tagging of optional variables. + */ +bool +is_optional(const char *guc_name) +{ + const char *optionalPrefix = "_pq_"; + bool isOptional = false; + + /* "_pq_" must be a proper prefix of the guc name in all encodings */ + if (guc_name_compare(guc_name, optionalPrefix) == 1 && + strstr(guc_name, optionalPrefix)) + isOptional = true; + + return isOptional; +} /* * Initialize GUC options during program startup. diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 0f85908b09..da0a3a79c3 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -13,6 +13,10 @@ #ifndef _POSTMASTER_H #define _POSTMASTER_H +#include "postgres.h" + +#include "libpq/libpq-be.h" + /* GUC options */ extern bool EnableSSL; extern int ReservedBackends; @@ -46,6 +50,9 @@ extern int postmaster_alive_fds[2]; extern const char *progname; +extern int NegotiateServerProtocol(Port *port); +extern int SendServerProtocolVersionMessage(Port *port); + extern void PostmasterMain(int argc, char *argv[]) pg_attribute_noreturn(); extern void ClosePostmasterPorts(bool am_syslogger); -- 2.13.2.windows.1