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