1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * pg_config.c |
4 | * Expose same output as pg_config except as an SRF |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * IDENTIFICATION |
10 | * src/backend/utils/misc/pg_config.c |
11 | * |
12 | *------------------------------------------------------------------------- |
13 | */ |
14 | |
15 | #include "postgres.h" |
16 | |
17 | #include "funcapi.h" |
18 | #include "miscadmin.h" |
19 | #include "catalog/pg_type.h" |
20 | #include "common/config_info.h" |
21 | #include "utils/builtins.h" |
22 | #include "port.h" |
23 | |
24 | Datum |
25 | pg_config(PG_FUNCTION_ARGS) |
26 | { |
27 | ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; |
28 | Tuplestorestate *tupstore; |
29 | HeapTuple tuple; |
30 | TupleDesc tupdesc; |
31 | AttInMetadata *attinmeta; |
32 | MemoryContext per_query_ctx; |
33 | MemoryContext oldcontext; |
34 | ConfigData *configdata; |
35 | size_t configdata_len; |
36 | char *values[2]; |
37 | int i = 0; |
38 | |
39 | /* check to see if caller supports us returning a tuplestore */ |
40 | if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) |
41 | ereport(ERROR, |
42 | (errcode(ERRCODE_SYNTAX_ERROR), |
43 | errmsg("materialize mode required, but it is not " |
44 | "allowed in this context" ))); |
45 | |
46 | per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; |
47 | oldcontext = MemoryContextSwitchTo(per_query_ctx); |
48 | |
49 | /* get the requested return tuple description */ |
50 | tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); |
51 | |
52 | /* |
53 | * Check to make sure we have a reasonable tuple descriptor |
54 | */ |
55 | if (tupdesc->natts != 2 || |
56 | TupleDescAttr(tupdesc, 0)->atttypid != TEXTOID || |
57 | TupleDescAttr(tupdesc, 1)->atttypid != TEXTOID) |
58 | ereport(ERROR, |
59 | (errcode(ERRCODE_SYNTAX_ERROR), |
60 | errmsg("query-specified return tuple and " |
61 | "function return type are not compatible" ))); |
62 | |
63 | /* OK to use it */ |
64 | attinmeta = TupleDescGetAttInMetadata(tupdesc); |
65 | |
66 | /* let the caller know we're sending back a tuplestore */ |
67 | rsinfo->returnMode = SFRM_Materialize; |
68 | |
69 | /* initialize our tuplestore */ |
70 | tupstore = tuplestore_begin_heap(true, false, work_mem); |
71 | |
72 | configdata = get_configdata(my_exec_path, &configdata_len); |
73 | for (i = 0; i < configdata_len; i++) |
74 | { |
75 | values[0] = configdata[i].name; |
76 | values[1] = configdata[i].setting; |
77 | |
78 | tuple = BuildTupleFromCStrings(attinmeta, values); |
79 | tuplestore_puttuple(tupstore, tuple); |
80 | } |
81 | |
82 | /* |
83 | * no longer need the tuple descriptor reference created by |
84 | * TupleDescGetAttInMetadata() |
85 | */ |
86 | ReleaseTupleDesc(tupdesc); |
87 | |
88 | tuplestore_donestoring(tupstore); |
89 | rsinfo->setResult = tupstore; |
90 | |
91 | /* |
92 | * SFRM_Materialize mode expects us to return a NULL Datum. The actual |
93 | * tuples are in our tuplestore and passed back through rsinfo->setResult. |
94 | * rsinfo->setDesc is set to the tuple description that we actually used |
95 | * to build our tuples with, so the caller can verify we did what it was |
96 | * expecting. |
97 | */ |
98 | rsinfo->setDesc = tupdesc; |
99 | MemoryContextSwitchTo(oldcontext); |
100 | |
101 | return (Datum) 0; |
102 | } |
103 | |