+/* create tables and setup data */
+static void
+init(void)
+{
+ PGconn *con;
+ PGresult *res;
+ /*
+ * Note: TPC-B requires at least 100 bytes per row, and the "filler"
+ * fields in these table declarations were intended to comply with that.
+ * But because they default to NULLs, they don't actually take any
+ * space. We could fix that by giving them non-null default values.
+ * However, that would completely break comparability of pgbench
+ * results with prior versions. Since pgbench has never pretended
+ * to be fully TPC-B compliant anyway, we stick with the historical
+ * behavior.
+ */
+ static char *DDLs[] = {
+ "drop table if exists branches",
+ "create table branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=%d)",
+ "drop table if exists tellers",
+ "create table tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=%d)",
+ "drop table if exists accounts",
+ "create table accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=%d)",
+ "drop table if exists history",
+ "create table history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22))"};
+ static char *DDLAFTERs[] = {
+ "alter table branches add primary key (bid)",
+ "alter table tellers add primary key (tid)",
+ "alter table accounts add primary key (aid)"};
+
+
+ char sql[256];
+
+ int i;
+
+ if ((con = doConnect()) == NULL)
+ exit(1);
+
+ for (i = 0; i < lengthof(DDLs); i++)
+ {
+ /*
+ * set fillfactor for branches, tellers and accounts tables
+ */
+ if ((strstr(DDLs[i], "create table branches") == DDLs[i]) ||
+ (strstr(DDLs[i], "create table tellers") == DDLs[i]) ||
+ (strstr(DDLs[i], "create table accounts") == DDLs[i]))
+ {
+ char ddl_stmt[128];
+
+ snprintf(ddl_stmt, 128, DDLs[i], fillfactor);
+ executeStatement(con, ddl_stmt);
+ continue;
+ }
+ else
+ executeStatement(con, DDLs[i]);
+ }
+
+ executeStatement(con, "begin");
+
+ for (i = 0; i < nbranches * scale; i++)
+ {
+ snprintf(sql, 256, "insert into branches(bid,bbalance) values(%d,0)", i + 1);
+ executeStatement(con, sql);
+ }
+
+ for (i = 0; i < ntellers * scale; i++)
+ {
+ snprintf(sql, 256, "insert into tellers(tid,bid,tbalance) values (%d,%d,0)"
+ ,i + 1, i / ntellers + 1);
+ executeStatement(con, sql);
+ }
+
+ executeStatement(con, "commit");
+
+ /*
+ * fill the accounts table with some data
+ */
+ fprintf(stderr, "creating tables...\n");
+
+ executeStatement(con, "begin");
+ executeStatement(con, "truncate accounts");
+
+ res = PQexec(con, "copy accounts from stdin");
+ if (PQresultStatus(res) != PGRES_COPY_IN)
+ {
+ fprintf(stderr, "%s", PQerrorMessage(con));
+ exit(1);
+ }
+ PQclear(res);
+
+ for (i = 0; i < naccounts * scale; i++)
+ {
+ int j = i + 1;
+
+ snprintf(sql, 256, "%d\t%d\t%d\t\n", j, i / naccounts + 1, 0);
+ if (PQputline(con, sql))
+ {
+ fprintf(stderr, "PQputline failed\n");
+ exit(1);
+ }
+
+ if (j % 10000 == 0)
+ fprintf(stderr, "%d tuples done.\n", j);
+ }
+ if (PQputline(con, "\\.\n"))
+ {
+ fprintf(stderr, "very last PQputline failed\n");
+ exit(1);
+ }
+ if (PQendcopy(con))
+ {
+ fprintf(stderr, "PQendcopy failed\n");
+ exit(1);
+ }
+ executeStatement(con, "commit");
+
+ /*
+ * create indexes
+ */
+ fprintf(stderr, "set primary key...\n");
+ for (i = 0; i < lengthof(DDLAFTERs); i++)
+ executeStatement(con, DDLAFTERs[i]);
+
+ /* vacuum */
+ fprintf(stderr, "vacuum...");
+ executeStatement(con, "vacuum analyze");
+
+ fprintf(stderr, "done.\n");
+ PQfinish(con);
+}
+
+/*
+ * Parse the raw sql and replace :param to $n.
+ */
+static bool
+parseQuery(Command *cmd, const char *raw_sql)
+{
+ char *sql,
+ *p;
+
+ sql = strdup(raw_sql);
+ if (sql == NULL)
+ return false;
+ cmd->argc = 1;
+
+ p = sql;
+ while ((p = strchr(p, ':')) != NULL)
+ {
+ char var[12];
+ char *name;
+ int eaten;
+
+ name = parseVariable(p, &eaten);
+ if (name == NULL)
+ {
+ while (*p == ':') { p++; }
+ continue;
+ }
+
+ if (cmd->argc >= MAX_ARGS)
+ {
+ fprintf(stderr, "statement has too many arguments (maximum is %d): %s\n", MAX_ARGS - 1, raw_sql);
+ return false;
+ }
+
+ sprintf(var, "$%d", cmd->argc);
+ if ((p = replaceVariable(&sql, p, eaten, var)) == NULL)
+ return false;
+
+ cmd->argv[cmd->argc] = name;
+ cmd->argc++;
+ }
+
+ cmd->argv[0] = sql;
+ return true;
+}
+
+static Command *
+process_commands(char *buf)
+{
+ const char delim[] = " \f\n\r\t\v";
+
+ Command *my_commands;
+ int j;
+ char *p,
+ *tok;
+
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+
+ p = buf;
+ while (isspace((unsigned char) *p))
+ p++;
+
+ if (*p == '\0' || strncmp(p, "--", 2) == 0)
+ {
+ return NULL;
+ }
+
+ my_commands = (Command *) malloc(sizeof(Command));
+ if (my_commands == NULL)
+ {
+ return NULL;
+ }
+
+ my_commands->argc = 0;
+
+ if (*p == '\\')
+ {
+ my_commands->type = META_COMMAND;
+
+ j = 0;
+ tok = strtok(++p, delim);
+
+ while (tok != NULL)
+ {
+ if ((my_commands->argv[j] = strdup(tok)) == NULL)
+ return NULL;
+
+ my_commands->argc++;
+
+ j++;
+ tok = strtok(NULL, delim);
+ }
+
+ if (pg_strcasecmp(my_commands->argv[0], "setrandom") == 0)
+ {
+ if (my_commands->argc < 4)
+ {
+ fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ return NULL;
+ }
+
+ for (j = 4; j < my_commands->argc; j++)
+ fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ my_commands->argv[0], my_commands->argv[j]);
+ }
+ else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
+ {
+ if (my_commands->argc < 3)
+ {
+ fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ return NULL;
+ }
+
+ for (j = my_commands->argc < 5 ? 3 : 5; j < my_commands->argc; j++)
+ fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ my_commands->argv[0], my_commands->argv[j]);
+ }
+ else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0)
+ {
+ if (my_commands->argc < 2)
+ {
+ fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ return NULL;
+ }
+
+ if (my_commands->argc >= 3)
+ {
+ if (pg_strcasecmp(my_commands->argv[2], "us") != 0 &&
+ pg_strcasecmp(my_commands->argv[2], "ms") != 0 &&
+ pg_strcasecmp(my_commands->argv[2], "s"))
+ {
+ fprintf(stderr, "%s: unknown time unit '%s' - must be us, ms or s\n",
+ my_commands->argv[0], my_commands->argv[2]);
+ return NULL;
+ }
+ }
+
+ for (j = 3; j < my_commands->argc; j++)
+ fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ my_commands->argv[0], my_commands->argv[j]);
+ }
+ else
+ {
+ fprintf(stderr, "Invalid command %s\n", my_commands->argv[0]);
+ return NULL;
+ }
+ }
+ else
+ {
+ my_commands->type = SQL_COMMAND;
+
+ switch (querymode)
+ {
+ case QUERY_SIMPLE:
+ if ((my_commands->argv[0] = strdup(p)) == NULL)
+ return NULL;
+ my_commands->argc++;
+ break;
+ case QUERY_EXTENDED:
+ case QUERY_PREPARED:
+ if (!parseQuery(my_commands, p))
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ return my_commands;
+}
+
+static int
+process_file(char *filename)