On 2023-May-20, Alexander Lakhin wrote:

> Starting from 038f586d5, the following script:
> echo "
> \startpipeline
> \endpipeline
> " >test.sql
> pgbench -n -M prepared -f test.sql
> 
> leads to the pgbench's segfault:

Hah, yeah, that's because an empty pipeline never calls the code to
allocate the flag array.  Here's the trivial fix.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"El hombre nunca sabe de lo que es capaz hasta que lo intenta" (C. Dickens)
>From 50685847e057eb8e7509293fdd81247eb49854ef Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Mon, 22 May 2023 13:45:47 +0200
Subject: [PATCH] Fix pgbench in prepared mode with empty pipeline

---
 src/bin/pgbench/pgbench.c                    | 44 ++++++++++++--------
 src/bin/pgbench/t/001_pgbench_with_server.pl |  2 +
 2 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 7dbb2ed6a77..6caa84282c7 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -3049,6 +3049,27 @@ chooseScript(TState *thread)
 	return i - 1;
 }
 
+/*
+ * Allocate space for 'prepared' flags of a script: we need one boolean
+ * for each command of each script.
+ */
+static void
+allocatePreparedFlags(CState *st)
+{
+	Assert(st->prepared == NULL);
+
+	st->prepared = pg_malloc(sizeof(bool *) * num_scripts);
+	for (int i = 0; i < num_scripts; i++)
+	{
+		ParsedScript *script = &sql_script[i];
+		int			numcmds;
+
+		for (numcmds = 0; script->commands[numcmds] != NULL; numcmds++)
+			;
+		st->prepared[i] = pg_malloc0(sizeof(bool) * numcmds);
+	}
+}
+
 /*
  * Prepare the SQL command from st->use_file at command_num.
  */
@@ -3061,23 +3082,8 @@ prepareCommand(CState *st, int command_num)
 	if (command->type != SQL_COMMAND)
 		return;
 
-	/*
-	 * If not already done, allocate space for 'prepared' flags: one boolean
-	 * for each command of each script.
-	 */
 	if (!st->prepared)
-	{
-		st->prepared = pg_malloc(sizeof(bool *) * num_scripts);
-		for (int i = 0; i < num_scripts; i++)
-		{
-			ParsedScript *script = &sql_script[i];
-			int			numcmds;
-
-			for (numcmds = 0; script->commands[numcmds] != NULL; numcmds++)
-				;
-			st->prepared[i] = pg_malloc0(sizeof(bool) * numcmds);
-		}
-	}
+		allocatePreparedFlags(st);
 
 	if (!st->prepared[st->use_file][command_num])
 	{
@@ -3109,13 +3115,15 @@ prepareCommandsInPipeline(CState *st)
 	Assert(commands[st->command]->type == META_COMMAND &&
 		   commands[st->command]->meta == META_STARTPIPELINE);
 
+	if (!st->prepared)
+		allocatePreparedFlags(st);
+
 	/*
 	 * We set the 'prepared' flag on the \startpipeline itself to flag that we
 	 * don't need to do this next time without calling prepareCommand(), even
 	 * though we don't actually prepare this command.
 	 */
-	if (st->prepared &&
-		st->prepared[st->use_file][st->command])
+	if (st->prepared[st->use_file][st->command])
 		return;
 
 	for (j = st->command + 1; commands[j] != NULL; j++)
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 363a1ffabd5..f8ca8a922d1 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -790,6 +790,8 @@ $node->pgbench(
 		'001_pgbench_pipeline_prep' => q{
 -- test startpipeline
 \startpipeline
+\endpipeline
+\startpipeline
 } . "select 1;\n" x 10 . q{
 \endpipeline
 }
-- 
2.39.2

Reply via email to