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