Coverage Report

Created: 2026-09-28 06:55

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