| /*------------------------------------------------------------------------- |
| * |
| * pg_config.c |
| * Expose same output as pg_config except as an SRF |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * IDENTIFICATION |
| * src/backend/utils/misc/pg_config.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "catalog/pg_type.h" |
| #include "common/config_info.h" |
| #include "funcapi.h" |
| #include "miscadmin.h" |
| #include "port.h" |
| #include "utils/builtins.h" |
| |
| Datum |
| 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 */ |
| if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) |
| ereport(ERROR, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| 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); |
| |
| /* get the requested return tuple description */ |
| tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); |
| |
| /* |
| * 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); |
| |
| 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; |
| |
| tuple = BuildTupleFromCStrings(attinmeta, values); |
| tuplestore_puttuple(tupstore, tuple); |
| } |
| |
| /* |
| * no longer need the tuple descriptor reference created by |
| * TupleDescGetAttInMetadata() |
| */ |
| ReleaseTupleDesc(tupdesc); |
| |
| tuplestore_donestoring(tupstore); |
| 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; |
| } |