diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml index e3a0abb4c7..c5e4d17fd5 100644 --- a/doc/src/sgml/ref/pgbench.sgml +++ b/doc/src/sgml/ref/pgbench.sgml @@ -167,7 +167,7 @@ pgbench options d init_steps specifies the initialization steps to be performed, using one character per step. Each step is invoked in the specified order. - The default is dtgvp. + The default is dt(g)vp. The available steps are: @@ -193,12 +193,40 @@ pgbench options d - g (Generate data) + ( (begin transaction) + + + Emit a BEGIN. + + + Beware that some steps may not work when called within an explicit transaction. + + + Beware that using ( on the command line requires some protection or escaping. + + + + + g or G (Generate data, client or server side) Generate data and load it into the standard tables, replacing any data already present. + + When data is generated server side, there is no progress output. + + + + + ) (commit transaction) + + + Emit a COMMIT. + + + Beware that using ) on the command line requires some protection or escaping. + diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index e72ad0036e..597562248a 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -130,7 +130,13 @@ static int pthread_join(pthread_t th, void **thread_return); /******************************************************************** * some configurable parameters */ -#define DEFAULT_INIT_STEPS "dtgvp" /* default -I setting */ +/* + * we do all data generation in one transaction to enable the backend's + * data-loading optimizations + */ +#define DEFAULT_INIT_STEPS "dt(g)vp" /* default -I setting */ + +#define ALL_INIT_STEPS "dtgGvpf()" /* all possible steps */ #define LOG_STEP_SECONDS 5 /* seconds between log messages */ #define DEFAULT_NXACTS 10 /* default nxacts */ @@ -626,7 +632,7 @@ usage(void) " %s [OPTION]... [DBNAME]\n" "\nInitialization options:\n" " -i, --initialize invokes initialization mode\n" - " -I, --init-steps=[dtgvpf]+ (default \"dtgvp\")\n" + " -I, --init-steps=[" ALL_INIT_STEPS "]+ (default \"" DEFAULT_INIT_STEPS "\")\n" " run selected initialization steps\n" " -F, --fillfactor=NUM set fill factor\n" " -n, --no-vacuum do not run VACUUM during initialization\n" @@ -3802,10 +3808,23 @@ append_fillfactor(char *opts, int len) } /* - * Fill the standard tables with some data + * truncate away any old data, in one command in case there are foreign keys */ static void -initGenerateData(PGconn *con) +initTruncateTables(PGconn *con) +{ + executeStatement(con, "truncate table " + "pgbench_accounts, " + "pgbench_branches, " + "pgbench_history, " + "pgbench_tellers"); +} + +/* + * Fill the standard tables with some data, from the client side + */ +static void +initGenerateDataClientSide(PGconn *con) { char sql[256]; PGresult *res; @@ -3819,23 +3838,9 @@ initGenerateData(PGconn *con) remaining_sec; int log_interval = 1; - fprintf(stderr, "generating data...\n"); + fprintf(stderr, "generating data client side...\n"); - /* - * we do all of this in one transaction to enable the backend's - * data-loading optimizations - */ - executeStatement(con, "begin"); - - /* - * truncate away any old data, in one command in case there are foreign - * keys - */ - executeStatement(con, "truncate table " - "pgbench_accounts, " - "pgbench_branches, " - "pgbench_history, " - "pgbench_tellers"); + initTruncateTables(con); /* * fill branches, tellers, accounts in that order in case foreign keys @@ -3935,8 +3940,41 @@ initGenerateData(PGconn *con) fprintf(stderr, "PQendcopy failed\n"); exit(1); } +} - executeStatement(con, "commit"); +/* + * Fill the standard tables with some data, from the server side + * + * As already the case with the client-side filling, the filler + * column defaults to NULL in pgbench_branches and pgbench_tellers, + * and is a blank-padded string in pgbench_accounts. + */ +static void +initGenerateDataServerSide(PGconn *con) +{ + char sql[256]; + + fprintf(stderr, "generating data server side...\n"); + + initTruncateTables(con); + + snprintf(sql, sizeof(sql), + "insert into pgbench_branches(bid,bbalance) " + "select bid, 0 " + "from generate_series(1, %d) as bid", scale); + executeStatement(con, sql); + + snprintf(sql, sizeof(sql), + "insert into pgbench_tellers(tid,bid,tbalance) " + "select tid, (tid - 1) / %d + 1, 0 " + "from generate_series(1, %d) as tid", ntellers, scale * ntellers); + executeStatement(con, sql); + + snprintf(sql, sizeof(sql), + "insert into pgbench_accounts(aid,bid,abalance,filler) " + "select aid, (aid - 1) / %d + 1, 0, '' " + "from generate_series(1, %d) as aid", naccounts, scale * naccounts); + executeStatement(con, sql); } /* @@ -4020,6 +4058,7 @@ static void checkInitSteps(const char *initialize_steps) { const char *step; + int begins = 0; if (initialize_steps[0] == '\0') { @@ -4029,13 +4068,40 @@ checkInitSteps(const char *initialize_steps) for (step = initialize_steps; *step != '\0'; step++) { - if (strchr("dtgvpf ", *step) == NULL) + if (strchr(ALL_INIT_STEPS " ", *step) == NULL) { - fprintf(stderr, "unrecognized initialization step \"%c\"\n", + fprintf(stderr, + "unrecognized initialization step \"%c\"\n" + "Allowed step characters are: \"" ALL_INIT_STEPS "\".\n", *step); - fprintf(stderr, "allowed steps are: \"d\", \"t\", \"g\", \"v\", \"p\", \"f\"\n"); exit(1); } + + /* count BEGIN/COMMIT for matching */ + if (*step == '(') + begins++; + else if (*step == ')') + begins--; + + if (begins < 0) + { + fprintf(stderr, "COMMIT \")\" at char %ld of \"%s\" does not have a matching BEGIN \"(\"\n", + step - initialize_steps, initialize_steps); + exit(1); + } + else if (begins >= 2) + { + fprintf(stderr, "BEGIN \"(\" within a BEGIN at char %ld of \"%s\", nested transactions are not supported\n", + step - initialize_steps, initialize_steps); + exit(1); + } + } + + if (begins > 0) + { + fprintf(stderr, "a BEGIN \"(\" in \"%s\" does not have a matching COMMIT \")\"\n", + initialize_steps); + exit(1); } } @@ -4073,9 +4139,20 @@ runInitSteps(const char *initialize_steps) op = "create tables"; initCreateTables(con); break; + case '(': + executeStatement(con, "begin"); + break; case 'g': - op = "generate"; - initGenerateData(con); + op = "client generate"; + initGenerateDataClientSide(con); + break; + case 'G': + op = "server generate"; + initGenerateDataServerSide(con); + break; + case ')': + op = "commit"; + executeStatement(con, "commit"); break; case 'v': op = "vacuum"; diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl index c441626d7c..ddfdc79efa 100644 --- a/src/bin/pgbench/t/001_pgbench_with_server.pl +++ b/src/bin/pgbench/t/001_pgbench_with_server.pl @@ -113,7 +113,7 @@ pgbench( # Again, with all possible options pgbench( - '--initialize --init-steps=dtpvg --scale=1 --unlogged-tables --fillfactor=98 --foreign-keys --quiet --tablespace=regress_pgbench_tap_1_ts --index-tablespace=regress_pgbench_tap_1_ts --partitions=2 --partition-method=hash', + '--initialize --init-steps=dtpv(g) --scale=1 --unlogged-tables --fillfactor=98 --foreign-keys --quiet --tablespace=regress_pgbench_tap_1_ts --index-tablespace=regress_pgbench_tap_1_ts --partitions=2 --partition-method=hash', 0, [qr{^$}i], [ @@ -130,7 +130,7 @@ pgbench( # Test interaction of --init-steps with legacy step-selection options pgbench( - '--initialize --init-steps=dtpvgvv --no-vacuum --foreign-keys --unlogged-tables --partitions=3', + '--initialize --init-steps=dtpv(G)vv --no-vacuum --foreign-keys --unlogged-tables --partitions=3', 0, [qr{^$}], [ @@ -138,7 +138,7 @@ pgbench( qr{creating tables}, qr{creating 3 partitions}, qr{creating primary keys}, - qr{.* of .* tuples \(.*\) done}, + qr{generating data server side}, qr{creating foreign keys}, qr{(?!vacuuming)}, # no vacuum qr{done in \d+\.\d\d s } diff --git a/src/bin/pgbench/t/002_pgbench_no_server.pl b/src/bin/pgbench/t/002_pgbench_no_server.pl index 1e9542af3f..39badeb3aa 100644 --- a/src/bin/pgbench/t/002_pgbench_no_server.pl +++ b/src/bin/pgbench/t/002_pgbench_no_server.pl @@ -147,7 +147,22 @@ my @options = ( [ 'invalid init step', '-i -I dta', - [ qr{unrecognized initialization step}, qr{allowed steps are} ] + [ qr{unrecognized initialization step}, qr{Allowed step characters are} ] + ], + [ + 'invalid init step begin/begin', + '-i -I ((', + [ qr{nested transactions are not supported} ] + ], + [ + 'invalid init step end', + '-i -I )', + [ qr{does not have a matching BEGIN} ] + ], + [ + 'invalid init step begin', + '-i -I (dt', + [ qr{does not have a matching COMMIT} ] ], [ 'bad random seed',