LCOV - code coverage report
Current view: top level - src/backend/commands - variable.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 223 339 65.8 %
Date: 2020-06-01 09:07:10 Functions: 21 21 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-2020, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/commands/variable.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : 
      17             : #include "postgres.h"
      18             : 
      19             : #include <ctype.h>
      20             : 
      21             : #include "access/htup_details.h"
      22             : #include "access/parallel.h"
      23             : #include "access/xact.h"
      24             : #include "access/xlog.h"
      25             : #include "catalog/pg_authid.h"
      26             : #include "commands/variable.h"
      27             : #include "mb/pg_wchar.h"
      28             : #include "miscadmin.h"
      29             : #include "utils/acl.h"
      30             : #include "utils/builtins.h"
      31             : #include "utils/snapmgr.h"
      32             : #include "utils/syscache.h"
      33             : #include "utils/timestamp.h"
      34             : #include "utils/varlena.h"
      35             : 
      36             : /*
      37             :  * DATESTYLE
      38             :  */
      39             : 
      40             : /*
      41             :  * check_datestyle: GUC check_hook for datestyle
      42             :  */
      43             : bool
      44       16082 : check_datestyle(char **newval, void **extra, GucSource source)
      45             : {
      46       16082 :     int         newDateStyle = DateStyle;
      47       16082 :     int         newDateOrder = DateOrder;
      48       16082 :     bool        have_style = false;
      49       16082 :     bool        have_order = false;
      50       16082 :     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       16082 :     rawstring = pstrdup(*newval);
      59             : 
      60             :     /* Parse string into list of identifiers */
      61       16082 :     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       43172 :     foreach(l, elemlist)
      71             :     {
      72       27090 :         char       *tok = (char *) lfirst(l);
      73             : 
      74             :         /* Ugh. Somebody ought to write a table driven version -- mjl */
      75             : 
      76       27090 :         if (pg_strcasecmp(tok, "ISO") == 0)
      77             :         {
      78       10566 :             if (have_style && newDateStyle != USE_ISO_DATES)
      79           0 :                 ok = false;     /* conflicting styles */
      80       10566 :             newDateStyle = USE_ISO_DATES;
      81       10566 :             have_style = true;
      82             :         }
      83       16524 :         else if (pg_strcasecmp(tok, "SQL") == 0)
      84             :         {
      85          20 :             if (have_style && newDateStyle != USE_SQL_DATES)
      86           0 :                 ok = false;     /* conflicting styles */
      87          20 :             newDateStyle = USE_SQL_DATES;
      88          20 :             have_style = true;
      89             :         }
      90       16504 :         else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
      91             :         {
      92        5424 :             if (have_style && newDateStyle != USE_POSTGRES_DATES)
      93           0 :                 ok = false;     /* conflicting styles */
      94        5424 :             newDateStyle = USE_POSTGRES_DATES;
      95        5424 :             have_style = true;
      96             :         }
      97       11080 :         else if (pg_strcasecmp(tok, "GERMAN") == 0)
      98             :         {
      99          36 :             if (have_style && newDateStyle != USE_GERMAN_DATES)
     100           0 :                 ok = false;     /* conflicting styles */
     101          36 :             newDateStyle = USE_GERMAN_DATES;
     102          36 :             have_style = true;
     103             :             /* GERMAN also sets DMY, unless explicitly overridden */
     104          36 :             if (!have_order)
     105          36 :                 newDateOrder = DATEORDER_DMY;
     106             :         }
     107       11044 :         else if (pg_strcasecmp(tok, "YMD") == 0)
     108             :         {
     109          24 :             if (have_order && newDateOrder != DATEORDER_YMD)
     110           0 :                 ok = false;     /* conflicting orders */
     111          24 :             newDateOrder = DATEORDER_YMD;
     112          24 :             have_order = true;
     113             :         }
     114       22000 :         else if (pg_strcasecmp(tok, "DMY") == 0 ||
     115       10980 :                  pg_strncasecmp(tok, "EURO", 4) == 0)
     116             :         {
     117          52 :             if (have_order && newDateOrder != DATEORDER_DMY)
     118           0 :                 ok = false;     /* conflicting orders */
     119          52 :             newDateOrder = DATEORDER_DMY;
     120          52 :             have_order = true;
     121             :         }
     122       10980 :         else if (pg_strcasecmp(tok, "MDY") == 0 ||
     123          12 :                  pg_strcasecmp(tok, "US") == 0 ||
     124           0 :                  pg_strncasecmp(tok, "NONEURO", 7) == 0)
     125             :         {
     126       10968 :             if (have_order && newDateOrder != DATEORDER_MDY)
     127           0 :                 ok = false;     /* conflicting orders */
     128       10968 :             newDateOrder = DATEORDER_MDY;
     129       10968 :             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       16082 :     pfree(rawstring);
     173       16082 :     list_free(elemlist);
     174             : 
     175       16082 :     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       16082 :     result = (char *) malloc(32);
     185       16082 :     if (!result)
     186           0 :         return false;
     187             : 
     188       16082 :     switch (newDateStyle)
     189             :     {
     190       10582 :         case USE_ISO_DATES:
     191       10582 :             strcpy(result, "ISO");
     192       10582 :             break;
     193          20 :         case USE_SQL_DATES:
     194          20 :             strcpy(result, "SQL");
     195          20 :             break;
     196          36 :         case USE_GERMAN_DATES:
     197          36 :             strcpy(result, "German");
     198          36 :             break;
     199        5444 :         default:
     200        5444 :             strcpy(result, "Postgres");
     201        5444 :             break;
     202             :     }
     203       16082 :     switch (newDateOrder)
     204             :     {
     205          32 :         case DATEORDER_YMD:
     206          32 :             strcat(result, ", YMD");
     207          32 :             break;
     208          68 :         case DATEORDER_DMY:
     209          68 :             strcat(result, ", DMY");
     210          68 :             break;
     211       15982 :         default:
     212       15982 :             strcat(result, ", MDY");
     213       15982 :             break;
     214             :     }
     215             : 
     216       16082 :     free(*newval);
     217       16082 :     *newval = result;
     218             : 
     219             :     /*
     220             :      * Set up the "extra" struct actually used by assign_datestyle.
     221             :      */
     222       16082 :     myextra = (int *) malloc(2 * sizeof(int));
     223       16082 :     if (!myextra)
     224           0 :         return false;
     225       16082 :     myextra[0] = newDateStyle;
     226       16082 :     myextra[1] = newDateOrder;
     227       16082 :     *extra = (void *) myextra;
     228             : 
     229       16082 :     return true;
     230             : }
     231             : 
     232             : /*
     233             :  * assign_datestyle: GUC assign_hook for datestyle
     234             :  */
     235             : void
     236       20890 : assign_datestyle(const char *newval, void *extra)
     237             : {
     238       20890 :     int        *myextra = (int *) extra;
     239             : 
     240       20890 :     DateStyle = myextra[0];
     241       20890 :     DateOrder = myextra[1];
     242       20890 : }
     243             : 
     244             : 
     245             : /*
     246             :  * TIMEZONE
     247             :  */
     248             : 
     249             : /*
     250             :  * check_timezone: GUC check_hook for timezone
     251             :  */
     252             : bool
     253       10390 : 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       10390 :     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       10390 :         hours = strtod(*newval, &endptr);
     322       10390 :         if (endptr != *newval && *endptr == '\0')
     323             :         {
     324             :             /* Here we change from SQL to Unix sign convention */
     325          46 :             gmtoffset = -hours * SECS_PER_HOUR;
     326          46 :             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       10344 :             new_tz = pg_tzset(*newval);
     334             : 
     335       10344 :             if (!new_tz)
     336             :             {
     337             :                 /* Doesn't seem to be any great value in errdetail here */
     338           0 :                 return false;
     339             :             }
     340             : 
     341       10344 :             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       10390 :     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       10390 :     *extra = malloc(sizeof(pg_tz *));
     362       10390 :     if (!*extra)
     363           0 :         return false;
     364       10390 :     *((pg_tz **) *extra) = new_tz;
     365             : 
     366       10390 :     return true;
     367             : }
     368             : 
     369             : /*
     370             :  * assign_timezone: GUC assign_hook for timezone
     371             :  */
     372             : void
     373       10438 : assign_timezone(const char *newval, void *extra)
     374             : {
     375       10438 :     session_timezone = *((pg_tz **) extra);
     376       10438 : }
     377             : 
     378             : /*
     379             :  * show_timezone: GUC show_hook for timezone
     380             :  */
     381             : const char *
     382        8042 : show_timezone(void)
     383             : {
     384             :     const char *tzn;
     385             : 
     386             :     /* Always show the zone's canonical name */
     387        8042 :     tzn = pg_get_timezone_name(session_timezone);
     388             : 
     389        8042 :     if (tzn != NULL)
     390        8042 :         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        7090 : 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        7090 :     new_tz = pg_tzset(*newval);
     416             : 
     417        7090 :     if (!new_tz)
     418             :     {
     419             :         /* Doesn't seem to be any great value in errdetail here */
     420           0 :         return false;
     421             :     }
     422             : 
     423        7090 :     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        7090 :     *extra = malloc(sizeof(pg_tz *));
     435        7090 :     if (!*extra)
     436           0 :         return false;
     437        7090 :     *((pg_tz **) *extra) = new_tz;
     438             : 
     439        7090 :     return true;
     440             : }
     441             : 
     442             : /*
     443             :  * assign_log_timezone: GUC assign_hook for log_timezone
     444             :  */
     445             : void
     446        7086 : assign_log_timezone(const char *newval, void *extra)
     447             : {
     448        7086 :     log_timezone = *((pg_tz **) extra);
     449        7086 : }
     450             : 
     451             : /*
     452             :  * show_log_timezone: GUC show_hook for log_timezone
     453             :  */
     454             : const char *
     455          36 : show_log_timezone(void)
     456             : {
     457             :     const char *tzn;
     458             : 
     459             :     /* Always show the zone's canonical name */
     460          36 :     tzn = pg_get_timezone_name(log_timezone);
     461             : 
     462          36 :     if (tzn != NULL)
     463          36 :         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        8316 : check_transaction_read_only(bool *newval, void **extra, GucSource source)
     487             : {
     488        8316 :     if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker)
     489             :     {
     490             :         /* Can't go to r/w mode inside a r/o transaction */
     491          32 :         if (IsSubTransaction())
     492             :         {
     493           8 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     494           8 :             GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
     495           8 :             return false;
     496             :         }
     497             :         /* Top level transaction can't change to r/w after first snapshot. */
     498          24 :         if (FirstSnapshotSet)
     499             :         {
     500           4 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     501           4 :             GUC_check_errmsg("transaction read-write mode must be set before any query");
     502           4 :             return false;
     503             :         }
     504             :         /* Can't go to r/w mode while recovery is still active */
     505          20 :         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        8304 :     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       12642 : check_XactIsoLevel(int *newval, void **extra, GucSource source)
     526             : {
     527       12642 :     int         newXactIsoLevel = *newval;
     528             : 
     529       12642 :     if (newXactIsoLevel != XactIsoLevel && IsTransactionState())
     530             :     {
     531        5538 :         if (FirstSnapshotSet)
     532             :         {
     533           2 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     534           2 :             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
     535           2 :             return false;
     536             :         }
     537             :         /* We ignore a subtransaction setting it to the existing value. */
     538        5536 :         if (IsSubTransaction())
     539             :         {
     540           0 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     541           0 :             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
     542           0 :             return false;
     543             :         }
     544             :         /* Can't go to serializable mode while recovery is still active */
     545        5536 :         if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
     546             :         {
     547           0 :             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
     548           0 :             GUC_check_errmsg("cannot use serializable mode in a hot standby");
     549           0 :             GUC_check_errhint("You can use REPEATABLE READ instead.");
     550           0 :             return false;
     551             :         }
     552             :     }
     553             : 
     554       12640 :     return true;
     555             : }
     556             : 
     557             : /*
     558             :  * SET TRANSACTION [NOT] DEFERRABLE
     559             :  */
     560             : 
     561             : bool
     562        7526 : check_transaction_deferrable(bool *newval, void **extra, GucSource source)
     563             : {
     564        7526 :     if (IsSubTransaction())
     565             :     {
     566           0 :         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     567           0 :         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
     568           0 :         return false;
     569             :     }
     570        7526 :     if (FirstSnapshotSet)
     571             :     {
     572           0 :         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     573           0 :         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
     574           0 :         return false;
     575             :     }
     576             : 
     577        7526 :     return true;
     578             : }
     579             : 
     580             : /*
     581             :  * Random number seed
     582             :  *
     583             :  * We can't roll back the random sequence on error, and we don't want
     584             :  * config file reloads to affect it, so we only want interactive SET SEED
     585             :  * commands to set it.  We use the "extra" storage to ensure that rollbacks
     586             :  * don't try to do the operation again.
     587             :  */
     588             : 
     589             : bool
     590        2184 : check_random_seed(double *newval, void **extra, GucSource source)
     591             : {
     592        2184 :     *extra = malloc(sizeof(int));
     593        2184 :     if (!*extra)
     594           0 :         return false;
     595             :     /* Arm the assign only if source of value is an interactive SET */
     596        2184 :     *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
     597             : 
     598        2184 :     return true;
     599             : }
     600             : 
     601             : void
     602        2184 : assign_random_seed(double newval, void *extra)
     603             : {
     604             :     /* We'll do this at most once for any setting of the GUC variable */
     605        2184 :     if (*((int *) extra))
     606           0 :         DirectFunctionCall1(setseed, Float8GetDatum(newval));
     607        2184 :     *((int *) extra) = 0;
     608        2184 : }
     609             : 
     610             : const char *
     611          32 : show_random_seed(void)
     612             : {
     613          32 :     return "unavailable";
     614             : }
     615             : 
     616             : 
     617             : /*
     618             :  * SET CLIENT_ENCODING
     619             :  */
     620             : 
     621             : bool
     622       16286 : check_client_encoding(char **newval, void **extra, GucSource source)
     623             : {
     624             :     int         encoding;
     625             :     const char *canonical_name;
     626             : 
     627             :     /* Look up the encoding by name */
     628       16286 :     encoding = pg_valid_client_encoding(*newval);
     629       16286 :     if (encoding < 0)
     630           0 :         return false;
     631             : 
     632             :     /* Get the canonical name (no aliases, uniform case) */
     633       16286 :     canonical_name = pg_encoding_to_char(encoding);
     634             : 
     635             :     /*
     636             :      * If we are not within a transaction then PrepareClientEncoding will not
     637             :      * be able to look up the necessary conversion procs.  If we are still
     638             :      * starting up, it will return "OK" anyway, and InitializeClientEncoding
     639             :      * will fix things once initialization is far enough along.  After
     640             :      * startup, we'll fail.  This would only happen if someone tries to change
     641             :      * client_encoding in postgresql.conf and then SIGHUP existing sessions.
     642             :      * It seems like a bad idea for client_encoding to change that way anyhow,
     643             :      * so we don't go out of our way to support it.
     644             :      *
     645             :      * Note: in the postmaster, or any other process that never calls
     646             :      * InitializeClientEncoding, PrepareClientEncoding will always succeed,
     647             :      * and so will SetClientEncoding; but they won't do anything, which is OK.
     648             :      */
     649       16286 :     if (PrepareClientEncoding(encoding) < 0)
     650             :     {
     651           0 :         if (IsTransactionState())
     652             :         {
     653             :             /* Must be a genuine no-such-conversion problem */
     654           0 :             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
     655           0 :             GUC_check_errdetail("Conversion between %s and %s is not supported.",
     656             :                                 canonical_name,
     657             :                                 GetDatabaseEncodingName());
     658             :         }
     659             :         else
     660             :         {
     661             :             /* Provide a useful complaint */
     662           0 :             GUC_check_errdetail("Cannot change \"client_encoding\" now.");
     663             :         }
     664           0 :         return false;
     665             :     }
     666             : 
     667             :     /*
     668             :      * Replace the user-supplied string with the encoding's canonical name.
     669             :      * This gets rid of aliases and case-folding variations.
     670             :      *
     671             :      * XXX Although canonicalizing seems like a good idea in the abstract, it
     672             :      * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE"
     673             :      * as the client_encoding setting then it will read back the same way. As
     674             :      * a workaround, don't replace the string if it's "UNICODE".  Remove that
     675             :      * hack when pre-9.1 JDBC drivers are no longer in use.
     676             :      */
     677       16286 :     if (strcmp(*newval, canonical_name) != 0 &&
     678          12 :         strcmp(*newval, "UNICODE") != 0)
     679             :     {
     680          12 :         free(*newval);
     681          12 :         *newval = strdup(canonical_name);
     682          12 :         if (!*newval)
     683           0 :             return false;
     684             :     }
     685             : 
     686             :     /*
     687             :      * Save the encoding's ID in *extra, for use by assign_client_encoding.
     688             :      */
     689       16286 :     *extra = malloc(sizeof(int));
     690       16286 :     if (!*extra)
     691           0 :         return false;
     692       16286 :     *((int *) *extra) = encoding;
     693             : 
     694       16286 :     return true;
     695             : }
     696             : 
     697             : void
     698       15552 : assign_client_encoding(const char *newval, void *extra)
     699             : {
     700       15552 :     int         encoding = *((int *) extra);
     701             : 
     702             :     /*
     703             :      * Parallel workers send data to the leader, not the client.  They always
     704             :      * send data using the database encoding.
     705             :      */
     706       15552 :     if (IsParallelWorker())
     707             :     {
     708             :         /*
     709             :          * During parallel worker startup, we want to accept the leader's
     710             :          * client_encoding setting so that anyone who looks at the value in
     711             :          * the worker sees the same value that they would see in the leader.
     712             :          */
     713        4710 :         if (InitializingParallelWorker)
     714        4710 :             return;
     715             : 
     716             :         /*
     717             :          * A change other than during startup, for example due to a SET clause
     718             :          * attached to a function definition, should be rejected, as there is
     719             :          * nothing we can do inside the worker to make it take effect.
     720             :          */
     721           0 :         ereport(ERROR,
     722             :                 (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
     723             :                  errmsg("cannot change client_encoding during a parallel operation")));
     724             :     }
     725             : 
     726             :     /* We do not expect an error if PrepareClientEncoding succeeded */
     727       10842 :     if (SetClientEncoding(encoding) < 0)
     728           0 :         elog(LOG, "SetClientEncoding(%d) failed", encoding);
     729             : }
     730             : 
     731             : 
     732             : /*
     733             :  * SET SESSION AUTHORIZATION
     734             :  */
     735             : 
     736             : typedef struct
     737             : {
     738             :     /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
     739             :     Oid         roleid;
     740             :     bool        is_superuser;
     741             : } role_auth_extra;
     742             : 
     743             : bool
     744       15934 : check_session_authorization(char **newval, void **extra, GucSource source)
     745             : {
     746             :     HeapTuple   roleTup;
     747             :     Form_pg_authid roleform;
     748             :     Oid         roleid;
     749             :     bool        is_superuser;
     750             :     role_auth_extra *myextra;
     751             : 
     752             :     /* Do nothing for the boot_val default of NULL */
     753       15934 :     if (*newval == NULL)
     754        3754 :         return true;
     755             : 
     756       12180 :     if (!IsTransactionState())
     757             :     {
     758             :         /*
     759             :          * Can't do catalog lookups, so fail.  The result of this is that
     760             :          * session_authorization cannot be set in postgresql.conf, which seems
     761             :          * like a good thing anyway, so we don't work hard to avoid it.
     762             :          */
     763           0 :         return false;
     764             :     }
     765             : 
     766             :     /* Look up the username */
     767       12180 :     roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
     768       12180 :     if (!HeapTupleIsValid(roleTup))
     769             :     {
     770           0 :         GUC_check_errmsg("role \"%s\" does not exist", *newval);
     771           0 :         return false;
     772             :     }
     773             : 
     774       12180 :     roleform = (Form_pg_authid) GETSTRUCT(roleTup);
     775       12180 :     roleid = roleform->oid;
     776       12180 :     is_superuser = roleform->rolsuper;
     777             : 
     778       12180 :     ReleaseSysCache(roleTup);
     779             : 
     780             :     /* Set up "extra" struct for assign_session_authorization to use */
     781       12180 :     myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
     782       12180 :     if (!myextra)
     783           0 :         return false;
     784       12180 :     myextra->roleid = roleid;
     785       12180 :     myextra->is_superuser = is_superuser;
     786       12180 :     *extra = (void *) myextra;
     787             : 
     788       12180 :     return true;
     789             : }
     790             : 
     791             : void
     792       16314 : assign_session_authorization(const char *newval, void *extra)
     793             : {
     794       16314 :     role_auth_extra *myextra = (role_auth_extra *) extra;
     795             : 
     796             :     /* Do nothing for the boot_val default of NULL */
     797       16314 :     if (!myextra)
     798        3754 :         return;
     799             : 
     800       12560 :     SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
     801             : }
     802             : 
     803             : 
     804             : /*
     805             :  * SET ROLE
     806             :  *
     807             :  * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
     808             :  * a translation of "none" to InvalidOid.  Otherwise this is much like
     809             :  * SET SESSION AUTHORIZATION.
     810             :  */
     811             : extern char *role_string;       /* in guc.c */
     812             : 
     813             : bool
     814        2682 : check_role(char **newval, void **extra, GucSource source)
     815             : {
     816             :     HeapTuple   roleTup;
     817             :     Oid         roleid;
     818             :     bool        is_superuser;
     819             :     role_auth_extra *myextra;
     820             :     Form_pg_authid roleform;
     821             : 
     822        2682 :     if (strcmp(*newval, "none") == 0)
     823             :     {
     824             :         /* hardwired translation */
     825        2184 :         roleid = InvalidOid;
     826        2184 :         is_superuser = false;
     827             :     }
     828             :     else
     829             :     {
     830         498 :         if (!IsTransactionState())
     831             :         {
     832             :             /*
     833             :              * Can't do catalog lookups, so fail.  The result of this is that
     834             :              * role cannot be set in postgresql.conf, which seems like a good
     835             :              * thing anyway, so we don't work hard to avoid it.
     836             :              */
     837           0 :             return false;
     838             :         }
     839             : 
     840             :         /* Look up the username */
     841         498 :         roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
     842         498 :         if (!HeapTupleIsValid(roleTup))
     843             :         {
     844           0 :             GUC_check_errmsg("role \"%s\" does not exist", *newval);
     845           0 :             return false;
     846             :         }
     847             : 
     848         498 :         roleform = (Form_pg_authid) GETSTRUCT(roleTup);
     849         498 :         roleid = roleform->oid;
     850         498 :         is_superuser = roleform->rolsuper;
     851             : 
     852         498 :         ReleaseSysCache(roleTup);
     853             : 
     854             :         /*
     855             :          * Verify that session user is allowed to become this role, but skip
     856             :          * this in parallel mode, where we must blindly recreate the parallel
     857             :          * leader's state.
     858             :          */
     859         498 :         if (!InitializingParallelWorker &&
     860         498 :             !is_member_of_role(GetSessionUserId(), roleid))
     861             :         {
     862           0 :             GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
     863           0 :             GUC_check_errmsg("permission denied to set role \"%s\"",
     864             :                              *newval);
     865           0 :             return false;
     866             :         }
     867             :     }
     868             : 
     869             :     /* Set up "extra" struct for assign_role to use */
     870        2682 :     myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
     871        2682 :     if (!myextra)
     872           0 :         return false;
     873        2682 :     myextra->roleid = roleid;
     874        2682 :     myextra->is_superuser = is_superuser;
     875        2682 :     *extra = (void *) myextra;
     876             : 
     877        2682 :     return true;
     878             : }
     879             : 
     880             : void
     881        3018 : assign_role(const char *newval, void *extra)
     882             : {
     883        3018 :     role_auth_extra *myextra = (role_auth_extra *) extra;
     884             : 
     885        3018 :     SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
     886        3018 : }
     887             : 
     888             : const char *
     889          32 : show_role(void)
     890             : {
     891             :     /*
     892             :      * Check whether SET ROLE is active; if not return "none".  This is a
     893             :      * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
     894             :      * resets SET ROLE to NONE, but we cannot set the GUC role variable from
     895             :      * assign_session_authorization (because we haven't got enough info to
     896             :      * call set_config_option).
     897             :      */
     898          32 :     if (!OidIsValid(GetCurrentRoleId()))
     899          32 :         return "none";
     900             : 
     901             :     /* Otherwise we can just use the GUC string */
     902           0 :     return role_string ? role_string : "none";
     903             : }

Generated by: LCOV version 1.13