LCOV - code coverage report
Current view: top level - src/backend/commands - variable.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 292 433 67.4 %
Date: 2024-09-08 22:11:48 Functions: 35 36 97.2 %
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-2024, 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 "access/xlogprefetcher.h"
      26             : #include "catalog/pg_authid.h"
      27             : #include "common/string.h"
      28             : #include "mb/pg_wchar.h"
      29             : #include "miscadmin.h"
      30             : #include "postmaster/postmaster.h"
      31             : #include "postmaster/syslogger.h"
      32             : #include "storage/bufmgr.h"
      33             : #include "utils/acl.h"
      34             : #include "utils/backend_status.h"
      35             : #include "utils/datetime.h"
      36             : #include "utils/fmgrprotos.h"
      37             : #include "utils/guc_hooks.h"
      38             : #include "utils/snapmgr.h"
      39             : #include "utils/syscache.h"
      40             : #include "utils/timestamp.h"
      41             : #include "utils/tzparser.h"
      42             : #include "utils/varlena.h"
      43             : 
      44             : /*
      45             :  * DATESTYLE
      46             :  */
      47             : 
      48             : /*
      49             :  * check_datestyle: GUC check_hook for datestyle
      50             :  */
      51             : bool
      52       23786 : check_datestyle(char **newval, void **extra, GucSource source)
      53             : {
      54       23786 :     int         newDateStyle = DateStyle;
      55       23786 :     int         newDateOrder = DateOrder;
      56       23786 :     bool        have_style = false;
      57       23786 :     bool        have_order = false;
      58       23786 :     bool        ok = true;
      59             :     char       *rawstring;
      60             :     int        *myextra;
      61             :     char       *result;
      62             :     List       *elemlist;
      63             :     ListCell   *l;
      64             : 
      65             :     /* Need a modifiable copy of string */
      66       23786 :     rawstring = pstrdup(*newval);
      67             : 
      68             :     /* Parse string into list of identifiers */
      69       23786 :     if (!SplitIdentifierString(rawstring, ',', &elemlist))
      70             :     {
      71             :         /* syntax error in list */
      72           0 :         GUC_check_errdetail("List syntax is invalid.");
      73           0 :         pfree(rawstring);
      74           0 :         list_free(elemlist);
      75           0 :         return false;
      76             :     }
      77             : 
      78       61674 :     foreach(l, elemlist)
      79             :     {
      80       37888 :         char       *tok = (char *) lfirst(l);
      81             : 
      82             :         /* Ugh. Somebody ought to write a table driven version -- mjl */
      83             : 
      84       37888 :         if (pg_strcasecmp(tok, "ISO") == 0)
      85             :         {
      86       16612 :             if (have_style && newDateStyle != USE_ISO_DATES)
      87           0 :                 ok = false;     /* conflicting styles */
      88       16612 :             newDateStyle = USE_ISO_DATES;
      89       16612 :             have_style = true;
      90             :         }
      91       21276 :         else if (pg_strcasecmp(tok, "SQL") == 0)
      92             :         {
      93          30 :             if (have_style && newDateStyle != USE_SQL_DATES)
      94           0 :                 ok = false;     /* conflicting styles */
      95          30 :             newDateStyle = USE_SQL_DATES;
      96          30 :             have_style = true;
      97             :         }
      98       21246 :         else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
      99             :         {
     100        7052 :             if (have_style && newDateStyle != USE_POSTGRES_DATES)
     101           0 :                 ok = false;     /* conflicting styles */
     102        7052 :             newDateStyle = USE_POSTGRES_DATES;
     103        7052 :             have_style = true;
     104             :         }
     105       14194 :         else if (pg_strcasecmp(tok, "GERMAN") == 0)
     106             :         {
     107          42 :             if (have_style && newDateStyle != USE_GERMAN_DATES)
     108           0 :                 ok = false;     /* conflicting styles */
     109          42 :             newDateStyle = USE_GERMAN_DATES;
     110          42 :             have_style = true;
     111             :             /* GERMAN also sets DMY, unless explicitly overridden */
     112          42 :             if (!have_order)
     113          42 :                 newDateOrder = DATEORDER_DMY;
     114             :         }
     115       14152 :         else if (pg_strcasecmp(tok, "YMD") == 0)
     116             :         {
     117          36 :             if (have_order && newDateOrder != DATEORDER_YMD)
     118           0 :                 ok = false;     /* conflicting orders */
     119          36 :             newDateOrder = DATEORDER_YMD;
     120          36 :             have_order = true;
     121             :         }
     122       28184 :         else if (pg_strcasecmp(tok, "DMY") == 0 ||
     123       14068 :                  pg_strncasecmp(tok, "EURO", 4) == 0)
     124             :         {
     125          66 :             if (have_order && newDateOrder != DATEORDER_DMY)
     126           0 :                 ok = false;     /* conflicting orders */
     127          66 :             newDateOrder = DATEORDER_DMY;
     128          66 :             have_order = true;
     129             :         }
     130       14068 :         else if (pg_strcasecmp(tok, "MDY") == 0 ||
     131          18 :                  pg_strcasecmp(tok, "US") == 0 ||
     132           0 :                  pg_strncasecmp(tok, "NONEURO", 7) == 0)
     133             :         {
     134       14050 :             if (have_order && newDateOrder != DATEORDER_MDY)
     135           0 :                 ok = false;     /* conflicting orders */
     136       14050 :             newDateOrder = DATEORDER_MDY;
     137       14050 :             have_order = true;
     138             :         }
     139           0 :         else if (pg_strcasecmp(tok, "DEFAULT") == 0)
     140             :         {
     141             :             /*
     142             :              * Easiest way to get the current DEFAULT state is to fetch the
     143             :              * DEFAULT string from guc.c and recursively parse it.
     144             :              *
     145             :              * We can't simply "return check_datestyle(...)" because we need
     146             :              * to handle constructs like "DEFAULT, ISO".
     147             :              */
     148             :             char       *subval;
     149           0 :             void       *subextra = NULL;
     150             : 
     151           0 :             subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
     152           0 :             if (!subval)
     153             :             {
     154           0 :                 ok = false;
     155           0 :                 break;
     156             :             }
     157           0 :             if (!check_datestyle(&subval, &subextra, source))
     158             :             {
     159           0 :                 guc_free(subval);
     160           0 :                 ok = false;
     161           0 :                 break;
     162             :             }
     163           0 :             myextra = (int *) subextra;
     164           0 :             if (!have_style)
     165           0 :                 newDateStyle = myextra[0];
     166           0 :             if (!have_order)
     167           0 :                 newDateOrder = myextra[1];
     168           0 :             guc_free(subval);
     169           0 :             guc_free(subextra);
     170             :         }
     171             :         else
     172             :         {
     173           0 :             GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
     174           0 :             pfree(rawstring);
     175           0 :             list_free(elemlist);
     176           0 :             return false;
     177             :         }
     178             :     }
     179             : 
     180       23786 :     pfree(rawstring);
     181       23786 :     list_free(elemlist);
     182             : 
     183       23786 :     if (!ok)
     184             :     {
     185           0 :         GUC_check_errdetail("Conflicting \"datestyle\" specifications.");
     186           0 :         return false;
     187             :     }
     188             : 
     189             :     /*
     190             :      * Prepare the canonical string to return.  GUC wants it guc_malloc'd.
     191             :      */
     192       23786 :     result = (char *) guc_malloc(LOG, 32);
     193       23786 :     if (!result)
     194           0 :         return false;
     195             : 
     196       23786 :     switch (newDateStyle)
     197             :     {
     198       16636 :         case USE_ISO_DATES:
     199       16636 :             strcpy(result, "ISO");
     200       16636 :             break;
     201          30 :         case USE_SQL_DATES:
     202          30 :             strcpy(result, "SQL");
     203          30 :             break;
     204          42 :         case USE_GERMAN_DATES:
     205          42 :             strcpy(result, "German");
     206          42 :             break;
     207        7078 :         default:
     208        7078 :             strcpy(result, "Postgres");
     209        7078 :             break;
     210             :     }
     211       23786 :     switch (newDateOrder)
     212             :     {
     213          48 :         case DATEORDER_YMD:
     214          48 :             strcat(result, ", YMD");
     215          48 :             break;
     216          88 :         case DATEORDER_DMY:
     217          88 :             strcat(result, ", DMY");
     218          88 :             break;
     219       23650 :         default:
     220       23650 :             strcat(result, ", MDY");
     221       23650 :             break;
     222             :     }
     223             : 
     224       23786 :     guc_free(*newval);
     225       23786 :     *newval = result;
     226             : 
     227             :     /*
     228             :      * Set up the "extra" struct actually used by assign_datestyle.
     229             :      */
     230       23786 :     myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
     231       23786 :     if (!myextra)
     232           0 :         return false;
     233       23786 :     myextra[0] = newDateStyle;
     234       23786 :     myextra[1] = newDateOrder;
     235       23786 :     *extra = (void *) myextra;
     236             : 
     237       23786 :     return true;
     238             : }
     239             : 
     240             : /*
     241             :  * assign_datestyle: GUC assign_hook for datestyle
     242             :  */
     243             : void
     244       31902 : assign_datestyle(const char *newval, void *extra)
     245             : {
     246       31902 :     int        *myextra = (int *) extra;
     247             : 
     248       31902 :     DateStyle = myextra[0];
     249       31902 :     DateOrder = myextra[1];
     250       31902 : }
     251             : 
     252             : 
     253             : /*
     254             :  * TIMEZONE
     255             :  */
     256             : 
     257             : /*
     258             :  * check_timezone: GUC check_hook for timezone
     259             :  */
     260             : bool
     261       14154 : check_timezone(char **newval, void **extra, GucSource source)
     262             : {
     263             :     pg_tz      *new_tz;
     264             :     long        gmtoffset;
     265             :     char       *endptr;
     266             :     double      hours;
     267             : 
     268       14154 :     if (pg_strncasecmp(*newval, "interval", 8) == 0)
     269             :     {
     270             :         /*
     271             :          * Support INTERVAL 'foo'.  This is for SQL spec compliance, not
     272             :          * because it has any actual real-world usefulness.
     273             :          */
     274           0 :         const char *valueptr = *newval;
     275             :         char       *val;
     276             :         Interval   *interval;
     277             : 
     278           0 :         valueptr += 8;
     279           0 :         while (isspace((unsigned char) *valueptr))
     280           0 :             valueptr++;
     281           0 :         if (*valueptr++ != '\'')
     282           0 :             return false;
     283           0 :         val = pstrdup(valueptr);
     284             :         /* Check and remove trailing quote */
     285           0 :         endptr = strchr(val, '\'');
     286           0 :         if (!endptr || endptr[1] != '\0')
     287             :         {
     288           0 :             pfree(val);
     289           0 :             return false;
     290             :         }
     291           0 :         *endptr = '\0';
     292             : 
     293             :         /*
     294             :          * Try to parse it.  XXX an invalid interval format will result in
     295             :          * ereport(ERROR), which is not desirable for GUC.  We did what we
     296             :          * could to guard against this in flatten_set_variable_args, but a
     297             :          * string coming in from postgresql.conf might contain anything.
     298             :          */
     299           0 :         interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
     300             :                                                          CStringGetDatum(val),
     301             :                                                          ObjectIdGetDatum(InvalidOid),
     302             :                                                          Int32GetDatum(-1)));
     303             : 
     304           0 :         pfree(val);
     305           0 :         if (interval->month != 0)
     306             :         {
     307           0 :             GUC_check_errdetail("Cannot specify months in time zone interval.");
     308           0 :             pfree(interval);
     309           0 :             return false;
     310             :         }
     311           0 :         if (interval->day != 0)
     312             :         {
     313           0 :             GUC_check_errdetail("Cannot specify days in time zone interval.");
     314           0 :             pfree(interval);
     315           0 :             return false;
     316             :         }
     317             : 
     318             :         /* Here we change from SQL to Unix sign convention */
     319           0 :         gmtoffset = -(interval->time / USECS_PER_SEC);
     320           0 :         new_tz = pg_tzset_offset(gmtoffset);
     321             : 
     322           0 :         pfree(interval);
     323             :     }
     324             :     else
     325             :     {
     326             :         /*
     327             :          * Try it as a numeric number of hours (possibly fractional).
     328             :          */
     329       14154 :         hours = strtod(*newval, &endptr);
     330       14154 :         if (endptr != *newval && *endptr == '\0')
     331             :         {
     332             :             /* Here we change from SQL to Unix sign convention */
     333          72 :             gmtoffset = -hours * SECS_PER_HOUR;
     334          72 :             new_tz = pg_tzset_offset(gmtoffset);
     335             :         }
     336             :         else
     337             :         {
     338             :             /*
     339             :              * Otherwise assume it is a timezone name, and try to load it.
     340             :              */
     341       14082 :             new_tz = pg_tzset(*newval);
     342             : 
     343       14082 :             if (!new_tz)
     344             :             {
     345             :                 /* Doesn't seem to be any great value in errdetail here */
     346           0 :                 return false;
     347             :             }
     348             : 
     349       14082 :             if (!pg_tz_acceptable(new_tz))
     350             :             {
     351           0 :                 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
     352             :                                  *newval);
     353           0 :                 GUC_check_errdetail("PostgreSQL does not support leap seconds.");
     354           0 :                 return false;
     355             :             }
     356             :         }
     357             :     }
     358             : 
     359             :     /* Test for failure in pg_tzset_offset, which we assume is out-of-range */
     360       14154 :     if (!new_tz)
     361             :     {
     362           0 :         GUC_check_errdetail("UTC timezone offset is out of range.");
     363           0 :         return false;
     364             :     }
     365             : 
     366             :     /*
     367             :      * Pass back data for assign_timezone to use
     368             :      */
     369       14154 :     *extra = guc_malloc(LOG, sizeof(pg_tz *));
     370       14154 :     if (!*extra)
     371           0 :         return false;
     372       14154 :     *((pg_tz **) *extra) = new_tz;
     373             : 
     374       14154 :     return true;
     375             : }
     376             : 
     377             : /*
     378             :  * assign_timezone: GUC assign_hook for timezone
     379             :  */
     380             : void
     381       14254 : assign_timezone(const char *newval, void *extra)
     382             : {
     383       14254 :     session_timezone = *((pg_tz **) extra);
     384       14254 : }
     385             : 
     386             : /*
     387             :  * show_timezone: GUC show_hook for timezone
     388             :  */
     389             : const char *
     390       52156 : show_timezone(void)
     391             : {
     392             :     const char *tzn;
     393             : 
     394             :     /* Always show the zone's canonical name */
     395       52156 :     tzn = pg_get_timezone_name(session_timezone);
     396             : 
     397       52156 :     if (tzn != NULL)
     398       52156 :         return tzn;
     399             : 
     400           0 :     return "unknown";
     401             : }
     402             : 
     403             : 
     404             : /*
     405             :  * LOG_TIMEZONE
     406             :  *
     407             :  * For log_timezone, we don't support the interval-based methods of setting a
     408             :  * zone, which are only there for SQL spec compliance not because they're
     409             :  * actually useful.
     410             :  */
     411             : 
     412             : /*
     413             :  * check_log_timezone: GUC check_hook for log_timezone
     414             :  */
     415             : bool
     416        9618 : check_log_timezone(char **newval, void **extra, GucSource source)
     417             : {
     418             :     pg_tz      *new_tz;
     419             : 
     420             :     /*
     421             :      * Assume it is a timezone name, and try to load it.
     422             :      */
     423        9618 :     new_tz = pg_tzset(*newval);
     424             : 
     425        9618 :     if (!new_tz)
     426             :     {
     427             :         /* Doesn't seem to be any great value in errdetail here */
     428           0 :         return false;
     429             :     }
     430             : 
     431        9618 :     if (!pg_tz_acceptable(new_tz))
     432             :     {
     433           0 :         GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
     434             :                          *newval);
     435           0 :         GUC_check_errdetail("PostgreSQL does not support leap seconds.");
     436           0 :         return false;
     437             :     }
     438             : 
     439             :     /*
     440             :      * Pass back data for assign_log_timezone to use
     441             :      */
     442        9618 :     *extra = guc_malloc(LOG, sizeof(pg_tz *));
     443        9618 :     if (!*extra)
     444           0 :         return false;
     445        9618 :     *((pg_tz **) *extra) = new_tz;
     446             : 
     447        9618 :     return true;
     448             : }
     449             : 
     450             : /*
     451             :  * assign_log_timezone: GUC assign_hook for log_timezone
     452             :  */
     453             : void
     454        9612 : assign_log_timezone(const char *newval, void *extra)
     455             : {
     456        9612 :     log_timezone = *((pg_tz **) extra);
     457        9612 : }
     458             : 
     459             : /*
     460             :  * show_log_timezone: GUC show_hook for log_timezone
     461             :  */
     462             : const char *
     463        4238 : show_log_timezone(void)
     464             : {
     465             :     const char *tzn;
     466             : 
     467             :     /* Always show the zone's canonical name */
     468        4238 :     tzn = pg_get_timezone_name(log_timezone);
     469             : 
     470        4238 :     if (tzn != NULL)
     471        4238 :         return tzn;
     472             : 
     473           0 :     return "unknown";
     474             : }
     475             : 
     476             : 
     477             : /*
     478             :  * TIMEZONE_ABBREVIATIONS
     479             :  */
     480             : 
     481             : /*
     482             :  * GUC check_hook for assign_timezone_abbreviations
     483             :  */
     484             : bool
     485       16450 : check_timezone_abbreviations(char **newval, void **extra, GucSource source)
     486             : {
     487             :     /*
     488             :      * The boot_val for timezone_abbreviations is NULL.  When we see that we
     489             :      * just do nothing.  If the value isn't overridden from the config file
     490             :      * then pg_timezone_abbrev_initialize() will eventually replace it with
     491             :      * "Default".  This hack has two purposes: to avoid wasting cycles loading
     492             :      * values that might soon be overridden from the config file, and to avoid
     493             :      * trying to read the timezone abbrev files during InitializeGUCOptions().
     494             :      * The latter doesn't work in an EXEC_BACKEND subprocess because
     495             :      * my_exec_path hasn't been set yet and so we can't locate PGSHAREDIR.
     496             :      */
     497       16450 :     if (*newval == NULL)
     498             :     {
     499             :         Assert(source == PGC_S_DEFAULT);
     500        4530 :         return true;
     501             :     }
     502             : 
     503             :     /* OK, load the file and produce a guc_malloc'd TimeZoneAbbrevTable */
     504       11920 :     *extra = load_tzoffsets(*newval);
     505             : 
     506             :     /* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
     507       11920 :     if (!*extra)
     508           0 :         return false;
     509             : 
     510       11920 :     return true;
     511             : }
     512             : 
     513             : /*
     514             :  * GUC assign_hook for assign_timezone_abbreviations
     515             :  */
     516             : void
     517       16272 : assign_timezone_abbreviations(const char *newval, void *extra)
     518             : {
     519             :     /* Do nothing for the boot_val default of NULL */
     520       16272 :     if (!extra)
     521        4530 :         return;
     522             : 
     523       11742 :     InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) extra);
     524             : }
     525             : 
     526             : 
     527             : /*
     528             :  * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE
     529             :  *
     530             :  * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and
     531             :  * we also always allow changes from read-write to read-only.  However,
     532             :  * read-only may be changed to read-write only when in a top-level transaction
     533             :  * that has not yet taken an initial snapshot.  Can't do it in a hot standby,
     534             :  * either.
     535             :  *
     536             :  * If we are not in a transaction at all, just allow the change; it means
     537             :  * nothing since XactReadOnly will be reset by the next StartTransaction().
     538             :  * The IsTransactionState() test protects us against trying to check
     539             :  * RecoveryInProgress() in contexts where shared memory is not accessible.
     540             :  * (Similarly, if we're restoring state in a parallel worker, just allow
     541             :  * the change.)
     542             :  */
     543             : bool
     544       10438 : check_transaction_read_only(bool *newval, void **extra, GucSource source)
     545             : {
     546       10438 :     if (*newval == false && XactReadOnly && IsTransactionState() && !InitializingParallelWorker)
     547             :     {
     548             :         /* Can't go to r/w mode inside a r/o transaction */
     549          64 :         if (IsSubTransaction())
     550             :         {
     551          12 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     552          12 :             GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
     553          12 :             return false;
     554             :         }
     555             :         /* Top level transaction can't change to r/w after first snapshot. */
     556          52 :         if (FirstSnapshotSet)
     557             :         {
     558           6 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     559           6 :             GUC_check_errmsg("transaction read-write mode must be set before any query");
     560           6 :             return false;
     561             :         }
     562             :         /* Can't go to r/w mode while recovery is still active */
     563          46 :         if (RecoveryInProgress())
     564             :         {
     565           0 :             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
     566           0 :             GUC_check_errmsg("cannot set transaction read-write mode during recovery");
     567           0 :             return false;
     568             :         }
     569             :     }
     570             : 
     571       10420 :     return true;
     572             : }
     573             : 
     574             : /*
     575             :  * SET TRANSACTION ISOLATION LEVEL
     576             :  *
     577             :  * We allow idempotent changes at any time, but otherwise this can only be
     578             :  * changed in a toplevel transaction that has not yet taken a snapshot.
     579             :  *
     580             :  * As in check_transaction_read_only, allow it if not inside a transaction,
     581             :  * or if restoring state in a parallel worker.
     582             :  */
     583             : bool
     584       15674 : check_transaction_isolation(int *newval, void **extra, GucSource source)
     585             : {
     586       15674 :     int         newXactIsoLevel = *newval;
     587             : 
     588       21462 :     if (newXactIsoLevel != XactIsoLevel &&
     589       11576 :         IsTransactionState() && !InitializingParallelWorker)
     590             :     {
     591        5708 :         if (FirstSnapshotSet)
     592             :         {
     593           2 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     594           2 :             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
     595           2 :             return false;
     596             :         }
     597             :         /* We ignore a subtransaction setting it to the existing value. */
     598        5706 :         if (IsSubTransaction())
     599             :         {
     600           0 :             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     601           0 :             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
     602           0 :             return false;
     603             :         }
     604             :         /* Can't go to serializable mode while recovery is still active */
     605        5706 :         if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
     606             :         {
     607           0 :             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
     608           0 :             GUC_check_errmsg("cannot use serializable mode in a hot standby");
     609           0 :             GUC_check_errhint("You can use REPEATABLE READ instead.");
     610           0 :             return false;
     611             :         }
     612             :     }
     613             : 
     614       15672 :     return true;
     615             : }
     616             : 
     617             : /*
     618             :  * SET TRANSACTION [NOT] DEFERRABLE
     619             :  */
     620             : 
     621             : bool
     622        9114 : check_transaction_deferrable(bool *newval, void **extra, GucSource source)
     623             : {
     624             :     /* Just accept the value when restoring state in a parallel worker */
     625        9114 :     if (InitializingParallelWorker)
     626        5328 :         return true;
     627             : 
     628        3786 :     if (IsSubTransaction())
     629             :     {
     630           0 :         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     631           0 :         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
     632           0 :         return false;
     633             :     }
     634        3786 :     if (FirstSnapshotSet)
     635             :     {
     636           0 :         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
     637           0 :         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
     638           0 :         return false;
     639             :     }
     640             : 
     641        3786 :     return true;
     642             : }
     643             : 
     644             : /*
     645             :  * Random number seed
     646             :  *
     647             :  * We can't roll back the random sequence on error, and we don't want
     648             :  * config file reloads to affect it, so we only want interactive SET SEED
     649             :  * commands to set it.  We use the "extra" storage to ensure that rollbacks
     650             :  * don't try to do the operation again.
     651             :  */
     652             : 
     653             : bool
     654        1866 : check_random_seed(double *newval, void **extra, GucSource source)
     655             : {
     656        1866 :     *extra = guc_malloc(LOG, sizeof(int));
     657        1866 :     if (!*extra)
     658           0 :         return false;
     659             :     /* Arm the assign only if source of value is an interactive SET */
     660        1866 :     *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
     661             : 
     662        1866 :     return true;
     663             : }
     664             : 
     665             : void
     666        1866 : assign_random_seed(double newval, void *extra)
     667             : {
     668             :     /* We'll do this at most once for any setting of the GUC variable */
     669        1866 :     if (*((int *) extra))
     670           0 :         DirectFunctionCall1(setseed, Float8GetDatum(newval));
     671        1866 :     *((int *) extra) = 0;
     672        1866 : }
     673             : 
     674             : const char *
     675           0 : show_random_seed(void)
     676             : {
     677           0 :     return "unavailable";
     678             : }
     679             : 
     680             : 
     681             : /*
     682             :  * SET CLIENT_ENCODING
     683             :  */
     684             : 
     685             : bool
     686       35922 : check_client_encoding(char **newval, void **extra, GucSource source)
     687             : {
     688             :     int         encoding;
     689             :     const char *canonical_name;
     690             : 
     691             :     /* Look up the encoding by name */
     692       35922 :     encoding = pg_valid_client_encoding(*newval);
     693       35922 :     if (encoding < 0)
     694           0 :         return false;
     695             : 
     696             :     /* Get the canonical name (no aliases, uniform case) */
     697       35922 :     canonical_name = pg_encoding_to_char(encoding);
     698             : 
     699             :     /*
     700             :      * Parallel workers send data to the leader, not the client.  They always
     701             :      * send data using the database encoding; therefore, we should never
     702             :      * actually change the client encoding in a parallel worker.  However,
     703             :      * during parallel worker startup, we want to accept the leader's
     704             :      * client_encoding setting so that anyone who looks at the value in the
     705             :      * worker sees the same value that they would see in the leader.  A change
     706             :      * other than during startup, for example due to a SET clause attached to
     707             :      * a function definition, should be rejected, as there is nothing we can
     708             :      * do inside the worker to make it take effect.
     709             :      */
     710       35922 :     if (IsParallelWorker() && !InitializingParallelWorker)
     711             :     {
     712           0 :         GUC_check_errcode(ERRCODE_INVALID_TRANSACTION_STATE);
     713           0 :         GUC_check_errdetail("Cannot change \"client_encoding\" during a parallel operation.");
     714           0 :         return false;
     715             :     }
     716             : 
     717             :     /*
     718             :      * If we are not within a transaction then PrepareClientEncoding will not
     719             :      * be able to look up the necessary conversion procs.  If we are still
     720             :      * starting up, it will return "OK" anyway, and InitializeClientEncoding
     721             :      * will fix things once initialization is far enough along.  After
     722             :      * startup, we'll fail.  This would only happen if someone tries to change
     723             :      * client_encoding in postgresql.conf and then SIGHUP existing sessions.
     724             :      * It seems like a bad idea for client_encoding to change that way anyhow,
     725             :      * so we don't go out of our way to support it.
     726             :      *
     727             :      * In a parallel worker, we might as well skip PrepareClientEncoding since
     728             :      * we're not going to use its results.
     729             :      *
     730             :      * Note: in the postmaster, or any other process that never calls
     731             :      * InitializeClientEncoding, PrepareClientEncoding will always succeed,
     732             :      * and so will SetClientEncoding; but they won't do anything, which is OK.
     733             :      */
     734       63852 :     if (!IsParallelWorker() &&
     735       27930 :         PrepareClientEncoding(encoding) < 0)
     736             :     {
     737           0 :         if (IsTransactionState())
     738             :         {
     739             :             /* Must be a genuine no-such-conversion problem */
     740           0 :             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
     741           0 :             GUC_check_errdetail("Conversion between %s and %s is not supported.",
     742             :                                 canonical_name,
     743             :                                 GetDatabaseEncodingName());
     744             :         }
     745             :         else
     746             :         {
     747             :             /* Provide a useful complaint */
     748           0 :             GUC_check_errdetail("Cannot change \"client_encoding\" now.");
     749             :         }
     750           0 :         return false;
     751             :     }
     752             : 
     753             :     /*
     754             :      * Replace the user-supplied string with the encoding's canonical name.
     755             :      * This gets rid of aliases and case-folding variations.
     756             :      *
     757             :      * XXX Although canonicalizing seems like a good idea in the abstract, it
     758             :      * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE"
     759             :      * as the client_encoding setting then it will read back the same way. As
     760             :      * a workaround, don't replace the string if it's "UNICODE".  Remove that
     761             :      * hack when pre-9.1 JDBC drivers are no longer in use.
     762             :      */
     763       35922 :     if (strcmp(*newval, canonical_name) != 0 &&
     764          34 :         strcmp(*newval, "UNICODE") != 0)
     765             :     {
     766          34 :         guc_free(*newval);
     767          34 :         *newval = guc_strdup(LOG, canonical_name);
     768          34 :         if (!*newval)
     769           0 :             return false;
     770             :     }
     771             : 
     772             :     /*
     773             :      * Save the encoding's ID in *extra, for use by assign_client_encoding.
     774             :      */
     775       35922 :     *extra = guc_malloc(LOG, sizeof(int));
     776       35922 :     if (!*extra)
     777           0 :         return false;
     778       35922 :     *((int *) *extra) = encoding;
     779             : 
     780       35922 :     return true;
     781             : }
     782             : 
     783             : void
     784       35732 : assign_client_encoding(const char *newval, void *extra)
     785             : {
     786       35732 :     int         encoding = *((int *) extra);
     787             : 
     788             :     /*
     789             :      * In a parallel worker, we never override the client encoding that was
     790             :      * set by ParallelWorkerMain().
     791             :      */
     792       35732 :     if (IsParallelWorker())
     793        7992 :         return;
     794             : 
     795             :     /* We do not expect an error if PrepareClientEncoding succeeded */
     796       27740 :     if (SetClientEncoding(encoding) < 0)
     797           0 :         elog(LOG, "SetClientEncoding(%d) failed", encoding);
     798             : }
     799             : 
     800             : 
     801             : /*
     802             :  * SET SESSION AUTHORIZATION
     803             :  */
     804             : 
     805             : typedef struct
     806             : {
     807             :     /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
     808             :     Oid         roleid;
     809             :     bool        is_superuser;
     810             : } role_auth_extra;
     811             : 
     812             : bool
     813       36816 : check_session_authorization(char **newval, void **extra, GucSource source)
     814             : {
     815             :     HeapTuple   roleTup;
     816             :     Form_pg_authid roleform;
     817             :     Oid         roleid;
     818             :     bool        is_superuser;
     819             :     role_auth_extra *myextra;
     820             : 
     821             :     /* Do nothing for the boot_val default of NULL */
     822       36816 :     if (*newval == NULL)
     823        4530 :         return true;
     824             : 
     825       32286 :     if (!IsTransactionState())
     826             :     {
     827             :         /*
     828             :          * Can't do catalog lookups, so fail.  The result of this is that
     829             :          * session_authorization cannot be set in postgresql.conf, which seems
     830             :          * like a good thing anyway, so we don't work hard to avoid it.
     831             :          */
     832           0 :         return false;
     833             :     }
     834             : 
     835             :     /*
     836             :      * When source == PGC_S_TEST, we don't throw a hard error for a
     837             :      * nonexistent user name or insufficient privileges, only a NOTICE. See
     838             :      * comments in guc.h.
     839             :      */
     840             : 
     841             :     /* Look up the username */
     842       32286 :     roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
     843       32286 :     if (!HeapTupleIsValid(roleTup))
     844             :     {
     845           0 :         if (source == PGC_S_TEST)
     846             :         {
     847           0 :             ereport(NOTICE,
     848             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     849             :                      errmsg("role \"%s\" does not exist", *newval)));
     850           0 :             return true;
     851             :         }
     852           0 :         GUC_check_errmsg("role \"%s\" does not exist", *newval);
     853           0 :         return false;
     854             :     }
     855             : 
     856       32286 :     roleform = (Form_pg_authid) GETSTRUCT(roleTup);
     857       32286 :     roleid = roleform->oid;
     858       32286 :     is_superuser = roleform->rolsuper;
     859             : 
     860       32286 :     ReleaseSysCache(roleTup);
     861             : 
     862             :     /*
     863             :      * Only superusers may SET SESSION AUTHORIZATION a role other than itself.
     864             :      * Note that in case of multiple SETs in a single session, the original
     865             :      * authenticated user's superuserness is what matters.
     866             :      */
     867       32286 :     if (roleid != GetAuthenticatedUserId() &&
     868        2518 :         !superuser_arg(GetAuthenticatedUserId()))
     869             :     {
     870           0 :         if (source == PGC_S_TEST)
     871             :         {
     872           0 :             ereport(NOTICE,
     873             :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     874             :                      errmsg("permission will be denied to set session authorization \"%s\"",
     875             :                             *newval)));
     876           0 :             return true;
     877             :         }
     878           0 :         GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
     879           0 :         GUC_check_errmsg("permission denied to set session authorization \"%s\"",
     880             :                          *newval);
     881           0 :         return false;
     882             :     }
     883             : 
     884             :     /* Set up "extra" struct for assign_session_authorization to use */
     885       32286 :     myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
     886       32286 :     if (!myextra)
     887           0 :         return false;
     888       32286 :     myextra->roleid = roleid;
     889       32286 :     myextra->is_superuser = is_superuser;
     890       32286 :     *extra = (void *) myextra;
     891             : 
     892       32286 :     return true;
     893             : }
     894             : 
     895             : void
     896       37704 : assign_session_authorization(const char *newval, void *extra)
     897             : {
     898       37704 :     role_auth_extra *myextra = (role_auth_extra *) extra;
     899             : 
     900             :     /* Do nothing for the boot_val default of NULL */
     901       37704 :     if (!myextra)
     902        4530 :         return;
     903             : 
     904       33174 :     SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
     905             : }
     906             : 
     907             : 
     908             : /*
     909             :  * SET ROLE
     910             :  *
     911             :  * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
     912             :  * a translation of "none" to InvalidOid.  Otherwise this is much like
     913             :  * SET SESSION AUTHORIZATION.
     914             :  */
     915             : 
     916             : bool
     917        2760 : check_role(char **newval, void **extra, GucSource source)
     918             : {
     919             :     HeapTuple   roleTup;
     920             :     Oid         roleid;
     921             :     bool        is_superuser;
     922             :     role_auth_extra *myextra;
     923             :     Form_pg_authid roleform;
     924             : 
     925        2760 :     if (strcmp(*newval, "none") == 0)
     926             :     {
     927             :         /* hardwired translation */
     928        1866 :         roleid = InvalidOid;
     929        1866 :         is_superuser = false;
     930             :     }
     931             :     else
     932             :     {
     933         894 :         if (!IsTransactionState())
     934             :         {
     935             :             /*
     936             :              * Can't do catalog lookups, so fail.  The result of this is that
     937             :              * role cannot be set in postgresql.conf, which seems like a good
     938             :              * thing anyway, so we don't work hard to avoid it.
     939             :              */
     940           0 :             return false;
     941             :         }
     942             : 
     943             :         /*
     944             :          * When source == PGC_S_TEST, we don't throw a hard error for a
     945             :          * nonexistent user name or insufficient privileges, only a NOTICE.
     946             :          * See comments in guc.h.
     947             :          */
     948             : 
     949             :         /* Look up the username */
     950         894 :         roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
     951         894 :         if (!HeapTupleIsValid(roleTup))
     952             :         {
     953           0 :             if (source == PGC_S_TEST)
     954             :             {
     955           0 :                 ereport(NOTICE,
     956             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     957             :                          errmsg("role \"%s\" does not exist", *newval)));
     958           0 :                 return true;
     959             :             }
     960           0 :             GUC_check_errmsg("role \"%s\" does not exist", *newval);
     961           0 :             return false;
     962             :         }
     963             : 
     964         894 :         roleform = (Form_pg_authid) GETSTRUCT(roleTup);
     965         894 :         roleid = roleform->oid;
     966         894 :         is_superuser = roleform->rolsuper;
     967             : 
     968         894 :         ReleaseSysCache(roleTup);
     969             : 
     970             :         /*
     971             :          * Verify that session user is allowed to become this role, but skip
     972             :          * this in parallel mode, where we must blindly recreate the parallel
     973             :          * leader's state.
     974             :          */
     975         894 :         if (!InitializingParallelWorker &&
     976         894 :             !member_can_set_role(GetSessionUserId(), roleid))
     977             :         {
     978          12 :             if (source == PGC_S_TEST)
     979             :             {
     980           0 :                 ereport(NOTICE,
     981             :                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     982             :                          errmsg("permission will be denied to set role \"%s\"",
     983             :                                 *newval)));
     984           0 :                 return true;
     985             :             }
     986          12 :             GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
     987          12 :             GUC_check_errmsg("permission denied to set role \"%s\"",
     988             :                              *newval);
     989          12 :             return false;
     990             :         }
     991             :     }
     992             : 
     993             :     /* Set up "extra" struct for assign_role to use */
     994        2748 :     myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
     995        2748 :     if (!myextra)
     996           0 :         return false;
     997        2748 :     myextra->roleid = roleid;
     998        2748 :     myextra->is_superuser = is_superuser;
     999        2748 :     *extra = (void *) myextra;
    1000             : 
    1001        2748 :     return true;
    1002             : }
    1003             : 
    1004             : void
    1005        3376 : assign_role(const char *newval, void *extra)
    1006             : {
    1007        3376 :     role_auth_extra *myextra = (role_auth_extra *) extra;
    1008             : 
    1009        3376 :     SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
    1010        3376 : }
    1011             : 
    1012             : const char *
    1013          12 : show_role(void)
    1014             : {
    1015             :     /*
    1016             :      * Check whether SET ROLE is active; if not return "none".  This is a
    1017             :      * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
    1018             :      * resets SET ROLE to NONE, but we cannot set the GUC role variable from
    1019             :      * assign_session_authorization (because we haven't got enough info to
    1020             :      * call set_config_option).
    1021             :      */
    1022          12 :     if (!OidIsValid(GetCurrentRoleId()))
    1023           0 :         return "none";
    1024             : 
    1025             :     /* Otherwise we can just use the GUC string */
    1026          12 :     return role_string ? role_string : "none";
    1027             : }
    1028             : 
    1029             : 
    1030             : /*
    1031             :  * PATH VARIABLES
    1032             :  *
    1033             :  * check_canonical_path is used for log_directory and some other GUCs where
    1034             :  * all we want to do is canonicalize the represented path name.
    1035             :  */
    1036             : 
    1037             : bool
    1038        3732 : check_canonical_path(char **newval, void **extra, GucSource source)
    1039             : {
    1040             :     /*
    1041             :      * Since canonicalize_path never enlarges the string, we can just modify
    1042             :      * newval in-place.  But watch out for NULL, which is the default value
    1043             :      * for external_pid_file.
    1044             :      */
    1045        3732 :     if (*newval)
    1046        1866 :         canonicalize_path(*newval);
    1047        3732 :     return true;
    1048             : }
    1049             : 
    1050             : 
    1051             : /*
    1052             :  * MISCELLANEOUS
    1053             :  */
    1054             : 
    1055             : /*
    1056             :  * GUC check_hook for application_name
    1057             :  */
    1058             : bool
    1059       29146 : check_application_name(char **newval, void **extra, GucSource source)
    1060             : {
    1061             :     char       *clean;
    1062             :     char       *ret;
    1063             : 
    1064             :     /* Only allow clean ASCII chars in the application name */
    1065       29146 :     clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
    1066       29146 :     if (!clean)
    1067           0 :         return false;
    1068             : 
    1069       29146 :     ret = guc_strdup(WARNING, clean);
    1070       29146 :     if (!ret)
    1071             :     {
    1072           0 :         pfree(clean);
    1073           0 :         return false;
    1074             :     }
    1075             : 
    1076       29146 :     pfree(clean);
    1077       29146 :     *newval = ret;
    1078       29146 :     return true;
    1079             : }
    1080             : 
    1081             : /*
    1082             :  * GUC assign_hook for application_name
    1083             :  */
    1084             : void
    1085       29118 : assign_application_name(const char *newval, void *extra)
    1086             : {
    1087             :     /* Update the pg_stat_activity view */
    1088       29118 :     pgstat_report_appname(newval);
    1089       29118 : }
    1090             : 
    1091             : /*
    1092             :  * GUC check_hook for cluster_name
    1093             :  */
    1094             : bool
    1095        3084 : check_cluster_name(char **newval, void **extra, GucSource source)
    1096             : {
    1097             :     char       *clean;
    1098             :     char       *ret;
    1099             : 
    1100             :     /* Only allow clean ASCII chars in the cluster name */
    1101        3084 :     clean = pg_clean_ascii(*newval, MCXT_ALLOC_NO_OOM);
    1102        3084 :     if (!clean)
    1103           0 :         return false;
    1104             : 
    1105        3084 :     ret = guc_strdup(WARNING, clean);
    1106        3084 :     if (!ret)
    1107             :     {
    1108           0 :         pfree(clean);
    1109           0 :         return false;
    1110             :     }
    1111             : 
    1112        3084 :     pfree(clean);
    1113        3084 :     *newval = ret;
    1114        3084 :     return true;
    1115             : }
    1116             : 
    1117             : /*
    1118             :  * GUC assign_hook for maintenance_io_concurrency
    1119             :  */
    1120             : void
    1121        1866 : assign_maintenance_io_concurrency(int newval, void *extra)
    1122             : {
    1123             : #ifdef USE_PREFETCH
    1124             :     /*
    1125             :      * Reconfigure recovery prefetching, because a setting it depends on
    1126             :      * changed.
    1127             :      */
    1128        1866 :     maintenance_io_concurrency = newval;
    1129        1866 :     if (AmStartupProcess())
    1130           0 :         XLogPrefetchReconfigure();
    1131             : #endif
    1132        1866 : }
    1133             : 
    1134             : 
    1135             : /*
    1136             :  * These show hooks just exist because we want to show the values in octal.
    1137             :  */
    1138             : 
    1139             : /*
    1140             :  * GUC show_hook for data_directory_mode
    1141             :  */
    1142             : const char *
    1143        4916 : show_data_directory_mode(void)
    1144             : {
    1145             :     static char buf[12];
    1146             : 
    1147        4916 :     snprintf(buf, sizeof(buf), "%04o", data_directory_mode);
    1148        4916 :     return buf;
    1149             : }
    1150             : 
    1151             : /*
    1152             :  * GUC show_hook for log_file_mode
    1153             :  */
    1154             : const char *
    1155        4238 : show_log_file_mode(void)
    1156             : {
    1157             :     static char buf[12];
    1158             : 
    1159        4238 :     snprintf(buf, sizeof(buf), "%04o", Log_file_mode);
    1160        4238 :     return buf;
    1161             : }
    1162             : 
    1163             : /*
    1164             :  * GUC show_hook for unix_socket_permissions
    1165             :  */
    1166             : const char *
    1167        4238 : show_unix_socket_permissions(void)
    1168             : {
    1169             :     static char buf[12];
    1170             : 
    1171        4238 :     snprintf(buf, sizeof(buf), "%04o", Unix_socket_permissions);
    1172        4238 :     return buf;
    1173             : }
    1174             : 
    1175             : 
    1176             : /*
    1177             :  * These check hooks do nothing more than reject non-default settings
    1178             :  * in builds that don't support them.
    1179             :  */
    1180             : 
    1181             : bool
    1182        1866 : check_bonjour(bool *newval, void **extra, GucSource source)
    1183             : {
    1184             : #ifndef USE_BONJOUR
    1185        1866 :     if (*newval)
    1186             :     {
    1187           0 :         GUC_check_errmsg("Bonjour is not supported by this build");
    1188           0 :         return false;
    1189             :     }
    1190             : #endif
    1191        1866 :     return true;
    1192             : }
    1193             : 
    1194             : bool
    1195        1878 : check_default_with_oids(bool *newval, void **extra, GucSource source)
    1196             : {
    1197        1878 :     if (*newval)
    1198             :     {
    1199             :         /* check the GUC's definition for an explanation */
    1200           6 :         GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
    1201           6 :         GUC_check_errmsg("tables declared WITH OIDS are not supported");
    1202             : 
    1203           6 :         return false;
    1204             :     }
    1205             : 
    1206        1872 :     return true;
    1207             : }
    1208             : 
    1209             : bool
    1210        2476 : check_effective_io_concurrency(int *newval, void **extra, GucSource source)
    1211             : {
    1212             : #ifndef USE_PREFETCH
    1213             :     if (*newval != 0)
    1214             :     {
    1215             :         GUC_check_errdetail("\"%s\" must be set to 0 on platforms that lack support for issuing read-ahead advice.",
    1216             :                             "effective_io_concurrency");
    1217             :         return false;
    1218             :     }
    1219             : #endif                          /* USE_PREFETCH */
    1220        2476 :     return true;
    1221             : }
    1222             : 
    1223             : bool
    1224        1866 : check_maintenance_io_concurrency(int *newval, void **extra, GucSource source)
    1225             : {
    1226             : #ifndef USE_PREFETCH
    1227             :     if (*newval != 0)
    1228             :     {
    1229             :         GUC_check_errdetail("\"%s\" must be set to 0 on platforms that lack support for issuing read-ahead advice.",
    1230             :                             "maintenance_io_concurrency");
    1231             :         return false;
    1232             :     }
    1233             : #endif                          /* USE_PREFETCH */
    1234        1866 :     return true;
    1235             : }
    1236             : 
    1237             : bool
    1238        1918 : check_ssl(bool *newval, void **extra, GucSource source)
    1239             : {
    1240             : #ifndef USE_SSL
    1241             :     if (*newval)
    1242             :     {
    1243             :         GUC_check_errmsg("SSL is not supported by this build");
    1244             :         return false;
    1245             :     }
    1246             : #endif
    1247        1918 :     return true;
    1248             : }

Generated by: LCOV version 1.14