diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index e0c985ef8b..dce30aed6c 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -22,6 +22,7 @@ #include "catalog/pg_type.h" #include "commands/createas.h" #include "commands/prepare.h" +#include "funcapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" @@ -716,30 +717,13 @@ pg_prepared_statement(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + /* need to build tuplestore in query context */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - /* - * build tupdesc for result tuples. This must match the definition of the - * pg_prepared_statements view in system_views.sql - */ - tupdesc = CreateTemplateTupleDesc(7); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time", - TIMESTAMPTZOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types", - REGTYPEARRAYOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql", - BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 6, "generic_plans", - INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 7, "custom_plans", - INT8OID, -1, 0); - /* * We put all the tuples into a tuplestore in one scan of the hashtable. * This avoids any issue of the hashtable possibly changing between calls. @@ -747,6 +731,9 @@ pg_prepared_statement(PG_FUNCTION_ARGS) tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; /* generate junk in short-term context */ MemoryContextSwitchTo(oldcontext); @@ -778,10 +765,6 @@ pg_prepared_statement(PG_FUNCTION_ARGS) } } - rsinfo->returnMode = SFRM_Materialize; - rsinfo->setResult = tupstore; - rsinfo->setDesc = tupdesc; - return (Datum) 0; } diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index d910bc2fbe..c3406c3b9d 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -499,17 +499,19 @@ IsImportableForeignTable(const char *tablename, /* - * deflist_to_tuplestore - Helper function to convert DefElem list to - * tuplestore usable in SRF. + * pg_options_to_table - Convert options array to name/value table + * + * This is useful to provide details for information_schema and pg_dump. */ -static void -deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) +Datum +pg_options_to_table(PG_FUNCTION_ARGS) { + Datum array = PG_GETARG_DATUM(0); ListCell *cell; + List *options; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; - Datum values[2]; - bool nulls[2]; MemoryContext per_query_ctx; MemoryContext oldcontext; @@ -524,6 +526,9 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); + options = untransformRelOptions(array); + rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -536,9 +541,13 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; + MemoryContextSwitchTo(oldcontext); + foreach(cell, options) { DefElem *def = lfirst(cell); + Datum values[2]; + bool nulls[2]; values[0] = CStringGetTextDatum(def->defname); nulls[0] = false; @@ -555,22 +564,6 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) tuplestore_putvalues(tupstore, tupdesc, values, nulls); } - MemoryContextSwitchTo(oldcontext); -} - - -/* - * Convert options array to name/value table. Useful for information - * schema and pg_dump. - */ -Datum -pg_options_to_table(PG_FUNCTION_ARGS) -{ - Datum array = PG_GETARG_DATUM(0); - - deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, - untransformRelOptions(array)); - return (Datum) 0; } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index bf7ec0d466..1e3650184b 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10174,6 +10174,9 @@ show_all_file_settings(PG_FUNCTION_ARGS) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + /* Scan the config files using current context as workspace */ conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3); @@ -10181,23 +10184,6 @@ show_all_file_settings(PG_FUNCTION_ARGS) per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - /* Build a tuple descriptor for our result type */ - tupdesc = CreateTemplateTupleDesc(NUM_PG_FILE_SETTINGS_ATTS); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "sourcefile", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "sourceline", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "seqno", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "setting", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 6, "applied", - BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 7, "error", - TEXTOID, -1, 0); - /* Build a tuplestore to return our results in */ tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c index 2dc875ebfb..e646a41910 100644 --- a/src/backend/utils/misc/pg_config.c +++ b/src/backend/utils/misc/pg_config.c @@ -26,14 +26,10 @@ pg_config(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Tuplestorestate *tupstore; - HeapTuple tuple; TupleDesc tupdesc; - AttInMetadata *attinmeta; - MemoryContext per_query_ctx; MemoryContext oldcontext; ConfigData *configdata; size_t configdata_len; - char *values[2]; int i = 0; /* check to see if caller supports us returning a tuplestore */ @@ -41,65 +37,38 @@ pg_config(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize) || - rsinfo->expectedDesc == NULL) + if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; - oldcontext = MemoryContextSwitchTo(per_query_ctx); + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); - /* get the requested return tuple description */ - tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); + /* Build tuplestore to hold the result rows */ + oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); - /* - * Check to make sure we have a reasonable tuple descriptor - */ - if (tupdesc->natts != 2 || - TupleDescAttr(tupdesc, 0)->atttypid != TEXTOID || - TupleDescAttr(tupdesc, 1)->atttypid != TEXTOID) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("query-specified return tuple and " - "function return type are not compatible"))); - - /* OK to use it */ - attinmeta = TupleDescGetAttInMetadata(tupdesc); - - /* let the caller know we're sending back a tuplestore */ - rsinfo->returnMode = SFRM_Materialize; - - /* initialize our tuplestore */ tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); configdata = get_configdata(my_exec_path, &configdata_len); for (i = 0; i < configdata_len; i++) { - values[0] = configdata[i].name; - values[1] = configdata[i].setting; + Datum values[2]; + bool nulls[2]; - tuple = BuildTupleFromCStrings(attinmeta, values); - tuplestore_puttuple(tupstore, tuple); + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + values[0] = CStringGetTextDatum(configdata[i].name); + values[1] = CStringGetTextDatum(configdata[i].setting); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); } - /* - * no longer need the tuple descriptor reference created by - * TupleDescGetAttInMetadata() - */ - ReleaseTupleDesc(tupdesc); - - rsinfo->setResult = tupstore; - - /* - * SFRM_Materialize mode expects us to return a NULL Datum. The actual - * tuples are in our tuplestore and passed back through rsinfo->setResult. - * rsinfo->setDesc is set to the tuple description that we actually used - * to build our tuples with, so the caller can verify we did what it was - * expecting. - */ - rsinfo->setDesc = tupdesc; - MemoryContextSwitchTo(oldcontext); - return (Datum) 0; } diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 7885344164..21ad87c024 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -21,6 +21,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" #include "commands/portalcmds.h" +#include "funcapi.h" #include "miscadmin.h" #include "storage/ipc.h" #include "utils/builtins.h" @@ -1152,23 +1153,8 @@ pg_cursor(PG_FUNCTION_ARGS) per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - /* - * build tupdesc for result tuples. This must match the definition of the - * pg_cursors view in system_views.sql - */ - tupdesc = CreateTemplateTupleDesc(6); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable", - BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary", - BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable", - BOOLOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time", - TIMESTAMPTZOID, -1, 0); + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); /* * We put all the tuples into a tuplestore in one scan of the hashtable. @@ -1177,6 +1163,9 @@ pg_cursor(PG_FUNCTION_ARGS) tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; /* generate junk in short-term context */ MemoryContextSwitchTo(oldcontext);