Fix pgbench in prepared mode with an empty pipeline
authorAlvaro Herrera <[email protected]>
Thu, 25 May 2023 10:36:18 +0000 (12:36 +0200)
committerAlvaro Herrera <[email protected]>
Thu, 25 May 2023 10:36:18 +0000 (12:36 +0200)
It crashes because it references memory that's not allocated in that
particular case.  Fix by allocating it.

Reported-by: Alexander Lakhin <[email protected]>
Discussion: https://postgr.es/m/bcf802a6-afc1-95b9-7bf4-c5dd868ec144@gmail.com

src/bin/pgbench/pgbench.c
src/bin/pgbench/t/001_pgbench_with_server.pl

index 7dbb2ed6a779d7cc3c17b8c0e169df274546b0ac..1d1670d4c2b570420011479c260b34ddfd0c19ff 100644 (file)
@@ -3049,6 +3049,27 @@ chooseScript(TState *thread)
        return i - 1;
 }
 
+/*
+ * Allocate space for CState->prepared: we need one boolean for each command
+ * of each script.
+ */
+static void
+allocCStatePrepared(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);
-               }
-       }
+               allocCStatePrepared(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)
+               allocCStatePrepared(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++)
index 363a1ffabd597ad8512af1f3f029cd7b094ae7c0..f8ca8a922d172da6b4e37b48b82bc05bb3dd9db3 100644 (file)
@@ -790,6 +790,8 @@ $node->pgbench(
                '001_pgbench_pipeline_prep' => q{
 -- test startpipeline
 \startpipeline
+\endpipeline
+\startpipeline
 } . "select 1;\n" x 10 . q{
 \endpipeline
 }