Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * variable.c
4 : * Routines for handling specialized SET variables.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2025, 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 24494 : check_datestyle(char **newval, void **extra, GucSource source)
53 : {
54 24494 : int newDateStyle = DateStyle;
55 24494 : int newDateOrder = DateOrder;
56 24494 : bool have_style = false;
57 24494 : bool have_order = false;
58 24494 : bool ok = true;
59 : char *rawstring;
60 : int *myextra;
61 : char *result;
62 : List *elemlist;
63 : ListCell *l;
64 :
65 : /* Need a modifiable copy of string */
66 24494 : rawstring = pstrdup(*newval);
67 :
68 : /* Parse string into list of identifiers */
69 24494 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
70 : {
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 : }
77 :
78 63584 : foreach(l, elemlist)
79 : {
80 39090 : char *tok = (char *) lfirst(l);
81 :
82 : /* Ugh. Somebody ought to write a table driven version -- mjl */
83 :
84 39090 : if (pg_strcasecmp(tok, "ISO") == 0)
85 : {
86 17144 : if (have_style && newDateStyle != USE_ISO_DATES)
87 0 : ok = false; /* conflicting styles */
88 17144 : newDateStyle = USE_ISO_DATES;
89 17144 : have_style = true;
90 : }
91 21946 : else if (pg_strcasecmp(tok, "SQL") == 0)
92 : {
93 30 : if (have_style && newDateStyle != USE_SQL_DATES)
94 0 : ok = false; /* conflicting styles */
95 30 : newDateStyle = USE_SQL_DATES;
96 30 : have_style = true;
97 : }
98 21916 : else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
99 : {
100 7222 : if (have_style && newDateStyle != USE_POSTGRES_DATES)
101 0 : ok = false; /* conflicting styles */
102 7222 : newDateStyle = USE_POSTGRES_DATES;
103 7222 : have_style = true;
104 : }
105 14694 : else if (pg_strcasecmp(tok, "GERMAN") == 0)
106 : {
107 48 : if (have_style && newDateStyle != USE_GERMAN_DATES)
108 0 : ok = false; /* conflicting styles */
109 48 : newDateStyle = USE_GERMAN_DATES;
110 48 : have_style = true;
111 : /* GERMAN also sets DMY, unless explicitly overridden */
112 48 : if (!have_order)
113 48 : newDateOrder = DATEORDER_DMY;
114 : }
115 14646 : else if (pg_strcasecmp(tok, "YMD") == 0)
116 : {
117 42 : if (have_order && newDateOrder != DATEORDER_YMD)
118 0 : ok = false; /* conflicting orders */
119 42 : newDateOrder = DATEORDER_YMD;
120 42 : have_order = true;
121 : }
122 29160 : else if (pg_strcasecmp(tok, "DMY") == 0 ||
123 14556 : pg_strncasecmp(tok, "EURO", 4) == 0)
124 : {
125 66 : if (have_order && newDateOrder != DATEORDER_DMY)
126 0 : ok = false; /* conflicting orders */
127 66 : newDateOrder = DATEORDER_DMY;
128 66 : have_order = true;
129 : }
130 14556 : else if (pg_strcasecmp(tok, "MDY") == 0 ||
131 18 : pg_strcasecmp(tok, "US") == 0 ||
132 0 : pg_strncasecmp(tok, "NONEURO", 7) == 0)
133 : {
134 14538 : if (have_order && newDateOrder != DATEORDER_MDY)
135 0 : ok = false; /* conflicting orders */
136 14538 : newDateOrder = DATEORDER_MDY;
137 14538 : have_order = true;
138 : }
139 0 : else if (pg_strcasecmp(tok, "DEFAULT") == 0)
140 : {
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 : char *subval;
149 0 : void *subextra = NULL;
150 :
151 0 : subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
152 0 : if (!subval)
153 : {
154 0 : ok = false;
155 0 : break;
156 : }
157 0 : if (!check_datestyle(&subval, &subextra, source))
158 : {
159 0 : guc_free(subval);
160 0 : ok = false;
161 0 : break;
162 : }
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 : }
171 : else
172 : {
173 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
174 0 : pfree(rawstring);
175 0 : list_free(elemlist);
176 0 : return false;
177 : }
178 : }
179 :
180 24494 : pfree(rawstring);
181 24494 : list_free(elemlist);
182 :
183 24494 : if (!ok)
184 : {
185 0 : GUC_check_errdetail("Conflicting \"DateStyle\" specifications.");
186 0 : return false;
187 : }
188 :
189 : /*
190 : * Prepare the canonical string to return. GUC wants it guc_malloc'd.
191 : */
192 24494 : result = (char *) guc_malloc(LOG, 32);
193 24494 : if (!result)
194 0 : return false;
195 :
196 24494 : switch (newDateStyle)
197 : {
198 17168 : case USE_ISO_DATES:
199 17168 : strcpy(result, "ISO");
200 17168 : break;
201 30 : case USE_SQL_DATES:
202 30 : strcpy(result, "SQL");
203 30 : break;
204 48 : case USE_GERMAN_DATES:
205 48 : strcpy(result, "German");
206 48 : break;
207 7248 : default:
208 7248 : strcpy(result, "Postgres");
209 7248 : break;
210 : }
211 24494 : switch (newDateOrder)
212 : {
213 54 : case DATEORDER_YMD:
214 54 : strcat(result, ", YMD");
215 54 : break;
216 94 : case DATEORDER_DMY:
217 94 : strcat(result, ", DMY");
218 94 : break;
219 24346 : default:
220 24346 : strcat(result, ", MDY");
221 24346 : break;
222 : }
223 :
224 24494 : guc_free(*newval);
225 24494 : *newval = result;
226 :
227 : /*
228 : * Set up the "extra" struct actually used by assign_datestyle.
229 : */
230 24494 : myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
231 24494 : if (!myextra)
232 0 : return false;
233 24494 : myextra[0] = newDateStyle;
234 24494 : myextra[1] = newDateOrder;
235 24494 : *extra = myextra;
236 :
237 24494 : return true;
238 : }
239 :
240 : /*
241 : * assign_datestyle: GUC assign_hook for datestyle
242 : */
243 : void
244 32742 : assign_datestyle(const char *newval, void *extra)
245 : {
246 32742 : int *myextra = (int *) extra;
247 :
248 32742 : DateStyle = myextra[0];
249 32742 : DateOrder = myextra[1];
250 32742 : }
251 :
252 :
253 : /*
254 : * TIMEZONE
255 : */
256 :
257 : /*
258 : * check_timezone: GUC check_hook for timezone
259 : */
260 : bool
261 14682 : check_timezone(char **newval, void **extra, GucSource source)
262 : {
263 : pg_tz *new_tz;
264 : long gmtoffset;
265 : char *endptr;
266 : double hours;
267 :
268 14682 : if (pg_strncasecmp(*newval, "interval", 8) == 0)
269 : {
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 : char *val;
276 : 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 : {
288 0 : pfree(val);
289 0 : return false;
290 : }
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 : CStringGetDatum(val),
301 : ObjectIdGetDatum(InvalidOid),
302 : Int32GetDatum(-1)));
303 :
304 0 : pfree(val);
305 0 : if (interval->month != 0)
306 : {
307 0 : GUC_check_errdetail("Cannot specify months in time zone interval.");
308 0 : pfree(interval);
309 0 : return false;
310 : }
311 0 : if (interval->day != 0)
312 : {
313 0 : GUC_check_errdetail("Cannot specify days in time zone interval.");
314 0 : pfree(interval);
315 0 : return false;
316 : }
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 : }
324 : else
325 : {
326 : /*
327 : * Try it as a numeric number of hours (possibly fractional).
328 : */
329 14682 : hours = strtod(*newval, &endptr);
330 14682 : if (endptr != *newval && *endptr == '\0')
331 : {
332 : /* Here we change from SQL to Unix sign convention */
333 72 : gmtoffset = -hours * SECS_PER_HOUR;
334 72 : new_tz = pg_tzset_offset(gmtoffset);
335 : }
336 : else
337 : {
338 : /*
339 : * Otherwise assume it is a timezone name, and try to load it.
340 : */
341 14610 : new_tz = pg_tzset(*newval);
342 :
343 14610 : if (!new_tz)
344 : {
345 : /* Doesn't seem to be any great value in errdetail here */
346 0 : return false;
347 : }
348 :
349 14610 : if (!pg_tz_acceptable(new_tz))
350 : {
351 0 : GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
352 : *newval);
353 0 : GUC_check_errdetail("PostgreSQL does not support leap seconds.");
354 0 : return false;
355 : }
356 : }
357 : }
358 :
359 : /* Test for failure in pg_tzset_offset, which we assume is out-of-range */
360 14682 : if (!new_tz)
361 : {
362 0 : GUC_check_errdetail("UTC timezone offset is out of range.");
363 0 : return false;
364 : }
365 :
366 : /*
367 : * Pass back data for assign_timezone to use
368 : */
369 14682 : *extra = guc_malloc(LOG, sizeof(pg_tz *));
370 14682 : if (!*extra)
371 0 : return false;
372 14682 : *((pg_tz **) *extra) = new_tz;
373 :
374 14682 : return true;
375 : }
376 :
377 : /*
378 : * assign_timezone: GUC assign_hook for timezone
379 : */
380 : void
381 14806 : assign_timezone(const char *newval, void *extra)
382 : {
383 14806 : session_timezone = *((pg_tz **) extra);
384 : /* datetime.c's cache of timezone abbrevs may now be obsolete */
385 14806 : ClearTimeZoneAbbrevCache();
386 14806 : }
387 :
388 : /*
389 : * show_timezone: GUC show_hook for timezone
390 : */
391 : const char *
392 49140 : show_timezone(void)
393 : {
394 : const char *tzn;
395 :
396 : /* Always show the zone's canonical name */
397 49140 : tzn = pg_get_timezone_name(session_timezone);
398 :
399 49140 : if (tzn != NULL)
400 49140 : return tzn;
401 :
402 0 : return "unknown";
403 : }
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 9984 : check_log_timezone(char **newval, void **extra, GucSource source)
419 : {
420 : pg_tz *new_tz;
421 :
422 : /*
423 : * Assume it is a timezone name, and try to load it.
424 : */
425 9984 : new_tz = pg_tzset(*newval);
426 :
427 9984 : if (!new_tz)
428 : {
429 : /* Doesn't seem to be any great value in errdetail here */
430 0 : return false;
431 : }
432 :
433 9984 : if (!pg_tz_acceptable(new_tz))
434 : {
435 0 : GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
436 : *newval);
437 0 : GUC_check_errdetail("PostgreSQL does not support leap seconds.");
438 0 : return false;
439 : }
440 :
441 : /*
442 : * Pass back data for assign_log_timezone to use
443 : */
444 9984 : *extra = guc_malloc(LOG, sizeof(pg_tz *));
445 9984 : if (!*extra)
446 0 : return false;
447 9984 : *((pg_tz **) *extra) = new_tz;
448 :
449 9984 : return true;
450 : }
451 :
452 : /*
453 : * assign_log_timezone: GUC assign_hook for log_timezone
454 : */
455 : void
456 9978 : assign_log_timezone(const char *newval, void *extra)
457 : {
458 9978 : log_timezone = *((pg_tz **) extra);
459 9978 : }
460 :
461 : /*
462 : * show_log_timezone: GUC show_hook for log_timezone
463 : */
464 : const char *
465 3408 : show_log_timezone(void)
466 : {
467 : const char *tzn;
468 :
469 : /* Always show the zone's canonical name */
470 3408 : tzn = pg_get_timezone_name(log_timezone);
471 :
472 3408 : if (tzn != NULL)
473 3408 : return tzn;
474 :
475 0 : return "unknown";
476 : }
477 :
478 :
479 : /*
480 : * TIMEZONE_ABBREVIATIONS
481 : */
482 :
483 : /*
484 : * GUC check_hook for timezone_abbreviations
485 : */
486 : bool
487 16978 : check_timezone_abbreviations(char **newval, void **extra, GucSource source)
488 : {
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 16978 : if (*newval == NULL)
500 : {
501 : Assert(source == PGC_S_DEFAULT);
502 4694 : return true;
503 : }
504 :
505 : /* OK, load the file and produce a guc_malloc'd TimeZoneAbbrevTable */
506 12284 : *extra = load_tzoffsets(*newval);
507 :
508 : /* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
509 12284 : if (!*extra)
510 0 : return false;
511 :
512 12284 : return true;
513 : }
514 :
515 : /*
516 : * GUC assign_hook for timezone_abbreviations
517 : */
518 : void
519 16790 : assign_timezone_abbreviations(const char *newval, void *extra)
520 : {
521 : /* Do nothing for the boot_val default of NULL */
522 16790 : if (!extra)
523 4694 : return;
524 :
525 12096 : InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) extra);
526 : }
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 10790 : check_transaction_read_only(bool *newval, void **extra, GucSource source)
547 : {
548 10790 : if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker)
549 : {
550 : /* Can't go to r/w mode inside a r/o transaction */
551 64 : if (IsSubTransaction())
552 : {
553 12 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
554 12 : GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
555 12 : return false;
556 : }
557 : /* Top level transaction can't change to r/w after first snapshot. */
558 52 : if (FirstSnapshotSet)
559 : {
560 6 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
561 6 : GUC_check_errmsg("transaction read-write mode must be set before any query");
562 6 : return false;
563 : }
564 : /* Can't go to r/w mode while recovery is still active */
565 46 : if (RecoveryInProgress())
566 : {
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 : }
571 : }
572 :
573 10772 : return true;
574 : }
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 16042 : check_transaction_isolation(int *newval, void **extra, GucSource source)
587 : {
588 16042 : int newXactIsoLevel = *newval;
589 :
590 21868 : if (newXactIsoLevel != XactIsoLevel &&
591 11652 : IsTransactionState() && !InitializingParallelWorker)
592 : {
593 5746 : if (FirstSnapshotSet)
594 : {
595 2 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
596 2 : GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
597 2 : return false;
598 : }
599 : /* We ignore a subtransaction setting it to the existing value. */
600 5744 : if (IsSubTransaction())
601 : {
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 : }
606 : /* Can't go to serializable mode while recovery is still active */
607 5744 : if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
608 : {
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 : }
614 : }
615 :
616 16040 : return true;
617 : }
618 :
619 : /*
620 : * SET TRANSACTION [NOT] DEFERRABLE
621 : */
622 :
623 : bool
624 9442 : check_transaction_deferrable(bool *newval, void **extra, GucSource source)
625 : {
626 : /* Just accept the value when restoring state in a parallel worker */
627 9442 : if (InitializingParallelWorker)
628 5424 : return true;
629 :
630 4018 : if (IsSubTransaction())
631 : {
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 : }
636 4018 : if (FirstSnapshotSet)
637 : {
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 : }
642 :
643 4018 : return true;
644 : }
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 1982 : check_random_seed(double *newval, void **extra, GucSource source)
657 : {
658 1982 : *extra = guc_malloc(LOG, sizeof(int));
659 1982 : if (!*extra)
660 0 : return false;
661 : /* Arm the assign only if source of value is an interactive SET */
662 1982 : *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
663 :
664 1982 : return true;
665 : }
666 :
667 : void
668 1982 : assign_random_seed(double newval, void *extra)
669 : {
670 : /* We'll do this at most once for any setting of the GUC variable */
671 1982 : if (*((int *) extra))
672 0 : DirectFunctionCall1(setseed, Float8GetDatum(newval));
673 1982 : *((int *) extra) = 0;
674 1982 : }
675 :
676 : const char *
677 0 : show_random_seed(void)
678 : {
679 0 : return "unavailable";
680 : }
681 :
682 :
683 : /*
684 : * SET CLIENT_ENCODING
685 : */
686 :
687 : bool
688 37468 : check_client_encoding(char **newval, void **extra, GucSource source)
689 : {
690 : int encoding;
691 : const char *canonical_name;
692 :
693 : /* Look up the encoding by name */
694 37468 : encoding = pg_valid_client_encoding(*newval);
695 37468 : if (encoding < 0)
696 0 : return false;
697 :
698 : /* Get the canonical name (no aliases, uniform case) */
699 37468 : 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 37468 : if (IsParallelWorker() && !InitializingParallelWorker)
713 : {
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 : }
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 66800 : if (!IsParallelWorker() &&
737 29332 : PrepareClientEncoding(encoding) < 0)
738 : {
739 0 : if (IsTransactionState())
740 : {
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 : canonical_name,
745 : GetDatabaseEncodingName());
746 : }
747 : else
748 : {
749 : /* Provide a useful complaint */
750 0 : GUC_check_errdetail("Cannot change \"client_encoding\" now.");
751 : }
752 0 : return false;
753 : }
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 37468 : if (strcmp(*newval, canonical_name) != 0 &&
766 58 : strcmp(*newval, "UNICODE") != 0)
767 : {
768 58 : guc_free(*newval);
769 58 : *newval = guc_strdup(LOG, canonical_name);
770 58 : if (!*newval)
771 0 : return false;
772 : }
773 :
774 : /*
775 : * Save the encoding's ID in *extra, for use by assign_client_encoding.
776 : */
777 37468 : *extra = guc_malloc(LOG, sizeof(int));
778 37468 : if (!*extra)
779 0 : return false;
780 37468 : *((int *) *extra) = encoding;
781 :
782 37468 : return true;
783 : }
784 :
785 : void
786 37292 : assign_client_encoding(const char *newval, void *extra)
787 : {
788 37292 : 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 37292 : if (IsParallelWorker())
795 8136 : return;
796 :
797 : /* We do not expect an error if PrepareClientEncoding succeeded */
798 29156 : if (SetClientEncoding(encoding) < 0)
799 0 : elog(LOG, "SetClientEncoding(%d) failed", encoding);
800 : }
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 30622 : check_session_authorization(char **newval, void **extra, GucSource source)
816 : {
817 : HeapTuple roleTup;
818 : Form_pg_authid roleform;
819 : Oid roleid;
820 : bool is_superuser;
821 : role_auth_extra *myextra;
822 :
823 : /* Do nothing for the boot_val default of NULL */
824 30622 : if (*newval == NULL)
825 1982 : return true;
826 :
827 28640 : if (InitializingParallelWorker)
828 : {
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 2712 : roleid = GetSessionUserId();
835 2712 : is_superuser = GetSessionUserIsSuperuser();
836 : }
837 : else
838 : {
839 25928 : if (!IsTransactionState())
840 : {
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 : }
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 25928 : roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
858 25928 : if (!HeapTupleIsValid(roleTup))
859 : {
860 0 : if (source == PGC_S_TEST)
861 : {
862 0 : ereport(NOTICE,
863 : (errcode(ERRCODE_UNDEFINED_OBJECT),
864 : errmsg("role \"%s\" does not exist", *newval)));
865 0 : return true;
866 : }
867 0 : GUC_check_errmsg("role \"%s\" does not exist", *newval);
868 0 : return false;
869 : }
870 :
871 25928 : roleform = (Form_pg_authid) GETSTRUCT(roleTup);
872 25928 : roleid = roleform->oid;
873 25928 : is_superuser = roleform->rolsuper;
874 :
875 25928 : 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 25928 : if (roleid != GetAuthenticatedUserId() &&
883 2554 : !superuser_arg(GetAuthenticatedUserId()))
884 : {
885 0 : if (source == PGC_S_TEST)
886 : {
887 0 : ereport(NOTICE,
888 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
889 : errmsg("permission will be denied to set session authorization \"%s\"",
890 : *newval)));
891 0 : return true;
892 : }
893 0 : GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
894 0 : GUC_check_errmsg("permission denied to set session authorization \"%s\"",
895 : *newval);
896 0 : return false;
897 : }
898 : }
899 :
900 : /* Set up "extra" struct for assign_session_authorization to use */
901 28640 : myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
902 28640 : if (!myextra)
903 0 : return false;
904 28640 : myextra->roleid = roleid;
905 28640 : myextra->is_superuser = is_superuser;
906 28640 : *extra = myextra;
907 :
908 28640 : return true;
909 : }
910 :
911 : void
912 31516 : assign_session_authorization(const char *newval, void *extra)
913 : {
914 31516 : role_auth_extra *myextra = (role_auth_extra *) extra;
915 :
916 : /* Do nothing for the boot_val default of NULL */
917 31516 : if (!myextra)
918 1982 : return;
919 :
920 29534 : SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
921 : }
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 31590 : check_role(char **newval, void **extra, GucSource source)
934 : {
935 : HeapTuple roleTup;
936 : Oid roleid;
937 : bool is_superuser;
938 : role_auth_extra *myextra;
939 : Form_pg_authid roleform;
940 :
941 31590 : if (strcmp(*newval, "none") == 0)
942 : {
943 : /* hardwired translation */
944 30602 : roleid = InvalidOid;
945 30602 : is_superuser = false;
946 : }
947 988 : else if (InitializingParallelWorker)
948 : {
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 12 : roleid = GetCurrentRoleId();
955 12 : is_superuser = current_role_is_superuser;
956 : }
957 : else
958 : {
959 976 : if (!IsTransactionState())
960 : {
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 : }
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 976 : roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
977 976 : if (!HeapTupleIsValid(roleTup))
978 : {
979 0 : if (source == PGC_S_TEST)
980 : {
981 0 : ereport(NOTICE,
982 : (errcode(ERRCODE_UNDEFINED_OBJECT),
983 : errmsg("role \"%s\" does not exist", *newval)));
984 0 : return true;
985 : }
986 0 : GUC_check_errmsg("role \"%s\" does not exist", *newval);
987 0 : return false;
988 : }
989 :
990 976 : roleform = (Form_pg_authid) GETSTRUCT(roleTup);
991 976 : roleid = roleform->oid;
992 976 : is_superuser = roleform->rolsuper;
993 :
994 976 : ReleaseSysCache(roleTup);
995 :
996 : /* Verify that session user is allowed to become this role */
997 976 : if (!member_can_set_role(GetSessionUserId(), roleid))
998 : {
999 12 : if (source == PGC_S_TEST)
1000 : {
1001 0 : ereport(NOTICE,
1002 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1003 : errmsg("permission will be denied to set role \"%s\"",
1004 : *newval)));
1005 0 : return true;
1006 : }
1007 12 : GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
1008 12 : GUC_check_errmsg("permission denied to set role \"%s\"",
1009 : *newval);
1010 12 : return false;
1011 : }
1012 : }
1013 :
1014 : /* Set up "extra" struct for assign_role to use */
1015 31578 : myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
1016 31578 : if (!myextra)
1017 0 : return false;
1018 31578 : myextra->roleid = roleid;
1019 31578 : myextra->is_superuser = is_superuser;
1020 31578 : *extra = myextra;
1021 :
1022 31578 : return true;
1023 : }
1024 :
1025 : void
1026 33136 : assign_role(const char *newval, void *extra)
1027 : {
1028 33136 : role_auth_extra *myextra = (role_auth_extra *) extra;
1029 :
1030 33136 : SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
1031 33136 : }
1032 :
1033 : const char *
1034 60 : show_role(void)
1035 : {
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 60 : if (!OidIsValid(GetCurrentRoleId()))
1044 24 : return "none";
1045 :
1046 : /* Otherwise we can just use the GUC string */
1047 36 : return role_string ? role_string : "none";
1048 : }
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 3964 : check_canonical_path(char **newval, void **extra, GucSource source)
1060 : {
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 3964 : if (*newval)
1067 1982 : canonicalize_path(*newval);
1068 3964 : return true;
1069 : }
1070 :
1071 :
1072 : /*
1073 : * MISCELLANEOUS
1074 : */
1075 :
1076 : /*
1077 : * GUC check_hook for application_name
1078 : */
1079 : bool
1080 28204 : check_application_name(char **newval, void **extra, GucSource source)
1081 : {
1082 : char *clean;
1083 : char *ret;
1084 :
1085 : /* Only allow clean ASCII chars in the application name */
1086 28204 : clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
1087 28204 : if (!clean)
1088 0 : return false;
1089 :
1090 28204 : ret = guc_strdup(WARNING, clean);
1091 28204 : if (!ret)
1092 : {
1093 0 : pfree(clean);
1094 0 : return false;
1095 : }
1096 :
1097 28204 : guc_free(*newval);
1098 :
1099 28204 : pfree(clean);
1100 28204 : *newval = ret;
1101 28204 : return true;
1102 : }
1103 :
1104 : /*
1105 : * GUC assign_hook for application_name
1106 : */
1107 : void
1108 28176 : assign_application_name(const char *newval, void *extra)
1109 : {
1110 : /* Update the pg_stat_activity view */
1111 28176 : pgstat_report_appname(newval);
1112 28176 : }
1113 :
1114 : /*
1115 : * GUC check_hook for cluster_name
1116 : */
1117 : bool
1118 3266 : check_cluster_name(char **newval, void **extra, GucSource source)
1119 : {
1120 : char *clean;
1121 : char *ret;
1122 :
1123 : /* Only allow clean ASCII chars in the cluster name */
1124 3266 : clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
1125 3266 : if (!clean)
1126 0 : return false;
1127 :
1128 3266 : ret = guc_strdup(WARNING, clean);
1129 3266 : if (!ret)
1130 : {
1131 0 : pfree(clean);
1132 0 : return false;
1133 : }
1134 :
1135 3266 : guc_free(*newval);
1136 :
1137 3266 : pfree(clean);
1138 3266 : *newval = ret;
1139 3266 : return true;
1140 : }
1141 :
1142 : /*
1143 : * GUC assign_hook for maintenance_io_concurrency
1144 : */
1145 : void
1146 1982 : assign_maintenance_io_concurrency(int newval, void *extra)
1147 : {
1148 : #ifdef USE_PREFETCH
1149 : /*
1150 : * Reconfigure recovery prefetching, because a setting it depends on
1151 : * changed.
1152 : */
1153 1982 : maintenance_io_concurrency = newval;
1154 1982 : if (AmStartupProcess())
1155 0 : XLogPrefetchReconfigure();
1156 : #endif
1157 1982 : }
1158 :
1159 :
1160 : /*
1161 : * These show hooks just exist because we want to show the values in octal.
1162 : */
1163 :
1164 : /*
1165 : * GUC show_hook for data_directory_mode
1166 : */
1167 : const char *
1168 4130 : show_data_directory_mode(void)
1169 : {
1170 : static char buf[12];
1171 :
1172 4130 : snprintf(buf, sizeof(buf), "%04o", data_directory_mode);
1173 4130 : return buf;
1174 : }
1175 :
1176 : /*
1177 : * GUC show_hook for log_file_mode
1178 : */
1179 : const char *
1180 3408 : show_log_file_mode(void)
1181 : {
1182 : static char buf[12];
1183 :
1184 3408 : snprintf(buf, sizeof(buf), "%04o", Log_file_mode);
1185 3408 : return buf;
1186 : }
1187 :
1188 : /*
1189 : * GUC show_hook for unix_socket_permissions
1190 : */
1191 : const char *
1192 3408 : show_unix_socket_permissions(void)
1193 : {
1194 : static char buf[12];
1195 :
1196 3408 : snprintf(buf, sizeof(buf), "%04o", Unix_socket_permissions);
1197 3408 : return buf;
1198 : }
1199 :
1200 :
1201 : /*
1202 : * These check hooks do nothing more than reject non-default settings
1203 : * in builds that don't support them.
1204 : */
1205 :
1206 : bool
1207 1982 : check_bonjour(bool *newval, void **extra, GucSource source)
1208 : {
1209 : #ifndef USE_BONJOUR
1210 1982 : if (*newval)
1211 : {
1212 0 : GUC_check_errmsg("Bonjour is not supported by this build");
1213 0 : return false;
1214 : }
1215 : #endif
1216 1982 : return true;
1217 : }
1218 :
1219 : bool
1220 1994 : check_default_with_oids(bool *newval, void **extra, GucSource source)
1221 : {
1222 1994 : if (*newval)
1223 : {
1224 : /* check the GUC's definition for an explanation */
1225 6 : GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
1226 6 : GUC_check_errmsg("tables declared WITH OIDS are not supported");
1227 :
1228 6 : return false;
1229 : }
1230 :
1231 1988 : return true;
1232 : }
1233 :
1234 : bool
1235 2594 : check_effective_io_concurrency(int *newval, void **extra, GucSource source)
1236 : {
1237 : #ifndef USE_PREFETCH
1238 : if (*newval != 0)
1239 : {
1240 : GUC_check_errdetail("\"%s\" must be set to 0 on platforms that lack support for issuing read-ahead advice.",
1241 : "effective_io_concurrency");
1242 : return false;
1243 : }
1244 : #endif /* USE_PREFETCH */
1245 2594 : return true;
1246 : }
1247 :
1248 : bool
1249 1982 : check_maintenance_io_concurrency(int *newval, void **extra, GucSource source)
1250 : {
1251 : #ifndef USE_PREFETCH
1252 : if (*newval != 0)
1253 : {
1254 : GUC_check_errdetail("\"%s\" must be set to 0 on platforms that lack support for issuing read-ahead advice.",
1255 : "maintenance_io_concurrency");
1256 : return false;
1257 : }
1258 : #endif /* USE_PREFETCH */
1259 1982 : return true;
1260 : }
1261 :
1262 : bool
1263 2072 : check_ssl(bool *newval, void **extra, GucSource source)
1264 : {
1265 : #ifndef USE_SSL
1266 : if (*newval)
1267 : {
1268 : GUC_check_errmsg("SSL is not supported by this build");
1269 : return false;
1270 : }
1271 : #endif
1272 2072 : return true;
1273 : }
|