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 25582 : ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
46 : {
47 25582 : 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 25582 : if (IsInParallelMode())
54 0 : ereport(ERROR,
55 : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
56 : errmsg("cannot set parameters during a parallel operation")));
57 :
58 25582 : switch (stmt->kind)
59 : {
60 21818 : case VAR_SET_VALUE:
61 : case VAR_SET_CURRENT:
62 21818 : if (stmt->is_local)
63 985 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
64 43632 : (void) set_config_option(stmt->name,
65 21818 : ExtractSetVariableArgs(stmt),
66 21818 : (superuser() ? PGC_SUSET : PGC_USERSET),
67 : PGC_S_SESSION,
68 : action, true, 0, false);
69 21678 : break;
70 418 : 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 418 : if (strcmp(stmt->name, "TRANSACTION") == 0)
80 : {
81 : ListCell *head;
82 :
83 383 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
84 :
85 1053 : foreach(head, stmt->args)
86 : {
87 683 : DefElem *item = (DefElem *) lfirst(head);
88 :
89 683 : if (strcmp(item->defname, "transaction_isolation") == 0)
90 315 : SetPGVariable("transaction_isolation",
91 315 : list_make1(item->arg), stmt->is_local);
92 368 : else if (strcmp(item->defname, "transaction_read_only") == 0)
93 364 : SetPGVariable("transaction_read_only",
94 364 : 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 35 : 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)
112 0 : SetPGVariable("default_transaction_isolation",
113 0 : list_make1(item->arg), stmt->is_local);
114 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);
117 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 : }
125 24 : else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0)
126 : {
127 24 : A_Const *con = linitial_node(A_Const, stmt->args);
128 :
129 24 : 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 24 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
135 24 : ImportSnapshot(strVal(&con->val));
136 : }
137 : else
138 0 : elog(ERROR, "unexpected SET MULTI element: %s",
139 : stmt->name);
140 397 : 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 3339 : (void) set_config_option(stmt->name,
147 : NULL,
148 3339 : (superuser() ? PGC_SUSET : PGC_USERSET),
149 : PGC_S_SESSION,
150 : action, true, 0, false);
151 3321 : 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 25403 : InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
159 : ACL_SET, stmt->kind, false);
160 25402 : }
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 22690 : ExtractSetVariableArgs(VariableSetStmt *stmt)
170 : {
171 22690 : switch (stmt->kind)
172 : {
173 22667 : case VAR_SET_VALUE:
174 22667 : 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 27260 : 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 27260 : 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 27256 : record = find_option(name, false, true, WARNING);
210 27256 : if (record)
211 27215 : flags = record->flags;
212 : else
213 41 : flags = 0;
214 :
215 : /*
216 : * Handle special cases for list input.
217 : */
218 27256 : if (flags & GUC_LIST_INPUT)
219 : {
220 : /* NULL represents an empty list. */
221 1166 : if (list_length(args) == 1)
222 : {
223 1015 : Node *arg = (Node *) linitial(args);
224 :
225 1015 : if (IsA(arg, A_Const) &&
226 1015 : ((A_Const *) arg)->isnull)
227 9 : return pstrdup("");
228 : }
229 : }
230 : else
231 : {
232 : /* Complain if list input and non-list variable. */
233 26090 : 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 27247 : 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 54688 : foreach(l, args)
247 : {
248 27445 : Node *arg = (Node *) lfirst(l);
249 : char *val;
250 27445 : TypeName *typeName = NULL;
251 : A_Const *con;
252 :
253 27445 : if (l != list_head(args))
254 198 : appendStringInfoString(&buf, ", ");
255 :
256 27445 : 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 27445 : if (!IsA(arg, A_Const))
265 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
266 27445 : con = (A_Const *) arg;
267 :
268 : /* Complain if NULL is used with a non-list variable. */
269 27445 : 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 27441 : switch (nodeTag(&con->val))
275 : {
276 10974 : case T_Integer:
277 10974 : appendStringInfo(&buf, "%d", intVal(&con->val));
278 10974 : break;
279 134 : case T_Float:
280 : /* represented as a string, so just copy it */
281 134 : appendStringInfoString(&buf, castNode(Float, &con->val)->fval);
282 134 : break;
283 16333 : case T_String:
284 16333 : val = strVal(&con->val);
285 16333 : 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 16333 : if (flags & GUC_LIST_QUOTE)
321 677 : appendStringInfoString(&buf, quote_identifier(val));
322 : else
323 15656 : appendStringInfoString(&buf, val);
324 : }
325 16333 : break;
326 0 : default:
327 0 : elog(ERROR, "unrecognized node type: %d",
328 : (int) nodeTag(&con->val));
329 : break;
330 : }
331 : }
332 :
333 27243 : 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 4593 : SetPGVariable(const char *name, List *args, bool is_local)
344 : {
345 4593 : char *argstring = flatten_set_variable_args(name, args);
346 :
347 : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
348 4593 : (void) set_config_option(name,
349 : argstring,
350 4593 : (superuser() ? PGC_SUSET : PGC_USERSET),
351 : PGC_S_SESSION,
352 : is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
353 : true, 0, false);
354 4580 : }
355 :
356 : /*
357 : * SET command wrapped as a SQL callable function.
358 : */
359 : Datum
360 4196 : set_config_by_name(PG_FUNCTION_ARGS)
361 : {
362 : char *name;
363 : char *value;
364 : char *new_value;
365 : bool is_local;
366 :
367 4196 : 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 4196 : name = TextDatumGetCString(PG_GETARG_DATUM(0));
374 :
375 : /* Get the desired value or set to NULL for a reset request */
376 4196 : if (PG_ARGISNULL(1))
377 0 : value = NULL;
378 : else
379 4196 : 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 4196 : if (PG_ARGISNULL(2))
386 0 : is_local = false;
387 : else
388 4196 : is_local = PG_GETARG_BOOL(2);
389 :
390 : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
391 4196 : (void) set_config_option(name,
392 : value,
393 4196 : (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 4195 : new_value = GetConfigOptionByName(name, NULL, false);
400 :
401 : /* Convert return string to text */
402 4195 : PG_RETURN_TEXT_P(cstring_to_text(new_value));
403 : }
404 :
405 :
406 : /*
407 : * SHOW command
408 : */
409 : void
410 1164 : GetPGVariable(const char *name, DestReceiver *dest)
411 : {
412 1164 : if (guc_name_compare(name, "all") == 0)
413 2 : ShowAllGUCConfig(dest);
414 : else
415 1162 : ShowGUCConfigOption(name, dest);
416 1164 : }
417 :
418 : /*
419 : * Get a tuple descriptor for SHOW's result
420 : */
421 : TupleDesc
422 585 : GetPGVariableResultDesc(const char *name)
423 : {
424 : TupleDesc tupdesc;
425 :
426 585 : 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 585 : (void) GetConfigOptionByName(name, &varname, false);
443 :
444 : /* need a tuple descriptor representing a single TEXT column */
445 567 : tupdesc = CreateTemplateTupleDesc(1);
446 567 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
447 : TEXTOID, -1, 0);
448 : }
449 567 : TupleDescFinalize(tupdesc);
450 567 : return tupdesc;
451 : }
452 :
453 : /*
454 : * SHOW one variable
455 : */
456 : static void
457 1162 : 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 1162 : value = GetConfigOptionByName(name, &varname, false);
466 :
467 : /* need a tuple descriptor representing a single TEXT column */
468 1162 : tupdesc = CreateTemplateTupleDesc(1);
469 1162 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname,
470 : TEXTOID, -1, 0);
471 1162 : TupleDescFinalize(tupdesc);
472 :
473 : /* prepare for projection of tuples */
474 1162 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
475 :
476 : /* Send it */
477 1162 : do_text_output_oneline(tstate, value);
478 :
479 1162 : end_tup_output(tstate);
480 1162 : }
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 837 : for (int i = 0; i < num_vars; i++)
512 : {
513 835 : struct config_generic *conf = guc_vars[i];
514 : char *setting;
515 :
516 : /* skip if marked NO_SHOW_ALL */
517 835 : if (conf->flags & GUC_NO_SHOW_ALL)
518 12 : continue;
519 :
520 : /* return only options visible to the current user */
521 823 : if (!ConfigOptionIsVisible(conf))
522 0 : continue;
523 :
524 : /* assign to the values array */
525 823 : values[0] = PointerGetDatum(cstring_to_text(conf->name));
526 :
527 823 : setting = ShowGUCOption(conf, true);
528 823 : if (setting)
529 : {
530 823 : values[1] = PointerGetDatum(cstring_to_text(setting));
531 823 : isnull[1] = false;
532 : }
533 : else
534 : {
535 0 : values[1] = PointerGetDatum(NULL);
536 0 : isnull[1] = true;
537 : }
538 :
539 823 : if (conf->short_desc)
540 : {
541 823 : values[2] = PointerGetDatum(cstring_to_text(conf->short_desc));
542 823 : 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 823 : do_tup_output(tstate, values, isnull);
552 :
553 : /* clean up */
554 823 : pfree(DatumGetPointer(values[0]));
555 823 : if (setting)
556 : {
557 823 : pfree(setting);
558 823 : pfree(DatumGetPointer(values[1]));
559 : }
560 823 : if (conf->short_desc)
561 823 : 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 2487 : pg_settings_get_flags(PG_FUNCTION_ARGS)
574 : {
575 : #define MAX_GUC_FLAGS 6
576 2487 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
577 : struct config_generic *record;
578 2487 : int cnt = 0;
579 : Datum flags[MAX_GUC_FLAGS];
580 : ArrayType *a;
581 :
582 2487 : record = find_option(varname, false, true, ERROR);
583 :
584 : /* return NULL if no such variable */
585 2487 : if (record == NULL)
586 4 : PG_RETURN_NULL();
587 :
588 2483 : if (record->flags & GUC_EXPLAIN)
589 378 : flags[cnt++] = CStringGetTextDatum("EXPLAIN");
590 2483 : if (record->flags & GUC_NO_RESET)
591 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET");
592 2483 : if (record->flags & GUC_NO_RESET_ALL)
593 18 : flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
594 2483 : if (record->flags & GUC_NO_SHOW_ALL)
595 0 : flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
596 2483 : if (record->flags & GUC_NOT_IN_SAMPLE)
597 336 : flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
598 2483 : 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 2483 : a = construct_array_builtin(flags, cnt, TEXTOID);
605 2483 : 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 788887 : ConfigOptionIsVisible(const struct config_generic *conf)
613 : {
614 788887 : if ((conf->flags & GUC_SUPERUSER_ONLY) &&
615 47309 : !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
616 151 : return false;
617 : else
618 788736 : return true;
619 : }
620 :
621 : /*
622 : * Extract fields to show in pg_settings for given variable.
623 : */
624 : static void
625 780750 : 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 780750 : values[0] = conf->name;
633 :
634 : /* setting: use ShowGUCOption in order to avoid duplicating the logic */
635 780750 : values[1] = ShowGUCOption(conf, false);
636 :
637 : /* unit, if any (NULL is fine) */
638 780750 : values[2] = get_config_unit_name(conf->flags);
639 :
640 : /* group */
641 780750 : values[3] = _(config_group_names[conf->group]);
642 :
643 : /* short_desc */
644 780750 : values[4] = conf->short_desc != NULL ? _(conf->short_desc) : NULL;
645 :
646 : /* extra_desc */
647 780750 : values[5] = conf->long_desc != NULL ? _(conf->long_desc) : NULL;
648 :
649 : /* context */
650 780750 : values[6] = GucContext_Names[conf->context];
651 :
652 : /* vartype */
653 780750 : values[7] = config_type_names[conf->vartype];
654 :
655 : /* source */
656 780750 : values[8] = GucSource_Names[conf->source];
657 :
658 : /* now get the type specific attributes */
659 780750 : switch (conf->vartype)
660 : {
661 228089 : case PGC_BOOL:
662 : {
663 228089 : const struct config_bool *lconf = &conf->_bool;
664 :
665 : /* min_val */
666 228089 : values[9] = NULL;
667 :
668 : /* max_val */
669 228089 : values[10] = NULL;
670 :
671 : /* enumvals */
672 228089 : values[11] = NULL;
673 :
674 : /* boot_val */
675 228089 : values[12] = pstrdup(lconf->boot_val ? "on" : "off");
676 :
677 : /* reset_val */
678 228089 : values[13] = pstrdup(lconf->reset_val ? "on" : "off");
679 : }
680 228089 : break;
681 :
682 279735 : case PGC_INT:
683 : {
684 279735 : const struct config_int *lconf = &conf->_int;
685 :
686 : /* min_val */
687 279735 : snprintf(buffer, sizeof(buffer), "%d", lconf->min);
688 279735 : values[9] = pstrdup(buffer);
689 :
690 : /* max_val */
691 279735 : snprintf(buffer, sizeof(buffer), "%d", lconf->max);
692 279735 : values[10] = pstrdup(buffer);
693 :
694 : /* enumvals */
695 279735 : values[11] = NULL;
696 :
697 : /* boot_val */
698 279735 : snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val);
699 279735 : values[12] = pstrdup(buffer);
700 :
701 : /* reset_val */
702 279735 : snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val);
703 279735 : values[13] = pstrdup(buffer);
704 : }
705 279735 : break;
706 :
707 49140 : case PGC_REAL:
708 : {
709 49140 : const struct config_real *lconf = &conf->_real;
710 :
711 : /* min_val */
712 49140 : snprintf(buffer, sizeof(buffer), "%g", lconf->min);
713 49140 : values[9] = pstrdup(buffer);
714 :
715 : /* max_val */
716 49140 : snprintf(buffer, sizeof(buffer), "%g", lconf->max);
717 49140 : values[10] = pstrdup(buffer);
718 :
719 : /* enumvals */
720 49140 : values[11] = NULL;
721 :
722 : /* boot_val */
723 49140 : snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val);
724 49140 : values[12] = pstrdup(buffer);
725 :
726 : /* reset_val */
727 49140 : snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val);
728 49140 : values[13] = pstrdup(buffer);
729 : }
730 49140 : break;
731 :
732 144737 : case PGC_STRING:
733 : {
734 144737 : const struct config_string *lconf = &conf->_string;
735 :
736 : /* min_val */
737 144737 : values[9] = NULL;
738 :
739 : /* max_val */
740 144737 : values[10] = NULL;
741 :
742 : /* enumvals */
743 144737 : values[11] = NULL;
744 :
745 : /* boot_val */
746 144737 : if (lconf->boot_val == NULL)
747 13201 : values[12] = NULL;
748 : else
749 131536 : values[12] = pstrdup(lconf->boot_val);
750 :
751 : /* reset_val */
752 144737 : if (lconf->reset_val == NULL)
753 1891 : values[13] = NULL;
754 : else
755 142846 : values[13] = pstrdup(lconf->reset_val);
756 : }
757 144737 : break;
758 :
759 79049 : case PGC_ENUM:
760 : {
761 79049 : const struct config_enum *lconf = &conf->_enum;
762 :
763 : /* min_val */
764 79049 : values[9] = NULL;
765 :
766 : /* max_val */
767 79049 : values[10] = NULL;
768 :
769 : /* enumvals */
770 :
771 : /*
772 : * NOTE! enumvals with double quotes in them are not
773 : * supported!
774 : */
775 79049 : values[11] = config_enum_get_options(lconf,
776 : "{\"", "\"}", "\",\"");
777 :
778 : /* boot_val */
779 79049 : values[12] = pstrdup(config_enum_lookup_by_value(conf,
780 79049 : lconf->boot_val));
781 :
782 : /* reset_val */
783 79049 : values[13] = pstrdup(config_enum_lookup_by_value(conf,
784 79049 : lconf->reset_val));
785 : }
786 79049 : break;
787 :
788 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 : */
817 835298 : if (conf->source == PGC_S_FILE &&
818 54548 : has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
819 : {
820 54410 : values[14] = conf->sourcefile;
821 54410 : snprintf(buffer, sizeof(buffer), "%d", conf->sourceline);
822 54410 : values[15] = pstrdup(buffer);
823 : }
824 : else
825 : {
826 726340 : values[14] = NULL;
827 726340 : values[15] = NULL;
828 : }
829 :
830 780750 : values[16] = (conf->status & GUC_PENDING_RESTART) ? "t" : "f";
831 780750 : }
832 :
833 : /*
834 : * show_config_by_name - equiv to SHOW X command but implemented as
835 : * a function.
836 : */
837 : Datum
838 1201 : show_config_by_name(PG_FUNCTION_ARGS)
839 : {
840 1201 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
841 : char *varval;
842 :
843 : /* Get the value */
844 1201 : varval = GetConfigOptionByName(varname, NULL, false);
845 :
846 : /* Convert to text */
847 1197 : 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 782640 : 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 782640 : if (SRF_IS_FIRSTCALL())
893 : {
894 : /* create a function context for cross-call persistence */
895 1890 : funcctx = SRF_FIRSTCALL_INIT();
896 :
897 : /*
898 : * switch to memory context appropriate for multiple function calls
899 : */
900 1890 : 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 1890 : tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS);
907 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
908 : TEXTOID, -1, 0);
909 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
910 : TEXTOID, -1, 0);
911 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "unit",
912 : TEXTOID, -1, 0);
913 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "category",
914 : TEXTOID, -1, 0);
915 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "short_desc",
916 : TEXTOID, -1, 0);
917 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "extra_desc",
918 : TEXTOID, -1, 0);
919 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "context",
920 : TEXTOID, -1, 0);
921 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "vartype",
922 : TEXTOID, -1, 0);
923 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "source",
924 : TEXTOID, -1, 0);
925 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_val",
926 : TEXTOID, -1, 0);
927 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val",
928 : TEXTOID, -1, 0);
929 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals",
930 : TEXTARRAYOID, -1, 0);
931 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val",
932 : TEXTOID, -1, 0);
933 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val",
934 : TEXTOID, -1, 0);
935 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile",
936 : TEXTOID, -1, 0);
937 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline",
938 : INT4OID, -1, 0);
939 1890 : TupleDescInitEntry(tupdesc, (AttrNumber) 17, "pending_restart",
940 : BOOLOID, -1, 0);
941 :
942 1890 : TupleDescFinalize(tupdesc);
943 :
944 : /*
945 : * Generate attribute metadata needed later to produce tuples from raw
946 : * C strings
947 : */
948 1890 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
949 1890 : funcctx->attinmeta = attinmeta;
950 :
951 : /* collect the variables, in sorted order */
952 1890 : guc_vars = get_guc_variables(&num_vars);
953 :
954 : /* use user_fctx to remember the array location */
955 1890 : funcctx->user_fctx = guc_vars;
956 :
957 : /* total number of tuples to be returned */
958 1890 : funcctx->max_calls = num_vars;
959 :
960 1890 : MemoryContextSwitchTo(oldcontext);
961 : }
962 :
963 : /* stuff done on every call of the function */
964 782640 : funcctx = SRF_PERCALL_SETUP();
965 :
966 782640 : guc_vars = (struct config_generic **) funcctx->user_fctx;
967 782640 : call_cntr = funcctx->call_cntr;
968 782640 : max_calls = funcctx->max_calls;
969 782640 : attinmeta = funcctx->attinmeta;
970 :
971 794142 : while (call_cntr < max_calls) /* do when there is more left to send */
972 : {
973 792252 : 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 792252 : if ((conf->flags & GUC_NO_SHOW_ALL) ||
980 780900 : !ConfigOptionIsVisible(conf))
981 : {
982 11502 : call_cntr = ++funcctx->call_cntr;
983 11502 : continue;
984 : }
985 :
986 : /* extract values for the current variable */
987 780750 : GetConfigOptionValues(conf, (const char **) values);
988 :
989 : /* build a tuple */
990 780750 : tuple = BuildTupleFromCStrings(attinmeta, values);
991 :
992 : /* make the tuple into a datum */
993 780750 : result = HeapTupleGetDatum(tuple);
994 :
995 780750 : SRF_RETURN_NEXT(funcctx, result);
996 : }
997 :
998 : /* do when there is no more left */
999 1890 : 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
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 */
1027 4 : InitMaterializedSRF(fcinfo, 0);
1028 :
1029 : /* Process the results and create a tuplestore */
1030 119 : 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 :
1035 115 : memset(values, 0, sizeof(values));
1036 115 : memset(nulls, 0, sizeof(nulls));
1037 :
1038 : /* sourcefile */
1039 115 : if (conf->filename)
1040 115 : values[0] = PointerGetDatum(cstring_to_text(conf->filename));
1041 : else
1042 0 : nulls[0] = true;
1043 :
1044 : /* sourceline (not meaningful if no sourcefile) */
1045 115 : if (conf->filename)
1046 115 : values[1] = Int32GetDatum(conf->sourceline);
1047 : else
1048 0 : nulls[1] = true;
1049 :
1050 : /* seqno */
1051 115 : values[2] = Int32GetDatum(seqno);
1052 :
1053 : /* name */
1054 115 : if (conf->name)
1055 115 : values[3] = PointerGetDatum(cstring_to_text(conf->name));
1056 : else
1057 0 : nulls[3] = true;
1058 :
1059 : /* setting */
1060 115 : if (conf->value)
1061 115 : values[4] = PointerGetDatum(cstring_to_text(conf->value));
1062 : else
1063 0 : nulls[4] = true;
1064 :
1065 : /* applied */
1066 115 : values[5] = BoolGetDatum(conf->applied);
1067 :
1068 : /* error */
1069 115 : if (conf->errmsg)
1070 0 : values[6] = PointerGetDatum(cstring_to_text(conf->errmsg));
1071 : else
1072 115 : nulls[6] = true;
1073 :
1074 : /* shove row into tuplestore */
1075 115 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1076 : }
1077 :
1078 4 : return (Datum) 0;
1079 : }
|