From 43eab75a74b841459489f5b532d80482a97ab7f2 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 20 Feb 2023 12:57:33 +0100 Subject: [PATCH v7 2/2] WIP: Dynamic result sets in extended query protocol This is currently broken due to/since acb7e4eb6b1c614c68a62fb3a6a5bba1af0a2659. 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/create_procedure.out | 22 +++++++++++-- 6 files changed, 76 insertions(+), 11 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/create_procedure.out b/src/test/regress/expected/create_procedure.out index 8cc009d1b2..9cec033efb 100644 --- a/src/test/regress/expected/create_procedure.out +++ b/src/test/regress/expected/create_procedure.out @@ -402,7 +402,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) CREATE PROCEDURE pdrstest2() LANGUAGE SQL DYNAMIC RESULT SETS 1 @@ -417,7 +424,11 @@ CALL pdrstest2(); (1 row) CALL pdrstest2() \bind \g -ERROR: dynamic result sets are not yet supported in extended query protocol + a +--- + 1 +(1 row) + CREATE PROCEDURE pdrstest3(INOUT a text) LANGUAGE SQL DYNAMIC RESULT SETS 1 @@ -439,7 +450,12 @@ CALL pdrstest3('x'); (3 rows) CALL pdrstest3($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 cp_test_dummy (a int); CREATE PROCEDURE pdrstest4a() -- 2.39.2