dbdpg icon indicating copy to clipboard operation
dbdpg copied to clipboard

Hanging execution of queries like "COPY TO STDIN" in asynchronous mode.

Open true-alex opened this issue 3 years ago • 2 comments

When calling "COPY ... TO STDIN" query in asynchronous mode after pg_ready returned 1, pg_result call hangs.

The reason is probably the implementation of the pg_db_result method in the dbdimp.c file. This method is written exactly with the libpq documentation, the async section, which says that PQgetResult must be called until it returns null. However, if PQgetResult returned a PGRES_COPY_* state, this state will not change until the copy operations are completed (according to the copy libpq documentation).

To test my theory, at the end of the main loop of the pg_db_result function, I put a check with a loop break:

diff -r DBD-Pg-3.15.1/dbdimp.c DBD-Pg-3.15.1_patch/dbdimp.c
5375a5376,5378
>         if ( rows == -1 ) {
>               break;
>         }

In the attachments there are two greatly simplified files, one synchronous one works correctly, the second asynchronous one without a patch freezes, with a patch it works correctly.

The problem has been reproduced on: Freebsd 9.4 + Postgress 9.3 Cenos 6 + Postgress 9.4 OracleLinux 8 + Postgress 13 Freebsd 13 + Postgress 13

test.tgz

true-alex avatar Sep 05 '22 16:09 true-alex

When researching the problem, I used the documentation: https://postgrespro.com/docs/postgrespro/13/libpq-copy The functions of this section should be executed only after obtaining a result status of PGRES_COPY_OUT or PGRES_COPY_IN from PQexec or PQgetResult. ... After PQgetCopyData returns -1, call PQgetResult to obtain the final result status of the COPY command. One can wait for this result to be available in the usual way. Then return to normal operation.

https://postgrespro.com/docs/postgresql/13/libpq-async PQgetResult must be called repeatedly until it returns a null pointer, indicating that the command is done. (If called when no command is active, PQgetResult will just return a null pointer at once.) Each non-null result from PQgetResult should be processed using the same PGresult accessor functions previously described. Don't forget to free each result object with PQclear when done with it. Note that PQgetResult will block only if a command is active and the necessary response data has not yet been read by PQconsumeInput .

true-alex avatar Sep 05 '22 16:09 true-alex

As mentioned in the PR, I don't think we should allow pg_result after a COPY, as it makes no sense to wait for the server, when the only way to finish is for the client to officially end things.

turnstep avatar Aug 23 '23 21:08 turnstep