LCOV - code coverage report
Current view: top level - src/backend/commands - variable.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 224 354 63.3 %
Date: 2017-09-29 15:12:54 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11