From 8f5e42d3346924b5d6330208d62ec1b19fdbc110 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Thu, 25 May 2023 12:36:18 +0200 Subject: [PATCH] Fix pgbench in prepared mode with an empty pipeline It crashes because it references memory that's not allocated in that particular case. Fix by allocating it. Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/bcf802a6-afc1-95b9-7bf4-c5dd868ec144@gmail.com --- 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 7dbb2ed6a7..1d1670d4c2 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 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++) diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl index 363a1ffabd..f8ca8a922d 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 }