@@ -537,6 +537,11 @@ typedef enum
537537 * CSTATE_WAIT_RESULT waits until we get a result set back from the server
538538 * for the current command.
539539 *
540+ * CSTATE_WAIT_PREPARE_RESULT waits for the result of an async PQsendPrepare
541+ * issued from CSTATE_START_COMMAND in QUERY_PREPARED mode. Once the prepare
542+ * succeeds, the prepared flag is set and we loop back to CSTATE_START_COMMAND
543+ * to send the actual query. On failure we go to CSTATE_ERROR.
544+ *
540545 * CSTATE_SLEEP waits until the end of \sleep.
541546 *
542547 * CSTATE_END_COMMAND records the end-of-command timestamp, increments the
@@ -549,6 +554,7 @@ typedef enum
549554 */
550555 CSTATE_START_COMMAND ,
551556 CSTATE_WAIT_RESULT ,
557+ CSTATE_WAIT_PREPARE_RESULT ,
552558 CSTATE_SLEEP ,
553559 CSTATE_END_COMMAND ,
554560 CSTATE_SKIP_COMMAND ,
@@ -3108,6 +3114,30 @@ prepareCommand(CState *st, int command_num)
31083114 }
31093115}
31103116
3117+ /*
3118+ * Asynchronously start preparing the SQL command from st->use_file at
3119+ * command_num. Caller must ensure st->prepared is allocated and that the
3120+ * command is not yet prepared. Returns true if the Prepare was sent; the
3121+ * caller must then wait for the result in CSTATE_WAIT_PREPARE_RESULT and set
3122+ * the prepared flag only after the result is read successfully.
3123+ *
3124+ * Unlike prepareCommand (synchronous, used for the pipeline path), this does
3125+ * not set st->prepared[...].
3126+ */
3127+ static bool
3128+ sendPrepareCommand (CState * st , int command_num )
3129+ {
3130+ Command * command = sql_script [st -> use_file ].commands [command_num ];
3131+
3132+ Assert (command -> type == SQL_COMMAND );
3133+ Assert (st -> prepared != NULL );
3134+ Assert (!st -> prepared [st -> use_file ][command_num ]);
3135+
3136+ pg_log_debug ("client %d preparing %s" , st -> id , command -> prepname );
3137+ return PQsendPrepare (st -> con , command -> prepname ,
3138+ command -> argv [0 ], command -> argc - 1 , NULL ) != 0 ;
3139+ }
3140+
31113141/*
31123142 * Prepare all the commands in the script that come after the \startpipeline
31133143 * that's at position st->command, and the first \endpipeline we find.
@@ -3147,8 +3177,16 @@ prepareCommandsInPipeline(CState *st)
31473177 st -> prepared [st -> use_file ][st -> command ] = true;
31483178}
31493179
3180+ /* Result of sendCommand(). */
3181+ typedef enum
3182+ {
3183+ SEND_FAILED , /* could not send */
3184+ SEND_OK , /* sent; wait for query result */
3185+ SEND_PREPARE_PENDING , /* async Prepare issued; wait for prepare result */
3186+ } SendCommandResult ;
3187+
31503188/* Send a SQL command, using the chosen querymode */
3151- static bool
3189+ static SendCommandResult
31523190sendCommand (CState * st , Command * command )
31533191{
31543192 int r ;
@@ -3179,7 +3217,20 @@ sendCommand(CState *st, Command *command)
31793217 {
31803218 const char * params [MAX_ARGS ];
31813219
3182- prepareCommand (st , st -> command );
3220+ if (!st -> prepared )
3221+ allocCStatePrepared (st );
3222+
3223+ if (!st -> prepared [st -> use_file ][st -> command ])
3224+ {
3225+ if (!sendPrepareCommand (st , st -> command ))
3226+ {
3227+ pg_log_debug ("client %d could not send %s" ,
3228+ st -> id , command -> argv [0 ]);
3229+ return SEND_FAILED ;
3230+ }
3231+ return SEND_PREPARE_PENDING ;
3232+ }
3233+
31833234 getQueryParams (& st -> variables , command , params );
31843235
31853236 pg_log_debug ("client %d sending %s" , st -> id , command -> prepname );
@@ -3192,10 +3243,10 @@ sendCommand(CState *st, Command *command)
31923243 if (r == 0 )
31933244 {
31943245 pg_log_debug ("client %d could not send %s" , st -> id , command -> argv [0 ]);
3195- return false ;
3246+ return SEND_FAILED ;
31963247 }
31973248 else
3198- return true ;
3249+ return SEND_OK ;
31993250}
32003251
32013252/*
@@ -3894,18 +3945,23 @@ advanceConnectionState(TState *thread, CState *st, StatsData *agg)
38943945 }
38953946 }
38963947
3897- if (! sendCommand (st , command ))
3948+ switch ( sendCommand (st , command ))
38983949 {
3899- commandFailed (st , "SQL" , "SQL command send failed" );
3900- st -> state = CSTATE_ABORTED ;
3901- }
3902- else
3903- {
3904- /* Wait for results, unless in pipeline mode */
3905- if (PQpipelineStatus (st -> con ) == PQ_PIPELINE_OFF )
3906- st -> state = CSTATE_WAIT_RESULT ;
3907- else
3908- st -> state = CSTATE_END_COMMAND ;
3950+ case SEND_FAILED :
3951+ commandFailed (st , "SQL" , "SQL command send failed" );
3952+ st -> state = CSTATE_ABORTED ;
3953+ break ;
3954+ case SEND_PREPARE_PENDING :
3955+ /* Wait for the async Prepare result */
3956+ st -> state = CSTATE_WAIT_PREPARE_RESULT ;
3957+ break ;
3958+ case SEND_OK :
3959+ /* Wait for results, unless in pipeline mode */
3960+ if (PQpipelineStatus (st -> con ) == PQ_PIPELINE_OFF )
3961+ st -> state = CSTATE_WAIT_RESULT ;
3962+ else
3963+ st -> state = CSTATE_END_COMMAND ;
3964+ break ;
39093965 }
39103966 }
39113967 else if (command -> type == META_COMMAND )
@@ -3927,6 +3983,7 @@ advanceConnectionState(TState *thread, CState *st, StatsData *agg)
39273983 * something bad happened.
39283984 */
39293985 Assert (st -> state == CSTATE_WAIT_RESULT ||
3986+ st -> state == CSTATE_WAIT_PREPARE_RESULT ||
39303987 st -> state == CSTATE_END_COMMAND ||
39313988 st -> state == CSTATE_SLEEP ||
39323989 st -> state == CSTATE_ABORTED );
@@ -4070,6 +4127,55 @@ advanceConnectionState(TState *thread, CState *st, StatsData *agg)
40704127 st -> state = CSTATE_ABORTED ;
40714128 break ;
40724129
4130+ /*
4131+ * Wait for the async PQsendPrepare result.
4132+ */
4133+ case CSTATE_WAIT_PREPARE_RESULT :
4134+ {
4135+ PGresult * res ;
4136+
4137+ pg_log_debug ("client %d receiving prepare result" , st -> id );
4138+
4139+ if (PQisBusy (st -> con ) && !PQconsumeInput (st -> con ))
4140+ {
4141+ commandFailed (st , "SQL" , "perhaps the backend died while preparing" );
4142+ st -> state = CSTATE_ABORTED ;
4143+ break ;
4144+ }
4145+ if (PQisBusy (st -> con ))
4146+ return ; /* don't have the whole result yet */
4147+
4148+ res = PQgetResult (st -> con );
4149+ if (PQresultStatus (res ) == PGRES_COMMAND_OK )
4150+ {
4151+ PQclear (res );
4152+ /* null terminator expected after a single result */
4153+ res = PQgetResult (st -> con );
4154+ Assert (res == NULL );
4155+
4156+ st -> prepared [st -> use_file ][st -> command ] = true;
4157+ /* Re-enter START_COMMAND to send the actual query */
4158+ st -> state = CSTATE_START_COMMAND ;
4159+ }
4160+ else
4161+ {
4162+ const char * sqlstate = PQresultErrorField (res ,
4163+ PG_DIAG_SQLSTATE );
4164+ st -> estatus = getSQLErrorStatus (st , sqlstate );
4165+ pg_log_error ("%s" , PQerrorMessage (st -> con ));
4166+ PQclear (res );
4167+ /* drain anything else pending on this command */
4168+ while ((res = PQgetResult (st -> con )) != NULL )
4169+ PQclear (res );
4170+ if (canRetryError (st -> estatus ) ||
4171+ canContinueOnError (st -> estatus ))
4172+ st -> state = CSTATE_ERROR ;
4173+ else
4174+ st -> state = CSTATE_ABORTED ;
4175+ }
4176+ break ;
4177+ }
4178+
40734179 /*
40744180 * Wait until sleep is done. This state is entered after a
40754181 * \sleep metacommand. The behavior is similar to
@@ -7631,6 +7737,7 @@ threadRun(void *arg)
76317737 min_usec = this_usec ;
76327738 }
76337739 else if (st -> state == CSTATE_WAIT_RESULT ||
7740+ st -> state == CSTATE_WAIT_PREPARE_RESULT ||
76347741 st -> state == CSTATE_WAIT_ROLLBACK_RESULT )
76357742 {
76367743 /*
@@ -7721,6 +7828,7 @@ threadRun(void *arg)
77217828 CState * st = & state [i ];
77227829
77237830 if (st -> state == CSTATE_WAIT_RESULT ||
7831+ st -> state == CSTATE_WAIT_PREPARE_RESULT ||
77247832 st -> state == CSTATE_WAIT_ROLLBACK_RESULT )
77257833 {
77267834 /* don't call advanceConnectionState unless data is available */
0 commit comments