]> granicus.if.org Git - postgresql/blob - src/backend/commands/variable.c
Update copyright for 2009.
[postgresql] / src / backend / commands / variable.c
1 /*-------------------------------------------------------------------------
2  *
3  * variable.c
4  *              Routines for handling specialized SET variables.
5  *
6  *
7  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.129 2009/01/01 17:23:40 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 #include "postgres.h"
18
19 #include <ctype.h>
20
21 #include "access/xact.h"
22 #include "catalog/pg_authid.h"
23 #include "commands/variable.h"
24 #include "miscadmin.h"
25 #include "utils/acl.h"
26 #include "utils/builtins.h"
27 #include "utils/syscache.h"
28 #include "utils/snapmgr.h"
29 #include "mb/pg_wchar.h"
30
31 /*
32  * DATESTYLE
33  */
34
35 /*
36  * assign_datestyle: GUC assign_hook for datestyle
37  */
38 const char *
39 assign_datestyle(const char *value, bool doit, GucSource source)
40 {
41         int                     newDateStyle = DateStyle;
42         int                     newDateOrder = DateOrder;
43         bool            have_style = false;
44         bool            have_order = false;
45         bool            ok = true;
46         char       *rawstring;
47         char       *result;
48         List       *elemlist;
49         ListCell   *l;
50
51         /* Need a modifiable copy of string */
52         rawstring = pstrdup(value);
53
54         /* Parse string into list of identifiers */
55         if (!SplitIdentifierString(rawstring, ',', &elemlist))
56         {
57                 /* syntax error in list */
58                 pfree(rawstring);
59                 list_free(elemlist);
60                 ereport(GUC_complaint_elevel(source),
61                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
62                                  errmsg("invalid list syntax for parameter \"datestyle\"")));
63                 return NULL;
64         }
65
66         foreach(l, elemlist)
67         {
68                 char       *tok = (char *) lfirst(l);
69
70                 /* Ugh. Somebody ought to write a table driven version -- mjl */
71
72                 if (pg_strcasecmp(tok, "ISO") == 0)
73                 {
74                         if (have_style && newDateStyle != USE_ISO_DATES)
75                                 ok = false;             /* conflicting styles */
76                         newDateStyle = USE_ISO_DATES;
77                         have_style = true;
78                 }
79                 else if (pg_strcasecmp(tok, "SQL") == 0)
80                 {
81                         if (have_style && newDateStyle != USE_SQL_DATES)
82                                 ok = false;             /* conflicting styles */
83                         newDateStyle = USE_SQL_DATES;
84                         have_style = true;
85                 }
86                 else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
87                 {
88                         if (have_style && newDateStyle != USE_POSTGRES_DATES)
89                                 ok = false;             /* conflicting styles */
90                         newDateStyle = USE_POSTGRES_DATES;
91                         have_style = true;
92                 }
93                 else if (pg_strcasecmp(tok, "GERMAN") == 0)
94                 {
95                         if (have_style && newDateStyle != USE_GERMAN_DATES)
96                                 ok = false;             /* conflicting styles */
97                         newDateStyle = USE_GERMAN_DATES;
98                         have_style = true;
99                         /* GERMAN also sets DMY, unless explicitly overridden */
100                         if (!have_order)
101                                 newDateOrder = DATEORDER_DMY;
102                 }
103                 else if (pg_strcasecmp(tok, "YMD") == 0)
104                 {
105                         if (have_order && newDateOrder != DATEORDER_YMD)
106                                 ok = false;             /* conflicting orders */
107                         newDateOrder = DATEORDER_YMD;
108                         have_order = true;
109                 }
110                 else if (pg_strcasecmp(tok, "DMY") == 0 ||
111                                  pg_strncasecmp(tok, "EURO", 4) == 0)
112                 {
113                         if (have_order && newDateOrder != DATEORDER_DMY)
114                                 ok = false;             /* conflicting orders */
115                         newDateOrder = DATEORDER_DMY;
116                         have_order = true;
117                 }
118                 else if (pg_strcasecmp(tok, "MDY") == 0 ||
119                                  pg_strcasecmp(tok, "US") == 0 ||
120                                  pg_strncasecmp(tok, "NONEURO", 7) == 0)
121                 {
122                         if (have_order && newDateOrder != DATEORDER_MDY)
123                                 ok = false;             /* conflicting orders */
124                         newDateOrder = DATEORDER_MDY;
125                         have_order = true;
126                 }
127                 else if (pg_strcasecmp(tok, "DEFAULT") == 0)
128                 {
129                         /*
130                          * Easiest way to get the current DEFAULT state is to fetch the
131                          * DEFAULT string from guc.c and recursively parse it.
132                          *
133                          * We can't simply "return assign_datestyle(...)" because we need
134                          * to handle constructs like "DEFAULT, ISO".
135                          */
136                         int                     saveDateStyle = DateStyle;
137                         int                     saveDateOrder = DateOrder;
138                         const char *subval;
139
140                         subval = assign_datestyle(GetConfigOptionResetString("datestyle"),
141                                                                           true, source);
142                         if (!have_style)
143                                 newDateStyle = DateStyle;
144                         if (!have_order)
145                                 newDateOrder = DateOrder;
146                         DateStyle = saveDateStyle;
147                         DateOrder = saveDateOrder;
148                         if (!subval)
149                         {
150                                 ok = false;
151                                 break;
152                         }
153                         /* Here we know that our own return value is always malloc'd */
154                         /* when doit is true */
155                         free((char *) subval);
156                 }
157                 else
158                 {
159                         ereport(GUC_complaint_elevel(source),
160                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
161                                          errmsg("unrecognized \"datestyle\" key word: \"%s\"",
162                                                         tok)));
163                         ok = false;
164                         break;
165                 }
166         }
167
168         pfree(rawstring);
169         list_free(elemlist);
170
171         if (!ok)
172         {
173                 ereport(GUC_complaint_elevel(source),
174                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
175                                  errmsg("conflicting \"datestyle\" specifications")));
176                 return NULL;
177         }
178
179         /*
180          * If we aren't going to do the assignment, just return OK indicator.
181          */
182         if (!doit)
183                 return value;
184
185         /*
186          * Prepare the canonical string to return.      GUC wants it malloc'd.
187          */
188         result = (char *) malloc(32);
189         if (!result)
190                 return NULL;
191
192         switch (newDateStyle)
193         {
194                 case USE_ISO_DATES:
195                         strcpy(result, "ISO");
196                         break;
197                 case USE_SQL_DATES:
198                         strcpy(result, "SQL");
199                         break;
200                 case USE_GERMAN_DATES:
201                         strcpy(result, "German");
202                         break;
203                 default:
204                         strcpy(result, "Postgres");
205                         break;
206         }
207         switch (newDateOrder)
208         {
209                 case DATEORDER_YMD:
210                         strcat(result, ", YMD");
211                         break;
212                 case DATEORDER_DMY:
213                         strcat(result, ", DMY");
214                         break;
215                 default:
216                         strcat(result, ", MDY");
217                         break;
218         }
219
220         /*
221          * Finally, it's safe to assign to the global variables; the assignment
222          * cannot fail now.
223          */
224         DateStyle = newDateStyle;
225         DateOrder = newDateOrder;
226
227         return result;
228 }
229
230
231 /*
232  * TIMEZONE
233  */
234
235 /*
236  * assign_timezone: GUC assign_hook for timezone
237  */
238 const char *
239 assign_timezone(const char *value, bool doit, GucSource source)
240 {
241         char       *result;
242         char       *endptr;
243         double          hours;
244
245         /*
246          * Check for INTERVAL 'foo'
247          */
248         if (pg_strncasecmp(value, "interval", 8) == 0)
249         {
250                 const char *valueptr = value;
251                 char       *val;
252                 Interval   *interval;
253
254                 valueptr += 8;
255                 while (isspace((unsigned char) *valueptr))
256                         valueptr++;
257                 if (*valueptr++ != '\'')
258                         return NULL;
259                 val = pstrdup(valueptr);
260                 /* Check and remove trailing quote */
261                 endptr = strchr(val, '\'');
262                 if (!endptr || endptr[1] != '\0')
263                 {
264                         pfree(val);
265                         return NULL;
266                 }
267                 *endptr = '\0';
268
269                 /*
270                  * Try to parse it.  XXX an invalid interval format will result in
271                  * ereport(ERROR), which is not desirable for GUC.  We did what we
272                  * could to guard against this in flatten_set_variable_args, but a
273                  * string coming in from postgresql.conf might contain anything.
274                  */
275                 interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
276                                                                                                                  CStringGetDatum(val),
277                                                                                                 ObjectIdGetDatum(InvalidOid),
278                                                                                                                  Int32GetDatum(-1)));
279
280                 pfree(val);
281                 if (interval->month != 0)
282                 {
283                         ereport(GUC_complaint_elevel(source),
284                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
285                                          errmsg("invalid interval value for time zone: month not allowed")));
286                         pfree(interval);
287                         return NULL;
288                 }
289                 if (interval->day != 0)
290                 {
291                         ereport(GUC_complaint_elevel(source),
292                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
293                                          errmsg("invalid interval value for time zone: day not allowed")));
294                         pfree(interval);
295                         return NULL;
296                 }
297                 if (doit)
298                 {
299                         /* Here we change from SQL to Unix sign convention */
300 #ifdef HAVE_INT64_TIMESTAMP
301                         CTimeZone = -(interval->time / USECS_PER_SEC);
302 #else
303                         CTimeZone = -interval->time;
304 #endif
305
306                         HasCTZSet = true;
307                 }
308                 pfree(interval);
309         }
310         else
311         {
312                 /*
313                  * Try it as a numeric number of hours (possibly fractional).
314                  */
315                 hours = strtod(value, &endptr);
316                 if (endptr != value && *endptr == '\0')
317                 {
318                         if (doit)
319                         {
320                                 /* Here we change from SQL to Unix sign convention */
321                                 CTimeZone = -hours * SECS_PER_HOUR;
322                                 HasCTZSet = true;
323                         }
324                 }
325                 else if (pg_strcasecmp(value, "UNKNOWN") == 0)
326                 {
327                         /*
328                          * UNKNOWN is the value shown as the "default" for TimeZone in
329                          * guc.c.  We interpret it as being a complete no-op; we don't
330                          * change the timezone setting.  Note that if there is a known
331                          * timezone setting, we will return that name rather than UNKNOWN
332                          * as the canonical spelling.
333                          *
334                          * During GUC initialization, since the timezone library isn't set
335                          * up yet, pg_get_timezone_name will return NULL and we will leave
336                          * the setting as UNKNOWN.      If this isn't overridden from the
337                          * config file then pg_timezone_initialize() will eventually
338                          * select a default value from the environment.
339                          */
340                         if (doit)
341                         {
342                                 const char *curzone = pg_get_timezone_name(session_timezone);
343
344                                 if (curzone)
345                                         value = curzone;
346                         }
347                 }
348                 else
349                 {
350                         /*
351                          * Otherwise assume it is a timezone name, and try to load it.
352                          */
353                         pg_tz      *new_tz;
354
355                         new_tz = pg_tzset(value);
356
357                         if (!new_tz)
358                         {
359                                 ereport(GUC_complaint_elevel(source),
360                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
361                                                  errmsg("unrecognized time zone name: \"%s\"",
362                                                                 value)));
363                                 return NULL;
364                         }
365
366                         if (!tz_acceptable(new_tz))
367                         {
368                                 ereport(GUC_complaint_elevel(source),
369                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
370                                            errmsg("time zone \"%s\" appears to use leap seconds",
371                                                           value),
372                                         errdetail("PostgreSQL does not support leap seconds.")));
373                                 return NULL;
374                         }
375
376                         if (doit)
377                         {
378                                 /* Save the changed TZ */
379                                 session_timezone = new_tz;
380                                 HasCTZSet = false;
381                         }
382                 }
383         }
384
385         /*
386          * If we aren't going to do the assignment, just return OK indicator.
387          */
388         if (!doit)
389                 return value;
390
391         /*
392          * Prepare the canonical string to return.      GUC wants it malloc'd.
393          */
394         if (HasCTZSet)
395         {
396                 result = (char *) malloc(64);
397                 if (!result)
398                         return NULL;
399                 snprintf(result, 64, "%.5f",
400                                  (double) (-CTimeZone) / (double) SECS_PER_HOUR);
401         }
402         else
403                 result = strdup(value);
404
405         return result;
406 }
407
408 /*
409  * show_timezone: GUC show_hook for timezone
410  */
411 const char *
412 show_timezone(void)
413 {
414         const char *tzn;
415
416         if (HasCTZSet)
417         {
418                 Interval        interval;
419
420                 interval.month = 0;
421                 interval.day = 0;
422 #ifdef HAVE_INT64_TIMESTAMP
423                 interval.time = -(CTimeZone * USECS_PER_SEC);
424 #else
425                 interval.time = -CTimeZone;
426 #endif
427
428                 tzn = DatumGetCString(DirectFunctionCall1(interval_out,
429                                                                                           IntervalPGetDatum(&interval)));
430         }
431         else
432                 tzn = pg_get_timezone_name(session_timezone);
433
434         if (tzn != NULL)
435                 return tzn;
436
437         return "unknown";
438 }
439
440
441 /*
442  * LOG_TIMEZONE
443  *
444  * For log_timezone, we don't support the interval-based methods of setting a
445  * zone, which are only there for SQL spec compliance not because they're
446  * actually useful.
447  */
448
449 /*
450  * assign_log_timezone: GUC assign_hook for log_timezone
451  */
452 const char *
453 assign_log_timezone(const char *value, bool doit, GucSource source)
454 {
455         char       *result;
456
457         if (pg_strcasecmp(value, "UNKNOWN") == 0)
458         {
459                 /*
460                  * UNKNOWN is the value shown as the "default" for log_timezone in
461                  * guc.c.  We interpret it as being a complete no-op; we don't change
462                  * the timezone setting.  Note that if there is a known timezone
463                  * setting, we will return that name rather than UNKNOWN as the
464                  * canonical spelling.
465                  *
466                  * During GUC initialization, since the timezone library isn't set up
467                  * yet, pg_get_timezone_name will return NULL and we will leave the
468                  * setting as UNKNOWN.  If this isn't overridden from the config file
469                  * then pg_timezone_initialize() will eventually select a default
470                  * value from the environment.
471                  */
472                 if (doit)
473                 {
474                         const char *curzone = pg_get_timezone_name(log_timezone);
475
476                         if (curzone)
477                                 value = curzone;
478                 }
479         }
480         else
481         {
482                 /*
483                  * Otherwise assume it is a timezone name, and try to load it.
484                  */
485                 pg_tz      *new_tz;
486
487                 new_tz = pg_tzset(value);
488
489                 if (!new_tz)
490                 {
491                         ereport(GUC_complaint_elevel(source),
492                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
493                                          errmsg("unrecognized time zone name: \"%s\"",
494                                                         value)));
495                         return NULL;
496                 }
497
498                 if (!tz_acceptable(new_tz))
499                 {
500                         ereport(GUC_complaint_elevel(source),
501                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
502                                          errmsg("time zone \"%s\" appears to use leap seconds",
503                                                         value),
504                                          errdetail("PostgreSQL does not support leap seconds.")));
505                         return NULL;
506                 }
507
508                 if (doit)
509                 {
510                         /* Save the changed TZ */
511                         log_timezone = new_tz;
512                 }
513         }
514
515         /*
516          * If we aren't going to do the assignment, just return OK indicator.
517          */
518         if (!doit)
519                 return value;
520
521         /*
522          * Prepare the canonical string to return.      GUC wants it malloc'd.
523          */
524         result = strdup(value);
525
526         return result;
527 }
528
529 /*
530  * show_log_timezone: GUC show_hook for log_timezone
531  */
532 const char *
533 show_log_timezone(void)
534 {
535         const char *tzn;
536
537         tzn = pg_get_timezone_name(log_timezone);
538
539         if (tzn != NULL)
540                 return tzn;
541
542         return "unknown";
543 }
544
545
546 /*
547  * SET TRANSACTION ISOLATION LEVEL
548  */
549
550 const char *
551 assign_XactIsoLevel(const char *value, bool doit, GucSource source)
552 {
553         if (FirstSnapshotSet)
554         {
555                 ereport(GUC_complaint_elevel(source),
556                                 (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
557                                  errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query")));
558                 /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
559                 if (source != PGC_S_OVERRIDE)
560                         return NULL;
561         }
562         else if (IsSubTransaction())
563         {
564                 ereport(GUC_complaint_elevel(source),
565                                 (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
566                                  errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction")));
567                 /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
568                 if (source != PGC_S_OVERRIDE)
569                         return NULL;
570         }
571
572         if (strcmp(value, "serializable") == 0)
573         {
574                 if (doit)
575                         XactIsoLevel = XACT_SERIALIZABLE;
576         }
577         else if (strcmp(value, "repeatable read") == 0)
578         {
579                 if (doit)
580                         XactIsoLevel = XACT_REPEATABLE_READ;
581         }
582         else if (strcmp(value, "read committed") == 0)
583         {
584                 if (doit)
585                         XactIsoLevel = XACT_READ_COMMITTED;
586         }
587         else if (strcmp(value, "read uncommitted") == 0)
588         {
589                 if (doit)
590                         XactIsoLevel = XACT_READ_UNCOMMITTED;
591         }
592         else if (strcmp(value, "default") == 0)
593         {
594                 if (doit)
595                         XactIsoLevel = DefaultXactIsoLevel;
596         }
597         else
598                 return NULL;
599
600         return value;
601 }
602
603 const char *
604 show_XactIsoLevel(void)
605 {
606         switch (XactIsoLevel)
607         {
608                 case XACT_READ_UNCOMMITTED:
609                         return "read uncommitted";
610                 case XACT_READ_COMMITTED:
611                         return "read committed";
612                 case XACT_REPEATABLE_READ:
613                         return "repeatable read";
614                 case XACT_SERIALIZABLE:
615                         return "serializable";
616                 default:
617                         return "bogus";
618         }
619 }
620
621
622 /*
623  * Random number seed
624  */
625
626 bool
627 assign_random_seed(double value, bool doit, GucSource source)
628 {
629         /* Can't really roll back on error, so ignore non-interactive setting */
630         if (doit && source >= PGC_S_INTERACTIVE)
631                 DirectFunctionCall1(setseed, Float8GetDatum(value));
632         return true;
633 }
634
635 const char *
636 show_random_seed(void)
637 {
638         return "unavailable";
639 }
640
641
642 /*
643  * encoding handling functions
644  */
645
646 const char *
647 assign_client_encoding(const char *value, bool doit, GucSource source)
648 {
649         int                     encoding;
650
651         encoding = pg_valid_client_encoding(value);
652         if (encoding < 0)
653                 return NULL;
654
655         /*
656          * Note: if we are in startup phase then SetClientEncoding may not be able
657          * to really set the encoding.  In this case we will assume that the
658          * encoding is okay, and InitializeClientEncoding() will fix things once
659          * initialization is complete.
660          */
661         if (SetClientEncoding(encoding, doit) < 0)
662         {
663                 ereport(GUC_complaint_elevel(source),
664                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
665                                  errmsg("conversion between %s and %s is not supported",
666                                                 value, GetDatabaseEncodingName())));
667                 return NULL;
668         }
669         return value;
670 }
671
672
673 /*
674  * SET SESSION AUTHORIZATION
675  *
676  * When resetting session auth after an error, we can't expect to do catalog
677  * lookups.  Hence, the stored form of the value must provide a numeric oid
678  * that can be re-used directly.  We store the string in the form of
679  * NAMEDATALEN 'x's, followed by T or F to indicate superuserness, followed
680  * by the numeric oid, followed by a comma, followed by the role name.
681  * This cannot be confused with a plain role name because of the NAMEDATALEN
682  * limit on names, so we can tell whether we're being passed an initial
683  * role name or a saved/restored value.  (NOTE: we rely on guc.c to have
684  * properly truncated any incoming value, but not to truncate already-stored
685  * values.      See GUC_IS_NAME processing.)
686  */
687 extern char *session_authorization_string;              /* in guc.c */
688
689 const char *
690 assign_session_authorization(const char *value, bool doit, GucSource source)
691 {
692         Oid                     roleid = InvalidOid;
693         bool            is_superuser = false;
694         const char *actual_rolename = NULL;
695         char       *result;
696
697         if (strspn(value, "x") == NAMEDATALEN &&
698                 (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
699         {
700                 /* might be a saved userid string */
701                 Oid                     savedoid;
702                 char       *endptr;
703
704                 savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
705
706                 if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
707                 {
708                         /* syntactically valid, so break out the data */
709                         roleid = savedoid;
710                         is_superuser = (value[NAMEDATALEN] == 'T');
711                         actual_rolename = endptr + 1;
712                 }
713         }
714
715         if (roleid == InvalidOid)
716         {
717                 /* not a saved ID, so look it up */
718                 HeapTuple       roleTup;
719
720                 if (InSecurityDefinerContext())
721                 {
722                         /*
723                          * Disallow SET SESSION AUTHORIZATION inside a security definer
724                          * context.  We need to do this because when we exit the context,
725                          * GUC won't be notified, leaving things out of sync.  Note that
726                          * this test is positioned so that restoring a previously saved
727                          * setting isn't prevented.
728                          */
729                         ereport(GUC_complaint_elevel(source),
730                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
731                                          errmsg("cannot set session authorization within security-definer function")));
732                         return NULL;
733                 }
734
735                 if (!IsTransactionState())
736                 {
737                         /*
738                          * Can't do catalog lookups, so fail.  The upshot of this is that
739                          * session_authorization cannot be set in postgresql.conf, which
740                          * seems like a good thing anyway.
741                          */
742                         return NULL;
743                 }
744
745                 roleTup = SearchSysCache(AUTHNAME,
746                                                                  PointerGetDatum(value),
747                                                                  0, 0, 0);
748                 if (!HeapTupleIsValid(roleTup))
749                 {
750                         ereport(GUC_complaint_elevel(source),
751                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
752                                          errmsg("role \"%s\" does not exist", value)));
753                         return NULL;
754                 }
755
756                 roleid = HeapTupleGetOid(roleTup);
757                 is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
758                 actual_rolename = value;
759
760                 ReleaseSysCache(roleTup);
761         }
762
763         if (doit)
764                 SetSessionAuthorization(roleid, is_superuser);
765
766         result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
767         if (!result)
768                 return NULL;
769
770         memset(result, 'x', NAMEDATALEN);
771
772         sprintf(result + NAMEDATALEN, "%c%u,%s",
773                         is_superuser ? 'T' : 'F',
774                         roleid,
775                         actual_rolename);
776
777         return result;
778 }
779
780 const char *
781 show_session_authorization(void)
782 {
783         /*
784          * Extract the user name from the stored string; see
785          * assign_session_authorization
786          */
787         const char *value = session_authorization_string;
788         Oid                     savedoid;
789         char       *endptr;
790
791         Assert(strspn(value, "x") == NAMEDATALEN &&
792                    (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
793
794         savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
795
796         Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
797
798         return endptr + 1;
799 }
800
801
802 /*
803  * SET ROLE
804  *
805  * When resetting session auth after an error, we can't expect to do catalog
806  * lookups.  Hence, the stored form of the value must provide a numeric oid
807  * that can be re-used directly.  We implement this exactly like SET
808  * SESSION AUTHORIZATION.
809  *
810  * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
811  * a translation of "none" to InvalidOid.
812  */
813 extern char *role_string;               /* in guc.c */
814
815 const char *
816 assign_role(const char *value, bool doit, GucSource source)
817 {
818         Oid                     roleid = InvalidOid;
819         bool            is_superuser = false;
820         const char *actual_rolename = value;
821         char       *result;
822
823         if (strspn(value, "x") == NAMEDATALEN &&
824                 (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
825         {
826                 /* might be a saved userid string */
827                 Oid                     savedoid;
828                 char       *endptr;
829
830                 savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
831
832                 if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
833                 {
834                         /* syntactically valid, so break out the data */
835                         roleid = savedoid;
836                         is_superuser = (value[NAMEDATALEN] == 'T');
837                         actual_rolename = endptr + 1;
838                 }
839         }
840
841         if (roleid == InvalidOid && InSecurityDefinerContext())
842         {
843                 /*
844                  * Disallow SET ROLE inside a security definer context.  We need to do
845                  * this because when we exit the context, GUC won't be notified,
846                  * leaving things out of sync.  Note that this test is arranged so
847                  * that restoring a previously saved setting isn't prevented.
848                  *
849                  * XXX it would be nice to allow this case in future, with the
850                  * behavior being that the SET ROLE's effects end when the security
851                  * definer context is exited.
852                  */
853                 ereport(GUC_complaint_elevel(source),
854                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
855                                  errmsg("cannot set role within security-definer function")));
856                 return NULL;
857         }
858
859         if (roleid == InvalidOid &&
860                 strcmp(actual_rolename, "none") != 0)
861         {
862                 /* not a saved ID, so look it up */
863                 HeapTuple       roleTup;
864
865                 if (!IsTransactionState())
866                 {
867                         /*
868                          * Can't do catalog lookups, so fail.  The upshot of this is that
869                          * role cannot be set in postgresql.conf, which seems like a good
870                          * thing anyway.
871                          */
872                         return NULL;
873                 }
874
875                 roleTup = SearchSysCache(AUTHNAME,
876                                                                  PointerGetDatum(value),
877                                                                  0, 0, 0);
878                 if (!HeapTupleIsValid(roleTup))
879                 {
880                         ereport(GUC_complaint_elevel(source),
881                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
882                                          errmsg("role \"%s\" does not exist", value)));
883                         return NULL;
884                 }
885
886                 roleid = HeapTupleGetOid(roleTup);
887                 is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
888
889                 ReleaseSysCache(roleTup);
890
891                 /*
892                  * Verify that session user is allowed to become this role
893                  */
894                 if (!is_member_of_role(GetSessionUserId(), roleid))
895                 {
896                         ereport(GUC_complaint_elevel(source),
897                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
898                                          errmsg("permission denied to set role \"%s\"",
899                                                         value)));
900                         return NULL;
901                 }
902         }
903
904         if (doit)
905                 SetCurrentRoleId(roleid, is_superuser);
906
907         result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
908         if (!result)
909                 return NULL;
910
911         memset(result, 'x', NAMEDATALEN);
912
913         sprintf(result + NAMEDATALEN, "%c%u,%s",
914                         is_superuser ? 'T' : 'F',
915                         roleid,
916                         actual_rolename);
917
918         return result;
919 }
920
921 const char *
922 show_role(void)
923 {
924         /*
925          * Extract the role name from the stored string; see assign_role
926          */
927         const char *value = role_string;
928         Oid                     savedoid;
929         char       *endptr;
930
931         /* This special case only applies if no SET ROLE has been done */
932         if (value == NULL || strcmp(value, "none") == 0)
933                 return "none";
934
935         Assert(strspn(value, "x") == NAMEDATALEN &&
936                    (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
937
938         savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
939
940         Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
941
942         /*
943          * Check that the stored string still matches the effective setting, else
944          * return "none".  This is a kluge to deal with the fact that SET SESSION
945          * AUTHORIZATION logically resets SET ROLE to NONE, but we cannot set the
946          * GUC role variable from assign_session_authorization (because we haven't
947          * got enough info to call set_config_option).
948          */
949         if (savedoid != GetCurrentRoleId())
950                 return "none";
951
952         return endptr + 1;
953 }