Для приготовления получения типов колонок за один запрос нам понадобится postgres. Можно также воспользоваться готовым образом.
Сразу спешу огорчить: реализация будет не на SQL! Хотя, кончено, и там есть подходящие функции. Но мне показался слишком сложным и некрасивым получающийся код, и поэтому, я всё реализовал на… C! Правда, к сожалению, не как расширение, а как модификация исходников.
Сразу спешу огорчить: реализация будет не на SQL! Хотя, кончено, и там есть подходящие функции. Но мне показался слишком сложным и некрасивым получающийся код, и поэтому, я всё реализовал на… C! Правда, к сожалению, не как расширение, а как модификация исходников.
Вот, собственно говоря, сам патч для добавления типов колонок к именам (для клиентов версий 2 и 3), с комментариями
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 24d6cd0249..6862b69258 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -22,6 +22,9 @@ #include "utils/lsyscache.h" #include "utils/memdebug.h" #include "utils/memutils.h" +#include "utils/guc.h" // подключаем +#include "utils/syscache.h" // нужные +#include "catalog/pg_type.h" // заголовки static void printtup_startup(DestReceiver *self, int operation, @@ -70,6 +73,8 @@ typedef struct MemoryContext tmpcontext; /* Memory context for per-row workspace */ } DR_printtup; +static bool append; // добавлять типы к именам колонок? + /* ---------------- * Initialize: create a DestReceiver for printtup * ---------------- @@ -132,6 +137,7 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { DR_printtup *myState = (DR_printtup *) self; Portal portal = myState->portal; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? /* * Create I/O buffer to be used for all messages. This cannot be inside @@ -236,6 +242,7 @@ SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, i * character set overhead. */ enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */ + + (append ? NAMEDATALEN * MAX_CONVERSION_GROWTH + sizeof("::") - 1 : 0) // если нужно добавлять типы к именам колонок, то добавим ещё места для их хранения + sizeof(Oid) /* resorigtbl */ + sizeof(AttrNumber) /* resorigcol */ + sizeof(Oid) /* atttypid */ @@ -283,6 +290,17 @@ SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, i else format = 0; + if (append) { // если нужно добавлять типы к именам колонок, то + HeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(att->atttypid)); // поищем тип колонки в кэше + StringInfoData buf_; // временный буфер для хранения + initStringInfo(&buf_); // иниализируем его + appendStringInfo(&buf_, "%s::", NameStr(att->attname)); // запишем в буфер сначала название колонки, потом два двоеточия + if (HeapTupleIsValid(typeTuple)) appendStringInfoString(&buf_, NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname)); // если нашли тип колонки, то запишем его в буфер + else appendStringInfo(&buf_, "%i", att->atttypid); // иначе в буфер запишем oid + pq_writestring(buf, buf_.data); // отправим данные клиенту + ReleaseSysCache(typeTuple); // разблокируем кэш + pfree(buf_.data); // очистим временный буфер + } else // ну а если не надо добавлять типы к именам колонок, то всё остаётся по-старому pq_writestring(buf, NameStr(att->attname)); pq_writeint32(buf, resorigtbl); pq_writeint16(buf, resorigcol); @@ -311,6 +329,17 @@ SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, i /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); + if (append) { // если нужно добавлять типы к именам колонок, то + HeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(att->atttypid)); // поищем тип колонки в кэше + StringInfoData buf_; // временный буфер для хранения + initStringInfo(&buf_); // иниализируем его + appendStringInfo(&buf_, "%s::", NameStr(att->attname)); // запишем в буфер сначала название колонки, потом два двоеточия + if (HeapTupleIsValid(typeTuple)) appendStringInfoString(&buf_, NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname)); // если нашли тип колонки, то запишем его в буфер + else appendStringInfo(&buf_, "%i", att->atttypid); // иначе в буфер запишем oid + pq_sendstring(buf, buf_.data); // отправим данные клиенту + ReleaseSysCache(typeTuple); // разблокируем кэш + pfree(buf_.data); // очистим временный буфер + } else // ну а если не надо добавлять типы к именам колонок, то всё остаётся по-старому pq_sendstring(buf, NameStr(att->attname)); /* column ID only info appears in protocol 3.0 and up */ pq_sendint32(buf, atttypid); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 6c2db93573..f23de7f788 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -992,6 +992,8 @@ exec_simple_query(const char *query_string) bool use_implicit_block; char msec_str[32]; + SetConfigOption("config.append_type_to_column_name", NULL, PGC_USERSET, PGC_S_SESSION); // перед каждым выполнением SQL-кода зануляем нашу переменную, чтобы пользователь мог сам решить, нужны ли ему типы колонок + /* * Report query to various monitoring facilities. */ @@ -1959,6 +1961,8 @@ exec_execute_message(const char *portal_name, long max_rows) bool was_logged = false; char msec_str[32]; + SetConfigOption("config.append_type_to_column_name", NULL, PGC_USERSET, PGC_S_SESSION); // перед каждым выполнением заранее подготовленного SQL-кода зануляем нашу переменную, чтобы пользователь мог сам решить, нужны ли ему типы колонок + /* Adjust destination to tell printtup.c what to do */ dest = whereToSendOutput; if (dest == DestRemote)
Ну и бонусом, аналогичный патч для добавления типов колонок к именам при преобразованиях в json
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 26d293709a..fb025adf58 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -32,6 +32,7 @@ #include "utils/jsonapi.h" #include "utils/typcache.h" #include "utils/syscache.h" +#include "utils/guc.h" // подключаем нужный заголовок /* * The context of the parser is maintained by the recursive descent @@ -106,6 +107,8 @@ static void add_json(Datum val, bool is_null, StringInfo result, Oid val_type, bool key_scalar); static text *catenate_stringinfo_string(StringInfo buffer, const char *addon); +static bool append; // добавлять типы к именам колонок? + /* the null action object used for pure validation */ static JsonSemAction nullSemAction = { @@ -1789,6 +1792,17 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds) needsep = true; attname = NameStr(att->attname); + if (append) { // если нужно добавлять типы к именам колонок, то + HeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(att->atttypid)); // поищем тип колонки в кэше + StringInfoData buf; // временный буфер для хранения + initStringInfo(&buf); // иниализируем его + appendStringInfo(&buf, "%s::", attname); // запишем в буфер сначала название колонки, потом два двоеточия + if (HeapTupleIsValid(typeTuple)) appendStringInfoString(&buf, NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname)); // если нашли тип колонки, то запишем его в буфер + else appendStringInfo(&buf, "%i", att->atttypid); // иначе в буфер запишем oid + escape_json(result, buf.data); // записываем результат + ReleaseSysCache(typeTuple); // разблокируем кэш + pfree(buf.data); // очистим временный буфер + } else // ну а если не надо добавлять типы к именам колонок, то всё остаётся по-старому escape_json(result, attname); appendStringInfoChar(result, ':'); @@ -1848,6 +1862,7 @@ array_to_json(PG_FUNCTION_ARGS) { Datum array = PG_GETARG_DATUM(0); StringInfo result; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? result = makeStringInfo(); @@ -1865,6 +1880,7 @@ array_to_json_pretty(PG_FUNCTION_ARGS) Datum array = PG_GETARG_DATUM(0); bool use_line_feeds = PG_GETARG_BOOL(1); StringInfo result; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? result = makeStringInfo(); @@ -1881,6 +1897,7 @@ row_to_json(PG_FUNCTION_ARGS) { Datum array = PG_GETARG_DATUM(0); StringInfo result; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? result = makeStringInfo(); @@ -1898,6 +1915,7 @@ row_to_json_pretty(PG_FUNCTION_ARGS) Datum array = PG_GETARG_DATUM(0); bool use_line_feeds = PG_GETARG_BOOL(1); StringInfo result; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? result = makeStringInfo(); @@ -1917,6 +1935,7 @@ to_json(PG_FUNCTION_ARGS) StringInfo result; JsonTypeCategory tcategory; Oid outfuncoid; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? if (val_type == InvalidOid) ereport(ERROR, @@ -1945,6 +1964,7 @@ json_agg_transfn(PG_FUNCTION_ARGS) oldcontext; JsonAggState *state; Datum val; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? if (!AggCheckCallContext(fcinfo, &aggcontext)) { @@ -2046,6 +2066,7 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) oldcontext; JsonAggState *state; Datum arg; + append = GetConfigOption("config.append_type_to_column_name", true, true) != NULL; // хочет ли пользователь базы добавлять типы к именам колонок? if (!AggCheckCallContext(fcinfo, &aggcontext)) {
Ну и зачем я всё это сделал?! Потому, что могу хотя, не только, есть у меня одна интересная идея, как это можно эффективно использовать, но пока руки не дошли…
ссылка на оригинал статьи https://habr.com/ru/post/502680/
Добавить комментарий