From fa0e6968985cbe1f100746ea562b7f7712baf646 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 26 Oct 2020 09:10:43 +0100 Subject: [PATCH v1] Add default_result_formats setting --- doc/src/sgml/config.sgml | 42 ++++++++++++++ doc/src/sgml/libpq.sgml | 3 + doc/src/sgml/protocol.sgml | 2 +- src/backend/tcop/pquery.c | 100 ++++++++++++++++++++++++++++++++- src/backend/utils/misc/guc.c | 12 ++++ src/include/tcop/pquery.h | 5 ++ src/interfaces/libpq/fe-exec.c | 14 ++++- 7 files changed, 173 insertions(+), 5 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index f043433e31..6009e13899 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -8530,6 +8530,48 @@ Statement Behavior + + default_result_formats (string) + + default_result_formats + configuration parameter + + + + + This parameter specifies the default result formats by data type for + rows returned in the extended query protocol when no result formats + are specified in the Bind message. It is intended to be used by + client libraries that prefer to handle certain data types in binary + format. The typical usage would be that the client library sets this + value when it starts a connection. (A client library that wants to + handle all types in binary doesn't need to use + this because it can just specify the format code for all types at once + in the protocol message.) + + + The value is a list of + typeoid=format, + separated by commas. typeoid is the OID of + a type, from the pg_type system catalog. + format is the format code, currently 0 for + text and 1 for binary. 0 is the default for all types, so only 1 + needs to be specified explicitly. For example, if you want to + automatically get values of the types int2, + int4, and int8 in binary while leaving the + rest in text, an appropriate setting would be + 21=1,23=1,20=1. + + + Invalid format codes are an error. Nonexistent type OIDs are not + diagnosed. This setting applies only to result rows from the extended + query protocol, so it does not affect usage of + psql or pg_dump + for example. Also, it does not affect the format of query parameters. + + + + diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index de60281fcb..6361d5a93b 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2711,6 +2711,9 @@ Main Functions results in binary format. (There is not currently a provision to obtain different result columns in different formats, although that is possible in the underlying protocol.) + + XXX Specify -1 to send no result formats, so that can take effect. diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 3a64db6f55..e98f92ee78 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -3694,7 +3694,7 @@ Message Formats (denoted R below). This can be zero to indicate that there are no result columns or that the result columns should all use the default format - (text); + (usually text, but see ); or one, in which case the specified format code is applied to all result columns (if any); or it can equal the actual number of result columns of the query. diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 96ea74f118..1dff772d1e 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -26,6 +26,7 @@ #include "tcop/utility.h" #include "utils/memutils.h" #include "utils/snapmgr.h" +#include "utils/varlena.h" /* @@ -597,6 +598,98 @@ PortalStart(Portal portal, ParamListInfo params, portal->status = PORTAL_READY; } +char *default_result_formats; + +bool +check_default_result_formats(char **newval, void **extra, GucSource source) +{ + char *rawstring; + List *elemlist; + ListCell *lc; + + rawstring = pstrdup(*newval); + if (!SplitGUCList(rawstring, ',', &elemlist)) + { + GUC_check_errdetail("List syntax is invalid."); + pfree(rawstring); + list_free(elemlist); + return false; + } + + foreach(lc, elemlist) + { + char *str = lfirst(lc); + Oid oid; + short int format; + + if (sscanf(str, "%u=%hd", &oid, &format) != 2) + { + GUC_check_errdetail("Invalid list entry: %s", str); + pfree(rawstring); + list_free(elemlist); + return false; + } + + if (format !=0 && format != 1) + { + GUC_check_errdetail("Invalid format code: %d", format); + pfree(rawstring); + list_free(elemlist); + return false; + } + } + + return true; +} + +List *default_result_formats_binary = NIL; + +void +assign_default_result_formats(const char *newval, void *extra) +{ + char *rawstring; + List *elemlist; + ListCell *lc; + + rawstring = pstrdup(newval); + if (!SplitGUCList(rawstring, ',', &elemlist)) + { + pfree(rawstring); + list_free(elemlist); + return; + } + + foreach(lc, elemlist) + { + char *str = lfirst(lc); + Oid oid; + short int format; + MemoryContext oldcontext; + + if (sscanf(str, "%u=%hd", &oid, &format) != 2) + { + pfree(rawstring); + list_free(elemlist); + return; + } + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + switch (format) + { + case 0: + default_result_formats_binary = list_delete_oid(default_result_formats_binary, oid); + break; + case 1: + default_result_formats_binary = list_append_unique_oid(default_result_formats_binary, oid); + break; + } + MemoryContextSwitchTo(oldcontext); + } + + pfree(rawstring); + list_free(elemlist); +} + /* * PortalSetResultFormat * Select the format codes for a portal's output. @@ -642,7 +735,12 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats) { /* use default format for all columns */ for (i = 0; i < natts; i++) - portal->formats[i] = 0; + { + if (list_member_oid(default_result_formats_binary, TupleDescAttr(portal->tupDesc, i)->atttypid)) + portal->formats[i] = 1; + else + portal->formats[i] = 0; + } } } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a62d64eaa4..8d4cd50055 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -83,6 +83,7 @@ #include "storage/predicate.h" #include "storage/proc.h" #include "storage/standby.h" +#include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tsearch/ts_cache.h" #include "utils/acl.h" @@ -4448,6 +4449,17 @@ static struct config_string ConfigureNamesString[] = check_backtrace_functions, assign_backtrace_functions, NULL }, + { + {"default_result_formats", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Which format codes to use for types if nothing else is specified in the protocol."), + NULL, + GUC_LIST_INPUT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &default_result_formats, + "", + check_default_result_formats, assign_default_result_formats, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index 437642cc72..1d431fe013 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -15,10 +15,12 @@ #define PQUERY_H #include "nodes/parsenodes.h" +#include "utils/guc.h" #include "utils/portal.h" extern PGDLLIMPORT Portal ActivePortal; +extern char *default_result_formats; extern PortalStrategy ChoosePortalStrategy(List *stmts); @@ -30,6 +32,9 @@ extern List *FetchStatementTargetList(Node *stmt); extern void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot); +extern bool check_default_result_formats(char **newval, void **extra, GucSource source); +extern void assign_default_result_formats(const char *newval, void *extra); + extern void PortalSetResultFormat(Portal portal, int nFormats, int16 *formats); diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index eea0237c3a..e41216e2a1 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -1617,9 +1617,17 @@ PQsendQueryGuts(PGconn *conn, goto sendFailed; } } - if (pqPutInt(1, 2, conn) < 0 || - pqPutInt(resultFormat, 2, conn)) - goto sendFailed; + if (resultFormat >= 0) + { + if (pqPutInt(1, 2, conn) < 0 || + pqPutInt(resultFormat, 2, conn)) + goto sendFailed; + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } if (pqPutMsgEnd(conn) < 0) goto sendFailed; -- 2.28.0