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