From cf72d69ce77fb1bebf37e67ca8a49ead9d160170 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Sun, 7 Jan 2024 11:24:10 +0100
Subject: [PATCH v6 09/10] Add tests for ParameterSet and
 _pq_.protocol_managed_params

---
 .../modules/libpq_pipeline/libpq_pipeline.c   | 410 ++++++++++++++++++
 .../libpq_pipeline/t/001_libpq_pipeline.pl    |   2 +-
 .../libpq_pipeline/traces/parameter_set.trace | 182 ++++++++
 3 files changed, 593 insertions(+), 1 deletion(-)
 create mode 100644 src/test/modules/libpq_pipeline/traces/parameter_set.trace

diff --git a/src/test/modules/libpq_pipeline/libpq_pipeline.c b/src/test/modules/libpq_pipeline/libpq_pipeline.c
index 71cd04c5f23..6abaab45e93 100644
--- a/src/test/modules/libpq_pipeline/libpq_pipeline.c
+++ b/src/test/modules/libpq_pipeline/libpq_pipeline.c
@@ -331,6 +331,413 @@ test_nosync(PGconn *conn)
 	fprintf(stderr, "ok\n");
 }
 
+static void
+test_parameter_set(PGconn *conn)
+{
+	PGresult   *res = NULL;
+	const char *val;
+
+	res = PQparameterSet(conn, "work_mem", "123MB");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "123MB") != 0)
+		pg_fatal("expected 123MB, got %s", val);
+	PQclear(res);
+
+
+	/* Outside of a pipeline */
+	res = PQexec(conn, "SHOW _pq_.protocol_managed_params");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "") != 0)
+		pg_fatal("expected work_mem, got %s", val);
+
+	if (PQsendParameterSet(conn, "_pq_.protocol_managed_params", "work_mem") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+	PQclear(res);
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	res = PQexec(conn, "SHOW _pq_.protocol_managed_params");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "work_mem") != 0)
+		pg_fatal("expected work_mem, got %s", val);
+
+	if (PQsendParameterSet(conn, "work_mem", "42MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+	PQclear(res);
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "42MB") != 0)
+		pg_fatal("expected 42MB, got %s", val);
+	PQclear(res);
+
+	res = PQexec(conn, "RESET ALL");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW _pq_.protocol_managed_params");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "work_mem") != 0)
+		pg_fatal("expected 42MB, got %s", val);
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "42MB") != 0)
+		pg_fatal("expected 42MB, got %s", val);
+	PQclear(res);
+
+	/* In a pipeline with other queries */
+	if (PQenterPipelineMode(conn) != 1)
+		pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
+	if (PQsendParameterSet(conn, "work_mem", "10MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	PQsendFlushRequest(conn);
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+	PQclear(res);
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	/*
+	 * This is fine, but it opens an implicit transaction which should cause
+	 * the next ParameterSet message to fail
+	 */
+	if (PQsendQueryParams(conn, "SHOW work_mem", 0, NULL, NULL, NULL, NULL, 0) != 1)
+		pg_fatal("failed to send query: %s", PQerrorMessage(conn));
+	PQsendFlushRequest(conn);
+	res = PQgetResult(conn);
+	if (res == NULL)
+		pg_fatal("PQgetResult returned null when there's a pipeline item: %s",
+				 PQerrorMessage(conn));
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("unexpected result code %s from first pipeline item",
+				 PQresStatus(PQresultStatus(res)));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "10MB") != 0)
+		pg_fatal("expected 10MB, got %s", val);
+
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	if (PQsendParameterSet(conn, "work_mem", "12MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	PQsendFlushRequest(conn);
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to set work_mem anymore using ParameterSet in an implicit transaction");
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	if (PQpipelineSync(conn) != 1)
+		pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+		pg_fatal("Unexpected result code %s instead of PGRES_PIPELINE_SYNC, error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	if (PQexitPipelineMode(conn) != 1)
+		pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "10MB") != 0)
+		pg_fatal("expected 10MB, got %s", val);
+	PQclear(res);
+
+	/* In transaction */
+	res = PQexec(conn, "BEGIN");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to begin transaction: %s", PQerrorMessage(conn));
+
+	res = PQparameterSet(conn, "work_mem", "30MB");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to set work_mem anymore using ParameterSet in an explicit transaction");
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("transaction should have been aborted");
+
+	res = PQexec(conn, "ROLLBACK");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	/* it behaves transactionally */
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "10MB") != 0)
+		pg_fatal("expected 10MB, got %s", val);
+	PQclear(res);
+
+	/* In a failed pipeline */
+	if (PQenterPipelineMode(conn) != 1)
+		pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
+	if (PQsendQueryParams(conn, "SELECT 0/0", 0, NULL, NULL, NULL, NULL, 0) != 1)
+		pg_fatal("failed to send query: %s", PQerrorMessage(conn));
+	if (PQsendParameterSet(conn, "work_mem", "12MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	if (PQpipelineSync(conn) != 1)
+		pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("PQexec should have failed with division by zero");
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_PIPELINE_ABORTED)
+		pg_fatal("pipeline was not aborted");
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+		pg_fatal("Unexpected result code %s instead of PGRES_PIPELINE_SYNC, error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQexitPipelineMode(conn) != 1)
+		pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "10MB") != 0)
+		pg_fatal("expected 10MB, got %s", val);
+	PQclear(res);
+
+	res = PQparameterSet(conn, "_pq_.protocol_managed_params", "role,session_authorization");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	res = PQparameterSet(conn, "work_mem", "19MB");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	/*
+	 * Now ParemeterSet for work_mem should be allowed again in transaction
+	 * and it should behave transactionally. First we show an implicit
+	 * transaction that commits.
+	 */
+	if (PQenterPipelineMode(conn) != 1)
+		pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
+	if (PQsendQueryParams(conn, "SHOW work_mem", 0, NULL, NULL, NULL, NULL, 0) != 1)
+		pg_fatal("failed to send query: %s", PQerrorMessage(conn));
+	PQsendFlushRequest(conn);
+	res = PQgetResult(conn);
+	if (res == NULL)
+		pg_fatal("PQgetResult returned null when there's a pipeline item: %s",
+				 PQerrorMessage(conn));
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("unexpected result code %s from first pipeline item",
+				 PQresStatus(PQresultStatus(res)));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "19MB") != 0)
+		pg_fatal("expected 19MB, got %s", val);
+
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	if (PQsendParameterSet(conn, "work_mem", "12MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	PQsendFlushRequest(conn);
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("Unexpected result code %s instead of PGRES_COMMAND_OK error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	if (PQpipelineSync(conn) != 1)
+		pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+		pg_fatal("Unexpected result code %s instead of PGRES_PIPELINE_SYNC, error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	if (PQexitPipelineMode(conn) != 1)
+		pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "12MB") != 0)
+		pg_fatal("expected 12MB, got %s", val);
+	PQclear(res);
+
+	/* Then we show an implicit transaction that rolls back ParameterSet  */
+	if (PQenterPipelineMode(conn) != 1)
+		pg_fatal("failed to enter pipeline mode: %s", PQerrorMessage(conn));
+	if (PQsendParameterSet(conn, "work_mem", "28MB") != 1)
+		pg_fatal("PQsendParameterSet failed: %s", PQerrorMessage(conn));
+	if (PQsendQueryParams(conn, "SELECT 0/0", 0, NULL, NULL, NULL, NULL, 0) != 1)
+		pg_fatal("failed to send query: %s", PQerrorMessage(conn));
+	if (PQpipelineSync(conn) != 1)
+		pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("Unexpected result code %s instead of PGRES_COMMAND_OK error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("PQexec should have failed with division by zero");
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+
+	res = PQgetResult(conn);
+	if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+		pg_fatal("Unexpected result code %s instead of PGRES_PIPELINE_SYNC, error: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	res = PQgetResult(conn);
+	if (res != NULL)
+		pg_fatal("did not receive terminating NULL");
+	if (PQexitPipelineMode(conn) != 1)
+		pg_fatal("exiting pipeline failed: %s", PQerrorMessage(conn));
+
+	/* check that it is rolled back */
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "12MB") != 0)
+		pg_fatal("expected 12MB, got %s", val);
+	PQclear(res);
+
+
+	/* Then we show an explicit transaction that rolls back ParameterSet  */
+	res = PQexec(conn, "BEGIN");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to begin transaction: %s", PQerrorMessage(conn));
+
+	res = PQparameterSet(conn, "work_mem", "30MB");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	if (PQntuples(res) != 1)
+		pg_fatal("expected 1 result, got %d", PQntuples(res));
+
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "30MB") != 0)
+		pg_fatal("expected 30MB, got %s", val);
+	PQclear(res);
+
+	res = PQexec(conn, "ROLLBACK");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+		pg_fatal("failed to set parameter: %s", PQerrorMessage(conn));
+
+	/* it behaves transactionally */
+	res = PQexec(conn, "SHOW work_mem");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+		pg_fatal("Expected tuples, got %s: %s",
+				 PQresStatus(PQresultStatus(res)), PQerrorMessage(conn));
+	val = PQgetvalue(res, 0, 0);
+	if (strcmp(val, "12MB") != 0)
+		pg_fatal("expected 12MB, got %s", val);
+	PQclear(res);
+
+
+	res = PQexec(conn, "SET SESSION AUTHORIZATION 'postgres'");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to set SESSION AUTHORIZATION anymore using ParameterSet");
+
+	res = PQexec(conn, "SET ROLE 'postgres'");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to set ROLE anymore using ParameterSet");
+	res = PQparameterSet(conn, "_pq_.protocol_managed_params", "doesnotexist");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to use an unknown GUC");
+
+	res = PQparameterSet(conn, "_pq_.protocol_managed_params", "work_mem,_pq_.protocol_managed_params");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to use a protocol extension");
+
+	res = PQparameterSet(conn, "_pq_.protocol_managed_params", "\"");
+	if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+		pg_fatal("Should not be allowed to use invalid list syntax");
+}
+
 /*
  * When an operation in a pipeline fails the rest of the pipeline is flushed. We
  * still have to get results for each pipeline item, but the item will just be
@@ -1749,6 +2156,7 @@ print_test_list(void)
 	printf("disallowed_in_pipeline\n");
 	printf("multi_pipelines\n");
 	printf("nosync\n");
+	printf("parameter_set\n");
 	printf("pipeline_abort\n");
 	printf("pipeline_idle\n");
 	printf("pipelined_insert\n");
@@ -1853,6 +2261,8 @@ main(int argc, char **argv)
 		test_multi_pipelines(conn);
 	else if (strcmp(testname, "nosync") == 0)
 		test_nosync(conn);
+	else if (strcmp(testname, "parameter_set") == 0)
+		test_parameter_set(conn);
 	else if (strcmp(testname, "pipeline_abort") == 0)
 		test_pipeline_abort(conn);
 	else if (strcmp(testname, "pipeline_idle") == 0)
diff --git a/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl b/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl
index f9e6d07fc0b..eec5d954bf0 100644
--- a/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl
+++ b/src/test/modules/libpq_pipeline/t/001_libpq_pipeline.pl
@@ -37,7 +37,7 @@ for my $testname (@tests)
 {
 	my @extraargs = ('-r', $numrows);
 	my $cmptrace = grep(/^$testname$/,
-		qw(simple_pipeline nosync multi_pipelines prepared singlerow
+		qw(simple_pipeline nosync multi_pipelines parameter_set prepared singlerow
 		  pipeline_abort pipeline_idle transaction
 		  disallowed_in_pipeline)) > 0;
 
diff --git a/src/test/modules/libpq_pipeline/traces/parameter_set.trace b/src/test/modules/libpq_pipeline/traces/parameter_set.trace
new file mode 100644
index 00000000000..66770252f18
--- /dev/null
+++ b/src/test/modules/libpq_pipeline/traces/parameter_set.trace
@@ -0,0 +1,182 @@
+F	19	ParameterSet	 "work_mem" "123MB"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	15	DataRow	 1 5 '123MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	38	Query	 "SHOW _pq_.protocol_managed_params"
+B	53	RowDescription	 1 "_pq_.protocol_managed_params" NNNN 0 NNNN 65535 -1 0
+B	10	DataRow	 1 0 ''
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	42	ParameterSet	 "_pq_.protocol_managed_params" "work_mem"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 I
+F	38	Query	 "SHOW _pq_.protocol_managed_params"
+B	53	RowDescription	 1 "_pq_.protocol_managed_params" NNNN 0 NNNN 65535 -1 0
+B	18	DataRow	 1 8 'work_mem'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	18	ParameterSet	 "work_mem" "42MB"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '42MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	14	Query	 "RESET ALL"
+B	10	CommandComplete	 "RESET"
+B	5	ReadyForQuery	 I
+F	38	Query	 "SHOW _pq_.protocol_managed_params"
+B	53	RowDescription	 1 "_pq_.protocol_managed_params" NNNN 0 NNNN 65535 -1 0
+B	18	DataRow	 1 8 'work_mem'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '42MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	18	ParameterSet	 "work_mem" "10MB"
+F	4	Flush
+B	4	ParameterSetComplete
+F	21	Parse	 "" "SHOW work_mem" 0
+F	14	Bind	 "" "" 0 0 1 0
+F	6	Describe	 P ""
+F	9	Execute	 "" 0
+F	4	Flush
+B	4	ParseComplete
+B	4	BindComplete
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '10MB'
+B	9	CommandComplete	 "SHOW"
+F	18	ParameterSet	 "work_mem" "12MB"
+F	4	Flush
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "25001" M "parameter "work_mem" cannot be changed within a transaction" F "SSSS" L "SSSS" R "SSSS" \x00
+F	4	Sync
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '10MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	10	Query	 "BEGIN"
+B	10	CommandComplete	 "BEGIN"
+B	5	ReadyForQuery	 T
+F	18	ParameterSet	 "work_mem" "30MB"
+F	4	Sync
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "25001" M "parameter "work_mem" cannot be changed within a transaction" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 E
+F	18	Query	 "SHOW work_mem"
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "25P02" M "current transaction is aborted, commands ignored until end of transaction block" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 E
+F	13	Query	 "ROLLBACK"
+B	13	CommandComplete	 "ROLLBACK"
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '10MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	18	Parse	 "" "SELECT 0/0" 0
+F	14	Bind	 "" "" 0 0 1 0
+F	6	Describe	 P ""
+F	9	Execute	 "" 0
+F	18	ParameterSet	 "work_mem" "12MB"
+F	4	Sync
+B	4	ParseComplete
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "22012" M "division by zero" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '10MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	60	ParameterSet	 "_pq_.protocol_managed_params" "role,session_authorization"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 I
+F	18	ParameterSet	 "work_mem" "19MB"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 I
+F	21	Parse	 "" "SHOW work_mem" 0
+F	14	Bind	 "" "" 0 0 1 0
+F	6	Describe	 P ""
+F	9	Execute	 "" 0
+F	4	Flush
+B	4	ParseComplete
+B	4	BindComplete
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '19MB'
+B	9	CommandComplete	 "SHOW"
+F	18	ParameterSet	 "work_mem" "12MB"
+F	4	Flush
+B	4	ParameterSetComplete
+F	4	Sync
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '12MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	18	ParameterSet	 "work_mem" "28MB"
+F	18	Parse	 "" "SELECT 0/0" 0
+F	14	Bind	 "" "" 0 0 1 0
+F	6	Describe	 P ""
+F	9	Execute	 "" 0
+F	4	Sync
+B	4	ParameterSetComplete
+B	4	ParseComplete
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "22012" M "division by zero" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '12MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	10	Query	 "BEGIN"
+B	10	CommandComplete	 "BEGIN"
+B	5	ReadyForQuery	 T
+F	18	ParameterSet	 "work_mem" "30MB"
+F	4	Sync
+B	4	ParameterSetComplete
+B	5	ReadyForQuery	 T
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '30MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 T
+F	13	Query	 "ROLLBACK"
+B	13	CommandComplete	 "ROLLBACK"
+B	5	ReadyForQuery	 I
+F	18	Query	 "SHOW work_mem"
+B	33	RowDescription	 1 "work_mem" NNNN 0 NNNN 65535 -1 0
+B	14	DataRow	 1 4 '12MB'
+B	9	CommandComplete	 "SHOW"
+B	5	ReadyForQuery	 I
+F	41	Query	 "SET SESSION AUTHORIZATION 'postgres'"
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "55P02" M "parameter "session_authorization" cannot be set using SQL" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	24	Query	 "SET ROLE 'postgres'"
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "55P02" M "parameter "role" cannot be set using SQL" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	46	ParameterSet	 "_pq_.protocol_managed_params" "doesnotexist"
+F	4	Sync
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "42704" M "unrecognized configuration parameter "doesnotexist"" F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	71	ParameterSet	 "_pq_.protocol_managed_params" "work_mem,_pq_.protocol_managed_params"
+F	4	Sync
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "22023" M "invalid value for parameter "_pq_.protocol_managed_params": "work_mem,_pq_.protocol_managed_params"" D "Parameter "_pq_.protocol_managed_params" is a protocol extension." F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	35	ParameterSet	 "_pq_.protocol_managed_params" """
+F	4	Sync
+B	NN	ErrorResponse	 S "ERROR" V "ERROR" C "22023" M "invalid value for parameter "_pq_.protocol_managed_params": """" D "List syntax is invalid." F "SSSS" L "SSSS" R "SSSS" \x00
+B	5	ReadyForQuery	 I
+F	4	Terminate
-- 
2.34.1

