Vaishnavi Prabakaran wrote: > Yes, I have created a new patch entry into the commitfest 2017-03 and > attached the latest patch with this e-mail.
Please find attached a companion patch implementing the batch API in pgbench, exposed as \beginbatch and \endbatch meta-commands (without documentation). The idea for now is to make it easier to exercise the API and test how batching performs. I guess I'll submit the patch separately in a future CF, depending on when/if the libpq patch goes in. While developing this, I noted a few things with 0001-v4: 1. lack of initialization for count in PQbatchQueueCount. Trivial fix: --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -1874,7 +1874,7 @@ PQisBusy(PGconn *conn) int PQbatchQueueCount(PGconn *conn) { - int count; + int count = 0; PGcommandQueueEntry *entry; 2. misleading error message in PQexecStart. It gets called by a few other functions than PQexec, such as PQprepare. As I understand it, the intent here is to forbid the synchronous functions in batch mode, so this error message should not single out PQexec. @@ -1932,6 +2425,13 @@ PQexecStart(PGconn *conn) if (!conn) return false; + if (conn->asyncStatus == PGASYNC_QUEUED || conn->batch_status != PQBATCH_MODE_OFF) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot PQexec in batch mode\n")); + return false; + } + 3. In relation to #2, PQsendQuery() is not forbidden in batch mode although I don't think it can work with it, as it's based on the old protocol. Best regards, -- Daniel Vérité PostgreSQL-powered mailer: http://www.manitou-mail.org Twitter: @DanielVerite
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index f6cb5d4..9b2fce8 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -269,7 +269,8 @@ typedef enum * * CSTATE_START_COMMAND starts the execution of a command. On a SQL * command, the command is sent to the server, and we move to - * CSTATE_WAIT_RESULT state. On a \sleep meta-command, the timer is set, + * CSTATE_WAIT_RESULT state unless in batch mode. + * On a \sleep meta-command, the timer is set, * and we enter the CSTATE_SLEEP state to wait for it to expire. Other * meta-commands are executed immediately. * @@ -1882,11 +1883,24 @@ sendCommand(CState *st, Command *command) if (commands[j]->type != SQL_COMMAND) continue; preparedStatementName(name, st->use_file, j); - res = PQprepare(st->con, name, - commands[j]->argv[0], commands[j]->argc - 1, NULL); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - fprintf(stderr, "%s", PQerrorMessage(st->con)); - PQclear(res); + if (PQbatchStatus(st->con) != PQBATCH_MODE_ON) + { + res = PQprepare(st->con, name, + commands[j]->argv[0], commands[j]->argc - 1, NULL); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + fprintf(stderr, "%s", PQerrorMessage(st->con)); + PQclear(res); + } + else + { + /* + * In batch mode, we use asynchronous functions. If a server-side + * error occurs, it will be processed later among the other results. + */ + if (!PQsendPrepare(st->con, name, + commands[j]->argv[0], commands[j]->argc - 1, NULL)) + fprintf(stderr, "%s", PQerrorMessage(st->con)); + } } st->prepared[st->use_file] = true; } @@ -2165,7 +2179,13 @@ doCustom(TState *thread, CState *st, StatsData *agg) return; } else - st->state = CSTATE_WAIT_RESULT; + { + /* Wait for results, unless in batch mode */ + if (PQbatchStatus(st->con) != PQBATCH_MODE_ON) + st->state = CSTATE_WAIT_RESULT; + else + st->state = CSTATE_END_COMMAND; + } } else if (command->type == META_COMMAND) { @@ -2207,7 +2227,47 @@ doCustom(TState *thread, CState *st, StatsData *agg) } else { - if (pg_strcasecmp(argv[0], "set") == 0) + if (pg_strcasecmp(argv[0], "beginbatch") == 0) + { + /* + * In batch mode, we use a workflow based on libpq batch + * functions. + */ + if (querymode == QUERY_SIMPLE) + { + commandFailed(st, "cannot use batch mode with the simple query protocol"); + st->state = CSTATE_ABORTED; + break; + } + + if (PQbatchStatus(st->con) != PQBATCH_MODE_OFF) + { + commandFailed(st, "already in batch mode"); + break; + } + PQbatchBegin(st->con); + } + else if (pg_strcasecmp(argv[0], "endbatch") == 0) + { + if (PQbatchStatus(st->con) != PQBATCH_MODE_ON) + { + commandFailed(st, "not in batch mode"); + break; + } + if (!PQbatchQueueSync(st->con)) + { + commandFailed(st, "failed to end the batch"); + st->state = CSTATE_ABORTED; + break; + } + if (PQbatchEnd(st->con) == 0) + { + /* collect pending results before getting out of batch mode */ + st->state = CSTATE_WAIT_RESULT; + break; + } + } + else if (pg_strcasecmp(argv[0], "set") == 0) { PgBenchExpr *expr = command->expr; PgBenchValue result; @@ -2279,6 +2339,7 @@ doCustom(TState *thread, CState *st, StatsData *agg) } break; + /* * Wait for the current SQL command to complete */ @@ -2295,6 +2356,13 @@ doCustom(TState *thread, CState *st, StatsData *agg) if (PQisBusy(st->con)) return; /* don't have the whole result yet */ + if (PQbatchStatus(st->con) == PQBATCH_MODE_ON && + !PQbatchQueueProcess(st->con)) + { + /* no complete result yet in batch mode*/ + return; + } + /* * Read and discard the query result; */ @@ -2307,7 +2375,22 @@ doCustom(TState *thread, CState *st, StatsData *agg) /* OK */ PQclear(res); discard_response(st); - st->state = CSTATE_END_COMMAND; + /* + * In non-batch mode, only one result per command is expected. + * In batch mode, keep waiting for results until getting + * PGRES_BATCH_END. + */ + if (PQbatchStatus(st->con) != PQBATCH_MODE_ON) + st->state = CSTATE_END_COMMAND; + break; + case PGRES_BATCH_END: + if (PQbatchEnd(st->con) == 1) + { + /* all results collected, exit out of command and batch mode */ + st->state = CSTATE_END_COMMAND; + } + else + fprintf(stderr, "client %d to exit batch mode", st->id); break; default: commandFailed(st, PQerrorMessage(st->con)); @@ -3173,6 +3256,13 @@ process_backslash_command(PsqlScanState sstate, const char *source) syntax_error(source, lineno, my_command->line, my_command->argv[0], "missing command", NULL, -1); } + else if (pg_strcasecmp(my_command->argv[0], "beginbatch") == 0 || + pg_strcasecmp(my_command->argv[0], "endbatch") == 0 ) + { + if (my_command->argc > 1) + syntax_error(source, lineno, my_command->line, my_command->argv[0], + "unexpected argument", NULL, -1); + } else { syntax_error(source, lineno, my_command->line, my_command->argv[0],
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers