mirror of
https://github.com/postgres/postgres.git
synced 2026-05-23 18:57:08 -04:00
\close has been introduced in d55322b0da to be able to close a
prepared statement using the extended protocol in psql. Per discussion,
the name "close" is ambiguous. At the SQL level, CLOSE is used to close
a cursor. At protocol level, the close message can be used to either
close a statement or a portal.
This patch renames \close to \close_prepared to avoid any ambiguity and
make it clear that this is used to close a prepared statement. This new
name has been chosen based on the feedback from the author and the
reviewers.
Author: Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Jelte Fennema-Nio <postgres@jeltef.nl>
Discussion: https://postgr.es/m/3e694442-0df5-4f92-a08f-c5d4c4346b85@eisentraut.org
442 lines
10 KiB
SQL
442 lines
10 KiB
SQL
--
|
|
-- Tests using psql pipelining
|
|
--
|
|
|
|
CREATE TABLE psql_pipeline(a INTEGER PRIMARY KEY, s TEXT);
|
|
|
|
-- Single query
|
|
\startpipeline
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
\endpipeline
|
|
\startpipeline
|
|
SELECT 'val1';
|
|
\endpipeline
|
|
|
|
-- Multiple queries
|
|
\startpipeline
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline
|
|
SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline
|
|
SELECT 'val4';
|
|
SELECT 'val5', 'val6';
|
|
\endpipeline
|
|
|
|
-- Multiple queries in single line, separated by semicolons
|
|
\startpipeline
|
|
SELECT 1; SELECT 2; SELECT 3
|
|
;
|
|
\echo :PIPELINE_COMMAND_COUNT
|
|
\endpipeline
|
|
|
|
-- Test \flush
|
|
\startpipeline
|
|
\flush
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
\flush
|
|
SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline
|
|
SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline
|
|
\flush
|
|
SELECT 'val4';
|
|
SELECT 'val5', 'val6';
|
|
\endpipeline
|
|
|
|
-- Send multiple syncs
|
|
\startpipeline
|
|
\echo :PIPELINE_COMMAND_COUNT
|
|
\echo :PIPELINE_SYNC_COUNT
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
\syncpipeline
|
|
\syncpipeline
|
|
SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline
|
|
\syncpipeline
|
|
SELECT $1, $2 \bind 'val4' 'val5' \sendpipeline
|
|
\echo :PIPELINE_COMMAND_COUNT
|
|
\echo :PIPELINE_SYNC_COUNT
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
SELECT 'val7';
|
|
\syncpipeline
|
|
\syncpipeline
|
|
SELECT 'val8';
|
|
\syncpipeline
|
|
SELECT 'val9';
|
|
\echo :PIPELINE_COMMAND_COUNT
|
|
\echo :PIPELINE_SYNC_COUNT
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
\endpipeline
|
|
|
|
-- Query terminated with a semicolon replaces an unnamed prepared
|
|
-- statement.
|
|
\startpipeline
|
|
SELECT $1 \parse ''
|
|
SELECT 1;
|
|
\bind_named ''
|
|
\endpipeline
|
|
|
|
-- Extended query is appended to pipeline by a semicolon after a
|
|
-- newline.
|
|
\startpipeline
|
|
SELECT $1 \bind 1
|
|
;
|
|
SELECT 2;
|
|
\endpipeline
|
|
|
|
-- \startpipeline should not have any effect if already in a pipeline.
|
|
\startpipeline
|
|
\startpipeline
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Convert an implicit transaction block to an explicit transaction block.
|
|
\startpipeline
|
|
INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline
|
|
BEGIN \bind \sendpipeline
|
|
INSERT INTO psql_pipeline VALUES ($1) \bind 2 \sendpipeline
|
|
ROLLBACK \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Multiple explicit transactions
|
|
\startpipeline
|
|
BEGIN \bind \sendpipeline
|
|
INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline
|
|
ROLLBACK \bind \sendpipeline
|
|
BEGIN \bind \sendpipeline
|
|
INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline
|
|
COMMIT \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Use \parse and \bind_named
|
|
\startpipeline
|
|
SELECT $1 \parse ''
|
|
SELECT $1, $2 \parse ''
|
|
SELECT $2 \parse pipeline_1
|
|
\bind_named '' 1 2 \sendpipeline
|
|
\bind_named pipeline_1 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- \getresults displays all results preceding a \flushrequest.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\flushrequest
|
|
\getresults
|
|
\endpipeline
|
|
|
|
-- \getresults displays all results preceding a \syncpipeline.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\syncpipeline
|
|
\getresults
|
|
\endpipeline
|
|
|
|
-- \getresults immediately returns if there is no result to fetch.
|
|
\startpipeline
|
|
\getresults
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\getresults
|
|
\flushrequest
|
|
\endpipeline
|
|
\getresults
|
|
|
|
-- \getresults only fetches results preceding a \flushrequest.
|
|
\startpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\flushrequest
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\getresults
|
|
\endpipeline
|
|
|
|
-- \getresults only fetches results preceding a \syncpipeline.
|
|
\startpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\syncpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\getresults
|
|
\endpipeline
|
|
|
|
-- Use pipeline with chunked results for both \getresults and \endpipeline.
|
|
\startpipeline
|
|
\set FETCH_COUNT 10
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\flushrequest
|
|
\getresults
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
\unset FETCH_COUNT
|
|
|
|
-- \getresults with specific number of requested results.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
SELECT $1 \bind 3 \sendpipeline
|
|
\echo :PIPELINE_SYNC_COUNT
|
|
\syncpipeline
|
|
\echo :PIPELINE_SYNC_COUNT
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
\getresults 1
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
SELECT $1 \bind 4 \sendpipeline
|
|
\getresults 3
|
|
\echo :PIPELINE_RESULT_COUNT
|
|
\endpipeline
|
|
|
|
-- \syncpipeline count as one command to fetch for \getresults.
|
|
\startpipeline
|
|
\syncpipeline
|
|
\syncpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
\flushrequest
|
|
\getresults 2
|
|
\getresults 1
|
|
\endpipeline
|
|
|
|
-- \getresults 0 should get all the results.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
SELECT $1 \bind 3 \sendpipeline
|
|
\syncpipeline
|
|
\getresults 0
|
|
\endpipeline
|
|
|
|
--
|
|
-- Pipeline errors
|
|
--
|
|
|
|
-- \endpipeline outside of pipeline should fail
|
|
\endpipeline
|
|
|
|
-- After an aborted pipeline, commands after a \syncpipeline should be
|
|
-- displayed.
|
|
\startpipeline
|
|
SELECT $1 \bind \sendpipeline
|
|
\syncpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- For an incorrect number of parameters, the pipeline is aborted and
|
|
-- the following queries will not be executed.
|
|
\startpipeline
|
|
SELECT \bind 'val1' \sendpipeline
|
|
SELECT $1 \bind 'val1' \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Using a semicolon with a parameter triggers an error and aborts
|
|
-- the pipeline.
|
|
\startpipeline
|
|
SELECT $1;
|
|
SELECT 1;
|
|
\endpipeline
|
|
|
|
-- An explicit transaction with an error needs to be rollbacked after
|
|
-- the pipeline.
|
|
\startpipeline
|
|
BEGIN \bind \sendpipeline
|
|
INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline
|
|
ROLLBACK \bind \sendpipeline
|
|
\endpipeline
|
|
ROLLBACK;
|
|
|
|
-- \watch is not allowed in a pipeline.
|
|
\startpipeline
|
|
SELECT \bind \sendpipeline
|
|
\watch 1
|
|
\endpipeline
|
|
|
|
-- \gdesc should fail as synchronous commands are not allowed in a pipeline,
|
|
-- and the pipeline should still be usable.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \gdesc
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- \gset is not allowed in a pipeline, pipeline should still be usable.
|
|
\startpipeline
|
|
SELECT $1 as i, $2 as j \parse ''
|
|
SELECT $1 as k, $2 as l \parse 'second'
|
|
\bind_named '' 1 2 \gset
|
|
\bind_named second 1 2 \gset pref02_ \echo :pref02_i :pref02_j
|
|
\bind_named '' 1 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- \g and \gx are not allowed, pipeline should still be usable.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \g
|
|
SELECT $1 \bind 1 \g (format=unaligned tuples_only=on)
|
|
SELECT $1 \bind 1 \gx
|
|
SELECT $1 \bind 1 \gx (format=unaligned tuples_only=on)
|
|
\reset
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- \g and \gx warnings should be emitted in an aborted pipeline, with
|
|
-- pipeline still usable.
|
|
\startpipeline
|
|
SELECT $1 \bind \sendpipeline
|
|
\flushrequest
|
|
\getresults
|
|
SELECT $1 \bind 1 \g
|
|
SELECT $1 \bind 1 \gx
|
|
\endpipeline
|
|
|
|
-- \sendpipeline is not allowed outside of a pipeline
|
|
\sendpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
\reset
|
|
|
|
-- \sendpipeline is not allowed if not preceded by \bind or \bind_named
|
|
\startpipeline
|
|
\sendpipeline
|
|
SELECT 1 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- \gexec is not allowed, pipeline should still be usable.
|
|
\startpipeline
|
|
SELECT 'INSERT INTO psql_pipeline(a) SELECT generate_series(1, 10)' \parse 'insert_stmt'
|
|
\bind_named insert_stmt \gexec
|
|
\bind_named insert_stmt \sendpipeline
|
|
SELECT COUNT(*) FROM psql_pipeline \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- After an error, pipeline is aborted and requires \syncpipeline to be
|
|
-- reusable.
|
|
\startpipeline
|
|
SELECT $1 \bind \sendpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \parse a
|
|
\bind_named a 1 \sendpipeline
|
|
\close_prepared a
|
|
\flushrequest
|
|
\getresults
|
|
-- Pipeline is aborted.
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \parse a
|
|
\bind_named a 1 \sendpipeline
|
|
\close_prepared a
|
|
-- Sync allows pipeline to recover.
|
|
\syncpipeline
|
|
\getresults
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \parse a
|
|
\bind_named a 1 \sendpipeline
|
|
\close_prepared a
|
|
\flushrequest
|
|
\getresults
|
|
\endpipeline
|
|
|
|
-- In an aborted pipeline, \getresults 1 aborts commands one at a time.
|
|
\startpipeline
|
|
SELECT $1 \bind \sendpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
SELECT $1 \parse a
|
|
\bind_named a 1 \sendpipeline
|
|
\syncpipeline
|
|
\getresults 1
|
|
\getresults 1
|
|
\getresults 1
|
|
\getresults 1
|
|
\getresults 1
|
|
\endpipeline
|
|
|
|
-- Test chunked results with an aborted pipeline.
|
|
\startpipeline
|
|
\set FETCH_COUNT 10
|
|
SELECT $1 \bind \sendpipeline
|
|
\flushrequest
|
|
\getresults
|
|
SELECT $1 \bind \sendpipeline
|
|
\endpipeline
|
|
\unset FETCH_COUNT
|
|
|
|
-- \getresults returns an error when an incorrect number is provided.
|
|
\startpipeline
|
|
\getresults -1
|
|
\endpipeline
|
|
|
|
-- \getresults when there is no result should not impact the next
|
|
-- query executed.
|
|
\getresults 1
|
|
select 1;
|
|
|
|
-- Error messages accumulate and are repeated.
|
|
\startpipeline
|
|
SELECT 1 \bind \sendpipeline
|
|
\gdesc
|
|
\gdesc
|
|
\endpipeline
|
|
|
|
--
|
|
-- Pipelines and transaction blocks
|
|
--
|
|
|
|
-- SET LOCAL will issue a warning when modifying a GUC outside of a
|
|
-- transaction block. The change will still be valid as a pipeline
|
|
-- runs within an implicit transaction block. Sending a sync will
|
|
-- commit the implicit transaction block. The first command after a
|
|
-- sync will not be seen as belonging to a pipeline.
|
|
\startpipeline
|
|
SET LOCAL statement_timeout='1h' \bind \sendpipeline
|
|
SHOW statement_timeout \bind \sendpipeline
|
|
\syncpipeline
|
|
SHOW statement_timeout \bind \sendpipeline
|
|
SET LOCAL statement_timeout='2h' \bind \sendpipeline
|
|
SHOW statement_timeout \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- REINDEX CONCURRENTLY fails if not the first command in a pipeline.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- REINDEX CONCURRENTLY works if it is the first command in a pipeline.
|
|
\startpipeline
|
|
REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Subtransactions are not allowed in a pipeline.
|
|
\startpipeline
|
|
SAVEPOINT a \bind \sendpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
ROLLBACK TO SAVEPOINT a \bind \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- LOCK fails as the first command in a pipeline, as not seen in an
|
|
-- implicit transaction block.
|
|
\startpipeline
|
|
LOCK psql_pipeline \bind \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- LOCK succeeds as it is not the first command in a pipeline,
|
|
-- seen in an implicit transaction block.
|
|
\startpipeline
|
|
SELECT $1 \bind 1 \sendpipeline
|
|
LOCK psql_pipeline \bind \sendpipeline
|
|
SELECT $1 \bind 2 \sendpipeline
|
|
\endpipeline
|
|
|
|
-- VACUUM works as the first command in a pipeline.
|
|
\startpipeline
|
|
VACUUM psql_pipeline \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- VACUUM fails when not the first command in a pipeline.
|
|
\startpipeline
|
|
SELECT 1 \bind \sendpipeline
|
|
VACUUM psql_pipeline \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- VACUUM works after a \syncpipeline.
|
|
\startpipeline
|
|
SELECT 1 \bind \sendpipeline
|
|
\syncpipeline
|
|
VACUUM psql_pipeline \bind \sendpipeline
|
|
\endpipeline
|
|
|
|
-- Clean up
|
|
DROP TABLE psql_pipeline;
|