diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 03e1212..8e3d090 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -25,6 +25,12 @@
pgbench
+ --custom-initailize={custom_init_command [, ...]}
+ option
+ dbname
+
+
+ pgbench
option
dbname
@@ -72,10 +78,10 @@ tps = 85.296346 (excluding connections establishing)
The default TPC-B-like transaction test requires specific tables to be
set up beforehand. pgbench> should be invoked with
- the -i> (initialize) option to create and populate these
- tables. (When you are testing a custom script, you don't need this
- step, but will instead need to do whatever setup your test needs.)
- Initialization looks like:
+ the -i> (initialize) or --custom-initialize> option
+ to create and populate these tables. (When you are testing a custom
+ script, you don't need this step, but will instead need to do whatever
+ setup your test needs.) Initialization looks like:
pgbench -i other-options> dbname>
@@ -211,6 +217,64 @@ pgbench options> dbname>
+ --custom-initialize={custom_init_command [, ...]}
+
+
+ Required to invoke custom initialization mode.
+ custom_init_command specified custom
+ initialization commands for the initialization. Each command is invoked
+ in the specified order. The supported commands are:
+
+
+
+ create_table
+
+
+ Create four tables pgbench_accounts>,
+ pgbench_branches>, pgbench_history>,
+ and pgbench_tellers>, destroying any existing
+ tables of these names.
+
+
+
+
+ load_data
+
+
+ Load data to standard tables.
+
+
+
+
+ vacuum
+
+
+ Invoke vacuum on standard tables.
+
+
+
+
+ create_pkey
+
+
+ Create primary keys on standard tables.
+
+
+
+
+ create_fkey
+
+
+ Create foreign keys constraints between the standard tables.
+
+
+
+
+
+
+
+
+
--foreign-keys
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4d364a1..8cb9d3a 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -36,6 +36,7 @@
#include "getopt_long.h"
#include "libpq-fe.h"
#include "portability/instr_time.h"
+#include "fe_utils/simple_list.h"
#include
#include
@@ -445,7 +446,6 @@ static const BuiltinScript builtin_script[] =
}
};
-
/* Function prototypes */
static void setIntValue(PgBenchValue *pv, int64 ival);
static void setDoubleValue(PgBenchValue *pv, double dval);
@@ -458,7 +458,12 @@ static void pgbench_error(const char *fmt,...) pg_attribute_printf(1, 2);
static void addScript(ParsedScript script);
static void *threadRun(void *arg);
static void setalarm(int seconds);
-
+static bool parseCustomInitialize(char *rawstring, SimpleStringList *list);
+static void initCreateTables(PGconn *con);
+static void initLoadData(PGconn *con);
+static void initVacuum(PGconn *con);
+static void initCreatePKeys(PGconn *con);
+static void initCreateFKeys(PGconn *con);
/* callback functions for our flex lexer */
static const PsqlScanCallbacks pgbench_callbacks = {
@@ -484,6 +489,8 @@ usage(void)
" create indexes in the specified tablespace\n"
" --tablespace=TABLESPACE create tables in the specified tablespace\n"
" --unlogged-tables create tables as unlogged tables\n"
+ " --custom-initilize={[create_table|load_data|vacuum|create_pkey|create_fkey] [, ...]}\n"
+ " invokes custom initialization\n"
"\nOptions to select what to run:\n"
" -b, --builtin=NAME[@W] add builtin script NAME weighted at W (default: 1)\n"
" (use \"-b list\" to list available scripts)\n"
@@ -2566,9 +2573,78 @@ disconnect_all(CState *state, int length)
}
}
-/* create tables and setup data */
+
+/*
+ * Split rawstring by ',' and parse the custom initialization
+ * command; return a list of custom initialization command.
+ */
+static bool
+parseCustomInitialize(char *rawstring, SimpleStringList *list)
+{
+ char *nextp = rawstring;
+ bool done = false;
+
+ while (isspace(*nextp))
+ nextp++; /* skip leading whitespace */
+
+ do
+ {
+ char *curname;
+ char *parsedname;
+ char *endp;
+ int len;
+
+ curname = nextp;
+ while (*nextp && *nextp != ',' && !isspace(*nextp))
+ nextp++;
+ endp = nextp;
+ if (curname == nextp)
+ return false;
+
+ len = endp - curname;
+ parsedname = pg_malloc(sizeof(char) * len + 1);
+ strncpy(parsedname, curname, len);
+
+ while (isspace(*nextp))
+ nextp++; /* skip trailing whitespace */
+ if (*nextp == ',')
+ {
+ nextp++;
+ while (isspace(*nextp))
+ nextp++; /* skip leading whitespace for next */
+ /* we expect another name, so done remains false */
+ }
+ else if (*nextp == '\0')
+ done = true;
+ else
+ return false; /* invalid syntax */
+
+ *endp = '\0';
+
+ if (!(pg_strncasecmp(parsedname, "create_table", 12) == 0 ||
+ pg_strncasecmp(parsedname, "load_data", 9) == 0 ||
+ pg_strncasecmp(parsedname, "vacuum", 6) == 0 ||
+ pg_strncasecmp(parsedname, "create_pkey", 12) == 0 ||
+ pg_strncasecmp(parsedname, "create_fkey", 12) == 0))
+ {
+ fprintf(stderr, "invalid custom initialization script command \"%s\"\n", parsedname);
+ return false;
+ }
+
+ /* FInished current name, add it to list */
+ simple_string_list_append(list, parsedname);
+
+ /* Loop back if we didn't reach end of string */
+ } while(!done);
+
+ return true;
+}
+
+/*
+ * Create tables, remove old tables if exist.
+ */
static void
-init(bool is_no_vacuum)
+initCreateTables(PGconn *con)
{
/*
* The scale factor at/beyond which 32-bit integers are insufficient for
@@ -2623,34 +2699,8 @@ init(bool is_no_vacuum)
1
}
};
- static const char *const DDLINDEXes[] = {
- "alter table pgbench_branches add primary key (bid)",
- "alter table pgbench_tellers add primary key (tid)",
- "alter table pgbench_accounts add primary key (aid)"
- };
- static const char *const DDLKEYs[] = {
- "alter table pgbench_tellers add foreign key (bid) references pgbench_branches",
- "alter table pgbench_accounts add foreign key (bid) references pgbench_branches",
- "alter table pgbench_history add foreign key (bid) references pgbench_branches",
- "alter table pgbench_history add foreign key (tid) references pgbench_tellers",
- "alter table pgbench_history add foreign key (aid) references pgbench_accounts"
- };
- PGconn *con;
- PGresult *res;
- char sql[256];
- int i;
- int64 k;
-
- /* used to track elapsed time and estimate of the remaining time */
- instr_time start,
- diff;
- double elapsed_sec,
- remaining_sec;
- int log_interval = 1;
-
- if ((con = doConnect()) == NULL)
- exit(1);
+ int i;
for (i = 0; i < lengthof(DDLs); i++)
{
@@ -2687,6 +2737,95 @@ init(bool is_no_vacuum)
executeStatement(con, buffer);
}
+}
+
+/* Invoke vacuum on all tables */
+static void
+initVacuum(PGconn *con)
+{
+ fprintf(stderr, "vacuum...\n");
+ executeStatement(con, "vacuum analyze pgbench_branches");
+ executeStatement(con, "vacuum analyze pgbench_tellers");
+ executeStatement(con, "vacuum analyze pgbench_accounts");
+ executeStatement(con, "vacuum analyze pgbench_history");
+}
+
+/*
+ * Create foreign key constraints between the standard tables
+ */
+static void
+initCreateFKeys(PGconn *con)
+{
+ static const char *const DDLKEYs[] = {
+ "alter table pgbench_tellers add foreign key (bid) references pgbench_branches",
+ "alter table pgbench_accounts add foreign key (bid) references pgbench_branches",
+ "alter table pgbench_history add foreign key (bid) references pgbench_branches",
+ "alter table pgbench_history add foreign key (tid) references pgbench_tellers",
+ "alter table pgbench_history add foreign key (aid) references pgbench_accounts"
+ };
+
+ int i;
+
+ fprintf(stderr, "set foreign keys...\n");
+ for (i = 0; i < lengthof(DDLKEYs); i++)
+ {
+ executeStatement(con, DDLKEYs[i]);
+ }
+}
+
+/*
+ * Create primary keys on three tables; pgbench_accounts,
+ * pgbench_branches and pgbench_tellers.
+ */
+static void
+initCreatePKeys(PGconn *con)
+{
+ static const char *const DDLINDEXes[] = {
+ "alter table pgbench_branches add primary key (bid)",
+ "alter table pgbench_tellers add primary key (tid)",
+ "alter table pgbench_accounts add primary key (aid)"
+ };
+ int i;
+
+ fprintf(stderr, "set primary keys...\n");
+ for (i = 0; i < lengthof(DDLINDEXes); i++)
+ {
+ char buffer[256];
+
+ strlcpy(buffer, DDLINDEXes[i], sizeof(buffer));
+
+ if (index_tablespace != NULL)
+ {
+ char *escape_tablespace;
+
+ escape_tablespace = PQescapeIdentifier(con, index_tablespace,
+ strlen(index_tablespace));
+ snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
+ " using index tablespace %s", escape_tablespace);
+ PQfreemem(escape_tablespace);
+ }
+
+ executeStatement(con, buffer);
+ }
+}
+
+/*
+ * Fill the standard table with some data.
+ */
+static void
+initLoadData(PGconn *con)
+{
+ char sql[256];
+ PGresult *res;
+ int i;
+ int64 k;
+
+ /* used to track elapsed time and estimate of the remaining time */
+ instr_time start,
+ diff;
+ double elapsed_sec,
+ remaining_sec;
+ int log_interval = 1;
executeStatement(con, "begin");
@@ -2792,52 +2931,68 @@ init(bool is_no_vacuum)
exit(1);
}
executeStatement(con, "commit");
+}
- /* vacuum */
- if (!is_no_vacuum)
+/* invoke each custom initialization commands */
+static void
+custom_init(SimpleStringList custom_initialize_list)
+{
+ SimpleStringListCell *cell;
+ PGconn *con;
+
+ if ((con = doConnect()) == NULL)
+ exit(1);
+
+ for (cell = custom_initialize_list.head; cell; cell = cell->next)
{
- fprintf(stderr, "vacuum...\n");
- executeStatement(con, "vacuum analyze pgbench_branches");
- executeStatement(con, "vacuum analyze pgbench_tellers");
- executeStatement(con, "vacuum analyze pgbench_accounts");
- executeStatement(con, "vacuum analyze pgbench_history");
+ if (pg_strncasecmp(cell->val, "create_table", 12) == 0)
+ initCreateTables(con);
+ else if (pg_strncasecmp(cell->val, "load_data", 9) == 0)
+ initLoadData(con);
+ else if (pg_strncasecmp(cell->val, "vacuum", 6) == 0)
+ initVacuum(con);
+ else if (pg_strncasecmp(cell->val, "create_pkey", 11) == 0)
+ initCreatePKeys(con);
+ else if (pg_strncasecmp(cell->val, "create_fkey", 11) == 0)
+ initCreateFKeys(con);
+ else
+ {
+ fprintf(stderr, "invalid custom initialization script command \"%s\"\n", cell->val);
+ fprintf(stderr, "possible commands are: \"create_table\", \"load_data\", \"vacuum\", \"create_pkeys\", \"create_fkyes\"\n");
+ PQfinish(con);
+ exit(1);
+ }
}
- /*
- * create indexes
- */
- fprintf(stderr, "set primary keys...\n");
- for (i = 0; i < lengthof(DDLINDEXes); i++)
- {
- char buffer[256];
+ fprintf(stderr, "done.\n");
+ PQfinish(con);
+}
- strlcpy(buffer, DDLINDEXes[i], sizeof(buffer));
+/* create tables and setup data */
+static void
+init(bool is_no_vacuum)
+{
+ PGconn *con;
- if (index_tablespace != NULL)
- {
- char *escape_tablespace;
+ if ((con = doConnect()) == NULL)
+ exit(1);
- escape_tablespace = PQescapeIdentifier(con, index_tablespace,
- strlen(index_tablespace));
- snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
- " using index tablespace %s", escape_tablespace);
- PQfreemem(escape_tablespace);
- }
+ /* create tables */
+ initCreateTables(con);
- executeStatement(con, buffer);
- }
+ /* load data */
+ initLoadData(con);
- /*
- * create foreign keys
- */
+ /* vacuum */
+ if (!is_no_vacuum)
+ initVacuum(con);
+
+ /* create indexes */
+ initCreatePKeys(con);
+
+ /* create foreign keys */
if (foreign_keys)
- {
- fprintf(stderr, "set foreign keys...\n");
- for (i = 0; i < lengthof(DDLKEYs); i++)
- {
- executeStatement(con, DDLKEYs[i]);
- }
- }
+ initCreateFKeys(con);
fprintf(stderr, "done.\n");
PQfinish(con);
@@ -3623,6 +3778,7 @@ main(int argc, char **argv)
{"log", no_argument, NULL, 'l'},
{"latency-limit", required_argument, NULL, 'L'},
{"no-vacuum", no_argument, NULL, 'n'},
+ {"no-primary-keys", no_argument, NULL, 'I'},
{"port", required_argument, NULL, 'p'},
{"progress", required_argument, NULL, 'P'},
{"protocol", required_argument, NULL, 'M'},
@@ -3645,12 +3801,16 @@ main(int argc, char **argv)
{"aggregate-interval", required_argument, NULL, 5},
{"progress-timestamp", no_argument, NULL, 6},
{"log-prefix", required_argument, NULL, 7},
+ {"custom-initialize", required_argument, NULL, 8},
{NULL, 0, NULL, 0}
};
int c;
int is_init_mode = 0; /* initialize mode? */
+ int is_initialize_suite = 0;
+ int is_custom_initialize = 0;
int is_no_vacuum = 0; /* no vacuum at all before testing? */
+ SimpleStringList custom_initialize_list = {NULL, NULL};
int do_vacuum_accounts = 0; /* do vacuum accounts before testing? */
int optindex;
bool scale_given = false;
@@ -3719,6 +3879,7 @@ main(int argc, char **argv)
{
case 'i':
is_init_mode++;
+ is_initialize_suite++;
break;
case 'h':
pghost = pg_strdup(optarg);
@@ -3991,6 +4152,20 @@ main(int argc, char **argv)
benchmarking_option_set = true;
logfile_prefix = pg_strdup(optarg);
break;
+ case 8:
+ {
+ char *rawstring = pg_strdup(optarg);
+
+ if (!parseCustomInitialize(rawstring, &custom_initialize_list))
+ {
+ fprintf(stderr, "invalid syntax of custom initialization \"%s\"\n",
+ optarg);
+ exit(1);
+ }
+ is_init_mode++;
+ is_custom_initialize++;
+ }
+ break;
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
@@ -4052,7 +4227,16 @@ main(int argc, char **argv)
exit(1);
}
- init(is_no_vacuum);
+ if (is_initialize_suite && is_custom_initialize)
+ {
+ fprintf(stderr, "cannot initialize specific custom initialize mode and initialize mode at the same time\n");
+ exit(1);
+ }
+
+ if (is_custom_initialize)
+ custom_init(custom_initialize_list);
+ else
+ init(is_no_vacuum);
exit(0);
}
else