Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/commands/variable.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * variable.c
4
 *    Routines for handling specialized SET variables.
5
 *
6
 *
7
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8
 * Portions Copyright (c) 1994, Regents of the University of California
9
 *
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/commands/variable.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
17
#include "postgres.h"
18
19
#include <ctype.h>
20
21
#include "access/htup_details.h"
22
#include "access/parallel.h"
23
#include "access/xact.h"
24
#include "access/xlog.h"
25
#include "access/xlogprefetcher.h"
26
#include "catalog/pg_authid.h"
27
#include "common/string.h"
28
#include "mb/pg_wchar.h"
29
#include "miscadmin.h"
30
#include "postmaster/postmaster.h"
31
#include "postmaster/syslogger.h"
32
#include "storage/bufmgr.h"
33
#include "utils/acl.h"
34
#include "utils/backend_status.h"
35
#include "utils/datetime.h"
36
#include "utils/fmgrprotos.h"
37
#include "utils/guc_hooks.h"
38
#include "utils/snapmgr.h"
39
#include "utils/syscache.h"
40
#include "utils/timestamp.h"
41
#include "utils/tzparser.h"
42
#include "utils/varlena.h"
43
44
/*
45
 * DATESTYLE
46
 */
47
48
/*
49
 * check_datestyle: GUC check_hook for datestyle
50
 */
51
bool
52
check_datestyle(char **newval, void **extra, GucSource source)
53
2
{
54
2
  int     newDateStyle = DateStyle;
55
2
  int     newDateOrder = DateOrder;
56
2
  bool    have_style = false;
57
2
  bool    have_order = false;
58
2
  bool    ok = true;
59
2
  char     *rawstring;
60
2
  int      *myextra;
61
2
  char     *result;
62
2
  List     *elemlist;
63
2
  ListCell   *l;
64
65
  /* Need a modifiable copy of string */
66
2
  rawstring = pstrdup(*newval);
67
68
  /* Parse string into list of identifiers */
69
2
  if (!SplitIdentifierString(rawstring, ',', &elemlist))
70
0
  {
71
    /* syntax error in list */
72
0
    GUC_check_errdetail("List syntax is invalid.");
73
0
    pfree(rawstring);
74
0
    list_free(elemlist);
75
0
    return false;
76
0
  }
77
78
2
  foreach(l, elemlist)
79
4
  {
80
4
    char     *tok = (char *) lfirst(l);
81
82
    /* Ugh. Somebody ought to write a table driven version -- mjl */
83
84
4
    if (pg_strcasecmp(tok, "ISO") == 0)
85
2
    {
86
2
      if (have_style && newDateStyle != USE_ISO_DATES)
87
0
        ok = false;   /* conflicting styles */
88
2
      newDateStyle = USE_ISO_DATES;
89
2
      have_style = true;
90
2
    }
91
2
    else if (pg_strcasecmp(tok, "SQL") == 0)
92
0
    {
93
0
      if (have_style && newDateStyle != USE_SQL_DATES)
94
0
        ok = false;   /* conflicting styles */
95
0
      newDateStyle = USE_SQL_DATES;
96
0
      have_style = true;
97
0
    }
98
2
    else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
99
0
    {
100
0
      if (have_style && newDateStyle != USE_POSTGRES_DATES)
101
0
        ok = false;   /* conflicting styles */
102
0
      newDateStyle = USE_POSTGRES_DATES;
103
0
      have_style = true;
104
0
    }
105
2
    else if (pg_strcasecmp(tok, "GERMAN") == 0)
106
0
    {
107
0
      if (have_style && newDateStyle != USE_GERMAN_DATES)
108
0
        ok = false;   /* conflicting styles */
109
0
      newDateStyle = USE_GERMAN_DATES;
110
0
      have_style = true;
111
      /* GERMAN also sets DMY, unless explicitly overridden */
112
0
      if (!have_order)
113
0
        newDateOrder = DATEORDER_DMY;
114
0
    }
115
2
    else if (pg_strcasecmp(tok, "YMD") == 0)
116
0
    {
117
0
      if (have_order && newDateOrder != DATEORDER_YMD)
118
0
        ok = false;   /* conflicting orders */
119
0
      newDateOrder = DATEORDER_YMD;
120
0
      have_order = true;
121
0
    }
122
2
    else if (pg_strcasecmp(tok, "DMY") == 0 ||
123
2
         pg_strncasecmp(tok, "EURO", 4) == 0)
124
0
    {
125
0
      if (have_order && newDateOrder != DATEORDER_DMY)
126
0
        ok = false;   /* conflicting orders */
127
0
      newDateOrder = DATEORDER_DMY;
128
0
      have_order = true;
129
0
    }
130
2
    else if (pg_strcasecmp(tok, "MDY") == 0 ||
131
0
         pg_strcasecmp(tok, "US") == 0 ||
132
0
         pg_strncasecmp(tok, "NONEURO", 7) == 0)
133
2
    {
134
2
      if (have_order && newDateOrder != DATEORDER_MDY)
135
0
        ok = false;   /* conflicting orders */
136
2
      newDateOrder = DATEORDER_MDY;
137
2
      have_order = true;
138
2
    }
139
0
    else if (pg_strcasecmp(tok, "DEFAULT") == 0)
140
0
    {
141
      /*
142
       * Easiest way to get the current DEFAULT state is to fetch the
143
       * DEFAULT string from guc.c and recursively parse it.
144
       *
145
       * We can't simply "return check_datestyle(...)" because we need
146
       * to handle constructs like "DEFAULT, ISO".
147
       */
148
0
      char     *subval;
149
0
      void     *subextra = NULL;
150
151
0
      subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
152
0
      if (!subval)
153
0
      {
154
0
        ok = false;
155
0
        break;
156
0
      }
157
0
      if (!check_datestyle(&subval, &subextra, source))
158
0
      {
159
0
        guc_free(subval);
160
0
        ok = false;
161
0
        break;
162
0
      }
163
0
      myextra = (int *) subextra;
164
0
      if (!have_style)
165
0
        newDateStyle = myextra[0];
166
0
      if (!have_order)
167
0
        newDateOrder = myextra[1];
168
0
      guc_free(subval);
169
0
      guc_free(subextra);
170
0
    }
171
0
    else
172
0
    {
173
0
      GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
174
0
      pfree(rawstring);
175
0
      list_free(elemlist);
176
0
      return false;
177
0
    }
178
4
  }
179
180
2
  pfree(rawstring);
181
2
  list_free(elemlist);
182
183
2
  if (!ok)
184
0
  {
185
0
    GUC_check_errdetail("Conflicting \"DateStyle\" specifications.");
186
0
    return false;
187
0
  }
188
189
  /*
190
   * Prepare the canonical string to return.  GUC wants it guc_malloc'd.
191
   */
192
2
  result = (char *) guc_malloc(LOG, 32);
193
2
  if (!result)
194
0
    return false;
195
196
2
  switch (newDateStyle)
197
2
  {
198
2
    case USE_ISO_DATES:
199
2
      strcpy(result, "ISO");
200
2
      break;
201
0
    case USE_SQL_DATES:
202
0
      strcpy(result, "SQL");
203
0
      break;
204
0
    case USE_GERMAN_DATES:
205
0
      strcpy(result, "German");
206
0
      break;
207
0
    default:
208
0
      strcpy(result, "Postgres");
209
0
      break;
210
2
  }
211
2
  switch (newDateOrder)
212
2
  {
213
0
    case DATEORDER_YMD:
214
0
      strcat(result, ", YMD");
215
0
      break;
216
0
    case DATEORDER_DMY:
217
0
      strcat(result, ", DMY");
218
0
      break;
219
2
    default:
220
2
      strcat(result, ", MDY");
221
2
      break;
222
2
  }
223
224
2
  guc_free(*newval);
225
2
  *newval = result;
226
227
  /*
228
   * Set up the "extra" struct actually used by assign_datestyle.
229
   */
230
2
  myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
231
2
  if (!myextra)
232
0
    return false;
233
2
  myextra[0] = newDateStyle;
234
2
  myextra[1] = newDateOrder;
235
2
  *extra = myextra;
236
237
2
  return true;
238
2
}
239
240
/*
241
 * assign_datestyle: GUC assign_hook for datestyle
242
 */
243
void
244
assign_datestyle(const char *newval, void *extra)
245
2
{
246
2
  int      *myextra = (int *) extra;
247
248
2
  DateStyle = myextra[0];
249
2
  DateOrder = myextra[1];
250
2
}
251
252
253
/*
254
 * TIMEZONE
255
 */
256
257
/*
258
 * check_timezone: GUC check_hook for timezone
259
 */
260
bool
261
check_timezone(char **newval, void **extra, GucSource source)
262
2
{
263
2
  pg_tz    *new_tz;
264
2
  long    gmtoffset;
265
2
  char     *endptr;
266
2
  double    hours;
267
268
2
  if (pg_strncasecmp(*newval, "interval", 8) == 0)
269
0
  {
270
    /*
271
     * Support INTERVAL 'foo'.  This is for SQL spec compliance, not
272
     * because it has any actual real-world usefulness.
273
     */
274
0
    const char *valueptr = *newval;
275
0
    char     *val;
276
0
    Interval   *interval;
277
278
0
    valueptr += 8;
279
0
    while (isspace((unsigned char) *valueptr))
280
0
      valueptr++;
281
0
    if (*valueptr++ != '\'')
282
0
      return false;
283
0
    val = pstrdup(valueptr);
284
    /* Check and remove trailing quote */
285
0
    endptr = strchr(val, '\'');
286
0
    if (!endptr || endptr[1] != '\0')
287
0
    {
288
0
      pfree(val);
289
0
      return false;
290
0
    }
291
0
    *endptr = '\0';
292
293
    /*
294
     * Try to parse it.  XXX an invalid interval format will result in
295
     * ereport(ERROR), which is not desirable for GUC.  We did what we
296
     * could to guard against this in flatten_set_variable_args, but a
297
     * string coming in from postgresql.conf might contain anything.
298
     */
299
0
    interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
300
0
                             CStringGetDatum(val),
301
0
                             ObjectIdGetDatum(InvalidOid),
302
0
                             Int32GetDatum(-1)));
303
304
0
    pfree(val);
305
0
    if (interval->month != 0)
306
0
    {
307
0
      GUC_check_errdetail("Cannot specify months in time zone interval.");
308
0
      pfree(interval);
309
0
      return false;
310
0
    }
311
0
    if (interval->day != 0)
312
0
    {
313
0
      GUC_check_errdetail("Cannot specify days in time zone interval.");
314
0
      pfree(interval);
315
0
      return false;
316
0
    }
317
318
    /* Here we change from SQL to Unix sign convention */
319
0
    gmtoffset = -(interval->time / USECS_PER_SEC);
320
0
    new_tz = pg_tzset_offset(gmtoffset);
321
322
0
    pfree(interval);
323
0
  }
324
2
  else
325
2
  {
326
    /*
327
     * Try it as a numeric number of hours (possibly fractional).
328
     */
329
2
    hours = strtod(*newval, &endptr);
330
2
    if (endptr != *newval && *endptr == '\0')
331
0
    {
332
      /* Here we change from SQL to Unix sign convention */
333
0
      gmtoffset = -hours * SECS_PER_HOUR;
334
0
      new_tz = pg_tzset_offset(gmtoffset);
335
0
    }
336
2
    else
337
2
    {
338
      /*
339
       * Otherwise assume it is a timezone name, and try to load it.
340
       */
341
2
      new_tz = pg_tzset(*newval);
342
343
2
      if (!new_tz)
344
0
      {
345
        /* Doesn't seem to be any great value in errdetail here */
346
0
        return false;
347
0
      }
348
349
2
      if (!pg_tz_acceptable(new_tz))
350
0
      {
351
0
        GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
352
0
                 *newval);
353
0
        GUC_check_errdetail("PostgreSQL does not support leap seconds.");
354
0
        return false;
355
0
      }
356
2
    }
357
2
  }
358
359
  /* Test for failure in pg_tzset_offset, which we assume is out-of-range */
360
2
  if (!new_tz)
361
0
  {
362
0
    GUC_check_errdetail("UTC timezone offset is out of range.");
363
0
    return false;
364
0
  }
365
366
  /*
367
   * Pass back data for assign_timezone to use
368
   */
369
2
  *extra = guc_malloc(LOG, sizeof(pg_tz *));
370
2
  if (!*extra)
371
0
    return false;
372
2
  *((pg_tz **) *extra) = new_tz;
373
374
2
  return true;
375
2
}
376
377
/*
378
 * assign_timezone: GUC assign_hook for timezone
379
 */
380
void
381
assign_timezone(const char *newval, void *extra)
382
2
{
383
2
  session_timezone = *((pg_tz **) extra);
384
  /* datetime.c's cache of timezone abbrevs may now be obsolete */
385
2
  ClearTimeZoneAbbrevCache();
386
2
}
387
388
/*
389
 * show_timezone: GUC show_hook for timezone
390
 */
391
const char *
392
show_timezone(void)
393
0
{
394
0
  const char *tzn;
395
396
  /* Always show the zone's canonical name */
397
0
  tzn = pg_get_timezone_name(session_timezone);
398
399
0
  if (tzn != NULL)
400
0
    return tzn;
401
402
0
  return "unknown";
403
0
}
404
405
406
/*
407
 * LOG_TIMEZONE
408
 *
409
 * For log_timezone, we don't support the interval-based methods of setting a
410
 * zone, which are only there for SQL spec compliance not because they're
411
 * actually useful.
412
 */
413
414
/*
415
 * check_log_timezone: GUC check_hook for log_timezone
416
 */
417
bool
418
check_log_timezone(char **newval, void **extra, GucSource source)
419
2
{
420
2
  pg_tz    *new_tz;
421
422
  /*
423
   * Assume it is a timezone name, and try to load it.
424
   */
425
2
  new_tz = pg_tzset(*newval);
426
427
2
  if (!new_tz)
428
0
  {
429
    /* Doesn't seem to be any great value in errdetail here */
430
0
    return false;
431
0
  }
432
433
2
  if (!pg_tz_acceptable(new_tz))
434
0
  {
435
0
    GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
436
0
             *newval);
437
0
    GUC_check_errdetail("PostgreSQL does not support leap seconds.");
438
0
    return false;
439
0
  }
440
441
  /*
442
   * Pass back data for assign_log_timezone to use
443
   */
444
2
  *extra = guc_malloc(LOG, sizeof(pg_tz *));
445
2
  if (!*extra)
446
0
    return false;
447
2
  *((pg_tz **) *extra) = new_tz;
448
449
2
  return true;
450
2
}
451
452
/*
453
 * assign_log_timezone: GUC assign_hook for log_timezone
454
 */
455
void
456
assign_log_timezone(const char *newval, void *extra)
457
2
{
458
2
  log_timezone = *((pg_tz **) extra);
459
2
}
460
461
/*
462
 * show_log_timezone: GUC show_hook for log_timezone
463
 */
464
const char *
465
show_log_timezone(void)
466
0
{
467
0
  const char *tzn;
468
469
  /* Always show the zone's canonical name */
470
0
  tzn = pg_get_timezone_name(log_timezone);
471
472
0
  if (tzn != NULL)
473
0
    return tzn;
474
475
0
  return "unknown";
476
0
}
477
478
479
/*
480
 * TIMEZONE_ABBREVIATIONS
481
 */
482
483
/*
484
 * GUC check_hook for timezone_abbreviations
485
 */
486
bool
487
check_timezone_abbreviations(char **newval, void **extra, GucSource source)
488
2
{
489
  /*
490
   * The boot_val for timezone_abbreviations is NULL.  When we see that we
491
   * just do nothing.  If the value isn't overridden from the config file
492
   * then pg_timezone_abbrev_initialize() will eventually replace it with
493
   * "Default".  This hack has two purposes: to avoid wasting cycles loading
494
   * values that might soon be overridden from the config file, and to avoid
495
   * trying to read the timezone abbrev files during InitializeGUCOptions().
496
   * The latter doesn't work in an EXEC_BACKEND subprocess because
497
   * my_exec_path hasn't been set yet and so we can't locate PGSHAREDIR.
498
   */
499
2
  if (*newval == NULL)
500
2
  {
501
2
    Assert(source == PGC_S_DEFAULT);
502
2
    return true;
503
2
  }
504
505
  /* OK, load the file and produce a guc_malloc'd TimeZoneAbbrevTable */
506
0
  *extra = load_tzoffsets(*newval);
507
508
  /* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
509
0
  if (!*extra)
510
0
    return false;
511
512
0
  return true;
513
0
}
514
515
/*
516
 * GUC assign_hook for timezone_abbreviations
517
 */
518
void
519
assign_timezone_abbreviations(const char *newval, void *extra)
520
2
{
521
  /* Do nothing for the boot_val default of NULL */
522
2
  if (!extra)
523
2
    return;
524
525
0
  InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) extra);
526
0
}
527
528
529
/*
530
 * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE
531
 *
532
 * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and
533
 * we also always allow changes from read-write to read-only.  However,
534
 * read-only may be changed to read-write only when in a top-level transaction
535
 * that has not yet taken an initial snapshot.  Can't do it in a hot standby,
536
 * either.
537
 *
538
 * If we are not in a transaction at all, just allow the change; it means
539
 * nothing since XactReadOnly will be reset by the next StartTransaction().
540
 * The IsTransactionState() test protects us against trying to check
541
 * RecoveryInProgress() in contexts where shared memory is not accessible.
542
 * (Similarly, if we're restoring state in a parallel worker, just allow
543
 * the change.)
544
 */
545
bool
546
check_transaction_read_only(bool *newval, void **extra, GucSource source)
547
4
{
548
4
  if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker)
549
0
  {
550
    /* Can't go to r/w mode inside a r/o transaction */
551
0
    if (IsSubTransaction())
552
0
    {
553
0
      GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
554
0
      GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
555
0
      return false;
556
0
    }
557
    /* Top level transaction can't change to r/w after first snapshot. */
558
0
    if (FirstSnapshotSet)
559
0
    {
560
0
      GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
561
0
      GUC_check_errmsg("transaction read-write mode must be set before any query");
562
0
      return false;
563
0
    }
564
    /* Can't go to r/w mode while recovery is still active */
565
0
    if (RecoveryInProgress())
566
0
    {
567
0
      GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
568
0
      GUC_check_errmsg("cannot set transaction read-write mode during recovery");
569
0
      return false;
570
0
    }
571
0
  }
572
573
4
  return true;
574
4
}
575
576
/*
577
 * SET TRANSACTION ISOLATION LEVEL
578
 *
579
 * We allow idempotent changes at any time, but otherwise this can only be
580
 * changed in a toplevel transaction that has not yet taken a snapshot.
581
 *
582
 * As in check_transaction_read_only, allow it if not inside a transaction,
583
 * or if restoring state in a parallel worker.
584
 */
585
bool
586
check_transaction_isolation(int *newval, void **extra, GucSource source)
587
4
{
588
4
  int     newXactIsoLevel = *newval;
589
590
4
  if (newXactIsoLevel != XactIsoLevel &&
591
0
    IsTransactionState() && !InitializingParallelWorker)
592
0
  {
593
0
    if (FirstSnapshotSet)
594
0
    {
595
0
      GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
596
0
      GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
597
0
      return false;
598
0
    }
599
    /* We ignore a subtransaction setting it to the existing value. */
600
0
    if (IsSubTransaction())
601
0
    {
602
0
      GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
603
0
      GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
604
0
      return false;
605
0
    }
606
    /* Can't go to serializable mode while recovery is still active */
607
0
    if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
608
0
    {
609
0
      GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
610
0
      GUC_check_errmsg("cannot use serializable mode in a hot standby");
611
0
      GUC_check_errhint("You can use REPEATABLE READ instead.");
612
0
      return false;
613
0
    }
614
0
  }
615
616
4
  return true;
617
4
}
618
619
/*
620
 * SET TRANSACTION [NOT] DEFERRABLE
621
 */
622
623
bool
624
check_transaction_deferrable(bool *newval, void **extra, GucSource source)
625
4
{
626
  /* Just accept the value when restoring state in a parallel worker */
627
4
  if (InitializingParallelWorker)
628
0
    return true;
629
630
4
  if (IsSubTransaction())
631
0
  {
632
0
    GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
633
0
    GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
634
0
    return false;
635
0
  }
636
4
  if (FirstSnapshotSet)
637
0
  {
638
0
    GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
639
0
    GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
640
0
    return false;
641
0
  }
642
643
4
  return true;
644
4
}
645
646
/*
647
 * Random number seed
648
 *
649
 * We can't roll back the random sequence on error, and we don't want
650
 * config file reloads to affect it, so we only want interactive SET SEED
651
 * commands to set it.  We use the "extra" storage to ensure that rollbacks
652
 * don't try to do the operation again.
653
 */
654
655
bool
656
check_random_seed(double *newval, void **extra, GucSource source)
657
2
{
658
2
  *extra = guc_malloc(LOG, sizeof(int));
659
2
  if (!*extra)
660
0
    return false;
661
  /* Arm the assign only if source of value is an interactive SET */
662
2
  *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
663
664
2
  return true;
665
2
}
666
667
void
668
assign_random_seed(double newval, void *extra)
669
2
{
670
  /* We'll do this at most once for any setting of the GUC variable */
671
2
  if (*((int *) extra))
672
0
    DirectFunctionCall1(setseed, Float8GetDatum(newval));
673
2
  *((int *) extra) = 0;
674
2
}
675
676
const char *
677
show_random_seed(void)
678
0
{
679
0
  return "unavailable";
680
0
}
681
682
683
/*
684
 * SET CLIENT_ENCODING
685
 */
686
687
bool
688
check_client_encoding(char **newval, void **extra, GucSource source)
689
2
{
690
2
  int     encoding;
691
2
  const char *canonical_name;
692
693
  /* Look up the encoding by name */
694
2
  encoding = pg_valid_client_encoding(*newval);
695
2
  if (encoding < 0)
696
0
    return false;
697
698
  /* Get the canonical name (no aliases, uniform case) */
699
2
  canonical_name = pg_encoding_to_char(encoding);
700
701
  /*
702
   * Parallel workers send data to the leader, not the client.  They always
703
   * send data using the database encoding; therefore, we should never
704
   * actually change the client encoding in a parallel worker.  However,
705
   * during parallel worker startup, we want to accept the leader's
706
   * client_encoding setting so that anyone who looks at the value in the
707
   * worker sees the same value that they would see in the leader.  A change
708
   * other than during startup, for example due to a SET clause attached to
709
   * a function definition, should be rejected, as there is nothing we can
710
   * do inside the worker to make it take effect.
711
   */
712
2
  if (IsParallelWorker() && !InitializingParallelWorker)
713
0
  {
714
0
    GUC_check_errcode(ERRCODE_INVALID_TRANSACTION_STATE);
715
0
    GUC_check_errdetail("Cannot change \"client_encoding\" during a parallel operation.");
716
0
    return false;
717
0
  }
718
719
  /*
720
   * If we are not within a transaction then PrepareClientEncoding will not
721
   * be able to look up the necessary conversion procs.  If we are still
722
   * starting up, it will return "OK" anyway, and InitializeClientEncoding
723
   * will fix things once initialization is far enough along.  After
724
   * startup, we'll fail.  This would only happen if someone tries to change
725
   * client_encoding in postgresql.conf and then SIGHUP existing sessions.
726
   * It seems like a bad idea for client_encoding to change that way anyhow,
727
   * so we don't go out of our way to support it.
728
   *
729
   * In a parallel worker, we might as well skip PrepareClientEncoding since
730
   * we're not going to use its results.
731
   *
732
   * Note: in the postmaster, or any other process that never calls
733
   * InitializeClientEncoding, PrepareClientEncoding will always succeed,
734
   * and so will SetClientEncoding; but they won't do anything, which is OK.
735
   */
736
2
  if (!IsParallelWorker() &&
737
2
    PrepareClientEncoding(encoding) < 0)
738
0
  {
739
0
    if (IsTransactionState())
740
0
    {
741
      /* Must be a genuine no-such-conversion problem */
742
0
      GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
743
0
      GUC_check_errdetail("Conversion between %s and %s is not supported.",
744
0
                canonical_name,
745
0
                GetDatabaseEncodingName());
746
0
    }
747
0
    else
748
0
    {
749
      /* Provide a useful complaint */
750
0
      GUC_check_errdetail("Cannot change \"client_encoding\" now.");
751
0
    }
752
0
    return false;
753
0
  }
754
755
  /*
756
   * Replace the user-supplied string with the encoding's canonical name.
757
   * This gets rid of aliases and case-folding variations.
758
   *
759
   * XXX Although canonicalizing seems like a good idea in the abstract, it
760
   * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE"
761
   * as the client_encoding setting then it will read back the same way. As
762
   * a workaround, don't replace the string if it's "UNICODE".  Remove that
763
   * hack when pre-9.1 JDBC drivers are no longer in use.
764
   */
765
2
  if (strcmp(*newval, canonical_name) != 0 &&
766
0
    strcmp(*newval, "UNICODE") != 0)
767
0
  {
768
0
    guc_free(*newval);
769
0
    *newval = guc_strdup(LOG, canonical_name);
770
0
    if (!*newval)
771
0
      return false;
772
0
  }
773
774
  /*
775
   * Save the encoding's ID in *extra, for use by assign_client_encoding.
776
   */
777
2
  *extra = guc_malloc(LOG, sizeof(int));
778
2
  if (!*extra)
779
0
    return false;
780
2
  *((int *) *extra) = encoding;
781
782
2
  return true;
783
2
}
784
785
void
786
assign_client_encoding(const char *newval, void *extra)
787
2
{
788
2
  int     encoding = *((int *) extra);
789
790
  /*
791
   * In a parallel worker, we never override the client encoding that was
792
   * set by ParallelWorkerMain().
793
   */
794
2
  if (IsParallelWorker())
795
0
    return;
796
797
  /* We do not expect an error if PrepareClientEncoding succeeded */
798
2
  if (SetClientEncoding(encoding) < 0)
799
2
    elog(LOG, "SetClientEncoding(%d) failed", encoding);
800
2
}
801
802
803
/*
804
 * SET SESSION AUTHORIZATION
805
 */
806
807
typedef struct
808
{
809
  /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
810
  Oid     roleid;
811
  bool    is_superuser;
812
} role_auth_extra;
813
814
bool
815
check_session_authorization(char **newval, void **extra, GucSource source)
816
2
{
817
2
  HeapTuple roleTup;
818
2
  Form_pg_authid roleform;
819
2
  Oid     roleid;
820
2
  bool    is_superuser;
821
2
  role_auth_extra *myextra;
822
823
  /* Do nothing for the boot_val default of NULL */
824
2
  if (*newval == NULL)
825
2
    return true;
826
827
0
  if (InitializingParallelWorker)
828
0
  {
829
    /*
830
     * In parallel worker initialization, we want to copy the leader's
831
     * state even if it no longer matches the catalogs. ParallelWorkerMain
832
     * already installed the correct role OID and superuser state.
833
     */
834
0
    roleid = GetSessionUserId();
835
0
    is_superuser = GetSessionUserIsSuperuser();
836
0
  }
837
0
  else
838
0
  {
839
0
    if (!IsTransactionState())
840
0
    {
841
      /*
842
       * Can't do catalog lookups, so fail.  The result of this is that
843
       * session_authorization cannot be set in postgresql.conf, which
844
       * seems like a good thing anyway, so we don't work hard to avoid
845
       * it.
846
       */
847
0
      return false;
848
0
    }
849
850
    /*
851
     * When source == PGC_S_TEST, we don't throw a hard error for a
852
     * nonexistent user name or insufficient privileges, only a NOTICE.
853
     * See comments in guc.h.
854
     */
855
856
    /* Look up the username */
857
0
    roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
858
0
    if (!HeapTupleIsValid(roleTup))
859
0
    {
860
0
      if (source == PGC_S_TEST)
861
0
      {
862
0
        ereport(NOTICE,
863
0
            (errcode(ERRCODE_UNDEFINED_OBJECT),
864
0
             errmsg("role \"%s\" does not exist", *newval)));
865
0
        return true;
866
0
      }
867
0
      GUC_check_errmsg("role \"%s\" does not exist", *newval);
868
0
      return false;
869
0
    }
870
871
0
    roleform = (Form_pg_authid) GETSTRUCT(roleTup);
872
0
    roleid = roleform->oid;
873
0
    is_superuser = roleform->rolsuper;
874
875
0
    ReleaseSysCache(roleTup);
876
877
    /*
878
     * Only superusers may SET SESSION AUTHORIZATION a role other than
879
     * itself. Note that in case of multiple SETs in a single session, the
880
     * original authenticated user's superuserness is what matters.
881
     */
882
0
    if (roleid != GetAuthenticatedUserId() &&
883
0
      !superuser_arg(GetAuthenticatedUserId()))
884
0
    {
885
0
      if (source == PGC_S_TEST)
886
0
      {
887
0
        ereport(NOTICE,
888
0
            (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
889
0
             errmsg("permission will be denied to set session authorization \"%s\"",
890
0
                *newval)));
891
0
        return true;
892
0
      }
893
0
      GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
894
0
      GUC_check_errmsg("permission denied to set session authorization \"%s\"",
895
0
               *newval);
896
0
      return false;
897
0
    }
898
0
  }
899
900
  /* Set up "extra" struct for assign_session_authorization to use */
901
0
  myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
902
0
  if (!myextra)
903
0
    return false;
904
0
  myextra->roleid = roleid;
905
0
  myextra->is_superuser = is_superuser;
906
0
  *extra = myextra;
907
908
0
  return true;
909
0
}
910
911
void
912
assign_session_authorization(const char *newval, void *extra)
913
2
{
914
2
  role_auth_extra *myextra = (role_auth_extra *) extra;
915
916
  /* Do nothing for the boot_val default of NULL */
917
2
  if (!myextra)
918
2
    return;
919
920
0
  SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
921
0
}
922
923
924
/*
925
 * SET ROLE
926
 *
927
 * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
928
 * a translation of "none" to InvalidOid.  Otherwise this is much like
929
 * SET SESSION AUTHORIZATION.
930
 */
931
932
bool
933
check_role(char **newval, void **extra, GucSource source)
934
2
{
935
2
  HeapTuple roleTup;
936
2
  Oid     roleid;
937
2
  bool    is_superuser;
938
2
  role_auth_extra *myextra;
939
2
  Form_pg_authid roleform;
940
941
2
  if (strcmp(*newval, "none") == 0)
942
2
  {
943
    /* hardwired translation */
944
2
    roleid = InvalidOid;
945
2
    is_superuser = false;
946
2
  }
947
0
  else if (InitializingParallelWorker)
948
0
  {
949
    /*
950
     * In parallel worker initialization, we want to copy the leader's
951
     * state even if it no longer matches the catalogs. ParallelWorkerMain
952
     * already installed the correct role OID and superuser state.
953
     */
954
0
    roleid = GetCurrentRoleId();
955
0
    is_superuser = current_role_is_superuser;
956
0
  }
957
0
  else
958
0
  {
959
0
    if (!IsTransactionState())
960
0
    {
961
      /*
962
       * Can't do catalog lookups, so fail.  The result of this is that
963
       * role cannot be set in postgresql.conf, which seems like a good
964
       * thing anyway, so we don't work hard to avoid it.
965
       */
966
0
      return false;
967
0
    }
968
969
    /*
970
     * When source == PGC_S_TEST, we don't throw a hard error for a
971
     * nonexistent user name or insufficient privileges, only a NOTICE.
972
     * See comments in guc.h.
973
     */
974
975
    /* Look up the username */
976
0
    roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
977
0
    if (!HeapTupleIsValid(roleTup))
978
0
    {
979
0
      if (source == PGC_S_TEST)
980
0
      {
981
0
        ereport(NOTICE,
982
0
            (errcode(ERRCODE_UNDEFINED_OBJECT),
983
0
             errmsg("role \"%s\" does not exist", *newval)));
984
0
        return true;
985
0
      }
986
0
      GUC_check_errmsg("role \"%s\" does not exist", *newval);
987
0
      return false;
988
0
    }
989
990
0
    roleform = (Form_pg_authid) GETSTRUCT(roleTup);
991
0
    roleid = roleform->oid;
992
0
    is_superuser = roleform->rolsuper;
993
994
0
    ReleaseSysCache(roleTup);
995
996
    /* Verify that session user is allowed to become this role */
997
0
    if (!member_can_set_role(GetSessionUserId(), roleid))
998
0
    {
999
0
      if (source == PGC_S_TEST)
1000
0
      {
1001
0
        ereport(NOTICE,
1002
0
            (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1003
0
             errmsg("permission will be denied to set role \"%s\"",
1004
0
                *newval)));
1005
0
        return true;
1006
0
      }
1007
0
      GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
1008
0
      GUC_check_errmsg("permission denied to set role \"%s\"",
1009
0
               *newval);
1010
0
      return false;
1011
0
    }
1012
0
  }
1013
1014
  /* Set up "extra" struct for assign_role to use */
1015
2
  myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
1016
2
  if (!myextra)
1017
0
    return false;
1018
2
  myextra->roleid = roleid;
1019
2
  myextra->is_superuser = is_superuser;
1020
2
  *extra = myextra;
1021
1022
2
  return true;
1023
2
}
1024
1025
void
1026
assign_role(const char *newval, void *extra)
1027
2
{
1028
2
  role_auth_extra *myextra = (role_auth_extra *) extra;
1029
1030
2
  SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
1031
2
}
1032
1033
const char *
1034
show_role(void)
1035
0
{
1036
  /*
1037
   * Check whether SET ROLE is active; if not return "none".  This is a
1038
   * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
1039
   * resets SET ROLE to NONE, but we cannot set the GUC role variable from
1040
   * assign_session_authorization (because we haven't got enough info to
1041
   * call set_config_option).
1042
   */
1043
0
  if (!OidIsValid(GetCurrentRoleId()))
1044
0
    return "none";
1045
1046
  /* Otherwise we can just use the GUC string */
1047
0
  return role_string ? role_string : "none";
1048
0
}
1049
1050
1051
/*
1052
 * PATH VARIABLES
1053
 *
1054
 * check_canonical_path is used for log_directory and some other GUCs where
1055
 * all we want to do is canonicalize the represented path name.
1056
 */
1057
1058
bool
1059
check_canonical_path(char **newval, void **extra, GucSource source)
1060
4
{
1061
  /*
1062
   * Since canonicalize_path never enlarges the string, we can just modify
1063
   * newval in-place.  But watch out for NULL, which is the default value
1064
   * for external_pid_file.
1065
   */
1066
4
  if (*newval)
1067
2
    canonicalize_path(*newval);
1068
4
  return true;
1069
4
}
1070
1071
1072
/*
1073
 * MISCELLANEOUS
1074
 */
1075
1076
/*
1077
 * GUC check_hook for application_name
1078
 */
1079
bool
1080
check_application_name(char **newval, void **extra, GucSource source)
1081
2
{
1082
2
  char     *clean;
1083
2
  char     *ret;
1084
1085
  /* Only allow clean ASCII chars in the application name */
1086
2
  clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
1087
2
  if (!clean)
1088
0
    return false;
1089
1090
2
  ret = guc_strdup(LOG, clean);
1091
2
  if (!ret)
1092
0
  {
1093
0
    pfree(clean);
1094
0
    return false;
1095
0
  }
1096
1097
2
  guc_free(*newval);
1098
1099
2
  pfree(clean);
1100
2
  *newval = ret;
1101
2
  return true;
1102
2
}
1103
1104
/*
1105
 * GUC assign_hook for application_name
1106
 */
1107
void
1108
assign_application_name(const char *newval, void *extra)
1109
2
{
1110
  /* Update the pg_stat_activity view */
1111
2
  pgstat_report_appname(newval);
1112
2
}
1113
1114
/*
1115
 * GUC check_hook for cluster_name
1116
 */
1117
bool
1118
check_cluster_name(char **newval, void **extra, GucSource source)
1119
2
{
1120
2
  char     *clean;
1121
2
  char     *ret;
1122
1123
  /* Only allow clean ASCII chars in the cluster name */
1124
2
  clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
1125
2
  if (!clean)
1126
0
    return false;
1127
1128
2
  ret = guc_strdup(LOG, clean);
1129
2
  if (!ret)
1130
0
  {
1131
0
    pfree(clean);
1132
0
    return false;
1133
0
  }
1134
1135
2
  guc_free(*newval);
1136
1137
2
  pfree(clean);
1138
2
  *newval = ret;
1139
2
  return true;
1140
2
}
1141
1142
/*
1143
 * GUC assign_hook for maintenance_io_concurrency
1144
 */
1145
void
1146
assign_maintenance_io_concurrency(int newval, void *extra)
1147
2
{
1148
  /*
1149
   * Reconfigure recovery prefetching, because a setting it depends on
1150
   * changed.
1151
   */
1152
2
  maintenance_io_concurrency = newval;
1153
2
  if (AmStartupProcess())
1154
0
    XLogPrefetchReconfigure();
1155
2
}
1156
1157
/*
1158
 * GUC assign hooks that recompute io_combine_limit whenever
1159
 * io_combine_limit_guc and io_max_combine_limit are changed.  These are needed
1160
 * because the GUC subsystem doesn't support dependencies between GUCs, and
1161
 * they may be assigned in either order.
1162
 */
1163
void
1164
assign_io_max_combine_limit(int newval, void *extra)
1165
2
{
1166
2
  io_combine_limit = Min(newval, io_combine_limit_guc);
1167
2
}
1168
void
1169
assign_io_combine_limit(int newval, void *extra)
1170
2
{
1171
2
  io_combine_limit = Min(io_max_combine_limit, newval);
1172
2
}
1173
1174
1175
/*
1176
 * These check hooks do nothing more than reject non-default settings
1177
 * in builds that don't support them.
1178
 */
1179
1180
bool
1181
check_bonjour(bool *newval, void **extra, GucSource source)
1182
2
{
1183
2
#ifndef USE_BONJOUR
1184
2
  if (*newval)
1185
0
  {
1186
0
    GUC_check_errmsg("Bonjour is not supported by this build");
1187
0
    return false;
1188
0
  }
1189
2
#endif
1190
2
  return true;
1191
2
}
1192
1193
bool
1194
check_default_with_oids(bool *newval, void **extra, GucSource source)
1195
2
{
1196
2
  if (*newval)
1197
0
  {
1198
    /* check the GUC's definition for an explanation */
1199
0
    GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
1200
0
    GUC_check_errmsg("tables declared WITH OIDS are not supported");
1201
1202
0
    return false;
1203
0
  }
1204
1205
2
  return true;
1206
2
}
1207
1208
bool
1209
check_ssl(bool *newval, void **extra, GucSource source)
1210
2
{
1211
2
#ifndef USE_SSL
1212
2
  if (*newval)
1213
0
  {
1214
0
    GUC_check_errmsg("SSL is not supported by this build");
1215
0
    return false;
1216
0
  }
1217
2
#endif
1218
2
  return true;
1219
2
}
1220
1221
bool
1222
check_ssl_sni(bool *newval, void **extra, GucSource source)
1223
2
{
1224
2
#ifndef USE_SSL
1225
2
  if (*newval)
1226
0
  {
1227
0
    GUC_check_errmsg("SSL is not supported by this build");
1228
0
    return false;
1229
0
  }
1230
#else
1231
#ifndef HAVE_SSL_CTX_SET_CLIENT_HELLO_CB
1232
  if (*newval)
1233
  {
1234
    GUC_check_errmsg("SNI requires OpenSSL 1.1.1 or later");
1235
    return false;
1236
  }
1237
#endif
1238
#endif
1239
2
  return true;
1240
2
}
1241
1242
bool
1243
check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
1244
2
{
1245
2
  if (!*newval)
1246
0
  {
1247
    /* check the GUC's definition for an explanation */
1248
0
    GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
1249
0
    GUC_check_errmsg("non-standard string literals are not supported");
1250
1251
0
    return false;
1252
0
  }
1253
1254
2
  return true;
1255
2
}