Branch data 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
45 : 24932 : ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
46 : : {
47 : 24932 : 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 [ - + ]: 24932 : if (IsInParallelMode())
54 [ # # ]: 0 : ereport(ERROR,
55 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
56 : : errmsg("cannot set parameters during a parallel operation")));
57 : :
58 [ + + + + : 24932 : switch (stmt->kind)
+ - ]
59 : : {
60 : 21189 : case VAR_SET_VALUE:
61 : : case VAR_SET_CURRENT:
62 [ + + ]: 21189 : if (stmt->is_local)
63 : 1061 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
64 [ + + ]: 42374 : (void) set_config_option(stmt->name,
65 : 21189 : ExtractSetVariableArgs(stmt),
66 : 21189 : (superuser() ? PGC_SUSET : PGC_USERSET),
67 : : PGC_S_SESSION,
68 : : action, true, 0, false);
69 : 21027 : break;
70 : 354 : 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 [ + + ]: 354 : if (strcmp(stmt->name, "TRANSACTION") == 0)
80 : : {
81 : : ListCell *head;
82 : :
83 : 317 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
84 : :
85 [ + - + + : 855 : foreach(head, stmt->args)
+ + ]
86 : : {
87 : 551 : DefElem *item = (DefElem *) lfirst(head);
88 : :
89 [ + + ]: 551 : if (strcmp(item->defname, "transaction_isolation") == 0)
90 : 249 : SetPGVariable("transaction_isolation",
91 : 249 : list_make1(item->arg), stmt->is_local);
92 [ + + ]: 302 : else if (strcmp(item->defname, "transaction_read_only") == 0)
93 : 298 : SetPGVariable("transaction_read_only",
94 : 298 : 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
99 [ # # ]: 0 : elog(ERROR, "unexpected SET TRANSACTION element: %s",
100 : : item->defname);
101 : : }
102 : : }
103 [ + + ]: 37 : else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0)
104 : : {
105 : : ListCell *head;
106 : :
107 [ + - + + : 28 : foreach(head, stmt->args)
+ + ]
108 : : {
109 : 16 : DefElem *item = (DefElem *) lfirst(head);
110 : :
111 [ + + ]: 16 : if (strcmp(item->defname, "transaction_isolation") == 0)
112 : 1 : SetPGVariable("default_transaction_isolation",
113 : 1 : list_make1(item->arg), stmt->is_local);
114 [ + + ]: 15 : else if (strcmp(item->defname, "transaction_read_only") == 0)
115 : 14 : SetPGVariable("default_transaction_read_only",
116 : 14 : list_make1(item->arg), stmt->is_local);
117 [ + - ]: 1 : else if (strcmp(item->defname, "transaction_deferrable") == 0)
118 : 1 : SetPGVariable("default_transaction_deferrable",
119 : 1 : list_make1(item->arg), stmt->is_local);
120 : : else
121 [ # # ]: 0 : elog(ERROR, "unexpected SET SESSION element: %s",
122 : : item->defname);
123 : : }
124 : : }
125 [ + - ]: 25 : else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0)
126 : : {
127 : 25 : A_Const *con = linitial_node(A_Const, stmt->args);
128 : :
129 [ - + ]: 25 : if (stmt->is_local)
130 [ # # ]: 0 : ereport(ERROR,
131 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
132 : : errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));
133 : :
134 : 25 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
135 : 25 : ImportSnapshot(strVal(&con->val));
136 : : }
137 : : else
138 [ # # ]: 0 : elog(ERROR, "unexpected SET MULTI element: %s",
139 : : stmt->name);
140 : 333 : break;
141 : 109 : case VAR_SET_DEFAULT:
142 [ + + ]: 109 : if (stmt->is_local)
143 : 3 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
144 : : pg_fallthrough;
145 : : case VAR_RESET:
146 [ + + ]: 3382 : (void) set_config_option(stmt->name,
147 : : NULL,
148 : 3382 : (superuser() ? PGC_SUSET : PGC_USERSET),
149 : : PGC_S_SESSION,
150 : : action, true, 0, false);
151 : 3364 : 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 [ + + ]: 24731 : InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
159 : : ACL_SET, stmt->kind, false);
160 : 24730 : }
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 : 22116 : ExtractSetVariableArgs(VariableSetStmt *stmt)
170 : : {
171 [ + + + ]: 22116 : switch (stmt->kind)
172 : : {
173 : 22093 : case VAR_SET_VALUE:
174 : 22093 : return flatten_set_variable_args(stmt->name, stmt->args);
175 : 2 : case VAR_SET_CURRENT:
176 : 2 : return GetConfigOptionByName(stmt->name, NULL, false);
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 : 26661 : 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 [ + + ]: 26661 : 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 : 26657 : record = find_option(name, false, true, WARNING);
210 [ + + ]: 26657 : if (record)
211 : 26615 : flags = record->flags;
212 : : else
213 : 42 : flags = 0;
214 : :
215 : : /*
216 : : * Handle special cases for list input.
217 : : */
218 [ + + ]: 26657 : if (flags & GUC_LIST_INPUT)
219 : : {
220 : : /* NULL represents an empty list. */
221 [ + + ]: 1071 : if (list_length(args) == 1)
222 : : {
223 : 962 : Node *arg = (Node *) linitial(args);
224 : :
225 [ + - ]: 962 : if (IsA(arg, A_Const) &&
226 [ + + ]: 962 : ((A_Const *) arg)->isnull)
227 : 9 : return pstrdup("");
228 : : }
229 : : }
230 : : else
231 : : {
232 : : /* Complain if list input and non-list variable. */
233 [ - + ]: 25586 : if (list_length(args) != 1)
234 [ # # ]: 0 : ereport(ERROR,
235 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
236 : : errmsg("SET %s takes only one argument", name)));
237 : : }
238 : :
239 : 26648 : 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 [ + - + + : 53433 : foreach(l, args)
+ + ]
247 : : {
248 : 26789 : Node *arg = (Node *) lfirst(l);
249 : : char *val;
250 : 26789 : TypeName *typeName = NULL;
251 : : A_Const *con;
252 : :
253 [ + + ]: 26789 : if (l != list_head(args))
254 : 141 : appendStringInfoString(&buf, ", ");
255 : :
256 [ - + ]: 26789 : if (IsA(arg, TypeCast))
257 : : {
258 : 0 : TypeCast *tc = (TypeCast *) arg;
259 : :
260 : 0 : arg = tc->arg;
261 : 0 : typeName = tc->typeName;
262 : : }
263 : :
264 [ - + ]: 26789 : if (!IsA(arg, A_Const))
265 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
266 : 26789 : con = (A_Const *) arg;
267 : :
268 : : /* Complain if NULL is used with a non-list variable. */
269 [ + + ]: 26789 : if (con->isnull)
270 [ + - ]: 4 : ereport(ERROR,
271 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
272 : : errmsg("NULL is an invalid value for %s", name)));
273 : :
274 [ + + + - ]: 26785 : switch (nodeTag(&con->val))
275 : : {
276 : 10649 : case T_Integer:
277 : 10649 : appendStringInfo(&buf, "%d", intVal(&con->val));
278 : 10649 : 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 : 16000 : case T_String:
284 : 16000 : val = strVal(&con->val);
285 [ - + ]: 16000 : 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 */
298 : : Assert(!(flags & GUC_LIST_QUOTE));
299 : :
300 : 0 : typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
301 : : 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 : : */
320 [ + + ]: 16000 : if (flags & GUC_LIST_QUOTE)
321 : 587 : appendStringInfoString(&buf, quote_identifier(val));
322 : : else
323 : 15413 : appendStringInfoString(&buf, val);
324 : : }
325 : 16000 : break;
326 : 0 : default:
327 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
328 : : (int) nodeTag(&con->val));
329 : : break;
330 : : }
331 : : }
332 : :
333 : 26644 : 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 : 4568 : SetPGVariable(const char *name, List *args, bool is_local)
344 : : {
345 : 4568 : char *argstring = flatten_set_variable_args(name, args);
346 : :
347 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
348 [ + + ]: 4568 : (void) set_config_option(name,
349 : : argstring,
350 : 4568 : (superuser() ? PGC_SUSET : PGC_USERSET),
351 : : PGC_S_SESSION,
352 : : is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
353 : : true, 0, false);
354 : 4555 : }
355 : :
356 : : /*
357 : : * SET command wrapped as a SQL callable function.
358 : : */
359 : : Datum
360 : 4165 : set_config_by_name(PG_FUNCTION_ARGS)
361 : : {
362 : : char *name;
363 : : char *value;
364 : : char *new_value;
365 : : bool is_local;
366 : :
367 [ - + ]: 4165 : if (PG_ARGISNULL(0))
368 [ # # ]: 0 : ereport(ERROR,
369 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
370 : : errmsg("SET requires parameter name")));
371 : :
372 : : /* Get the GUC variable name */
373 : 4165 : name = TextDatumGetCString(PG_GETARG_DATUM(0));
374 : :
375 : : /* Get the desired value or set to NULL for a reset request */
376 [ - + ]: 4165 : if (PG_ARGISNULL(1))
377 : 0 : value = NULL;
378 : : else
379 : 4165 : 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 [ - + ]: 4165 : if (PG_ARGISNULL(2))
386 : 0 : is_local = false;
387 : : else
388 : 4165 : is_local = PG_GETARG_BOOL(2);
389 : :
390 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
391 [ + + ]: 4165 : (void) set_config_option(name,
392 : : value,
393 : 4165 : (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 : 4164 : new_value = GetConfigOptionByName(name, NULL, false);
400 : :
401 : : /* Convert return string to text */
402 : 4164 : PG_RETURN_TEXT_P(cstring_to_text(new_value));
403 : : }
404 : :
405 : :
406 : : /*
407 : : * SHOW command
408 : : */
409 : : void
410 : 1233 : GetPGVariable(const char *name, DestReceiver *dest)
411 : : {
412 [ + + ]: 1233 : if (guc_name_compare(name, "all") == 0)
413 : 2 : ShowAllGUCConfig(dest);
414 : : else
415 : 1231 : ShowGUCConfigOption(name, dest);
416 : 1233 : }
417 : :
418 : : /*
419 : : * Get a tuple descriptor for SHOW's result
420 : : */
421 : : TupleDesc
422 : 590 : GetPGVariableResultDesc(const char *name)
423 : : {
424 : : TupleDesc tupdesc;
425 : :
426 [ - + ]: 590 : if (guc_name_compare(name, "all") == 0)
427 : : {
428 : : /* need a tuple descriptor representing three TEXT columns */
429 : 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 */
442 : 590 : (void) GetConfigOptionByName(name, &varname, false);
443 : :
444 : : /* need a tuple descriptor representing a single TEXT column */
445 : 572 : tupdesc = CreateTemplateTupleDesc(1);
446 : 572 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
447 : : TEXTOID, -1, 0);
448 : : }
449 : 572 : TupleDescFinalize(tupdesc);
450 : 572 : return tupdesc;
451 : : }
452 : :
453 : : /*
454 : : * SHOW one variable
455 : : */
456 : : static void
457 : 1231 : 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 : 1231 : value = GetConfigOptionByName(name, &varname, false);
466 : :
467 : : /* need a tuple descriptor representing a single TEXT column */
468 : 1231 : tupdesc = CreateTemplateTupleDesc(1);
469 : 1231 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname,
470 : : TEXTOID, -1, 0);
471 : 1231 : TupleDescFinalize(tupdesc);
472 : :
473 : : /* prepare for projection of tuples */
474 : 1231 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
475 : :
476 : : /* Send it */
477 : 1231 : do_text_output_oneline(tstate, value);
478 : :
479 : 1231 : end_tup_output(tstate);
480 : 1231 : }
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 */
496 : 2 : guc_vars = get_guc_variables(&num_vars);
497 : :
498 : : /* need a tuple descriptor representing three TEXT columns */
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);
506 : 2 : TupleDescFinalize(tupdesc);
507 : :
508 : : /* prepare for projection of tuples */
509 : 2 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
510 : :
511 [ + + ]: 867 : for (int i = 0; i < num_vars; i++)
512 : : {
513 : 865 : struct config_generic *conf = guc_vars[i];
514 : : char *setting;
515 : :
516 : : /* skip if marked NO_SHOW_ALL */
517 [ + + ]: 865 : if (conf->flags & GUC_NO_SHOW_ALL)
518 : 12 : continue;
519 : :
520 : : /* return only options visible to the current user */
521 [ - + ]: 853 : if (!ConfigOptionIsVisible(conf))
522 : 0 : continue;
523 : :
524 : : /* assign to the values array */
525 : 853 : values[0] = PointerGetDatum(cstring_to_text(conf->name));
526 : :
527 : 853 : setting = ShowGUCOption(conf, true);
528 [ + - ]: 853 : if (setting)
529 : : {
530 : 853 : values[1] = PointerGetDatum(cstring_to_text(setting));
531 : 853 : isnull[1] = false;
532 : : }
533 : : else
534 : : {
535 : 0 : values[1] = PointerGetDatum(NULL);
536 : 0 : isnull[1] = true;
537 : : }
538 : :
539 [ + - ]: 853 : if (conf->short_desc)
540 : : {
541 : 853 : values[2] = PointerGetDatum(cstring_to_text(conf->short_desc));
542 : 853 : isnull[2] = false;
543 : : }
544 : : else
545 : : {
546 : 0 : values[2] = PointerGetDatum(NULL);
547 : 0 : isnull[2] = true;
548 : : }
549 : :
550 : : /* send it to dest */
551 : 853 : do_tup_output(tstate, values, isnull);
552 : :
553 : : /* clean up */
554 : 853 : pfree(DatumGetPointer(values[0]));
555 [ + - ]: 853 : if (setting)
556 : : {
557 : 853 : pfree(setting);
558 : 853 : pfree(DatumGetPointer(values[1]));
559 : : }
560 [ + - ]: 853 : if (conf->short_desc)
561 : 853 : 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 : 2577 : pg_settings_get_flags(PG_FUNCTION_ARGS)
574 : : {
575 : : #define MAX_GUC_FLAGS 6
576 : 2577 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
577 : : struct config_generic *record;
578 : 2577 : int cnt = 0;
579 : : Datum flags[MAX_GUC_FLAGS];
580 : : ArrayType *a;
581 : :
582 : 2577 : record = find_option(varname, false, true, ERROR);
583 : :
584 : : /* return NULL if no such variable */
585 [ + + ]: 2577 : if (record == NULL)
586 : 4 : PG_RETURN_NULL();
587 : :
588 [ + + ]: 2573 : if (record->flags & GUC_EXPLAIN)
589 : 384 : flags[cnt++] = CStringGetTextDatum("EXPLAIN");
590 [ + + ]: 2573 : if (record->flags & GUC_NO_RESET)
591 : 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET");
592 [ + + ]: 2573 : if (record->flags & GUC_NO_RESET_ALL)
593 : 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
594 [ - + ]: 2573 : if (record->flags & GUC_NO_SHOW_ALL)
595 : 0 : flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
596 [ + + ]: 2573 : if (record->flags & GUC_NOT_IN_SAMPLE)
597 : 336 : flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
598 [ + + ]: 2573 : if (record->flags & GUC_RUNTIME_COMPUTED)
599 : 36 : flags[cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED");
600 : :
601 : : Assert(cnt <= MAX_GUC_FLAGS);
602 : :
603 : : /* Returns the record as Datum */
604 : 2573 : a = construct_array_builtin(flags, cnt, TEXTOID);
605 : 2573 : 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
612 : 886283 : ConfigOptionIsVisible(const struct config_generic *conf)
613 : : {
614 [ + + ]: 886283 : if ((conf->flags & GUC_SUPERUSER_ONLY) &&
615 [ + + ]: 53153 : !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
616 : 157 : return false;
617 : : else
618 : 886126 : return true;
619 : : }
620 : :
621 : : /*
622 : : * Extract fields to show in pg_settings for given variable.
623 : : */
624 : : static void
625 : 873972 : GetConfigOptionValues(const struct config_generic *conf, const char **values)
626 : : {
627 : : char buffer[256];
628 : :
629 : : /* first get the generic attributes */
630 : :
631 : : /* name */
632 : 873972 : values[0] = conf->name;
633 : :
634 : : /* setting: use ShowGUCOption in order to avoid duplicating the logic */
635 : 873972 : values[1] = ShowGUCOption(conf, false);
636 : :
637 : : /* unit, if any (NULL is fine) */
638 : 873972 : values[2] = get_config_unit_name(conf->flags);
639 : :
640 : : /* group */
641 : 873972 : values[3] = _(config_group_names[conf->group]);
642 : :
643 : : /* short_desc */
644 [ + - ]: 873972 : values[4] = conf->short_desc != NULL ? _(conf->short_desc) : NULL;
645 : :
646 : : /* extra_desc */
647 [ + + ]: 873972 : values[5] = conf->long_desc != NULL ? _(conf->long_desc) : NULL;
648 : :
649 : : /* context */
650 : 873972 : values[6] = GucContext_Names[conf->context];
651 : :
652 : : /* vartype */
653 : 873972 : values[7] = config_type_names[conf->vartype];
654 : :
655 : : /* source */
656 : 873972 : values[8] = GucSource_Names[conf->source];
657 : :
658 : : /* now get the type specific attributes */
659 [ + + + + : 873972 : switch (conf->vartype)
+ - ]
660 : : {
661 : 246355 : case PGC_BOOL:
662 : : {
663 : 246355 : const struct config_bool *lconf = &conf->_bool;
664 : :
665 : : /* min_val */
666 : 246355 : values[9] = NULL;
667 : :
668 : : /* max_val */
669 : 246355 : values[10] = NULL;
670 : :
671 : : /* enumvals */
672 : 246355 : values[11] = NULL;
673 : :
674 : : /* boot_val */
675 [ + + ]: 246355 : values[12] = pstrdup(lconf->boot_val ? "on" : "off");
676 : :
677 : : /* reset_val */
678 [ + + ]: 246355 : values[13] = pstrdup(lconf->reset_val ? "on" : "off");
679 : : }
680 : 246355 : break;
681 : :
682 : 316527 : case PGC_INT:
683 : : {
684 : 316527 : const struct config_int *lconf = &conf->_int;
685 : : const char *fmt;
686 : :
687 : : /* choose display format */
688 [ + + ]: 316527 : if (conf->flags & GUC_SHOW_IN_OCTAL)
689 : 6126 : fmt = "0%03o";
690 : : else
691 : 310401 : fmt = "%d";
692 : :
693 : : /* min_val */
694 : 316527 : snprintf(buffer, sizeof(buffer), fmt, lconf->min);
695 : 316527 : values[9] = pstrdup(buffer);
696 : :
697 : : /* max_val */
698 : 316527 : snprintf(buffer, sizeof(buffer), fmt, lconf->max);
699 : 316527 : values[10] = pstrdup(buffer);
700 : :
701 : : /* enumvals */
702 : 316527 : values[11] = NULL;
703 : :
704 : : /* boot_val */
705 : 316527 : snprintf(buffer, sizeof(buffer), fmt, lconf->boot_val);
706 : 316527 : values[12] = pstrdup(buffer);
707 : :
708 : : /* reset_val */
709 : 316527 : snprintf(buffer, sizeof(buffer), fmt, lconf->reset_val);
710 : 316527 : values[13] = pstrdup(buffer);
711 : : }
712 : 316527 : break;
713 : :
714 : 63302 : case PGC_REAL:
715 : : {
716 : 63302 : const struct config_real *lconf = &conf->_real;
717 : :
718 : : /* min_val */
719 : 63302 : snprintf(buffer, sizeof(buffer), "%g", lconf->min);
720 : 63302 : values[9] = pstrdup(buffer);
721 : :
722 : : /* max_val */
723 : 63302 : snprintf(buffer, sizeof(buffer), "%g", lconf->max);
724 : 63302 : values[10] = pstrdup(buffer);
725 : :
726 : : /* enumvals */
727 : 63302 : values[11] = NULL;
728 : :
729 : : /* boot_val */
730 : 63302 : snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val);
731 : 63302 : values[12] = pstrdup(buffer);
732 : :
733 : : /* reset_val */
734 : 63302 : snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val);
735 : 63302 : values[13] = pstrdup(buffer);
736 : : }
737 : 63302 : break;
738 : :
739 : 158339 : case PGC_STRING:
740 : : {
741 : 158339 : const struct config_string *lconf = &conf->_string;
742 : :
743 : : /* min_val */
744 : 158339 : values[9] = NULL;
745 : :
746 : : /* max_val */
747 : 158339 : values[10] = NULL;
748 : :
749 : : /* enumvals */
750 : 158339 : values[11] = NULL;
751 : :
752 : : /* boot_val */
753 [ + + ]: 158339 : if (lconf->boot_val == NULL)
754 : 14267 : values[12] = NULL;
755 : : else
756 : 144072 : values[12] = pstrdup(lconf->boot_val);
757 : :
758 : : /* reset_val */
759 [ + + ]: 158339 : if (lconf->reset_val == NULL)
760 : 2045 : values[13] = NULL;
761 : : else
762 : 156294 : values[13] = pstrdup(lconf->reset_val);
763 : : }
764 : 158339 : break;
765 : :
766 : 89449 : case PGC_ENUM:
767 : : {
768 : 89449 : const struct config_enum *lconf = &conf->_enum;
769 : :
770 : : /* min_val */
771 : 89449 : values[9] = NULL;
772 : :
773 : : /* max_val */
774 : 89449 : values[10] = NULL;
775 : :
776 : : /* enumvals */
777 : :
778 : : /*
779 : : * NOTE! enumvals with double quotes in them are not
780 : : * supported!
781 : : */
782 : 89449 : values[11] = config_enum_get_options(lconf,
783 : : "{\"", "\"}", "\",\"");
784 : :
785 : : /* boot_val */
786 : 89449 : values[12] = pstrdup(config_enum_lookup_by_value(conf,
787 : 89449 : lconf->boot_val));
788 : :
789 : : /* reset_val */
790 : 89449 : values[13] = pstrdup(config_enum_lookup_by_value(conf,
791 : 89449 : lconf->reset_val));
792 : : }
793 : 89449 : break;
794 : :
795 : 0 : default:
796 : : {
797 : : /*
798 : : * should never get here, but in case we do, set 'em to NULL
799 : : */
800 : :
801 : : /* min_val */
802 : 0 : values[9] = NULL;
803 : :
804 : : /* max_val */
805 : 0 : values[10] = NULL;
806 : :
807 : : /* enumvals */
808 : 0 : values[11] = NULL;
809 : :
810 : : /* boot_val */
811 : 0 : values[12] = NULL;
812 : :
813 : : /* reset_val */
814 : 0 : values[13] = NULL;
815 : : }
816 : 0 : break;
817 : : }
818 : :
819 : : /*
820 : : * If the setting came from a config file, set the source location. For
821 : : * security reasons, we don't show source file/line number for
822 : : * insufficiently-privileged users.
823 : : */
824 [ + + + + ]: 934417 : if (conf->source == PGC_S_FILE &&
825 : 60445 : has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
826 : : {
827 : 60307 : values[14] = conf->sourcefile;
828 : 60307 : snprintf(buffer, sizeof(buffer), "%d", conf->sourceline);
829 : 60307 : values[15] = pstrdup(buffer);
830 : : }
831 : : else
832 : : {
833 : 813665 : values[14] = NULL;
834 : 813665 : values[15] = NULL;
835 : : }
836 : :
837 [ - + ]: 873972 : values[16] = (conf->status & GUC_PENDING_RESTART) ? "t" : "f";
838 : 873972 : }
839 : :
840 : : /*
841 : : * show_config_by_name - equiv to SHOW X command but implemented as
842 : : * a function.
843 : : */
844 : : Datum
845 : 5304 : show_config_by_name(PG_FUNCTION_ARGS)
846 : : {
847 : 5304 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
848 : : char *varval;
849 : :
850 : : /* Get the value */
851 : 5304 : varval = GetConfigOptionByName(varname, NULL, false);
852 : :
853 : : /* Convert to text */
854 : 5300 : PG_RETURN_TEXT_P(cstring_to_text(varval));
855 : : }
856 : :
857 : : /*
858 : : * show_config_by_name_missing_ok - equiv to SHOW X command but implemented as
859 : : * a function. If X does not exist, suppress the error and just return NULL
860 : : * if missing_ok is true.
861 : : */
862 : : Datum
863 : 16 : show_config_by_name_missing_ok(PG_FUNCTION_ARGS)
864 : : {
865 : 16 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
866 : 16 : bool missing_ok = PG_GETARG_BOOL(1);
867 : : char *varval;
868 : :
869 : : /* Get the value */
870 : 16 : varval = GetConfigOptionByName(varname, NULL, missing_ok);
871 : :
872 : : /* return NULL if no such variable */
873 [ + + ]: 12 : if (varval == NULL)
874 : 4 : PG_RETURN_NULL();
875 : :
876 : : /* Convert to text */
877 : 8 : PG_RETURN_TEXT_P(cstring_to_text(varval));
878 : : }
879 : :
880 : : /*
881 : : * show_all_settings - equiv to SHOW ALL command but implemented as
882 : : * a Table Function.
883 : : */
884 : : #define NUM_PG_SETTINGS_ATTS 17
885 : :
886 : : Datum
887 : 876014 : show_all_settings(PG_FUNCTION_ARGS)
888 : : {
889 : : FuncCallContext *funcctx;
890 : : struct config_generic **guc_vars;
891 : : int num_vars;
892 : : TupleDesc tupdesc;
893 : : int call_cntr;
894 : : int max_calls;
895 : : AttInMetadata *attinmeta;
896 : : MemoryContext oldcontext;
897 : :
898 : : /* stuff done only on the first call of the function */
899 [ + + ]: 876014 : if (SRF_IS_FIRSTCALL())
900 : : {
901 : : /* create a function context for cross-call persistence */
902 : 2042 : funcctx = SRF_FIRSTCALL_INIT();
903 : :
904 : : /*
905 : : * switch to memory context appropriate for multiple function calls
906 : : */
907 : 2042 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
908 : :
909 : : /*
910 : : * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns
911 : : * of the appropriate types
912 : : */
913 : 2042 : tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS);
914 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
915 : : TEXTOID, -1, 0);
916 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
917 : : TEXTOID, -1, 0);
918 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "unit",
919 : : TEXTOID, -1, 0);
920 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "category",
921 : : TEXTOID, -1, 0);
922 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "short_desc",
923 : : TEXTOID, -1, 0);
924 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "extra_desc",
925 : : TEXTOID, -1, 0);
926 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "context",
927 : : TEXTOID, -1, 0);
928 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "vartype",
929 : : TEXTOID, -1, 0);
930 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "source",
931 : : TEXTOID, -1, 0);
932 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_val",
933 : : TEXTOID, -1, 0);
934 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val",
935 : : TEXTOID, -1, 0);
936 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals",
937 : : TEXTARRAYOID, -1, 0);
938 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val",
939 : : TEXTOID, -1, 0);
940 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val",
941 : : TEXTOID, -1, 0);
942 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile",
943 : : TEXTOID, -1, 0);
944 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline",
945 : : INT4OID, -1, 0);
946 : 2042 : TupleDescInitEntry(tupdesc, (AttrNumber) 17, "pending_restart",
947 : : BOOLOID, -1, 0);
948 : :
949 : 2042 : TupleDescFinalize(tupdesc);
950 : :
951 : : /*
952 : : * Generate attribute metadata needed later to produce tuples from raw
953 : : * C strings
954 : : */
955 : 2042 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
956 : 2042 : funcctx->attinmeta = attinmeta;
957 : :
958 : : /* collect the variables, in sorted order */
959 : 2042 : guc_vars = get_guc_variables(&num_vars);
960 : :
961 : : /* use user_fctx to remember the array location */
962 : 2042 : funcctx->user_fctx = guc_vars;
963 : :
964 : : /* total number of tuples to be returned */
965 : 2042 : funcctx->max_calls = num_vars;
966 : :
967 : 2042 : MemoryContextSwitchTo(oldcontext);
968 : : }
969 : :
970 : : /* stuff done on every call of the function */
971 : 876014 : funcctx = SRF_PERCALL_SETUP();
972 : :
973 : 876014 : guc_vars = (struct config_generic **) funcctx->user_fctx;
974 : 876014 : call_cntr = funcctx->call_cntr;
975 : 876014 : max_calls = funcctx->max_calls;
976 : 876014 : attinmeta = funcctx->attinmeta;
977 : :
978 [ + + ]: 888446 : while (call_cntr < max_calls) /* do when there is more left to send */
979 : : {
980 : 886404 : struct config_generic *conf = guc_vars[call_cntr];
981 : : char *values[NUM_PG_SETTINGS_ATTS];
982 : : HeapTuple tuple;
983 : : Datum result;
984 : :
985 : : /* skip if marked NO_SHOW_ALL or if not visible to current user */
986 [ + + ]: 886404 : if ((conf->flags & GUC_NO_SHOW_ALL) ||
987 [ + + ]: 874128 : !ConfigOptionIsVisible(conf))
988 : : {
989 : 12432 : call_cntr = ++funcctx->call_cntr;
990 : 12432 : continue;
991 : : }
992 : :
993 : : /* extract values for the current variable */
994 : 873972 : GetConfigOptionValues(conf, (const char **) values);
995 : :
996 : : /* build a tuple */
997 : 873972 : tuple = BuildTupleFromCStrings(attinmeta, values);
998 : :
999 : : /* make the tuple into a datum */
1000 : 873972 : result = HeapTupleGetDatum(tuple);
1001 : :
1002 : 873972 : SRF_RETURN_NEXT(funcctx, result);
1003 : : }
1004 : :
1005 : : /* do when there is no more left */
1006 : 2042 : SRF_RETURN_DONE(funcctx);
1007 : : }
1008 : :
1009 : : /*
1010 : : * show_all_file_settings
1011 : : *
1012 : : * Returns a table of all parameter settings in all configuration files
1013 : : * which includes the config file pathname, the line number, a sequence number
1014 : : * indicating the order in which the settings were encountered, the parameter
1015 : : * name and value, a bool showing if the value could be applied, and possibly
1016 : : * an associated error message. (For problems such as syntax errors, the
1017 : : * parameter name/value might be NULL.)
1018 : : *
1019 : : * Note: no filtering is done here, instead we depend on the GRANT system
1020 : : * to prevent unprivileged users from accessing this function or the view
1021 : : * built on top of it.
1022 : : */
1023 : : Datum
1024 : 4 : show_all_file_settings(PG_FUNCTION_ARGS)
1025 : : {
1026 : : #define NUM_PG_FILE_SETTINGS_ATTS 7
1027 : 4 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1028 : : ConfigVariable *conf;
1029 : :
1030 : : /* Scan the config files using current context as workspace */
1031 : 4 : conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
1032 : :
1033 : : /* Build a tuplestore to return our results in */
1034 : 4 : InitMaterializedSRF(fcinfo, 0);
1035 : :
1036 : : /* Process the results and create a tuplestore */
1037 [ + + ]: 120 : for (int seqno = 1; conf != NULL; conf = conf->next, seqno++)
1038 : : {
1039 : : Datum values[NUM_PG_FILE_SETTINGS_ATTS];
1040 : : bool nulls[NUM_PG_FILE_SETTINGS_ATTS];
1041 : :
1042 : 116 : memset(values, 0, sizeof(values));
1043 : 116 : memset(nulls, 0, sizeof(nulls));
1044 : :
1045 : : /* sourcefile */
1046 [ + - ]: 116 : if (conf->filename)
1047 : 116 : values[0] = PointerGetDatum(cstring_to_text(conf->filename));
1048 : : else
1049 : 0 : nulls[0] = true;
1050 : :
1051 : : /* sourceline (not meaningful if no sourcefile) */
1052 [ + - ]: 116 : if (conf->filename)
1053 : 116 : values[1] = Int32GetDatum(conf->sourceline);
1054 : : else
1055 : 0 : nulls[1] = true;
1056 : :
1057 : : /* seqno */
1058 : 116 : values[2] = Int32GetDatum(seqno);
1059 : :
1060 : : /* name */
1061 [ + - ]: 116 : if (conf->name)
1062 : 116 : values[3] = PointerGetDatum(cstring_to_text(conf->name));
1063 : : else
1064 : 0 : nulls[3] = true;
1065 : :
1066 : : /* setting */
1067 [ + - ]: 116 : if (conf->value)
1068 : 116 : values[4] = PointerGetDatum(cstring_to_text(conf->value));
1069 : : else
1070 : 0 : nulls[4] = true;
1071 : :
1072 : : /* applied */
1073 : 116 : values[5] = BoolGetDatum(conf->applied);
1074 : :
1075 : : /* error */
1076 [ - + ]: 116 : if (conf->errmsg)
1077 : 0 : values[6] = PointerGetDatum(cstring_to_text(conf->errmsg));
1078 : : else
1079 : 116 : nulls[6] = true;
1080 : :
1081 : : /* shove row into tuplestore */
1082 : 116 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1083 : : }
1084 : :
1085 : 4 : return (Datum) 0;
1086 : : }
|