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