summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorHeikki Linnakangas2025-04-02 13:41:45 +0000
committerHeikki Linnakangas2025-04-02 13:41:45 +0000
commit285613c60a7aff5daaf281c67002483b0d26e715 (patch)
treecee61e8b4cba4c93fba029532d65370428e004d9 /src/test
parent5070349102af12832c8528651c8ed18b16346323 (diff)
libpq: Add min/max_protocol_version connection options
All supported version of the PostgreSQL server send the NegotiateProtocolVersion message when an unsupported minor protocol version is requested by a client. But many other applications that implement the PostgreSQL protocol (connection poolers, or other databases) do not, and the same is true for PostgreSQL server versions older than 9.3. Connecting to such other applications thus fails if a client requests a protocol version different than 3.0. This patch adds a max_protocol_version connection option to libpq that specifies the protocol version that libpq should request from the server. Currently only 3.0 is supported, but that will change in a future commit that bumps the protocol version. Even after that version bump the default will likely stay 3.0 for the time being. Once more of the ecosystem supports the NegotiateProtocolVersion message we might want to change the default to the latest minor version. This also adds the similar min_protocol_version connection option, to allow the client to specify that connecting should fail if a lower protocol version is attempted by the server. This can be used to ensure that certain protocol features are used, which can be particularly useful if those features impact security. Author: Jelte Fennema-Nio <postgres@jeltef.nl> Reviewed-by: Robert Haas <robertmhaas@gmail.com> (earlier versions) Discussion: https://www.postgresql.org/message-id/CAGECzQTfc_O%2BHXqAo5_-xG4r3EFVsTefUeQzSvhEyyLDba-O9w@mail.gmail.com Discussion: https://www.postgresql.org/message-id/CAGECzQRbAGqJnnJJxTdKewTsNOovUt4bsx3NFfofz3m2j-t7tA@mail.gmail.com
Diffstat (limited to 'src/test')
-rw-r--r--src/test/modules/libpq_pipeline/libpq_pipeline.c113
1 files changed, 111 insertions, 2 deletions
diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c
index ac9ac95135f..9a3c0236325 100644
--- a/src/test/modules/libpq_pipeline/libpq_pipeline.c
+++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c
@@ -206,15 +206,17 @@ copy_connection(PGconn *conn)
PQconninfoOption *opts = PQconninfo(conn);
const char **keywords;
const char **vals;
- int nopts = 1;
- int i = 0;
+ int nopts = 0;
+ int i;
for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
nopts++;
+ nopts++; /* for the NULL terminator */
keywords = pg_malloc(sizeof(char *) * nopts);
vals = pg_malloc(sizeof(char *) * nopts);
+ i = 0;
for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
{
if (opt->val)
@@ -1405,6 +1407,110 @@ test_prepared(PGconn *conn)
fprintf(stderr, "ok\n");
}
+/*
+ * Test max_protocol_version options.
+ */
+static void
+test_protocol_version(PGconn *conn)
+{
+ const char **keywords;
+ const char **vals;
+ int nopts;
+ PQconninfoOption *opts = PQconninfo(conn);
+ int protocol_version;
+ int max_protocol_version_index;
+ int i;
+
+ /*
+ * Prepare keywords/vals arrays, copied from the existing connection, with
+ * an extra slot for 'max_protocol_version'.
+ */
+ nopts = 0;
+ for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+ nopts++;
+ nopts++; /* max_protocol_version */
+ nopts++; /* NULL terminator */
+
+ keywords = pg_malloc0(sizeof(char *) * nopts);
+ vals = pg_malloc0(sizeof(char *) * nopts);
+
+ i = 0;
+ for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+ {
+ if (opt->val)
+ {
+ keywords[i] = opt->keyword;
+ vals[i] = opt->val;
+ i++;
+ }
+ }
+
+ max_protocol_version_index = i;
+ keywords[i] = "max_protocol_version"; /* value is filled in below */
+ i++;
+ keywords[i] = vals[i] = NULL;
+
+ /*
+ * Test max_protocol_version=3.0
+ */
+ vals[max_protocol_version_index] = "3.0";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30000)
+ pg_fatal("expected 30000, got %d", protocol_version);
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=3.1. It's not valid, we went straight from
+ * 3.0 to 3.2.
+ */
+ vals[max_protocol_version_index] = "3.1";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_BAD)
+ pg_fatal("Connecting with max_protocol_version 3.1 should have failed.");
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=3.2
+ */
+ vals[max_protocol_version_index] = "3.2";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30002)
+ pg_fatal("expected 30002, got %d", protocol_version);
+
+ PQfinish(conn);
+
+ /*
+ * Test max_protocol_version=latest. 'latest' currently means '3.2'.
+ */
+ vals[max_protocol_version_index] = "latest";
+ conn = PQconnectdbParams(keywords, vals, false);
+
+ if (PQstatus(conn) != CONNECTION_OK)
+ pg_fatal("Connection to database failed: %s",
+ PQerrorMessage(conn));
+
+ protocol_version = PQfullProtocolVersion(conn);
+ if (protocol_version != 30002)
+ pg_fatal("expected 30002, got %d", protocol_version);
+
+ PQfinish(conn);
+}
+
/* Notice processor: print notices, and count how many we got */
static void
notice_processor(void *arg, const char *message)
@@ -2153,6 +2259,7 @@ print_test_list(void)
printf("pipeline_idle\n");
printf("pipelined_insert\n");
printf("prepared\n");
+ printf("protocol_version\n");
printf("simple_pipeline\n");
printf("singlerow\n");
printf("transaction\n");
@@ -2263,6 +2370,8 @@ main(int argc, char **argv)
test_pipelined_insert(conn, numrows);
else if (strcmp(testname, "prepared") == 0)
test_prepared(conn);
+ else if (strcmp(testname, "protocol_version") == 0)
+ test_protocol_version(conn);
else if (strcmp(testname, "simple_pipeline") == 0)
test_simple_pipeline(conn);
else if (strcmp(testname, "singlerow") == 0)