Age Owner Branch data TLA Line data Source code
1 : : /*--------------------------------------------------------------------
2 : : *
3 : : * guc_funcs.c
4 : : *
5 : : * SQL commands and SQL-accessible functions related to GUC variables.
6 : : *
7 : : *
8 : : * Copyright (c) 2000-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
579 tgl@sss.pgh.pa.us 43 :CBC 17016 : ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
44 : : {
45 : 17016 : 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 [ - + ]: 17016 : if (IsInParallelMode())
579 tgl@sss.pgh.pa.us 52 [ # # ]:UBC 0 : ereport(ERROR,
53 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
54 : : errmsg("cannot set parameters during a parallel operation")));
55 : :
579 tgl@sss.pgh.pa.us 56 [ + + + + :CBC 17016 : switch (stmt->kind)
- - ]
57 : : {
58 : 14679 : case VAR_SET_VALUE:
59 : : case VAR_SET_CURRENT:
60 [ + + ]: 14679 : if (stmt->is_local)
61 : 550 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
62 [ + + ]: 29358 : (void) set_config_option(stmt->name,
63 : 14679 : ExtractSetVariableArgs(stmt),
64 : 14679 : (superuser() ? PGC_SUSET : PGC_USERSET),
65 : : PGC_S_SESSION,
66 : : action, true, 0, false);
67 : 14620 : break;
68 : 290 : 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 [ + + ]: 290 : if (strcmp(stmt->name, "TRANSACTION") == 0)
78 : : {
79 : : ListCell *head;
80 : :
81 : 260 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
82 : :
83 [ + - + + : 706 : foreach(head, stmt->args)
+ + ]
84 : : {
85 : 456 : DefElem *item = (DefElem *) lfirst(head);
86 : :
87 [ + + ]: 456 : if (strcmp(item->defname, "transaction_isolation") == 0)
88 : 209 : SetPGVariable("transaction_isolation",
89 : 209 : list_make1(item->arg), stmt->is_local);
90 [ + + ]: 247 : else if (strcmp(item->defname, "transaction_read_only") == 0)
91 : 244 : SetPGVariable("transaction_read_only",
92 : 244 : list_make1(item->arg), stmt->is_local);
93 [ + - ]: 3 : else if (strcmp(item->defname, "transaction_deferrable") == 0)
94 : 3 : SetPGVariable("transaction_deferrable",
95 : 3 : list_make1(item->arg), stmt->is_local);
96 : : else
579 tgl@sss.pgh.pa.us 97 [ # # ]:UBC 0 : elog(ERROR, "unexpected SET TRANSACTION element: %s",
98 : : item->defname);
99 : : }
100 : : }
579 tgl@sss.pgh.pa.us 101 [ + + ]:CBC 30 : else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0)
102 : : {
103 : : ListCell *head;
104 : :
105 [ + - + + : 12 : foreach(head, stmt->args)
+ + ]
106 : : {
107 : 6 : DefElem *item = (DefElem *) lfirst(head);
108 : :
109 [ - + ]: 6 : if (strcmp(item->defname, "transaction_isolation") == 0)
579 tgl@sss.pgh.pa.us 110 :UBC 0 : SetPGVariable("default_transaction_isolation",
111 : 0 : list_make1(item->arg), stmt->is_local);
579 tgl@sss.pgh.pa.us 112 [ + - ]:CBC 6 : else if (strcmp(item->defname, "transaction_read_only") == 0)
113 : 6 : SetPGVariable("default_transaction_read_only",
114 : 6 : list_make1(item->arg), stmt->is_local);
579 tgl@sss.pgh.pa.us 115 [ # # ]:UBC 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 : : }
579 tgl@sss.pgh.pa.us 123 [ + - ]:CBC 24 : else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0)
124 : : {
125 : 24 : A_Const *con = linitial_node(A_Const, stmt->args);
126 : :
127 [ - + ]: 24 : if (stmt->is_local)
579 tgl@sss.pgh.pa.us 128 [ # # ]:UBC 0 : ereport(ERROR,
129 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
130 : : errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));
131 : :
579 tgl@sss.pgh.pa.us 132 :CBC 24 : WarnNoTransactionBlock(isTopLevel, "SET TRANSACTION");
133 : 24 : ImportSnapshot(strVal(&con->val));
134 : : }
135 : : else
579 tgl@sss.pgh.pa.us 136 [ # # ]:UBC 0 : elog(ERROR, "unexpected SET MULTI element: %s",
137 : : stmt->name);
579 tgl@sss.pgh.pa.us 138 :CBC 274 : break;
139 : 74 : case VAR_SET_DEFAULT:
140 [ + + ]: 74 : if (stmt->is_local)
141 : 1 : WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
142 : : /* fall through */
143 : : case VAR_RESET:
144 [ + + ]: 2047 : (void) set_config_option(stmt->name,
145 : : NULL,
146 : 2047 : (superuser() ? PGC_SUSET : PGC_USERSET),
147 : : PGC_S_SESSION,
148 : : action, true, 0, false);
149 : 2032 : break;
579 tgl@sss.pgh.pa.us 150 :UBC 0 : case VAR_RESET_ALL:
151 : 0 : ResetAllOptions();
152 : 0 : break;
153 : : }
154 : :
155 : : /* Invoke the post-alter hook for setting this GUC variable, by name. */
579 tgl@sss.pgh.pa.us 156 [ + + ]:CBC 16926 : InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
157 : : ACL_SET, stmt->kind, false);
158 : 16925 : }
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 : 15354 : ExtractSetVariableArgs(VariableSetStmt *stmt)
168 : : {
169 [ + - + ]: 15354 : switch (stmt->kind)
170 : : {
171 : 15340 : case VAR_SET_VALUE:
172 : 15340 : return flatten_set_variable_args(stmt->name, stmt->args);
579 tgl@sss.pgh.pa.us 173 :UBC 0 : case VAR_SET_CURRENT:
333 akorotkov@postgresql 174 : 0 : return GetConfigOptionByName(stmt->name, NULL, false);
579 tgl@sss.pgh.pa.us 175 :CBC 14 : default:
176 : 14 : 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 : 19383 : 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 [ + + ]: 19383 : if (args == NIL)
201 : 3 : 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 : 19380 : record = find_option(name, false, true, WARNING);
208 [ + + ]: 19380 : if (record)
209 : 19349 : flags = record->flags;
210 : : else
211 : 31 : flags = 0;
212 : :
213 : : /* Complain if list input and non-list variable */
214 [ + + - + ]: 38036 : if ((flags & GUC_LIST_INPUT) == 0 &&
215 : 18656 : list_length(args) != 1)
579 tgl@sss.pgh.pa.us 216 [ # # ]:UBC 0 : ereport(ERROR,
217 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
218 : : errmsg("SET %s takes only one argument", name)));
219 : :
579 tgl@sss.pgh.pa.us 220 :CBC 19380 : 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 [ + - + + : 38849 : foreach(l, args)
+ + ]
228 : : {
229 : 19469 : Node *arg = (Node *) lfirst(l);
230 : : char *val;
231 : 19469 : TypeName *typeName = NULL;
232 : : A_Const *con;
233 : :
234 [ + + ]: 19469 : if (l != list_head(args))
235 : 89 : appendStringInfoString(&buf, ", ");
236 : :
237 [ - + ]: 19469 : if (IsA(arg, TypeCast))
238 : : {
579 tgl@sss.pgh.pa.us 239 :UBC 0 : TypeCast *tc = (TypeCast *) arg;
240 : :
241 : 0 : arg = tc->arg;
242 : 0 : typeName = tc->typeName;
243 : : }
244 : :
579 tgl@sss.pgh.pa.us 245 [ - + ]:CBC 19469 : if (!IsA(arg, A_Const))
579 tgl@sss.pgh.pa.us 246 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
579 tgl@sss.pgh.pa.us 247 :CBC 19469 : con = (A_Const *) arg;
248 : :
249 [ + + + - ]: 19469 : switch (nodeTag(&con->val))
250 : : {
251 : 7800 : case T_Integer:
252 : 7800 : appendStringInfo(&buf, "%d", intVal(&con->val));
253 : 7800 : break;
254 : 109 : case T_Float:
255 : : /* represented as a string, so just copy it */
256 : 109 : appendStringInfoString(&buf, castNode(Float, &con->val)->fval);
257 : 109 : break;
258 : 11560 : case T_String:
259 : 11560 : val = strVal(&con->val);
260 [ - + ]: 11560 : 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 : :
579 tgl@sss.pgh.pa.us 272 :UBC 0 : typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
273 [ # # ]: 0 : 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 : : */
579 tgl@sss.pgh.pa.us 292 [ + + ]:CBC 11560 : if (flags & GUC_LIST_QUOTE)
293 : 375 : appendStringInfoString(&buf, quote_identifier(val));
294 : : else
295 : 11185 : appendStringInfoString(&buf, val);
296 : : }
297 : 11560 : break;
579 tgl@sss.pgh.pa.us 298 :UBC 0 : default:
299 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
300 : : (int) nodeTag(&con->val));
301 : : break;
302 : : }
303 : : }
304 : :
579 tgl@sss.pgh.pa.us 305 :CBC 19380 : 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 : 4043 : SetPGVariable(const char *name, List *args, bool is_local)
316 : : {
317 : 4043 : char *argstring = flatten_set_variable_args(name, args);
318 : :
319 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
320 [ + + ]: 4043 : (void) set_config_option(name,
321 : : argstring,
322 : 4043 : (superuser() ? PGC_SUSET : PGC_USERSET),
323 : : PGC_S_SESSION,
324 : : is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
325 : : true, 0, false);
326 : 4033 : }
327 : :
328 : : /*
329 : : * SET command wrapped as a SQL callable function.
330 : : */
331 : : Datum
332 : 2373 : set_config_by_name(PG_FUNCTION_ARGS)
333 : : {
334 : : char *name;
335 : : char *value;
336 : : char *new_value;
337 : : bool is_local;
338 : :
339 [ - + ]: 2373 : if (PG_ARGISNULL(0))
579 tgl@sss.pgh.pa.us 340 [ # # ]:UBC 0 : ereport(ERROR,
341 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
342 : : errmsg("SET requires parameter name")));
343 : :
344 : : /* Get the GUC variable name */
579 tgl@sss.pgh.pa.us 345 :CBC 2373 : name = TextDatumGetCString(PG_GETARG_DATUM(0));
346 : :
347 : : /* Get the desired value or set to NULL for a reset request */
348 [ - + ]: 2373 : if (PG_ARGISNULL(1))
579 tgl@sss.pgh.pa.us 349 :UBC 0 : value = NULL;
350 : : else
579 tgl@sss.pgh.pa.us 351 :CBC 2373 : 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 [ - + ]: 2373 : if (PG_ARGISNULL(2))
579 tgl@sss.pgh.pa.us 358 :UBC 0 : is_local = false;
359 : : else
579 tgl@sss.pgh.pa.us 360 :CBC 2373 : is_local = PG_GETARG_BOOL(2);
361 : :
362 : : /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
363 [ + + ]: 2373 : (void) set_config_option(name,
364 : : value,
365 : 2373 : (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 : 2372 : new_value = GetConfigOptionByName(name, NULL, false);
372 : :
373 : : /* Convert return string to text */
374 : 2372 : PG_RETURN_TEXT_P(cstring_to_text(new_value));
375 : : }
376 : :
377 : :
378 : : /*
379 : : * SHOW command
380 : : */
381 : : void
382 : 916 : GetPGVariable(const char *name, DestReceiver *dest)
383 : : {
384 [ + + ]: 916 : if (guc_name_compare(name, "all") == 0)
385 : 2 : ShowAllGUCConfig(dest);
386 : : else
387 : 914 : ShowGUCConfigOption(name, dest);
388 : 916 : }
389 : :
390 : : /*
391 : : * Get a tuple descriptor for SHOW's result
392 : : */
393 : : TupleDesc
394 : 402 : GetPGVariableResultDesc(const char *name)
395 : : {
396 : : TupleDesc tupdesc;
397 : :
398 [ - + ]: 402 : if (guc_name_compare(name, "all") == 0)
399 : : {
400 : : /* need a tuple descriptor representing three TEXT columns */
579 tgl@sss.pgh.pa.us 401 :UBC 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 */
579 tgl@sss.pgh.pa.us 414 :CBC 402 : (void) GetConfigOptionByName(name, &varname, false);
415 : :
416 : : /* need a tuple descriptor representing a single TEXT column */
417 : 388 : tupdesc = CreateTemplateTupleDesc(1);
418 : 388 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
419 : : TEXTOID, -1, 0);
420 : : }
421 : 388 : return tupdesc;
422 : : }
423 : :
424 : : /*
425 : : * SHOW one variable
426 : : */
427 : : static void
428 : 914 : 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 : 914 : value = GetConfigOptionByName(name, &varname, false);
437 : :
438 : : /* need a tuple descriptor representing a single TEXT column */
439 : 914 : tupdesc = CreateTemplateTupleDesc(1);
440 : 914 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname,
441 : : TEXTOID, -1, 0);
442 : :
443 : : /* prepare for projection of tuples */
444 : 914 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
445 : :
446 : : /* Send it */
447 : 914 : do_text_output_oneline(tstate, value);
448 : :
449 : 914 : end_tup_output(tstate);
450 : 914 : }
451 : :
452 : : /*
453 : : * SHOW ALL command
454 : : */
455 : : static void
456 : 2 : 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 : 2 : bool isnull[3] = {false, false, false};
464 : :
465 : : /* collect the variables, in sorted order */
548 466 : 2 : guc_vars = get_guc_variables(&num_vars);
467 : :
468 : : /* need a tuple descriptor representing three TEXT columns */
579 469 : 2 : tupdesc = CreateTemplateTupleDesc(3);
470 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, "name",
471 : : TEXTOID, -1, 0);
472 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 2, "setting",
473 : : TEXTOID, -1, 0);
474 : 2 : TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 3, "description",
475 : : TEXTOID, -1, 0);
476 : :
477 : : /* prepare for projection of tuples */
478 : 2 : tstate = begin_tup_output_tupdesc(dest, tupdesc, &TTSOpsVirtual);
479 : :
548 480 [ + + ]: 775 : for (int i = 0; i < num_vars; i++)
481 : : {
482 : 773 : struct config_generic *conf = guc_vars[i];
483 : : char *setting;
484 : :
485 : : /* skip if marked NO_SHOW_ALL */
443 486 [ + + ]: 773 : if (conf->flags & GUC_NO_SHOW_ALL)
487 : 12 : continue;
488 : :
489 : : /* return only options visible to the current user */
490 [ - + ]: 761 : if (!ConfigOptionIsVisible(conf))
579 tgl@sss.pgh.pa.us 491 :UBC 0 : continue;
492 : :
493 : : /* assign to the values array */
579 tgl@sss.pgh.pa.us 494 :CBC 761 : values[0] = PointerGetDatum(cstring_to_text(conf->name));
495 : :
496 : 761 : setting = ShowGUCOption(conf, true);
497 [ + - ]: 761 : if (setting)
498 : : {
499 : 761 : values[1] = PointerGetDatum(cstring_to_text(setting));
500 : 761 : isnull[1] = false;
501 : : }
502 : : else
503 : : {
579 tgl@sss.pgh.pa.us 504 :UBC 0 : values[1] = PointerGetDatum(NULL);
505 : 0 : isnull[1] = true;
506 : : }
507 : :
579 tgl@sss.pgh.pa.us 508 [ + - ]:CBC 761 : if (conf->short_desc)
509 : : {
510 : 761 : values[2] = PointerGetDatum(cstring_to_text(conf->short_desc));
511 : 761 : isnull[2] = false;
512 : : }
513 : : else
514 : : {
579 tgl@sss.pgh.pa.us 515 :UBC 0 : values[2] = PointerGetDatum(NULL);
516 : 0 : isnull[2] = true;
517 : : }
518 : :
519 : : /* send it to dest */
579 tgl@sss.pgh.pa.us 520 :CBC 761 : do_tup_output(tstate, values, isnull);
521 : :
522 : : /* clean up */
523 : 761 : pfree(DatumGetPointer(values[0]));
524 [ + - ]: 761 : if (setting)
525 : : {
526 : 761 : pfree(setting);
527 : 761 : pfree(DatumGetPointer(values[1]));
528 : : }
529 [ + - ]: 761 : if (conf->short_desc)
530 : 761 : pfree(DatumGetPointer(values[2]));
531 : : }
532 : :
533 : 2 : end_tup_output(tstate);
534 : 2 : }
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 : 1912 : pg_settings_get_flags(PG_FUNCTION_ARGS)
543 : : {
544 : : #define MAX_GUC_FLAGS 6
545 : 1912 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
546 : : struct config_generic *record;
547 : 1912 : int cnt = 0;
548 : : Datum flags[MAX_GUC_FLAGS];
549 : : ArrayType *a;
550 : :
551 : 1912 : record = find_option(varname, false, true, ERROR);
552 : :
553 : : /* return NULL if no such variable */
554 [ + + ]: 1912 : if (record == NULL)
555 : 3 : PG_RETURN_NULL();
556 : :
557 [ + + ]: 1909 : if (record->flags & GUC_EXPLAIN)
558 : 300 : flags[cnt++] = CStringGetTextDatum("EXPLAIN");
565 559 [ + + ]: 1909 : if (record->flags & GUC_NO_RESET)
560 : 15 : flags[cnt++] = CStringGetTextDatum("NO_RESET");
579 561 [ + + ]: 1909 : if (record->flags & GUC_NO_RESET_ALL)
562 : 15 : flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
563 [ - + ]: 1909 : if (record->flags & GUC_NO_SHOW_ALL)
579 tgl@sss.pgh.pa.us 564 :UBC 0 : flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
579 tgl@sss.pgh.pa.us 565 [ + + ]:CBC 1909 : if (record->flags & GUC_NOT_IN_SAMPLE)
566 : 250 : flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
567 [ + + ]: 1909 : if (record->flags & GUC_RUNTIME_COMPUTED)
568 : 25 : flags[cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED");
569 : :
570 [ - + ]: 1909 : Assert(cnt <= MAX_GUC_FLAGS);
571 : :
572 : : /* Returns the record as Datum */
573 : 1909 : a = construct_array_builtin(flags, cnt, TEXTOID);
574 : 1909 : 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
443 581 : 729771 : ConfigOptionIsVisible(struct config_generic *conf)
582 : : {
583 [ + + ]: 729771 : if ((conf->flags & GUC_SUPERUSER_ONLY) &&
584 [ + + ]: 37887 : !has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
585 : 1 : return false;
586 : : else
587 : 729770 : return true;
588 : : }
589 : :
590 : : /*
591 : : * Extract fields to show in pg_settings for given variable.
592 : : */
593 : : static void
594 : 724461 : GetConfigOptionValues(struct config_generic *conf, const char **values)
595 : : {
596 : : char buffer[256];
597 : :
598 : : /* first get the generic attributes */
599 : :
600 : : /* name */
579 601 : 724461 : values[0] = conf->name;
602 : :
603 : : /* setting: use ShowGUCOption in order to avoid duplicating the logic */
604 : 724461 : values[1] = ShowGUCOption(conf, false);
605 : :
606 : : /* unit, if any (NULL is fine) */
607 : 724461 : values[2] = get_config_unit_name(conf->flags);
608 : :
609 : : /* group */
610 : 724461 : values[3] = _(config_group_names[conf->group]);
611 : :
612 : : /* short_desc */
613 [ + - ]: 724461 : values[4] = conf->short_desc != NULL ? _(conf->short_desc) : NULL;
614 : :
615 : : /* extra_desc */
616 [ + + ]: 724461 : values[5] = conf->long_desc != NULL ? _(conf->long_desc) : NULL;
617 : :
618 : : /* context */
619 : 724461 : values[6] = GucContext_Names[conf->context];
620 : :
621 : : /* vartype */
622 : 724461 : values[7] = config_type_names[conf->vartype];
623 : :
624 : : /* source */
625 : 724461 : values[8] = GucSource_Names[conf->source];
626 : :
627 : : /* now get the type specific attributes */
628 [ + + + + : 724461 : switch (conf->vartype)
+ - ]
629 : : {
630 : 211840 : case PGC_BOOL:
631 : : {
632 : 211840 : struct config_bool *lconf = (struct config_bool *) conf;
633 : :
634 : : /* min_val */
635 : 211840 : values[9] = NULL;
636 : :
637 : : /* max_val */
638 : 211840 : values[10] = NULL;
639 : :
640 : : /* enumvals */
641 : 211840 : values[11] = NULL;
642 : :
643 : : /* boot_val */
644 [ + + ]: 211840 : values[12] = pstrdup(lconf->boot_val ? "on" : "off");
645 : :
646 : : /* reset_val */
647 [ + + ]: 211840 : values[13] = pstrdup(lconf->reset_val ? "on" : "off");
648 : : }
649 : 211840 : break;
650 : :
651 : 261102 : case PGC_INT:
652 : : {
653 : 261102 : struct config_int *lconf = (struct config_int *) conf;
654 : :
655 : : /* min_val */
656 : 261102 : snprintf(buffer, sizeof(buffer), "%d", lconf->min);
657 : 261102 : values[9] = pstrdup(buffer);
658 : :
659 : : /* max_val */
660 : 261102 : snprintf(buffer, sizeof(buffer), "%d", lconf->max);
661 : 261102 : values[10] = pstrdup(buffer);
662 : :
663 : : /* enumvals */
664 : 261102 : values[11] = NULL;
665 : :
666 : : /* boot_val */
667 : 261102 : snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val);
668 : 261102 : values[12] = pstrdup(buffer);
669 : :
670 : : /* reset_val */
671 : 261102 : snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val);
672 : 261102 : values[13] = pstrdup(buffer);
673 : : }
674 : 261102 : break;
675 : :
676 : 45408 : case PGC_REAL:
677 : : {
678 : 45408 : struct config_real *lconf = (struct config_real *) conf;
679 : :
680 : : /* min_val */
681 : 45408 : snprintf(buffer, sizeof(buffer), "%g", lconf->min);
682 : 45408 : values[9] = pstrdup(buffer);
683 : :
684 : : /* max_val */
685 : 45408 : snprintf(buffer, sizeof(buffer), "%g", lconf->max);
686 : 45408 : values[10] = pstrdup(buffer);
687 : :
688 : : /* enumvals */
689 : 45408 : values[11] = NULL;
690 : :
691 : : /* boot_val */
692 : 45408 : snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val);
693 : 45408 : values[12] = pstrdup(buffer);
694 : :
695 : : /* reset_val */
696 : 45408 : snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val);
697 : 45408 : values[13] = pstrdup(buffer);
698 : : }
699 : 45408 : break;
700 : :
701 : 132358 : case PGC_STRING:
702 : : {
703 : 132358 : struct config_string *lconf = (struct config_string *) conf;
704 : :
705 : : /* min_val */
706 : 132358 : values[9] = NULL;
707 : :
708 : : /* max_val */
709 : 132358 : values[10] = NULL;
710 : :
711 : : /* enumvals */
712 : 132358 : values[11] = NULL;
713 : :
714 : : /* boot_val */
715 [ + + ]: 132358 : if (lconf->boot_val == NULL)
716 : 11352 : values[12] = NULL;
717 : : else
718 : 121006 : values[12] = pstrdup(lconf->boot_val);
719 : :
720 : : /* reset_val */
721 [ + + ]: 132358 : if (lconf->reset_val == NULL)
722 : 1892 : values[13] = NULL;
723 : : else
724 : 130466 : values[13] = pstrdup(lconf->reset_val);
725 : : }
726 : 132358 : break;
727 : :
728 : 73753 : case PGC_ENUM:
729 : : {
730 : 73753 : struct config_enum *lconf = (struct config_enum *) conf;
731 : :
732 : : /* min_val */
733 : 73753 : values[9] = NULL;
734 : :
735 : : /* max_val */
736 : 73753 : values[10] = NULL;
737 : :
738 : : /* enumvals */
739 : :
740 : : /*
741 : : * NOTE! enumvals with double quotes in them are not
742 : : * supported!
743 : : */
744 : 73753 : values[11] = config_enum_get_options((struct config_enum *) conf,
745 : : "{\"", "\"}", "\",\"");
746 : :
747 : : /* boot_val */
748 : 73753 : values[12] = pstrdup(config_enum_lookup_by_value(lconf,
749 : : lconf->boot_val));
750 : :
751 : : /* reset_val */
752 : 73753 : values[13] = pstrdup(config_enum_lookup_by_value(lconf,
753 : : lconf->reset_val));
754 : : }
755 : 73753 : break;
756 : :
579 tgl@sss.pgh.pa.us 757 :UBC 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 : : */
579 tgl@sss.pgh.pa.us 786 [ + + + - ]:CBC 779025 : if (conf->source == PGC_S_FILE &&
787 : 54564 : has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
788 : : {
789 : 54564 : values[14] = conf->sourcefile;
790 : 54564 : snprintf(buffer, sizeof(buffer), "%d", conf->sourceline);
791 : 54564 : values[15] = pstrdup(buffer);
792 : : }
793 : : else
794 : : {
795 : 669897 : values[14] = NULL;
796 : 669897 : values[15] = NULL;
797 : : }
798 : :
799 [ - + ]: 724461 : values[16] = (conf->status & GUC_PENDING_RESTART) ? "t" : "f";
800 : 724461 : }
801 : :
802 : : /*
803 : : * show_config_by_name - equiv to SHOW X command but implemented as
804 : : * a function.
805 : : */
806 : : Datum
807 : 847 : show_config_by_name(PG_FUNCTION_ARGS)
808 : : {
809 : 847 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
810 : : char *varval;
811 : :
812 : : /* Get the value */
813 : 847 : varval = GetConfigOptionByName(varname, NULL, false);
814 : :
815 : : /* Convert to text */
816 : 844 : 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 : 12 : show_config_by_name_missing_ok(PG_FUNCTION_ARGS)
826 : : {
827 : 12 : char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
828 : 12 : bool missing_ok = PG_GETARG_BOOL(1);
829 : : char *varval;
830 : :
831 : : /* Get the value */
832 : 12 : varval = GetConfigOptionByName(varname, NULL, missing_ok);
833 : :
834 : : /* return NULL if no such variable */
835 [ + + ]: 9 : if (varval == NULL)
836 : 3 : PG_RETURN_NULL();
837 : :
838 : : /* Convert to text */
839 : 6 : 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 : 726353 : 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 [ + + ]: 726353 : if (SRF_IS_FIRSTCALL())
862 : : {
863 : : /* create a function context for cross-call persistence */
864 : 1892 : funcctx = SRF_FIRSTCALL_INIT();
865 : :
866 : : /*
867 : : * switch to memory context appropriate for multiple function calls
868 : : */
869 : 1892 : 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 : 1892 : tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS);
876 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
877 : : TEXTOID, -1, 0);
878 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
879 : : TEXTOID, -1, 0);
880 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "unit",
881 : : TEXTOID, -1, 0);
882 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "category",
883 : : TEXTOID, -1, 0);
884 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "short_desc",
885 : : TEXTOID, -1, 0);
886 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "extra_desc",
887 : : TEXTOID, -1, 0);
888 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 7, "context",
889 : : TEXTOID, -1, 0);
890 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 8, "vartype",
891 : : TEXTOID, -1, 0);
892 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 9, "source",
893 : : TEXTOID, -1, 0);
894 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 10, "min_val",
895 : : TEXTOID, -1, 0);
896 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val",
897 : : TEXTOID, -1, 0);
898 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals",
899 : : TEXTARRAYOID, -1, 0);
900 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val",
901 : : TEXTOID, -1, 0);
902 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val",
903 : : TEXTOID, -1, 0);
904 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile",
905 : : TEXTOID, -1, 0);
906 : 1892 : TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline",
907 : : INT4OID, -1, 0);
908 : 1892 : 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 : 1892 : attinmeta = TupleDescGetAttInMetadata(tupdesc);
916 : 1892 : funcctx->attinmeta = attinmeta;
917 : :
918 : : /* collect the variables, in sorted order */
548 919 : 1892 : guc_vars = get_guc_variables(&num_vars);
920 : :
921 : : /* use user_fctx to remember the array location */
922 : 1892 : funcctx->user_fctx = guc_vars;
923 : :
924 : : /* total number of tuples to be returned */
925 : 1892 : funcctx->max_calls = num_vars;
926 : :
579 927 : 1892 : MemoryContextSwitchTo(oldcontext);
928 : : }
929 : :
930 : : /* stuff done on every call of the function */
931 : 726353 : funcctx = SRF_PERCALL_SETUP();
932 : :
548 933 : 726353 : guc_vars = (struct config_generic **) funcctx->user_fctx;
579 934 : 726353 : call_cntr = funcctx->call_cntr;
935 : 726353 : max_calls = funcctx->max_calls;
936 : 726353 : attinmeta = funcctx->attinmeta;
937 : :
443 938 [ + + ]: 737714 : while (call_cntr < max_calls) /* do when there is more left to send */
939 : : {
940 : 735822 : 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 [ + + ]: 735822 : if ((conf->flags & GUC_NO_SHOW_ALL) ||
947 [ - + ]: 724461 : !ConfigOptionIsVisible(conf))
948 : : {
949 : 11361 : call_cntr = ++funcctx->call_cntr;
950 : 11361 : continue;
951 : : }
952 : :
953 : : /* extract values for the current variable */
954 : 724461 : GetConfigOptionValues(conf, (const char **) values);
955 : :
956 : : /* build a tuple */
579 957 : 724461 : tuple = BuildTupleFromCStrings(attinmeta, values);
958 : :
959 : : /* make the tuple into a datum */
960 : 724461 : result = HeapTupleGetDatum(tuple);
961 : :
962 : 724461 : SRF_RETURN_NEXT(funcctx, result);
963 : : }
964 : :
965 : : /* do when there is no more left */
443 966 : 1892 : 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
579 984 : 3 : show_all_file_settings(PG_FUNCTION_ARGS)
985 : : {
986 : : #define NUM_PG_FILE_SETTINGS_ATTS 7
987 : 3 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
988 : : ConfigVariable *conf;
989 : : int seqno;
990 : :
991 : : /* Scan the config files using current context as workspace */
992 : 3 : conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
993 : :
994 : : /* Build a tuplestore to return our results in */
544 michael@paquier.xyz 995 : 3 : InitMaterializedSRF(fcinfo, 0);
996 : :
997 : : /* Process the results and create a tuplestore */
579 tgl@sss.pgh.pa.us 998 [ + + ]: 82 : 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 : 79 : memset(values, 0, sizeof(values));
1004 : 79 : memset(nulls, 0, sizeof(nulls));
1005 : :
1006 : : /* sourcefile */
1007 [ + - ]: 79 : if (conf->filename)
1008 : 79 : values[0] = PointerGetDatum(cstring_to_text(conf->filename));
1009 : : else
579 tgl@sss.pgh.pa.us 1010 :UBC 0 : nulls[0] = true;
1011 : :
1012 : : /* sourceline (not meaningful if no sourcefile) */
579 tgl@sss.pgh.pa.us 1013 [ + - ]:CBC 79 : if (conf->filename)
1014 : 79 : values[1] = Int32GetDatum(conf->sourceline);
1015 : : else
579 tgl@sss.pgh.pa.us 1016 :UBC 0 : nulls[1] = true;
1017 : :
1018 : : /* seqno */
579 tgl@sss.pgh.pa.us 1019 :CBC 79 : values[2] = Int32GetDatum(seqno);
1020 : :
1021 : : /* name */
1022 [ + - ]: 79 : if (conf->name)
1023 : 79 : values[3] = PointerGetDatum(cstring_to_text(conf->name));
1024 : : else
579 tgl@sss.pgh.pa.us 1025 :UBC 0 : nulls[3] = true;
1026 : :
1027 : : /* setting */
579 tgl@sss.pgh.pa.us 1028 [ + - ]:CBC 79 : if (conf->value)
1029 : 79 : values[4] = PointerGetDatum(cstring_to_text(conf->value));
1030 : : else
579 tgl@sss.pgh.pa.us 1031 :UBC 0 : nulls[4] = true;
1032 : :
1033 : : /* applied */
579 tgl@sss.pgh.pa.us 1034 :CBC 79 : values[5] = BoolGetDatum(conf->applied);
1035 : :
1036 : : /* error */
1037 [ - + ]: 79 : if (conf->errmsg)
579 tgl@sss.pgh.pa.us 1038 :UBC 0 : values[6] = PointerGetDatum(cstring_to_text(conf->errmsg));
1039 : : else
579 tgl@sss.pgh.pa.us 1040 :CBC 79 : nulls[6] = true;
1041 : :
1042 : : /* shove row into tuplestore */
1043 : 79 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1044 : : }
1045 : :
1046 : 3 : return (Datum) 0;
1047 : : }
|