diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index cddf6e07531..2763486e268 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -3698,14 +3698,19 @@ testdb=> \setenv LESS -imx4F - Pipeline mode requires the use of the extended query protocol. All - queries need to be sent using the meta-commands - \bind, \bind_named, - \close or \parse. While a - pipeline is ongoing, \sendpipeline will append the - current query buffer to the pipeline. Other meta-commands like - \g, \gx or \gdesc - are not allowed in pipeline mode. + Pipeline mode requires the use of the extended query protocol. Queries + can be sent using the meta-commands \bind, + \bind_named, \close or + \parse. While a pipeline is ongoing, + \sendpipeline will append the current query + buffer to the pipeline. Other meta-commands like \g, + \gx or \gdesc are not allowed + in pipeline mode. + + + + Queries can also be sent using ;. While a pipeline is + ongoing, they will automatically be sent using extended query protocol. diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index a87ff7e4597..bbe337780ff 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3282,6 +3282,13 @@ exec_command_watch(PsqlScanState scan_state, bool active_branch, int iter = 0; int min_rows = 0; + if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF) + { + pg_log_error("\\watch not allowed in pipeline mode"); + clean_extended_state(); + success = false; + } + /* * Parse arguments. We allow either an unlabeled interval or * "name=value", where name is from the set ('i', 'interval', 'c', diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index ed340a466f9..5249336bcf2 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1668,7 +1668,15 @@ ExecQueryAndProcessResults(const char *query, } break; case PSQL_SEND_QUERY: - success = PQsendQuery(pset.db, query); + if (PQpipelineStatus(pset.db) != PQ_PIPELINE_OFF) + { + success = PQsendQueryParams(pset.db, query, + 0, NULL, NULL, NULL, NULL, 0); + if (success) + pset.piped_commands++; + } + else + success = PQsendQuery(pset.db, query); break; } diff --git a/src/test/regress/expected/psql_pipeline.out b/src/test/regress/expected/psql_pipeline.out index 68e3c19ea05..7dddf26a9fd 100644 --- a/src/test/regress/expected/psql_pipeline.out +++ b/src/test/regress/expected/psql_pipeline.out @@ -388,23 +388,267 @@ SELECT $1 \bind 3 \sendpipeline \endpipeline -- --- Pipeline errors +-- Tests pipelining queries with ';' -- --- \endpipeline outside of pipeline should fail +-- Single query sent with ';' +\startpipeline +SELECT 1; \endpipeline -cannot send pipeline when not in pipeline mode --- Query using simple protocol should not be sent and should leave the --- pipeline usable. + ?column? +---------- + 1 +(1 row) + +-- Multiple queries sent with ';' +\startpipeline +SELECT 1; +SELECT 2; +SELECT 3; +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 2 +(1 row) + + ?column? +---------- + 3 +(1 row) + +-- Multiple queries on the same line can be piped with ';' +\startpipeline +SELECT 1; SELECT 2; SELECT 3 +; +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 2 +(1 row) + + ?column? +---------- + 3 +(1 row) + +-- Test \flush with queries piped with ';' +\startpipeline +\flush +SELECT 1; +\flush +SELECT 2; +SELECT 3; +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 2 +(1 row) + + ?column? +---------- + 3 +(1 row) + +-- Send multiple syncs with queries piped with ';' +\startpipeline +\echo :PIPELINE_COMMAND_COUNT +0 +\echo :PIPELINE_SYNC_COUNT +0 +\echo :PIPELINE_RESULT_COUNT +0 +SELECT 1; +\syncpipeline +\syncpipeline +SELECT 2; +\syncpipeline +SELECT 3; +\echo :PIPELINE_COMMAND_COUNT +1 +\echo :PIPELINE_SYNC_COUNT +3 +\echo :PIPELINE_RESULT_COUNT +2 +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 2 +(1 row) + + ?column? +---------- + 3 +(1 row) + +-- Mix queries piped with ';' and \sendpipeline \startpipeline SELECT 1; -PQsendQuery not allowed in pipeline mode SELECT $1 \bind 'val1' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT 2; +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + val1 +(1 row) + + ?column? | ?column? +----------+---------- + val2 | val3 +(1 row) + + ?column? +---------- + 2 +(1 row) + +-- Piping a query with ';' will replace the unnamed prepared statement +\startpipeline +SELECT $1 \parse '' +SELECT 1; +\bind_named '' +\endpipeline + ?column? +---------- + 1 +(1 row) + +-- An extended query can be piped by a ';' after a newline +\startpipeline +SELECT $1 \bind 1 +; +SELECT 2; +\endpipeline + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 2 +(1 row) + +-- COPY FROM STDIN, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\endpipeline + ?column? +---------- + val1 +(1 row) + +-- COPY FROM STDIN with \flushrequest + \getresults, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\flushrequest +\getresults + ?column? +---------- + val1 +(1 row) + +message type 0x5a arrived from server while idle +\endpipeline +-- COPY FROM STDIN with \syncpipeline + \getresults, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\syncpipeline +\getresults + ?column? +---------- + val1 +(1 row) + \endpipeline +-- COPY TO STDOUT, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\endpipeline + ?column? +---------- + val1 +(1 row) + +1 \N +2 test2 +3 test3 +4 test4 +20 test2 +30 test3 +40 test4 +-- COPY TO STDOUT with \flushrequest + \getresults, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\flushrequest +\getresults ?column? ---------- val1 (1 row) +1 \N +2 test2 +3 test3 +4 test4 +20 test2 +30 test3 +40 test4 +\endpipeline +-- COPY TO STDOUT with \syncpipeline + \getresults, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\syncpipeline +\getresults + ?column? +---------- + val1 +(1 row) + +1 \N +2 test2 +3 test3 +4 test4 +20 test2 +30 test3 +40 test4 +\endpipeline +-- +-- Pipeline errors +-- +-- \endpipeline outside of pipeline should fail +\endpipeline +cannot send pipeline when not in pipeline mode -- After an aborted pipeline, commands after a \syncpipeline should be -- displayed. \startpipeline @@ -425,6 +669,13 @@ SELECT \bind 'val1' \sendpipeline SELECT $1 \bind 'val1' \sendpipeline \endpipeline ERROR: bind message supplies 1 parameters, but prepared statement "" requires 0 +-- Using ';' with a parameter will trigger an incorrect parameter error and +-- abort the pipeline +\startpipeline +SELECT $1; +SELECT 1; +\endpipeline +ERROR: bind message supplies 0 parameters, but prepared statement "" requires 1 -- An explicit transaction with an error needs to be rollbacked after -- the pipeline. \startpipeline @@ -435,12 +686,11 @@ ROLLBACK \bind \sendpipeline ERROR: duplicate key value violates unique constraint "psql_pipeline_pkey" DETAIL: Key (a)=(1) already exists. ROLLBACK; --- \watch sends a simple query, something not allowed within a pipeline. +-- \watch is not allowed within a pipeline. \startpipeline SELECT \bind \sendpipeline \watch 1 -PQsendQuery not allowed in pipeline mode - +\watch not allowed in pipeline mode \endpipeline -- (1 row) @@ -530,7 +780,7 @@ SELECT COUNT(*) FROM psql_pipeline \bind \sendpipeline count ------- - 4 + 7 (1 row) -- After an error, pipeline is aborted and requires \syncpipeline to be @@ -617,11 +867,11 @@ select 1; -- Error messages accumulate and are repeated. \startpipeline SELECT 1 \bind \sendpipeline -SELECT 1; -PQsendQuery not allowed in pipeline mode -SELECT 1; -PQsendQuery not allowed in pipeline mode -PQsendQuery not allowed in pipeline mode +\gdesc +synchronous command execution functions are not allowed in pipeline mode +\gdesc +synchronous command execution functions are not allowed in pipeline mode +synchronous command execution functions are not allowed in pipeline mode \endpipeline ?column? ---------- diff --git a/src/test/regress/sql/psql_pipeline.sql b/src/test/regress/sql/psql_pipeline.sql index e4d7e614af3..6f76adcba82 100644 --- a/src/test/regress/sql/psql_pipeline.sql +++ b/src/test/regress/sql/psql_pipeline.sql @@ -211,17 +211,129 @@ SELECT $1 \bind 3 \sendpipeline \endpipeline -- --- Pipeline errors +-- Tests pipelining queries with ';' -- --- \endpipeline outside of pipeline should fail +-- Single query sent with ';' +\startpipeline +SELECT 1; \endpipeline --- Query using simple protocol should not be sent and should leave the --- pipeline usable. +-- Multiple queries sent with ';' +\startpipeline +SELECT 1; +SELECT 2; +SELECT 3; +\endpipeline + +-- Multiple queries on the same line can be piped with ';' +\startpipeline +SELECT 1; SELECT 2; SELECT 3 +; +\endpipeline + +-- Test \flush with queries piped with ';' +\startpipeline +\flush +SELECT 1; +\flush +SELECT 2; +SELECT 3; +\endpipeline + +-- Send multiple syncs with queries piped with ';' +\startpipeline +\echo :PIPELINE_COMMAND_COUNT +\echo :PIPELINE_SYNC_COUNT +\echo :PIPELINE_RESULT_COUNT +SELECT 1; +\syncpipeline +\syncpipeline +SELECT 2; +\syncpipeline +SELECT 3; +\echo :PIPELINE_COMMAND_COUNT +\echo :PIPELINE_SYNC_COUNT +\echo :PIPELINE_RESULT_COUNT +\endpipeline + +-- Mix queries piped with ';' and \sendpipeline \startpipeline SELECT 1; SELECT $1 \bind 'val1' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT 2; +\endpipeline + +-- Piping a query with ';' will replace the unnamed prepared statement +\startpipeline +SELECT $1 \parse '' +SELECT 1; +\bind_named '' +\endpipeline + +-- An extended query can be piped by a ';' after a newline +\startpipeline +SELECT $1 \bind 1 +; +SELECT 2; +\endpipeline + +-- COPY FROM STDIN, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\endpipeline +20 test2 +\. + +-- COPY FROM STDIN with \flushrequest + \getresults, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\flushrequest +\getresults +30 test3 +\. +\endpipeline + +-- COPY FROM STDIN with \syncpipeline + \getresults, using ';' +\startpipeline +SELECT 'val1'; +COPY psql_pipeline FROM STDIN; +\syncpipeline +\getresults +40 test4 +\. +\endpipeline + +-- COPY TO STDOUT, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\endpipeline + +-- COPY TO STDOUT with \flushrequest + \getresults, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\flushrequest +\getresults +\endpipeline + +-- COPY TO STDOUT with \syncpipeline + \getresults, using ';' +\startpipeline +SELECT 'val1'; +copy psql_pipeline TO STDOUT; +\syncpipeline +\getresults +\endpipeline + +-- +-- Pipeline errors +-- + +-- \endpipeline outside of pipeline should fail \endpipeline -- After an aborted pipeline, commands after a \syncpipeline should be @@ -239,6 +351,13 @@ SELECT \bind 'val1' \sendpipeline SELECT $1 \bind 'val1' \sendpipeline \endpipeline +-- Using ';' with a parameter will trigger an incorrect parameter error and +-- abort the pipeline +\startpipeline +SELECT $1; +SELECT 1; +\endpipeline + -- An explicit transaction with an error needs to be rollbacked after -- the pipeline. \startpipeline @@ -248,7 +367,7 @@ ROLLBACK \bind \sendpipeline \endpipeline ROLLBACK; --- \watch sends a simple query, something not allowed within a pipeline. +-- \watch is not allowed within a pipeline. \startpipeline SELECT \bind \sendpipeline \watch 1 @@ -372,8 +491,8 @@ select 1; -- Error messages accumulate and are repeated. \startpipeline SELECT 1 \bind \sendpipeline -SELECT 1; -SELECT 1; +\gdesc +\gdesc \endpipeline --