1 /*-------------------------------------------------------------------------
2  *
3  * variable.c
4  *		Routines for handling specialized SET variables.
5  *
6  *
7  * Portions Copyright (c) 1996-2020, 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 "mb/pg_wchar.h"
28 #include "miscadmin.h"
29 #include "utils/acl.h"
30 #include "utils/builtins.h"
31 #include "utils/snapmgr.h"
32 #include "utils/syscache.h"
33 #include "utils/timestamp.h"
34 #include "utils/varlena.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 */
have_createrole_privilege(void)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)
CreateRole(ParseState * pstate,CreateRoleStmt * stmt)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
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.
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 	 */
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;
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)
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 	 */
361 	*extra = malloc(sizeof(pg_tz *));
362 	if (!*extra)
363 		return false;
364 	*((pg_tz **) *extra) = new_tz;
365 
366 	return true;
367 }
368 
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 *
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 
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 
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 	{
AlterRole(AlterRoleStmt * stmt)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))
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);
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
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;
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 {
AlterRoleSet(AlterRoleSetStmt * stmt)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