From 552a7fa9e79578b37f375579f7cb46cd897d100f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 24 Feb 2023 12:21:40 +0100 Subject: [PATCH v8 2/2] WIP: Dynamic result sets in extended query protocol This is currently broken due to/since acb7e4eb6b. TODO: consider minor protocol version bump (3.1) --- doc/src/sgml/protocol.sgml | 19 +++++++++++ src/backend/tcop/postgres.c | 32 ++++++++++++++++--- src/backend/tcop/pquery.c | 6 ++++ src/include/utils/portal.h | 2 ++ src/interfaces/libpq/fe-protocol3.c | 6 ++-- .../regress/expected/dynamic_result_sets.out | 31 +++++++++++++++--- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 93fc7167d4..ec605b12b5 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -959,6 +959,25 @@ Extended Query an empty query string), ErrorResponse, or PortalSuspended. + + Executing a portal may give rise to a dynamic result set + sequence. That means the command contained in the portal + created additional result sets beyond what it normally returns. (The + typical example is calling a stored procedure that creates dynamic result + sets.) Dynamic result sets are issued after whatever response the main + command issued. Each dynamic result set begins with a RowDescription + message followed by zero or more DataRow messages. (Since, as explained + above, an Execute message normally does not respond with a RowDescription, + the appearance of the first RowDescription marks the end of the primary + result set of the portal and the beginning of the first dynamic result + set.) The CommandComplete message that concludes the Execute message + response follows after all dynamic result sets. Note + that dynamic result sets cannot, by their nature, be decribed prior to the + execution of the portal. Multiple executions of the same prepared + statement could result in dynamic result sets with different row + descriptions being returned. + + At completion of each series of extended-query messages, the frontend should issue a Sync message. This parameterless message causes the diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 98ac9aa012..89da5c7512 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2086,6 +2086,7 @@ exec_execute_message(const char *portal_name, long max_rows) const char *sourceText; const char *prepStmtName; ParamListInfo portalParams; + ListCell *lc; bool save_log_statement_stats = log_statement_stats; bool is_xact_command; bool execute_is_fetch; @@ -2226,10 +2227,33 @@ exec_execute_message(const char *portal_name, long max_rows) receiver, &qc); - if (GetReturnableCursors()) - ereport(ERROR, - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("dynamic result sets are not yet supported in extended query protocol")); + /* + * Run portals for dynamic result sets. + */ + foreach (lc, GetReturnableCursors()) + { + Portal dyn_portal = lfirst(lc); + + if (dest == DestRemoteExecute) + SetRemoteDestReceiverParams(receiver, dyn_portal); + + PortalSetResultFormat(dyn_portal, 1, &portal->dynamic_format); + + SendRowDescriptionMessage(&row_description_buf, + dyn_portal->tupDesc, + FetchPortalTargetList(dyn_portal), + dyn_portal->formats); + + PortalRun(dyn_portal, + FETCH_ALL, + true, + true, + receiver, + receiver, + NULL); + + PortalDrop(dyn_portal, false); + } receiver->rDestroy(receiver); diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 5f0248acc5..6469940935 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -641,6 +641,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) errmsg("bind message has %d result formats but query has %d columns", nFormats, natts))); memcpy(portal->formats, formats, natts * sizeof(int16)); + + portal->dynamic_format = 0; } else if (nFormats > 0) { @@ -649,12 +651,16 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) for (i = 0; i < natts; i++) portal->formats[i] = format1; + + portal->dynamic_format = format1; } else { /* use default format for all columns */ for (i = 0; i < natts; i++) portal->formats[i] = 0; + + portal->dynamic_format = 0; } } diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h index 6f04362dfe..55406b8654 100644 --- a/src/include/utils/portal.h +++ b/src/include/utils/portal.h @@ -170,6 +170,8 @@ typedef struct PortalData TupleDesc tupDesc; /* descriptor for result tuples */ /* and these are the format codes to use for the columns: */ int16 *formats; /* a format code for each column */ + /* Format code for dynamic result sets */ + int16 dynamic_format; /* * Outermost ActiveSnapshot for execution of the portal's queries. For diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 8ab6a88416..863a09cf74 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -320,10 +320,8 @@ pqParseInput3(PGconn *conn) { /* * A new 'T' message is treated as the start of - * another PGresult. (It is not clear that this is - * really possible with the current backend.) We stop - * parsing until the application accepts the current - * result. + * another PGresult. We stop parsing until the + * application accepts the current result. */ conn->asyncStatus = PGASYNC_READY; return; diff --git a/src/test/regress/expected/dynamic_result_sets.out b/src/test/regress/expected/dynamic_result_sets.out index 7b2529c99e..3584f1ec8c 100644 --- a/src/test/regress/expected/dynamic_result_sets.out +++ b/src/test/regress/expected/dynamic_result_sets.out @@ -25,7 +25,14 @@ CALL pdrstest1(); (2 rows) CALL pdrstest1() \bind \g -ERROR: dynamic result sets are not yet supported in extended query protocol + a +--- + 1 + 2 + 3 +(3 rows) + +server sent data ("D" message) without prior row description ("T" message) -- return too many result sets from a procedure CREATE PROCEDURE pdrstest2() LANGUAGE SQL @@ -51,7 +58,14 @@ WARNING: attempt to return too many result sets CALL pdrstest2() \bind \g WARNING: attempt to return too many result sets -ERROR: dynamic result sets are not yet supported in extended query protocol + a +--- + 1 + 2 + 3 +(3 rows) + +server sent data ("D" message) without prior row description ("T" message) -- nested calls CREATE PROCEDURE pdrstest3() LANGUAGE SQL @@ -68,7 +82,11 @@ CALL pdrstest3(); (1 row) CALL pdrstest3() \bind \g -ERROR: dynamic result sets are not yet supported in extended query protocol + a +--- + 1 +(1 row) + -- both out parameter and result sets CREATE PROCEDURE pdrstest4(INOUT a text) LANGUAGE SQL @@ -91,7 +109,12 @@ CALL pdrstest4('x'); (3 rows) CALL pdrstest4($1) \bind 'y' \g -ERROR: dynamic result sets are not yet supported in extended query protocol + a +---- + yy +(1 row) + +server sent data ("D" message) without prior row description ("T" message) -- test the nested error handling CREATE TABLE drs_test_dummy (a int); CREATE PROCEDURE pdrstest5a() -- 2.39.2