Age Owner Branch data TLA Line data Source code
1 : : /*--------------------------------------------------------------------
2 : : *
3 : : * guc_funcs.c
4 : : *
5 : : * SQL commands and SQL-accessible functions related to GUC variables.
6 : : *
7 : : *
8 : : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
9 : : * Written by Peter Eisentraut <peter_e@gmx.net>.
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/utils/misc/guc_funcs.c
13 : : *
14 : : *--------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include <sys/stat.h>
19 : : #include <unistd.h>
20 : :
21 : : #include "access/xact.h"
22 : : #include "catalog/objectaccess.h"
23 : : #include "catalog/pg_authid.h"
24 : : #include "catalog/pg_parameter_acl.h"
25 : : #include "catalog/pg_type_d.h"
26 : : #include "funcapi.h"
27 : : #include "guc_internal.h"
28 : : #include "miscadmin.h"
29 : : #include "parser/parse_type.h"
30 : : #include "utils/acl.h"
31 : : #include "utils/builtins.h"
32 : : #include "utils/guc_tables.h"
33 : : #include "utils/snapmgr.h"
34 : : #include "utils/tuplestore.h"
35 : :
36 : : static char *flatten_set_variable_args(const char *name, List *args);
37 : : static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
38 : : static void ShowAllGUCConfig(DestReceiver *dest);
39 : :
40 : :
41 : : /*
42 : : * SET command
43 : : */
44 : : void
1330 tgl@sss.pgh.pa.us 45 :CBC 25997 : ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
46 : : {
47 : 25997 : GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
48 : :
49 : : /*
50 : : * Workers synchronize these parameters at the start of the parallel
51 : : * operation; then, we block SET during the operation.
52 : : */
53 [ - + ]: 25997 : if (IsInParallelMode())
1330 tgl@sss.pgh.pa.us 54 [ # # ]:UBC 0 : ereport(ERROR,
55 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
56 : : errmsg("cannot set parameters during a parallel operation")));
57 : :
1330 tgl@sss.pgh.pa.us 58 [ + + + + :CBC 25997 : switch (stmt->kind)
+ - ]
59 : : {
60 : 22208 : case VAR_SET_VALUE:
61 : : case VAR_SET_CURRENT:
62 [ + + ]: 22208 : if (stmt->is_local)
63 : 995 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
64 [ + + ]: 44412 : (void) set_config_option(stmt->name,
65 : 22208 : ExtractSetVariableArgs(stmt),
66 : 22208 : (superuser() ? PGC_SUSET : PGC_USERSET),
67 : : PGC_S_SESSION,
68 : : action, true, 0, false);
69 : 22065 : break;
70 : 422 : case VAR_SET_MULTI:
71 : :
72 : : /*
73 : : * Special-case SQL syntaxes. The TRANSACTION and SESSION
74 : : * CHARACTERISTICS cases effectively set more than one variable
75 : : * per statement. TRANSACTION SNAPSHOT only takes one argument,
76 : : * but we put it here anyway since it's a special case and not
77 : : * related to any GUC variable.
78 : : */
79 [ + + ]: 422 : if (strcmp(stmt->name, "TRANSACTION") == 0)
80 : : {
81 : : ListCell *head;
82 : :
83 : 385 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
84 : :
85 [ + - + + : 1060 : foreach(head, stmt->args)
+ + ]
86 : : {
87 : 688 : DefElem *item = (DefElem *) lfirst(head);
88 : :
89 [ + + ]: 688 : if (strcmp(item->defname, "transaction_isolation") == 0)
90 : 317 : SetPGVariable("transaction_isolation",
91 : 317 : list_make1(item->arg), stmt->is_local);
92 [ + + ]: 371 : else if (strcmp(item->defname, "transaction_read_only") == 0)
93 : 367 : SetPGVariable("transaction_read_only",
94 : 367 : list_make1(item->arg), stmt->is_local);
95 [ + - ]: 4 : else if (strcmp(item->defname, "transaction_deferrable") == 0)
96 : 4 : SetPGVariable("transaction_deferrable",
97 : 4 : list_make1(item->arg), stmt->is_local);
98 : : else
1330 tgl@sss.pgh.pa.us 99 [ # # ]:UBC 0 : elog(ERROR, "unexpected SET TRANSACTION element: %s",
100 : : item->defname);
101 : : }
102 : : }
1330 tgl@sss.pgh.pa.us 103 [ + + ]:CBC 37 : else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0)
104 : : {
105 : : ListCell *head;
106 : :
107 [ + - + + : 24 : foreach(head, stmt->args)
+ + ]
108 : : {
109 : 13 : DefElem *item = (DefElem *) lfirst(head);
110 : :
111 [ - + ]: 13 : if (strcmp(item->defname, "transaction_isolation") == 0)
1330 tgl@sss.pgh.pa.us 112 :UBC 0 : SetPGVariable("default_transaction_isolation",
113 : 0 : list_make1(item->arg), stmt->is_local);
1330 tgl@sss.pgh.pa.us 114 [ + - ]:CBC 13 : else if (strcmp(item->defname, "transaction_read_only") == 0)
115 : 13 : SetPGVariable("default_transaction_read_only",
116 : 13 : list_make1(item->arg), stmt->is_local);
1330 tgl@sss.pgh.pa.us 117 [ # # ]:UBC 0 : else if (strcmp(item->defname, "transaction_deferrable") == 0)
118 : 0 : SetPGVariable("default_transaction_deferrable",
119 : 0 : list_make1(item->arg), stmt->is_local);
120 : : else
121 [ # # ]: 0 : elog(ERROR, "unexpected SET SESSION element: %s",
122 : : item->defname);
123 : : }
124 : : }
1330 tgl@sss.pgh.pa.us 125 [ + - ]:CBC 26 : else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0)
126 : : {
127 : 26 : A_Const *con = linitial_node(A_Const, stmt->args);
128 : :
129 [ - + ]: 26 : if (stmt->is_local)
1330 tgl@sss.pgh.pa.us 130 [ # # ]:UBC 0 : ereport(ERROR,
131 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
132 : : errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));
133 : :
1330 tgl@sss.pgh.pa.us 134 :CBC 26 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
135 : 26 : ImportSnapshot(strVal(&con->val));
136 : : }
137 : : else
1330 tgl@sss.pgh.pa.us 138 [ # # ]:UBC 0 : elog(ERROR, "unexpected SET MULTI element: %s",
139 : : stmt->name);
1330 tgl@sss.pgh.pa.us 140 :CBC 401 : break;
141 : 108 : case VAR_SET_DEFAULT:
142 [ + + ]: 108 : if (stmt->is_local)
143 : 3 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
144 : : pg_fallthrough;
145 : : case VAR_RESET:
146 [ + + ]: 3360 : (void) set_config_option(stmt->name,
147 : : NULL,
148 : 3360 : (superuser() ? PGC_SUSET : PGC_USERSET),
149 : : PGC_S_SESSION,
150 : : action, true, 0, false);
151 : 3342 : break;
152 : 7 : case VAR_RESET_ALL:
153 : 7 : ResetAllOptions();
154 : 7 : break;
155 : : }
156 : :
157 : : /* Invoke the post-alter hook for setting this GUC variable, by name. */
158 [ + + ]: 25815 : InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
159 : : ACL_SET, stmt->kind, false);
160 : 25814 : }
161 : :
162 : : /*
163 : : * Get the value to assign for a VariableSetStmt, or NULL if it's RESET.
164 : : * The result is palloc'd.
165 : : *
166 : : * This is exported for use by actions such as ALTER ROLE SET.
167 : : */
168 : : char *
169 : 23091 : ExtractSetVariableArgs(VariableSetStmt *stmt)
170 : : {
171 [ + + + ]: 23091 : switch (stmt->kind)
172 : : {
173 : 23068 : case VAR_SET_VALUE:
174 : 23068 : return flatten_set_variable_args(stmt->name, stmt->args);
175 : 2 : case VAR_SET_CURRENT:
1084 akorotkov@postgresql 176 : 2 : return GetConfigOptionByName(stmt->name, NULL, false);
1330 tgl@sss.pgh.pa.us 177 : 21 : default:
178 : 21 : return NULL;
179 : : }
180 : : }
181 : :
182 : : /*
183 : : * flatten_set_variable_args
184 : : * Given a parsenode List as emitted by the grammar for SET,
185 : : * convert to the flat string representation used by GUC.
186 : : *
187 : : * We need to be told the name of the variable the args are for, because
188 : : * the flattening rules vary (ugh).
189 : : *
190 : : * The result is NULL if args is NIL (i.e., SET ... TO DEFAULT), otherwise
191 : : * a palloc'd string.
192 : : */
193 : : static char *
194 : 27943 : flatten_set_variable_args(const char *name, List *args)
195 : : {
196 : : struct config_generic *record;
197 : : int flags;
198 : : StringInfoData buf;
199 : : ListCell *l;
200 : :
201 : : /* Fast path if just DEFAULT */
202 [ + + ]: 27943 : if (args == NIL)
203 : 4 : return NULL;
204 : :
205 : : /*
206 : : * Get flags for the variable; if it's not known, use default flags.
207 : : * (Caller might throw error later, but not our business to do so here.)
208 : : */
209 : 27939 : record = find_option(name, false, true, WARNING);
210 [ + + ]: 27939 : if (record)
211 : 27893 : flags = record->flags;
212 : : else
213 : 46 : flags = 0;
214 : :
215 : : /*
216 : : * Handle special cases for list input.
217 : : */
182 tgl@sss.pgh.pa.us 218 [ + + ]:GNC 27939 : if (flags & GUC_LIST_INPUT)
219 : : {
220 : : /* NULL represents an empty list. */
221 [ + + ]: 1419 : if (list_length(args) == 1)
222 : : {
223 : 1019 : Node *arg = (Node *) linitial(args);
224 : :
225 [ + - ]: 1019 : if (IsA(arg, A_Const) &&
226 [ + + ]: 1019 : ((A_Const *) arg)->isnull)
227 : 9 : return pstrdup("");
228 : : }
229 : : }
230 : : else
231 : : {
232 : : /* Complain if list input and non-list variable. */
233 [ - + ]: 26520 : if (list_length(args) != 1)
182 tgl@sss.pgh.pa.us 234 [ # # ]:UNC 0 : ereport(ERROR,
235 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
236 : : errmsg("SET %s takes only one argument", name)));
237 : : }
238 : :
1330 tgl@sss.pgh.pa.us 239 :CBC 27930 : initStringInfo(&buf);
240 : :
241 : : /*
242 : : * Each list member may be a plain A_Const node, or an A_Const within a
243 : : * TypeCast; the latter case is supported only for ConstInterval arguments
244 : : * (for SET TIME ZONE).
245 : : */
246 [ + - + + : 56303 : foreach(l, args)
+ + ]
247 : : {
248 : 28377 : Node *arg = (Node *) lfirst(l);
249 : : char *val;
250 : 28377 : TypeName *typeName = NULL;
251 : : A_Const *con;
252 : :
253 [ + + ]: 28377 : if (l != list_head(args))
254 : 447 : appendStringInfoString(&buf, ", ");
255 : :
256 [ - + ]: 28377 : if (IsA(arg, TypeCast))
257 : : {
1330 tgl@sss.pgh.pa.us 258 :UBC 0 : TypeCast *tc = (TypeCast *) arg;
259 : :
260 : 0 : arg = tc->arg;
261 : 0 : typeName = tc->typeName;
262 : : }
263 : :
1330 tgl@sss.pgh.pa.us 264 [ - + ]:CBC 28377 : if (!IsA(arg, A_Const))
1330 tgl@sss.pgh.pa.us 265 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
1330 tgl@sss.pgh.pa.us 266 :CBC 28377 : con = (A_Const *) arg;
267 : :
268 : : /* Complain if NULL is used with a non-list variable. */
182 tgl@sss.pgh.pa.us 269 [ + + ]:GNC 28377 : if (con->isnull)
270 [ + - ]: 4 : ereport(ERROR,
271 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
272 : : errmsg("NULL is an invalid value for %s", name)));
273 : :
1330 tgl@sss.pgh.pa.us 274 [ + + + - ]:CBC 28373 : switch (nodeTag(&con->val))
275 : : {
276 : 11022 : case T_Integer:
277 : 11022 : appendStringInfo(&buf, "%d", intVal(&con->val));
278 : 11022 : break;
279 : 136 : case T_Float:
280 : : /* represented as a string, so just copy it */
281 : 136 : appendStringInfoString(&buf, castNode(Float, &con->val)->fval);
282 : 136 : break;
283 : 17215 : case T_String:
284 : 17215 : val = strVal(&con->val);
285 [ - + ]: 17215 : if (typeName != NULL)
286 : : {
287 : : /*
288 : : * Must be a ConstInterval argument for TIME ZONE. Coerce
289 : : * to interval and back to normalize the value and account
290 : : * for any typmod.
291 : : */
292 : : Oid typoid;
293 : : int32 typmod;
294 : : Datum interval;
295 : : char *intervalout;
296 : :
297 : : /* gram.y ensures this is only reachable for TIME ZONE */
182 tgl@sss.pgh.pa.us 298 [ # # ]:UNC 0 : Assert(!(flags & GUC_LIST_QUOTE));
299 : :
1330 tgl@sss.pgh.pa.us 300 :UBC 0 : typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
301 [ # # ]: 0 : Assert(typoid == INTERVALOID);
302 : :
303 : : interval =
304 : 0 : DirectFunctionCall3(interval_in,
305 : : CStringGetDatum(val),
306 : : ObjectIdGetDatum(InvalidOid),
307 : : Int32GetDatum(typmod));
308 : :
309 : : intervalout =
310 : 0 : DatumGetCString(DirectFunctionCall1(interval_out,
311 : : interval));
312 : 0 : appendStringInfo(&buf, "INTERVAL '%s'", intervalout);
313 : : }
314 : : else
315 : : {
316 : : /*
317 : : * Plain string literal or identifier. For quote mode,
318 : : * quote it if it's not a vanilla identifier.
319 : : */
1330 tgl@sss.pgh.pa.us 320 [ + + ]:CBC 17215 : if (flags & GUC_LIST_QUOTE)
321 : 691 : appendStringInfoString(&buf, quote_identifier(val));
322 : : else
323 : 16524 : appendStringInfoString(&buf, val);
324 : : }
325 : 17215 : break;
1330 tgl@sss.pgh.pa.us 326 :UBC 0 : default:
327 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
328 : : (int) nodeTag(&con->val));
329 : : break;
330 : : }
331 : : }
332 : :
1330 tgl@sss.pgh.pa.us 333 :CBC 27926 : return buf.data;
334 : : }
335 : :
336 : : /*
337 : : * SetPGVariable - SET command exported as an easily-C-callable function.
338 : : *
339 : : * This provides access to SET TO value, as well as SET TO DEFAULT (expressed
340 : : * by passing args == NIL), but not SET FROM CURRENT functionality.
341 : : */
342 : : void
343 : 4875 : SetPGVariable(const char *name, List *args, bool is_local)
344 : : {
345 : 4875 : char *argstring = flatten_set_variable_args(name, args);
346 : :
347 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
348 [ + + ]: 4875 : (void) set_config_option(name,
349 : : argstring,
350 : 4875 : (superuser() ? PGC_SUSET : PGC_USERSET),
351 : : PGC_S_SESSION,
352 : : is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
353 : : true, 0, false);
354 : 4862 : }
355 : :
356 : : /*
357 : : * SET command wrapped as a SQL callable function.
358 : : */
359 : : Datum
360 : 4224 : set_config_by_name(PG_FUNCTION_ARGS)
361 : : {
362 : : char *name;
363 : : char *value;
364 : : char *new_value;
365 : : bool is_local;
366 : :
367 [ - + ]: 4224 : if (PG_ARGISNULL(0))
1330 tgl@sss.pgh.pa.us 368 [ # # ]:UBC 0 : ereport(ERROR,
369 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
370 : : errmsg("SET requires parameter name")));
371 : :
372 : : /* Get the GUC variable name */
1330 tgl@sss.pgh.pa.us 373 :CBC 4224 : name = TextDatumGetCString(PG_GETARG_DATUM(0));
374 : :
375 : : /* Get the desired value or set to NULL for a reset request */
376 [ - + ]: 4224 : if (PG_ARGISNULL(1))
1330 tgl@sss.pgh.pa.us 377 :UBC 0 : value = NULL;
378 : : else
1330 tgl@sss.pgh.pa.us 379 :CBC 4224 : value = TextDatumGetCString(PG_GETARG_DATUM(1));
380 : :
381 : : /*
382 : : * Get the desired state of is_local. Default to false if provided value
383 : : * is NULL
384 : : */
385 [ - + ]: 4224 : if (PG_ARGISNULL(2))
1330 tgl@sss.pgh.pa.us 386 :UBC 0 : is_local = false;
387 : : else
1330 tgl@sss.pgh.pa.us 388 :CBC 4224 : is_local = PG_GETARG_BOOL(2);
389 : :
390 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
391 [ + + ]: 4224 : (void) set_config_option(name,
392 : : value,
393 : 4224 : (superuser() ? PGC_SUSET : PGC_USERSET),
394 : : PGC_S_SESSION,
395 : : is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
396 : : true, 0, false);
397 : :
398 : : /* get the new current value */
399 : 4223 : new_value = GetConfigOptionByName(name, NULL, false);
400 : :
401 : : /* Convert return string to text */
402 : 4223 : PG_RETURN_TEXT_P(cstring_to_text(new_value));
403 : : }
404 : :
405 : :
406 : : /*
407 : : * SHOW command
408 : : */
409 : : void
410 : 1192 : GetPGVariable(const char *name, DestReceiver *dest)
411 : : {
412 [ + + ]: 1192 : if (guc_name_compare(name, "all") == 0)
413 : 2 : ShowAllGUCConfig(dest);
414 : : else
415 : 1190 : ShowGUCConfigOption(name, dest);
416 : 1192 : }
417 : :
418 : : /*
419 : : * Get a tuple descriptor for SHOW's result
420 : : */
421 : : TupleDesc
422 : 584 : GetPGVariableResultDesc(const char *name)
423 : : {
424 : : TupleDesc tupdesc;
425 : :
426 [ - + ]: 584 : if (guc_name_compare(name, "all") == 0)
427 : : {
428 : : /* need a tuple descriptor representing three TEXT columns */
1330 tgl@sss.pgh.pa.us 429 :UBC 0 : tupdesc = CreateTemplateTupleDesc(3);
430 : 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
431 : : TEXTOID, -1, 0);
432 : 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
433 : : TEXTOID, -1, 0);
434 : 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description",
435 : : TEXTOID, -1, 0);
436 : : }
437 : : else
438 : : {
439 : : const char *varname;
440 : :
441 : : /* Get the canonical spelling of name */
1330 tgl@sss.pgh.pa.us 442 :CBC 584 : (void) GetConfigOptionByName(name, &varname, false);
443 : :
444 : : /* need a tuple descriptor representing a single TEXT column */
445 : 566 : tupdesc = CreateTemplateTupleDesc(1);
446 : 566 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
447 : : TEXTOID, -1, 0);
448 : : }
50 drowley@postgresql.o 449 :GNC 566 : TupleDescFinalize(tupdesc);
1330 tgl@sss.pgh.pa.us 450 :CBC 566 : return tupdesc;
451 : : }
452 : :
453 : : /*
454 : : * SHOW one variable
455 : : */
456 : : static void
457 : 1190 : ShowGUCConfigOption(const char *name, DestReceiver *dest)
458 : : {
459 : : TupOutputState *tstate;
460 : : TupleDesc tupdesc;
461 : : const char *varname;
462 : : char *value;
463 : :
464 : : /* Get the value and canonical spelling of name */
465 : 1190 : value = GetConfigOptionByName(name, &varname, false);
466 : :
467 : : /* need a tuple descriptor representing a single TEXT column */
468 : 1190 : tupdesc = CreateTemplateTupleDesc(1);
469 : 1190 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname,
470 : : TEXTOID, -1, 0);
50 drowley@postgresql.o 471 :GNC 1190 : TupleDescFinalize(tupdesc);
472 : :
473 : : /* prepare for projection of tuples */
1330 tgl@sss.pgh.pa.us 474 :CBC 1190 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
475 : :
476 : : /* Send it */
477 : 1190 : do_text_output_oneline(tstate, value);
478 : :
479 : 1190 : end_tup_output(tstate);
480 : 1190 : }
481 : :
482 : : /*
483 : : * SHOW ALL command
484 : : */
485 : : static void
486 : 2 : ShowAllGUCConfig(DestReceiver *dest)
487 : : {
488 : : struct config_generic **guc_vars;
489 : : int num_vars;
490 : : TupOutputState *tstate;
491 : : TupleDesc tupdesc;
492 : : Datum values[3];
493 : 2 : bool isnull[3] = {false, false, false};
494 : :
495 : : /* collect the variables, in sorted order */
1299 496 : 2 : guc_vars = get_guc_variables(&num_vars);
497 : :
498 : : /* need a tuple descriptor representing three TEXT columns */
1330 499 : 2 : tupdesc = CreateTemplateTupleDesc(3);
500 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, "name",
501 : : TEXTOID, -1, 0);
502 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 2, "setting",
503 : : TEXTOID, -1, 0);
504 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 3, "description",
505 : : TEXTOID, -1, 0);
50 drowley@postgresql.o 506 :GNC 2 : TupleDescFinalize(tupdesc);
507 : :
508 : : /* prepare for projection of tuples */
1330 tgl@sss.pgh.pa.us 509 :CBC 2 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
510 : :
1299 511 [ + + ]: 861 : for (int i = 0; i < num_vars; i++)
512 : : {
513 : 859 : struct config_generic *conf = guc_vars[i];
514 : : char *setting;
515 : :
516 : : /* skip if marked NO_SHOW_ALL */
1194 517 [ + + ]: 859 : if (conf->flags & GUC_NO_SHOW_ALL)
518 : 12 : continue;
519 : :
520 : : /* return only options visible to the current user */
521 [ - + ]: 847 : if (!ConfigOptionIsVisible(conf))
1330 tgl@sss.pgh.pa.us 522 :UBC 0 : continue;
523 : :
524 : : /* assign to the values array */
1330 tgl@sss.pgh.pa.us 525 :CBC 847 : values[0] = PointerGetDatum(cstring_to_text(conf->name));
526 : :
527 : 847 : setting = ShowGUCOption(conf, true);
528 [ + - ]: 847 : if (setting)
529 : : {
530 : 847 : values[1] = PointerGetDatum(cstring_to_text(setting));
531 : 847 : isnull[1] = false;
532 : : }
533 : : else
534 : : {
1330 tgl@sss.pgh.pa.us 535 :UBC 0 : values[1] = PointerGetDatum(NULL);
536 : 0 : isnull[1] = true;
537 : : }
538 : :
1330 tgl@sss.pgh.pa.us 539 [ + - ]:CBC 847 : if (conf->short_desc)
540 : : {
541 : 847 : values[2] = PointerGetDatum(cstring_to_text(conf->short_desc));
542 : 847 : isnull[2] = false;
543 : : }
544 : : else
545 : : {
1330 tgl@sss.pgh.pa.us 546 :UBC 0 : values[2] = PointerGetDatum(NULL);
547 : 0 : isnull[2] = true;
548 : : }
549 : :
550 : : /* send it to dest */
1330 tgl@sss.pgh.pa.us 551 :CBC 847 : do_tup_output(tstate, values, isnull);
552 : :
553 : : /* clean up */
554 : 847 : pfree(DatumGetPointer(values[0]));
555 [ + - ]: 847 : if (setting)
556 : : {
557 : 847 : pfree(setting);
558 : 847 : pfree(DatumGetPointer(values[1]));
559 : : }
560 [ + - ]: 847 : if (conf->short_desc)
561 : 847 : pfree(DatumGetPointer(values[2]));
562 : : }
563 : :
564 : 2 : end_tup_output(tstate);
565 : 2 : }
566 : :
567 : : /*
568 : : * Return some of the flags associated to the specified GUC in the shape of
569 : : * a text array, and NULL if it does not exist. An empty array is returned
570 : : * if the GUC exists without any meaningful flags to show.
571 : : */
572 : : Datum
573 : 2559 : pg_settings_get_flags(PG_FUNCTION_ARGS)
574 : : {
575 : : #define MAX_GUC_FLAGS 6
576 : 2559 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
577 : : struct config_generic *record;
578 : 2559 : int cnt = 0;
579 : : Datum flags[MAX_GUC_FLAGS];
580 : : ArrayType *a;
581 : :
582 : 2559 : record = find_option(varname, false, true, ERROR);
583 : :
584 : : /* return NULL if no such variable */
585 [ + + ]: 2559 : if (record == NULL)
586 : 4 : PG_RETURN_NULL();
587 : :
588 [ + + ]: 2555 : if (record->flags & GUC_EXPLAIN)
589 : 378 : flags[cnt++] = CStringGetTextDatum("EXPLAIN");
1316 590 [ + + ]: 2555 : if (record->flags & GUC_NO_RESET)
591 : 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET");
1330 592 [ + + ]: 2555 : if (record->flags & GUC_NO_RESET_ALL)
593 : 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
594 [ - + ]: 2555 : if (record->flags & GUC_NO_SHOW_ALL)
1330 tgl@sss.pgh.pa.us 595 :UBC 0 : flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
1330 tgl@sss.pgh.pa.us 596 [ + + ]:CBC 2555 : if (record->flags & GUC_NOT_IN_SAMPLE)
597 : 336 : flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
598 [ + + ]: 2555 : if (record->flags & GUC_RUNTIME_COMPUTED)
599 : 36 : flags[cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED");
600 : :
601 [ - + ]: 2555 : Assert(cnt <= MAX_GUC_FLAGS);
602 : :
603 : : /* Returns the record as Datum */
604 : 2555 : a = construct_array_builtin(flags, cnt, TEXTOID);
605 : 2555 : PG_RETURN_ARRAYTYPE_P(a);
606 : : }
607 : :
608 : : /*
609 : : * Return whether or not the GUC variable is visible to the current user.
610 : : */
611 : : bool
214 peter@eisentraut.org 612 :GNC 801937 : ConfigOptionIsVisible(const struct config_generic *conf)
613 : : {
1194 tgl@sss.pgh.pa.us 614 [ + + ]:CBC 801937 : if ((conf->flags & GUC_SUPERUSER_ONLY) &&
615 [ + + ]: 46759 : !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
616 : 151 : return false;
617 : : else
618 : 801786 : return true;
619 : : }
620 : :
621 : : /*
622 : : * Extract fields to show in pg_settings for given variable.
623 : : */
624 : : static void
214 peter@eisentraut.org 625 :GNC 793758 : GetConfigOptionValues(const struct config_generic *conf, const char **values)
626 : : {
627 : : char buffer[256];
628 : :
629 : : /* first get the generic attributes */
630 : :
631 : : /* name */
1330 tgl@sss.pgh.pa.us 632 :CBC 793758 : values[0] = conf->name;
633 : :
634 : : /* setting: use ShowGUCOption in order to avoid duplicating the logic */
635 : 793758 : values[1] = ShowGUCOption(conf, false);
636 : :
637 : : /* unit, if any (NULL is fine) */
638 : 793758 : values[2] = get_config_unit_name(conf->flags);
639 : :
640 : : /* group */
641 : 793758 : values[3] = _(config_group_names[conf->group]);
642 : :
643 : : /* short_desc */
644 [ + - ]: 793758 : values[4] = conf->short_desc != NULL ? _(conf->short_desc) : NULL;
645 : :
646 : : /* extra_desc */
647 [ + + ]: 793758 : values[5] = conf->long_desc != NULL ? _(conf->long_desc) : NULL;
648 : :
649 : : /* context */
650 : 793758 : values[6] = GucContext_Names[conf->context];
651 : :
652 : : /* vartype */
653 : 793758 : values[7] = config_type_names[conf->vartype];
654 : :
655 : : /* source */
656 : 793758 : values[8] = GucSource_Names[conf->source];
657 : :
658 : : /* now get the type specific attributes */
659 [ + + + + : 793758 : switch (conf->vartype)
+ - ]
660 : : {
661 : 223442 : case PGC_BOOL:
662 : : {
214 peter@eisentraut.org 663 :GNC 223442 : const struct config_bool *lconf = &conf->_bool;
664 : :
665 : : /* min_val */
1330 tgl@sss.pgh.pa.us 666 :CBC 223442 : values[9] = NULL;
667 : :
668 : : /* max_val */
669 : 223442 : values[10] = NULL;
670 : :
671 : : /* enumvals */
672 : 223442 : values[11] = NULL;
673 : :
674 : : /* boot_val */
675 [ + + ]: 223442 : values[12] = pstrdup(lconf->boot_val ? "on" : "off");
676 : :
677 : : /* reset_val */
678 [ + + ]: 223442 : values[13] = pstrdup(lconf->reset_val ? "on" : "off");
679 : : }
680 : 223442 : break;
681 : :
682 : 287688 : case PGC_INT:
683 : : {
214 peter@eisentraut.org 684 :GNC 287688 : const struct config_int *lconf = &conf->_int;
685 : :
686 : : /* min_val */
1330 tgl@sss.pgh.pa.us 687 :CBC 287688 : snprintf(buffer, sizeof(buffer), "%d", lconf->min);
688 : 287688 : values[9] = pstrdup(buffer);
689 : :
690 : : /* max_val */
691 : 287688 : snprintf(buffer, sizeof(buffer), "%d", lconf->max);
692 : 287688 : values[10] = pstrdup(buffer);
693 : :
694 : : /* enumvals */
695 : 287688 : values[11] = NULL;
696 : :
697 : : /* boot_val */
698 : 287688 : snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val);
699 : 287688 : values[12] = pstrdup(buffer);
700 : :
701 : : /* reset_val */
702 : 287688 : snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val);
703 : 287688 : values[13] = pstrdup(buffer);
704 : : }
705 : 287688 : break;
706 : :
707 : 57908 : case PGC_REAL:
708 : : {
214 peter@eisentraut.org 709 :GNC 57908 : const struct config_real *lconf = &conf->_real;
710 : :
711 : : /* min_val */
1330 tgl@sss.pgh.pa.us 712 :CBC 57908 : snprintf(buffer, sizeof(buffer), "%g", lconf->min);
713 : 57908 : values[9] = pstrdup(buffer);
714 : :
715 : : /* max_val */
716 : 57908 : snprintf(buffer, sizeof(buffer), "%g", lconf->max);
717 : 57908 : values[10] = pstrdup(buffer);
718 : :
719 : : /* enumvals */
720 : 57908 : values[11] = NULL;
721 : :
722 : : /* boot_val */
723 : 57908 : snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val);
724 : 57908 : values[12] = pstrdup(buffer);
725 : :
726 : : /* reset_val */
727 : 57908 : snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val);
728 : 57908 : values[13] = pstrdup(buffer);
729 : : }
730 : 57908 : break;
731 : :
732 : 142920 : case PGC_STRING:
733 : : {
214 peter@eisentraut.org 734 :GNC 142920 : const struct config_string *lconf = &conf->_string;
735 : :
736 : : /* min_val */
1330 tgl@sss.pgh.pa.us 737 :CBC 142920 : values[9] = NULL;
738 : :
739 : : /* max_val */
740 : 142920 : values[10] = NULL;
741 : :
742 : : /* enumvals */
743 : 142920 : values[11] = NULL;
744 : :
745 : : /* boot_val */
746 [ + + ]: 142920 : if (lconf->boot_val == NULL)
747 : 13048 : values[12] = NULL;
748 : : else
749 : 129872 : values[12] = pstrdup(lconf->boot_val);
750 : :
751 : : /* reset_val */
752 [ + + ]: 142920 : if (lconf->reset_val == NULL)
753 : 1870 : values[13] = NULL;
754 : : else
755 : 141050 : values[13] = pstrdup(lconf->reset_val);
756 : : }
757 : 142920 : break;
758 : :
759 : 81800 : case PGC_ENUM:
760 : : {
214 peter@eisentraut.org 761 :GNC 81800 : const struct config_enum *lconf = &conf->_enum;
762 : :
763 : : /* min_val */
1330 tgl@sss.pgh.pa.us 764 :CBC 81800 : values[9] = NULL;
765 : :
766 : : /* max_val */
767 : 81800 : values[10] = NULL;
768 : :
769 : : /* enumvals */
770 : :
771 : : /*
772 : : * NOTE! enumvals with double quotes in them are not
773 : : * supported!
774 : : */
214 peter@eisentraut.org 775 :GNC 81800 : values[11] = config_enum_get_options(lconf,
776 : : "{\"", "\"}", "\",\"");
777 : :
778 : : /* boot_val */
779 : 81800 : values[12] = pstrdup(config_enum_lookup_by_value(conf,
1330 tgl@sss.pgh.pa.us 780 :GIC 81800 : lconf->boot_val));
781 : :
782 : : /* reset_val */
214 peter@eisentraut.org 783 :GNC 81800 : values[13] = pstrdup(config_enum_lookup_by_value(conf,
1330 tgl@sss.pgh.pa.us 784 :GIC 81800 : lconf->reset_val));
785 : : }
1330 tgl@sss.pgh.pa.us 786 :CBC 81800 : break;
787 : :
1330 tgl@sss.pgh.pa.us 788 :UBC 0 : default:
789 : : {
790 : : /*
791 : : * should never get here, but in case we do, set 'em to NULL
792 : : */
793 : :
794 : : /* min_val */
795 : 0 : values[9] = NULL;
796 : :
797 : : /* max_val */
798 : 0 : values[10] = NULL;
799 : :
800 : : /* enumvals */
801 : 0 : values[11] = NULL;
802 : :
803 : : /* boot_val */
804 : 0 : values[12] = NULL;
805 : :
806 : : /* reset_val */
807 : 0 : values[13] = NULL;
808 : : }
809 : 0 : break;
810 : : }
811 : :
812 : : /*
813 : : * If the setting came from a config file, set the source location. For
814 : : * security reasons, we don't show source file/line number for
815 : : * insufficiently-privileged users.
816 : : */
1330 tgl@sss.pgh.pa.us 817 [ + + + + ]:CBC 847448 : if (conf->source == PGC_S_FILE &&
818 : 53690 : has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
819 : : {
820 : 53552 : values[14] = conf->sourcefile;
821 : 53552 : snprintf(buffer, sizeof(buffer), "%d", conf->sourceline);
822 : 53552 : values[15] = pstrdup(buffer);
823 : : }
824 : : else
825 : : {
826 : 740206 : values[14] = NULL;
827 : 740206 : values[15] = NULL;
828 : : }
829 : :
830 [ - + ]: 793758 : values[16] = (conf->status & GUC_PENDING_RESTART) ? "t" : "f";
831 : 793758 : }
832 : :
833 : : /*
834 : : * show_config_by_name - equiv to SHOW X command but implemented as
835 : : * a function.
836 : : */
837 : : Datum
838 : 1172 : show_config_by_name(PG_FUNCTION_ARGS)
839 : : {
840 : 1172 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
841 : : char *varval;
842 : :
843 : : /* Get the value */
844 : 1172 : varval = GetConfigOptionByName(varname, NULL, false);
845 : :
846 : : /* Convert to text */
847 : 1168 : PG_RETURN_TEXT_P(cstring_to_text(varval));
848 : : }
849 : :
850 : : /*
851 : : * show_config_by_name_missing_ok - equiv to SHOW X command but implemented as
852 : : * a function. If X does not exist, suppress the error and just return NULL
853 : : * if missing_ok is true.
854 : : */
855 : : Datum
856 : 16 : show_config_by_name_missing_ok(PG_FUNCTION_ARGS)
857 : : {
858 : 16 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
859 : 16 : bool missing_ok = PG_GETARG_BOOL(1);
860 : : char *varval;
861 : :
862 : : /* Get the value */
863 : 16 : varval = GetConfigOptionByName(varname, NULL, missing_ok);
864 : :
865 : : /* return NULL if no such variable */
866 [ + + ]: 12 : if (varval == NULL)
867 : 4 : PG_RETURN_NULL();
868 : :
869 : : /* Convert to text */
870 : 8 : PG_RETURN_TEXT_P(cstring_to_text(varval));
871 : : }
872 : :
873 : : /*
874 : : * show_all_settings - equiv to SHOW ALL command but implemented as
875 : : * a Table Function.
876 : : */
877 : : #define NUM_PG_SETTINGS_ATTS 17
878 : :
879 : : Datum
880 : 795626 : show_all_settings(PG_FUNCTION_ARGS)
881 : : {
882 : : FuncCallContext *funcctx;
883 : : struct config_generic **guc_vars;
884 : : int num_vars;
885 : : TupleDesc tupdesc;
886 : : int call_cntr;
887 : : int max_calls;
888 : : AttInMetadata *attinmeta;
889 : : MemoryContext oldcontext;
890 : :
891 : : /* stuff done only on the first call of the function */
892 [ + + ]: 795626 : if (SRF_IS_FIRSTCALL())
893 : : {
894 : : /* create a function context for cross-call persistence */
895 : 1868 : funcctx = SRF_FIRSTCALL_INIT();
896 : :
897 : : /*
898 : : * switch to memory context appropriate for multiple function calls
899 : : */
900 : 1868 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
901 : :
902 : : /*
903 : : * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns
904 : : * of the appropriate types
905 : : */
906 : 1868 : tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS);
907 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
908 : : TEXTOID, -1, 0);
909 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
910 : : TEXTOID, -1, 0);
911 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "unit",
912 : : TEXTOID, -1, 0);
913 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "category",
914 : : TEXTOID, -1, 0);
915 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "short_desc",
916 : : TEXTOID, -1, 0);
917 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "extra_desc",
918 : : TEXTOID, -1, 0);
919 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "context",
920 : : TEXTOID, -1, 0);
921 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "vartype",
922 : : TEXTOID, -1, 0);
923 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "source",
924 : : TEXTOID, -1, 0);
925 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_val",
926 : : TEXTOID, -1, 0);
927 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val",
928 : : TEXTOID, -1, 0);
929 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals",
930 : : TEXTARRAYOID, -1, 0);
931 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val",
932 : : TEXTOID, -1, 0);
933 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val",
934 : : TEXTOID, -1, 0);
935 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile",
936 : : TEXTOID, -1, 0);
937 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline",
938 : : INT4OID, -1, 0);
939 : 1868 : TupleDescInitEntry(tupdesc, (AttrNumber) 17, "pending_restart",
940 : : BOOLOID, -1, 0);
941 : :
50 drowley@postgresql.o 942 :GNC 1868 : TupleDescFinalize(tupdesc);
943 : :
944 : : /*
945 : : * Generate attribute metadata needed later to produce tuples from raw
946 : : * C strings
947 : : */
1330 tgl@sss.pgh.pa.us 948 :CBC 1868 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
949 : 1868 : funcctx->attinmeta = attinmeta;
950 : :
951 : : /* collect the variables, in sorted order */
1299 952 : 1868 : guc_vars = get_guc_variables(&num_vars);
953 : :
954 : : /* use user_fctx to remember the array location */
955 : 1868 : funcctx->user_fctx = guc_vars;
956 : :
957 : : /* total number of tuples to be returned */
958 : 1868 : funcctx->max_calls = num_vars;
959 : :
1330 960 : 1868 : MemoryContextSwitchTo(oldcontext);
961 : : }
962 : :
963 : : /* stuff done on every call of the function */
964 : 795626 : funcctx = SRF_PERCALL_SETUP();
965 : :
1299 966 : 795626 : guc_vars = (struct config_generic **) funcctx->user_fctx;
1330 967 : 795626 : call_cntr = funcctx->call_cntr;
968 : 795626 : max_calls = funcctx->max_calls;
969 : 795626 : attinmeta = funcctx->attinmeta;
970 : :
1194 971 [ + + ]: 806996 : while (call_cntr < max_calls) /* do when there is more left to send */
972 : : {
973 : 805128 : struct config_generic *conf = guc_vars[call_cntr];
974 : : char *values[NUM_PG_SETTINGS_ATTS];
975 : : HeapTuple tuple;
976 : : Datum result;
977 : :
978 : : /* skip if marked NO_SHOW_ALL or if not visible to current user */
979 [ + + ]: 805128 : if ((conf->flags & GUC_NO_SHOW_ALL) ||
980 [ + + ]: 793908 : !ConfigOptionIsVisible(conf))
981 : : {
982 : 11370 : call_cntr = ++funcctx->call_cntr;
983 : 11370 : continue;
984 : : }
985 : :
986 : : /* extract values for the current variable */
987 : 793758 : GetConfigOptionValues(conf, (const char **) values);
988 : :
989 : : /* build a tuple */
1330 990 : 793758 : tuple = BuildTupleFromCStrings(attinmeta, values);
991 : :
992 : : /* make the tuple into a datum */
993 : 793758 : result = HeapTupleGetDatum(tuple);
994 : :
995 : 793758 : SRF_RETURN_NEXT(funcctx, result);
996 : : }
997 : :
998 : : /* do when there is no more left */
1194 999 : 1868 : SRF_RETURN_DONE(funcctx);
1000 : : }
1001 : :
1002 : : /*
1003 : : * show_all_file_settings
1004 : : *
1005 : : * Returns a table of all parameter settings in all configuration files
1006 : : * which includes the config file pathname, the line number, a sequence number
1007 : : * indicating the order in which the settings were encountered, the parameter
1008 : : * name and value, a bool showing if the value could be applied, and possibly
1009 : : * an associated error message. (For problems such as syntax errors, the
1010 : : * parameter name/value might be NULL.)
1011 : : *
1012 : : * Note: no filtering is done here, instead we depend on the GRANT system
1013 : : * to prevent unprivileged users from accessing this function or the view
1014 : : * built on top of it.
1015 : : */
1016 : : Datum
1330 1017 : 4 : show_all_file_settings(PG_FUNCTION_ARGS)
1018 : : {
1019 : : #define NUM_PG_FILE_SETTINGS_ATTS 7
1020 : 4 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1021 : : ConfigVariable *conf;
1022 : :
1023 : : /* Scan the config files using current context as workspace */
1024 : 4 : conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
1025 : :
1026 : : /* Build a tuplestore to return our results in */
1295 michael@paquier.xyz 1027 : 4 : InitMaterializedSRF(fcinfo, 0);
1028 : :
1029 : : /* Process the results and create a tuplestore */
214 peter@eisentraut.org 1030 [ + + ]:GNC 120 : for (int seqno = 1; conf != NULL; conf = conf->next, seqno++)
1031 : : {
1032 : : Datum values[NUM_PG_FILE_SETTINGS_ATTS];
1033 : : bool nulls[NUM_PG_FILE_SETTINGS_ATTS];
1034 : :
1330 tgl@sss.pgh.pa.us 1035 :CBC 116 : memset(values, 0, sizeof(values));
1036 : 116 : memset(nulls, 0, sizeof(nulls));
1037 : :
1038 : : /* sourcefile */
1039 [ + - ]: 116 : if (conf->filename)
1040 : 116 : values[0] = PointerGetDatum(cstring_to_text(conf->filename));
1041 : : else
1330 tgl@sss.pgh.pa.us 1042 :UBC 0 : nulls[0] = true;
1043 : :
1044 : : /* sourceline (not meaningful if no sourcefile) */
1330 tgl@sss.pgh.pa.us 1045 [ + - ]:CBC 116 : if (conf->filename)
1046 : 116 : values[1] = Int32GetDatum(conf->sourceline);
1047 : : else
1330 tgl@sss.pgh.pa.us 1048 :UBC 0 : nulls[1] = true;
1049 : :
1050 : : /* seqno */
1330 tgl@sss.pgh.pa.us 1051 :CBC 116 : values[2] = Int32GetDatum(seqno);
1052 : :
1053 : : /* name */
1054 [ + - ]: 116 : if (conf->name)
1055 : 116 : values[3] = PointerGetDatum(cstring_to_text(conf->name));
1056 : : else
1330 tgl@sss.pgh.pa.us 1057 :UBC 0 : nulls[3] = true;
1058 : :
1059 : : /* setting */
1330 tgl@sss.pgh.pa.us 1060 [ + - ]:CBC 116 : if (conf->value)
1061 : 116 : values[4] = PointerGetDatum(cstring_to_text(conf->value));
1062 : : else
1330 tgl@sss.pgh.pa.us 1063 :UBC 0 : nulls[4] = true;
1064 : :
1065 : : /* applied */
1330 tgl@sss.pgh.pa.us 1066 :CBC 116 : values[5] = BoolGetDatum(conf->applied);
1067 : :
1068 : : /* error */
1069 [ - + ]: 116 : if (conf->errmsg)
1330 tgl@sss.pgh.pa.us 1070 :UBC 0 : values[6] = PointerGetDatum(cstring_to_text(conf->errmsg));
1071 : : else
1330 tgl@sss.pgh.pa.us 1072 :CBC 116 : nulls[6] = true;
1073 : :
1074 : : /* shove row into tuplestore */
1075 : 116 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1076 : : }
1077 : :
1078 : 4 : return (Datum) 0;
1079 : : }
|