Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * variable.c
4 : * Routines for handling specialized SET variables.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2017, 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 "catalog/pg_authid.h"
26 : #include "commands/variable.h"
27 : #include "miscadmin.h"
28 : #include "utils/acl.h"
29 : #include "utils/builtins.h"
30 : #include "utils/syscache.h"
31 : #include "utils/snapmgr.h"
32 : #include "utils/timestamp.h"
33 : #include "utils/varlena.h"
34 : #include "mb/pg_wchar.h"
35 :
36 : /*
37 : * DATESTYLE
38 : */
39 :
40 : /*
41 : * check_datestyle: GUC check_hook for datestyle
42 : */
43 : bool
44 493 : check_datestyle(char **newval, void **extra, GucSource source)
45 : {
46 493 : int newDateStyle = DateStyle;
47 493 : int newDateOrder = DateOrder;
48 493 : bool have_style = false;
49 493 : bool have_order = false;
50 493 : bool ok = true;
51 : char *rawstring;
52 : int *myextra;
53 : char *result;
54 : List *elemlist;
55 : ListCell *l;
56 :
57 : /* Need a modifiable copy of string */
58 493 : rawstring = pstrdup(*newval);
59 :
60 : /* Parse string into list of identifiers */
61 493 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
62 : {
63 : /* syntax error in list */
64 0 : GUC_check_errdetail("List syntax is invalid.");
65 0 : pfree(rawstring);
66 0 : list_free(elemlist);
67 0 : return false;
68 : }
69 :
70 1463 : foreach(l, elemlist)
71 : {
72 970 : char *tok = (char *) lfirst(l);
73 :
74 : /* Ugh. Somebody ought to write a table driven version -- mjl */
75 :
76 970 : if (pg_strcasecmp(tok, "ISO") == 0)
77 : {
78 133 : if (have_style && newDateStyle != USE_ISO_DATES)
79 0 : ok = false; /* conflicting styles */
80 133 : newDateStyle = USE_ISO_DATES;
81 133 : have_style = true;
82 : }
83 837 : else if (pg_strcasecmp(tok, "SQL") == 0)
84 : {
85 5 : if (have_style && newDateStyle != USE_SQL_DATES)
86 0 : ok = false; /* conflicting styles */
87 5 : newDateStyle = USE_SQL_DATES;
88 5 : have_style = true;
89 : }
90 832 : else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
91 : {
92 345 : if (have_style && newDateStyle != USE_POSTGRES_DATES)
93 0 : ok = false; /* conflicting styles */
94 345 : newDateStyle = USE_POSTGRES_DATES;
95 345 : have_style = true;
96 : }
97 487 : else if (pg_strcasecmp(tok, "GERMAN") == 0)
98 : {
99 3 : if (have_style && newDateStyle != USE_GERMAN_DATES)
100 0 : ok = false; /* conflicting styles */
101 3 : newDateStyle = USE_GERMAN_DATES;
102 3 : have_style = true;
103 : /* GERMAN also sets DMY, unless explicitly overridden */
104 3 : if (!have_order)
105 3 : newDateOrder = DATEORDER_DMY;
106 : }
107 484 : else if (pg_strcasecmp(tok, "YMD") == 0)
108 : {
109 6 : if (have_order && newDateOrder != DATEORDER_YMD)
110 0 : ok = false; /* conflicting orders */
111 6 : newDateOrder = DATEORDER_YMD;
112 6 : have_order = true;
113 : }
114 948 : else if (pg_strcasecmp(tok, "DMY") == 0 ||
115 470 : pg_strncasecmp(tok, "EURO", 4) == 0)
116 : {
117 11 : if (have_order && newDateOrder != DATEORDER_DMY)
118 0 : ok = false; /* conflicting orders */
119 11 : newDateOrder = DATEORDER_DMY;
120 11 : have_order = true;
121 : }
122 470 : else if (pg_strcasecmp(tok, "MDY") == 0 ||
123 3 : pg_strcasecmp(tok, "US") == 0 ||
124 0 : pg_strncasecmp(tok, "NONEURO", 7) == 0)
125 : {
126 467 : if (have_order && newDateOrder != DATEORDER_MDY)
127 0 : ok = false; /* conflicting orders */
128 467 : newDateOrder = DATEORDER_MDY;
129 467 : have_order = true;
130 : }
131 0 : else if (pg_strcasecmp(tok, "DEFAULT") == 0)
132 : {
133 : /*
134 : * Easiest way to get the current DEFAULT state is to fetch the
135 : * DEFAULT string from guc.c and recursively parse it.
136 : *
137 : * We can't simply "return check_datestyle(...)" because we need
138 : * to handle constructs like "DEFAULT, ISO".
139 : */
140 : char *subval;
141 0 : void *subextra = NULL;
142 :
143 0 : subval = strdup(GetConfigOptionResetString("datestyle"));
144 0 : if (!subval)
145 : {
146 0 : ok = false;
147 0 : break;
148 : }
149 0 : if (!check_datestyle(&subval, &subextra, source))
150 : {
151 0 : free(subval);
152 0 : ok = false;
153 0 : break;
154 : }
155 0 : myextra = (int *) subextra;
156 0 : if (!have_style)
157 0 : newDateStyle = myextra[0];
158 0 : if (!have_order)
159 0 : newDateOrder = myextra[1];
160 0 : free(subval);
161 0 : free(subextra);
162 : }
163 : else
164 : {
165 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
166 0 : pfree(rawstring);
167 0 : list_free(elemlist);
168 0 : return false;
169 : }
170 : }
171 :
172 493 : pfree(rawstring);
173 493 : list_free(elemlist);
174 :
175 493 : if (!ok)
176 : {
177 0 : GUC_check_errdetail("Conflicting \"datestyle\" specifications.");
178 0 : return false;
179 : }
180 :
181 : /*
182 : * Prepare the canonical string to return. GUC wants it malloc'd.
183 : */
184 493 : result = (char *) malloc(32);
185 493 : if (!result)
186 0 : return false;
187 :
188 493 : switch (newDateStyle)
189 : {
190 : case USE_ISO_DATES:
191 137 : strcpy(result, "ISO");
192 137 : break;
193 : case USE_SQL_DATES:
194 5 : strcpy(result, "SQL");
195 5 : break;
196 : case USE_GERMAN_DATES:
197 3 : strcpy(result, "German");
198 3 : break;
199 : default:
200 348 : strcpy(result, "Postgres");
201 348 : break;
202 : }
203 493 : switch (newDateOrder)
204 : {
205 : case DATEORDER_YMD:
206 8 : strcat(result, ", YMD");
207 8 : break;
208 : case DATEORDER_DMY:
209 14 : strcat(result, ", DMY");
210 14 : break;
211 : default:
212 471 : strcat(result, ", MDY");
213 471 : break;
214 : }
215 :
216 493 : free(*newval);
217 493 : *newval = result;
218 :
219 : /*
220 : * Set up the "extra" struct actually used by assign_datestyle.
221 : */
222 493 : myextra = (int *) malloc(2 * sizeof(int));
223 493 : if (!myextra)
224 0 : return false;
225 493 : myextra[0] = newDateStyle;
226 493 : myextra[1] = newDateOrder;
227 493 : *extra = (void *) myextra;
228 :
229 493 : return true;
230 : }
231 :
232 : /*
233 : * assign_datestyle: GUC assign_hook for datestyle
234 : */
235 : void
236 508 : assign_datestyle(const char *newval, void *extra)
237 : {
238 508 : int *myextra = (int *) extra;
239 :
240 508 : DateStyle = myextra[0];
241 508 : DateOrder = myextra[1];
242 508 : }
243 :
244 :
245 : /*
246 : * TIMEZONE
247 : */
248 :
249 : /*
250 : * check_timezone: GUC check_hook for timezone
251 : */
252 : bool
253 473 : check_timezone(char **newval, void **extra, GucSource source)
254 : {
255 : pg_tz *new_tz;
256 : long gmtoffset;
257 : char *endptr;
258 : double hours;
259 :
260 473 : if (pg_strncasecmp(*newval, "interval", 8) == 0)
261 : {
262 : /*
263 : * Support INTERVAL 'foo'. This is for SQL spec compliance, not
264 : * because it has any actual real-world usefulness.
265 : */
266 0 : const char *valueptr = *newval;
267 : char *val;
268 : Interval *interval;
269 :
270 0 : valueptr += 8;
271 0 : while (isspace((unsigned char) *valueptr))
272 0 : valueptr++;
273 0 : if (*valueptr++ != '\'')
274 0 : return false;
275 0 : val = pstrdup(valueptr);
276 : /* Check and remove trailing quote */
277 0 : endptr = strchr(val, '\'');
278 0 : if (!endptr || endptr[1] != '\0')
279 : {
280 0 : pfree(val);
281 0 : return false;
282 : }
283 0 : *endptr = '\0';
284 :
285 : /*
286 : * Try to parse it. XXX an invalid interval format will result in
287 : * ereport(ERROR), which is not desirable for GUC. We did what we
288 : * could to guard against this in flatten_set_variable_args, but a
289 : * string coming in from postgresql.conf might contain anything.
290 : */
291 0 : interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
292 : CStringGetDatum(val),
293 : ObjectIdGetDatum(InvalidOid),
294 : Int32GetDatum(-1)));
295 :
296 0 : pfree(val);
297 0 : if (interval->month != 0)
298 : {
299 0 : GUC_check_errdetail("Cannot specify months in time zone interval.");
300 0 : pfree(interval);
301 0 : return false;
302 : }
303 0 : if (interval->day != 0)
304 : {
305 0 : GUC_check_errdetail("Cannot specify days in time zone interval.");
306 0 : pfree(interval);
307 0 : return false;
308 : }
309 :
310 : /* Here we change from SQL to Unix sign convention */
311 0 : gmtoffset = -(interval->time / USECS_PER_SEC);
312 0 : new_tz = pg_tzset_offset(gmtoffset);
313 :
314 0 : pfree(interval);
315 : }
316 : else
317 : {
318 : /*
319 : * Try it as a numeric number of hours (possibly fractional).
320 : */
321 473 : hours = strtod(*newval, &endptr);
322 473 : if (endptr != *newval && *endptr == '\0')
323 : {
324 : /* Here we change from SQL to Unix sign convention */
325 6 : gmtoffset = -hours * SECS_PER_HOUR;
326 6 : new_tz = pg_tzset_offset(gmtoffset);
327 : }
328 : else
329 : {
330 : /*
331 : * Otherwise assume it is a timezone name, and try to load it.
332 : */
333 467 : new_tz = pg_tzset(*newval);
334 :
335 467 : if (!new_tz)
336 : {
337 : /* Doesn't seem to be any great value in errdetail here */
338 0 : return false;
339 : }
340 :
341 467 : if (!pg_tz_acceptable(new_tz))
342 : {
343 0 : GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
344 : *newval);
345 0 : GUC_check_errdetail("PostgreSQL does not support leap seconds.");
346 0 : return false;
347 : }
348 : }
349 : }
350 :
351 : /* Test for failure in pg_tzset_offset, which we assume is out-of-range */
352 473 : if (!new_tz)
353 : {
354 0 : GUC_check_errdetail("UTC timezone offset is out of range.");
355 0 : return false;
356 : }
357 :
358 : /*
359 : * Pass back data for assign_timezone to use
360 : */
361 473 : *extra = malloc(sizeof(pg_tz *));
362 473 : if (!*extra)
363 0 : return false;
364 473 : *((pg_tz **) *extra) = new_tz;
365 :
366 473 : return true;
367 : }
368 :
369 : /*
370 : * assign_timezone: GUC assign_hook for timezone
371 : */
372 : void
373 480 : assign_timezone(const char *newval, void *extra)
374 : {
375 480 : session_timezone = *((pg_tz **) extra);
376 480 : }
377 :
378 : /*
379 : * show_timezone: GUC show_hook for timezone
380 : */
381 : const char *
382 245 : show_timezone(void)
383 : {
384 : const char *tzn;
385 :
386 : /* Always show the zone's canonical name */
387 245 : tzn = pg_get_timezone_name(session_timezone);
388 :
389 245 : if (tzn != NULL)
390 245 : return tzn;
391 :
392 0 : return "unknown";
393 : }
394 :
395 :
396 : /*
397 : * LOG_TIMEZONE
398 : *
399 : * For log_timezone, we don't support the interval-based methods of setting a
400 : * zone, which are only there for SQL spec compliance not because they're
401 : * actually useful.
402 : */
403 :
404 : /*
405 : * check_log_timezone: GUC check_hook for log_timezone
406 : */
407 : bool
408 239 : check_log_timezone(char **newval, void **extra, GucSource source)
409 : {
410 : pg_tz *new_tz;
411 :
412 : /*
413 : * Assume it is a timezone name, and try to load it.
414 : */
415 239 : new_tz = pg_tzset(*newval);
416 :
417 239 : if (!new_tz)
418 : {
419 : /* Doesn't seem to be any great value in errdetail here */
420 0 : return false;
421 : }
422 :
423 239 : if (!pg_tz_acceptable(new_tz))
424 : {
425 0 : GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
426 : *newval);
427 0 : GUC_check_errdetail("PostgreSQL does not support leap seconds.");
428 0 : return false;
429 : }
430 :
431 : /*
432 : * Pass back data for assign_log_timezone to use
433 : */
434 239 : *extra = malloc(sizeof(pg_tz *));
435 239 : if (!*extra)
436 0 : return false;
437 239 : *((pg_tz **) *extra) = new_tz;
438 :
439 239 : return true;
440 : }
441 :
442 : /*
443 : * assign_log_timezone: GUC assign_hook for log_timezone
444 : */
445 : void
446 238 : assign_log_timezone(const char *newval, void *extra)
447 : {
448 238 : log_timezone = *((pg_tz **) extra);
449 238 : }
450 :
451 : /*
452 : * show_log_timezone: GUC show_hook for log_timezone
453 : */
454 : const char *
455 1 : show_log_timezone(void)
456 : {
457 : const char *tzn;
458 :
459 : /* Always show the zone's canonical name */
460 1 : tzn = pg_get_timezone_name(log_timezone);
461 :
462 1 : if (tzn != NULL)
463 1 : return tzn;
464 :
465 0 : return "unknown";
466 : }
467 :
468 :
469 : /*
470 : * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE
471 : *
472 : * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and
473 : * we also always allow changes from read-write to read-only. However,
474 : * read-only may be changed to read-write only when in a top-level transaction
475 : * that has not yet taken an initial snapshot. Can't do it in a hot standby,
476 : * either.
477 : *
478 : * If we are not in a transaction at all, just allow the change; it means
479 : * nothing since XactReadOnly will be reset by the next StartTransaction().
480 : * The IsTransactionState() test protects us against trying to check
481 : * RecoveryInProgress() in contexts where shared memory is not accessible.
482 : * (Similarly, if we're restoring state in a parallel worker, just allow
483 : * the change.)
484 : */
485 : bool
486 259 : check_transaction_read_only(bool *newval, void **extra, GucSource source)
487 : {
488 259 : if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker)
489 : {
490 : /* Can't go to r/w mode inside a r/o transaction */
491 5 : if (IsSubTransaction())
492 : {
493 2 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
494 2 : GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
495 2 : return false;
496 : }
497 : /* Top level transaction can't change to r/w after first snapshot. */
498 3 : if (FirstSnapshotSet)
499 : {
500 1 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
501 1 : GUC_check_errmsg("transaction read-write mode must be set before any query");
502 1 : return false;
503 : }
504 : /* Can't go to r/w mode while recovery is still active */
505 2 : if (RecoveryInProgress())
506 : {
507 0 : GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
508 0 : GUC_check_errmsg("cannot set transaction read-write mode during recovery");
509 0 : return false;
510 : }
511 : }
512 :
513 256 : return true;
514 : }
515 :
516 : /*
517 : * SET TRANSACTION ISOLATION LEVEL
518 : *
519 : * We allow idempotent changes at any time, but otherwise this can only be
520 : * changed in a toplevel transaction that has not yet taken a snapshot.
521 : *
522 : * As in check_transaction_read_only, allow it if not inside a transaction.
523 : */
524 : bool
525 258 : check_XactIsoLevel(char **newval, void **extra, GucSource source)
526 : {
527 : int newXactIsoLevel;
528 :
529 258 : if (strcmp(*newval, "serializable") == 0)
530 : {
531 13 : newXactIsoLevel = XACT_SERIALIZABLE;
532 : }
533 245 : else if (strcmp(*newval, "repeatable read") == 0)
534 : {
535 118 : newXactIsoLevel = XACT_REPEATABLE_READ;
536 : }
537 127 : else if (strcmp(*newval, "read committed") == 0)
538 : {
539 2 : newXactIsoLevel = XACT_READ_COMMITTED;
540 : }
541 125 : else if (strcmp(*newval, "read uncommitted") == 0)
542 : {
543 0 : newXactIsoLevel = XACT_READ_UNCOMMITTED;
544 : }
545 125 : else if (strcmp(*newval, "default") == 0)
546 : {
547 125 : newXactIsoLevel = DefaultXactIsoLevel;
548 : }
549 : else
550 0 : return false;
551 :
552 258 : if (newXactIsoLevel != XactIsoLevel && IsTransactionState())
553 : {
554 131 : if (FirstSnapshotSet)
555 : {
556 0 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
557 0 : GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
558 0 : return false;
559 : }
560 : /* We ignore a subtransaction setting it to the existing value. */
561 131 : if (IsSubTransaction())
562 : {
563 0 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
564 0 : GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
565 0 : return false;
566 : }
567 : /* Can't go to serializable mode while recovery is still active */
568 131 : if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
569 : {
570 0 : GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
571 0 : GUC_check_errmsg("cannot use serializable mode in a hot standby");
572 0 : GUC_check_errhint("You can use REPEATABLE READ instead.");
573 0 : return false;
574 : }
575 : }
576 :
577 258 : *extra = malloc(sizeof(int));
578 258 : if (!*extra)
579 0 : return false;
580 258 : *((int *) *extra) = newXactIsoLevel;
581 :
582 258 : return true;
583 : }
584 :
585 : void
586 274 : assign_XactIsoLevel(const char *newval, void *extra)
587 : {
588 274 : XactIsoLevel = *((int *) extra);
589 274 : }
590 :
591 : const char *
592 1 : show_XactIsoLevel(void)
593 : {
594 : /* We need this because we don't want to show "default". */
595 1 : switch (XactIsoLevel)
596 : {
597 : case XACT_READ_UNCOMMITTED:
598 0 : return "read uncommitted";
599 : case XACT_READ_COMMITTED:
600 1 : return "read committed";
601 : case XACT_REPEATABLE_READ:
602 0 : return "repeatable read";
603 : case XACT_SERIALIZABLE:
604 0 : return "serializable";
605 : default:
606 0 : return "bogus";
607 : }
608 : }
609 :
610 : /*
611 : * SET TRANSACTION [NOT] DEFERRABLE
612 : */
613 :
614 : bool
615 241 : check_transaction_deferrable(bool *newval, void **extra, GucSource source)
616 : {
617 241 : if (IsSubTransaction())
618 : {
619 0 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
620 0 : GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
621 0 : return false;
622 : }
623 241 : if (FirstSnapshotSet)
624 : {
625 0 : GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
626 0 : GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
627 0 : return false;
628 : }
629 :
630 241 : return true;
631 : }
632 :
633 : /*
634 : * Random number seed
635 : *
636 : * We can't roll back the random sequence on error, and we don't want
637 : * config file reloads to affect it, so we only want interactive SET SEED
638 : * commands to set it. We use the "extra" storage to ensure that rollbacks
639 : * don't try to do the operation again.
640 : */
641 :
642 : bool
643 5 : check_random_seed(double *newval, void **extra, GucSource source)
644 : {
645 5 : *extra = malloc(sizeof(int));
646 5 : if (!*extra)
647 0 : return false;
648 : /* Arm the assign only if source of value is an interactive SET */
649 5 : *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
650 :
651 5 : return true;
652 : }
653 :
654 : void
655 5 : assign_random_seed(double newval, void *extra)
656 : {
657 : /* We'll do this at most once for any setting of the GUC variable */
658 5 : if (*((int *) extra))
659 0 : DirectFunctionCall1(setseed, Float8GetDatum(newval));
660 5 : *((int *) extra) = 0;
661 5 : }
662 :
663 : const char *
664 1 : show_random_seed(void)
665 : {
666 1 : return "unavailable";
667 : }
668 :
669 :
670 : /*
671 : * SET CLIENT_ENCODING
672 : */
673 :
674 : bool
675 572 : check_client_encoding(char **newval, void **extra, GucSource source)
676 : {
677 : int encoding;
678 : const char *canonical_name;
679 :
680 : /* Look up the encoding by name */
681 572 : encoding = pg_valid_client_encoding(*newval);
682 572 : if (encoding < 0)
683 0 : return false;
684 :
685 : /* Get the canonical name (no aliases, uniform case) */
686 572 : canonical_name = pg_encoding_to_char(encoding);
687 :
688 : /*
689 : * If we are not within a transaction then PrepareClientEncoding will not
690 : * be able to look up the necessary conversion procs. If we are still
691 : * starting up, it will return "OK" anyway, and InitializeClientEncoding
692 : * will fix things once initialization is far enough along. After
693 : * startup, we'll fail. This would only happen if someone tries to change
694 : * client_encoding in postgresql.conf and then SIGHUP existing sessions.
695 : * It seems like a bad idea for client_encoding to change that way anyhow,
696 : * so we don't go out of our way to support it.
697 : *
698 : * Note: in the postmaster, or any other process that never calls
699 : * InitializeClientEncoding, PrepareClientEncoding will always succeed,
700 : * and so will SetClientEncoding; but they won't do anything, which is OK.
701 : */
702 572 : if (PrepareClientEncoding(encoding) < 0)
703 : {
704 0 : if (IsTransactionState())
705 : {
706 : /* Must be a genuine no-such-conversion problem */
707 0 : GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
708 0 : GUC_check_errdetail("Conversion between %s and %s is not supported.",
709 : canonical_name,
710 : GetDatabaseEncodingName());
711 : }
712 : else
713 : {
714 : /* Provide a useful complaint */
715 0 : GUC_check_errdetail("Cannot change \"client_encoding\" now.");
716 : }
717 0 : return false;
718 : }
719 :
720 : /*
721 : * Replace the user-supplied string with the encoding's canonical name.
722 : * This gets rid of aliases and case-folding variations.
723 : *
724 : * XXX Although canonicalizing seems like a good idea in the abstract, it
725 : * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE"
726 : * as the client_encoding setting then it will read back the same way. As
727 : * a workaround, don't replace the string if it's "UNICODE". Remove that
728 : * hack when pre-9.1 JDBC drivers are no longer in use.
729 : */
730 572 : if (strcmp(*newval, canonical_name) != 0 &&
731 0 : strcmp(*newval, "UNICODE") != 0)
732 : {
733 0 : free(*newval);
734 0 : *newval = strdup(canonical_name);
735 0 : if (!*newval)
736 0 : return false;
737 : }
738 :
739 : /*
740 : * Save the encoding's ID in *extra, for use by assign_client_encoding.
741 : */
742 572 : *extra = malloc(sizeof(int));
743 572 : if (!*extra)
744 0 : return false;
745 572 : *((int *) *extra) = encoding;
746 :
747 572 : return true;
748 : }
749 :
750 : void
751 572 : assign_client_encoding(const char *newval, void *extra)
752 : {
753 572 : int encoding = *((int *) extra);
754 :
755 : /*
756 : * Parallel workers send data to the leader, not the client. They always
757 : * send data using the database encoding.
758 : */
759 572 : if (IsParallelWorker())
760 : {
761 : /*
762 : * During parallel worker startup, we want to accept the leader's
763 : * client_encoding setting so that anyone who looks at the value in
764 : * the worker sees the same value that they would see in the leader.
765 : */
766 345 : if (InitializingParallelWorker)
767 917 : return;
768 :
769 : /*
770 : * A change other than during startup, for example due to a SET clause
771 : * attached to a function definition, should be rejected, as there is
772 : * nothing we can do inside the worker to make it take effect.
773 : */
774 0 : ereport(ERROR,
775 : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
776 : errmsg("cannot change client_encoding during a parallel operation")));
777 : }
778 :
779 : /* We do not expect an error if PrepareClientEncoding succeeded */
780 227 : if (SetClientEncoding(encoding) < 0)
781 0 : elog(LOG, "SetClientEncoding(%d) failed", encoding);
782 : }
783 :
784 :
785 : /*
786 : * SET SESSION AUTHORIZATION
787 : */
788 :
789 : typedef struct
790 : {
791 : /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
792 : Oid roleid;
793 : bool is_superuser;
794 : } role_auth_extra;
795 :
796 : bool
797 803 : check_session_authorization(char **newval, void **extra, GucSource source)
798 : {
799 : HeapTuple roleTup;
800 : Oid roleid;
801 : bool is_superuser;
802 : role_auth_extra *myextra;
803 :
804 : /* Do nothing for the boot_val default of NULL */
805 803 : if (*newval == NULL)
806 120 : return true;
807 :
808 683 : if (!IsTransactionState())
809 : {
810 : /*
811 : * Can't do catalog lookups, so fail. The result of this is that
812 : * session_authorization cannot be set in postgresql.conf, which seems
813 : * like a good thing anyway, so we don't work hard to avoid it.
814 : */
815 0 : return false;
816 : }
817 :
818 : /* Look up the username */
819 683 : roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
820 683 : if (!HeapTupleIsValid(roleTup))
821 : {
822 0 : GUC_check_errmsg("role \"%s\" does not exist", *newval);
823 0 : return false;
824 : }
825 :
826 683 : roleid = HeapTupleGetOid(roleTup);
827 683 : is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
828 :
829 683 : ReleaseSysCache(roleTup);
830 :
831 : /* Set up "extra" struct for assign_session_authorization to use */
832 683 : myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
833 683 : if (!myextra)
834 0 : return false;
835 683 : myextra->roleid = roleid;
836 683 : myextra->is_superuser = is_superuser;
837 683 : *extra = (void *) myextra;
838 :
839 683 : return true;
840 : }
841 :
842 : void
843 871 : assign_session_authorization(const char *newval, void *extra)
844 : {
845 871 : role_auth_extra *myextra = (role_auth_extra *) extra;
846 :
847 : /* Do nothing for the boot_val default of NULL */
848 871 : if (!myextra)
849 991 : return;
850 :
851 751 : SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
852 : }
853 :
854 :
855 : /*
856 : * SET ROLE
857 : *
858 : * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
859 : * a translation of "none" to InvalidOid. Otherwise this is much like
860 : * SET SESSION AUTHORIZATION.
861 : */
862 : extern char *role_string; /* in guc.c */
863 :
864 : bool
865 80 : check_role(char **newval, void **extra, GucSource source)
866 : {
867 : HeapTuple roleTup;
868 : Oid roleid;
869 : bool is_superuser;
870 : role_auth_extra *myextra;
871 :
872 80 : if (strcmp(*newval, "none") == 0)
873 : {
874 : /* hardwired translation */
875 5 : roleid = InvalidOid;
876 5 : is_superuser = false;
877 : }
878 : else
879 : {
880 75 : if (!IsTransactionState())
881 : {
882 : /*
883 : * Can't do catalog lookups, so fail. The result of this is that
884 : * role cannot be set in postgresql.conf, which seems like a good
885 : * thing anyway, so we don't work hard to avoid it.
886 : */
887 0 : return false;
888 : }
889 :
890 : /* Look up the username */
891 75 : roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
892 75 : if (!HeapTupleIsValid(roleTup))
893 : {
894 0 : GUC_check_errmsg("role \"%s\" does not exist", *newval);
895 0 : return false;
896 : }
897 :
898 75 : roleid = HeapTupleGetOid(roleTup);
899 75 : is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
900 :
901 75 : ReleaseSysCache(roleTup);
902 :
903 : /*
904 : * Verify that session user is allowed to become this role, but skip
905 : * this in parallel mode, where we must blindly recreate the parallel
906 : * leader's state.
907 : */
908 150 : if (!InitializingParallelWorker &&
909 75 : !is_member_of_role(GetSessionUserId(), roleid))
910 : {
911 0 : GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
912 0 : GUC_check_errmsg("permission denied to set role \"%s\"",
913 : *newval);
914 0 : return false;
915 : }
916 : }
917 :
918 : /* Set up "extra" struct for assign_role to use */
919 80 : myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
920 80 : if (!myextra)
921 0 : return false;
922 80 : myextra->roleid = roleid;
923 80 : myextra->is_superuser = is_superuser;
924 80 : *extra = (void *) myextra;
925 :
926 80 : return true;
927 : }
928 :
929 : void
930 125 : assign_role(const char *newval, void *extra)
931 : {
932 125 : role_auth_extra *myextra = (role_auth_extra *) extra;
933 :
934 125 : SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
935 125 : }
936 :
937 : const char *
938 1 : show_role(void)
939 : {
940 : /*
941 : * Check whether SET ROLE is active; if not return "none". This is a
942 : * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
943 : * resets SET ROLE to NONE, but we cannot set the GUC role variable from
944 : * assign_session_authorization (because we haven't got enough info to
945 : * call set_config_option).
946 : */
947 1 : if (!OidIsValid(GetCurrentRoleId()))
948 1 : return "none";
949 :
950 : /* Otherwise we can just use the GUC string */
951 0 : return role_string ? role_string : "none";
952 : }
|