Header And Logo

PostgreSQL
| The world's most advanced open source database.

variable.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * variable.c
00004  *      Routines for handling specialized SET variables.
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/commands/variable.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 
00017 #include "postgres.h"
00018 
00019 #include <ctype.h>
00020 
00021 #include "access/htup_details.h"
00022 #include "access/xact.h"
00023 #include "catalog/pg_authid.h"
00024 #include "commands/variable.h"
00025 #include "miscadmin.h"
00026 #include "utils/acl.h"
00027 #include "utils/builtins.h"
00028 #include "utils/syscache.h"
00029 #include "utils/snapmgr.h"
00030 #include "utils/timestamp.h"
00031 #include "mb/pg_wchar.h"
00032 
00033 /*
00034  * DATESTYLE
00035  */
00036 
00037 /*
00038  * check_datestyle: GUC check_hook for datestyle
00039  */
00040 bool
00041 check_datestyle(char **newval, void **extra, GucSource source)
00042 {
00043     int         newDateStyle = DateStyle;
00044     int         newDateOrder = DateOrder;
00045     bool        have_style = false;
00046     bool        have_order = false;
00047     bool        ok = true;
00048     char       *rawstring;
00049     int        *myextra;
00050     char       *result;
00051     List       *elemlist;
00052     ListCell   *l;
00053 
00054     /* Need a modifiable copy of string */
00055     rawstring = pstrdup(*newval);
00056 
00057     /* Parse string into list of identifiers */
00058     if (!SplitIdentifierString(rawstring, ',', &elemlist))
00059     {
00060         /* syntax error in list */
00061         GUC_check_errdetail("List syntax is invalid.");
00062         pfree(rawstring);
00063         list_free(elemlist);
00064         return false;
00065     }
00066 
00067     foreach(l, elemlist)
00068     {
00069         char       *tok = (char *) lfirst(l);
00070 
00071         /* Ugh. Somebody ought to write a table driven version -- mjl */
00072 
00073         if (pg_strcasecmp(tok, "ISO") == 0)
00074         {
00075             if (have_style && newDateStyle != USE_ISO_DATES)
00076                 ok = false;     /* conflicting styles */
00077             newDateStyle = USE_ISO_DATES;
00078             have_style = true;
00079         }
00080         else if (pg_strcasecmp(tok, "SQL") == 0)
00081         {
00082             if (have_style && newDateStyle != USE_SQL_DATES)
00083                 ok = false;     /* conflicting styles */
00084             newDateStyle = USE_SQL_DATES;
00085             have_style = true;
00086         }
00087         else if (pg_strncasecmp(tok, "POSTGRES", 8) == 0)
00088         {
00089             if (have_style && newDateStyle != USE_POSTGRES_DATES)
00090                 ok = false;     /* conflicting styles */
00091             newDateStyle = USE_POSTGRES_DATES;
00092             have_style = true;
00093         }
00094         else if (pg_strcasecmp(tok, "GERMAN") == 0)
00095         {
00096             if (have_style && newDateStyle != USE_GERMAN_DATES)
00097                 ok = false;     /* conflicting styles */
00098             newDateStyle = USE_GERMAN_DATES;
00099             have_style = true;
00100             /* GERMAN also sets DMY, unless explicitly overridden */
00101             if (!have_order)
00102                 newDateOrder = DATEORDER_DMY;
00103         }
00104         else if (pg_strcasecmp(tok, "YMD") == 0)
00105         {
00106             if (have_order && newDateOrder != DATEORDER_YMD)
00107                 ok = false;     /* conflicting orders */
00108             newDateOrder = DATEORDER_YMD;
00109             have_order = true;
00110         }
00111         else if (pg_strcasecmp(tok, "DMY") == 0 ||
00112                  pg_strncasecmp(tok, "EURO", 4) == 0)
00113         {
00114             if (have_order && newDateOrder != DATEORDER_DMY)
00115                 ok = false;     /* conflicting orders */
00116             newDateOrder = DATEORDER_DMY;
00117             have_order = true;
00118         }
00119         else if (pg_strcasecmp(tok, "MDY") == 0 ||
00120                  pg_strcasecmp(tok, "US") == 0 ||
00121                  pg_strncasecmp(tok, "NONEURO", 7) == 0)
00122         {
00123             if (have_order && newDateOrder != DATEORDER_MDY)
00124                 ok = false;     /* conflicting orders */
00125             newDateOrder = DATEORDER_MDY;
00126             have_order = true;
00127         }
00128         else if (pg_strcasecmp(tok, "DEFAULT") == 0)
00129         {
00130             /*
00131              * Easiest way to get the current DEFAULT state is to fetch the
00132              * DEFAULT string from guc.c and recursively parse it.
00133              *
00134              * We can't simply "return check_datestyle(...)" because we need
00135              * to handle constructs like "DEFAULT, ISO".
00136              */
00137             char       *subval;
00138             void       *subextra = NULL;
00139 
00140             subval = strdup(GetConfigOptionResetString("datestyle"));
00141             if (!subval)
00142             {
00143                 ok = false;
00144                 break;
00145             }
00146             if (!check_datestyle(&subval, &subextra, source))
00147             {
00148                 free(subval);
00149                 ok = false;
00150                 break;
00151             }
00152             myextra = (int *) subextra;
00153             if (!have_style)
00154                 newDateStyle = myextra[0];
00155             if (!have_order)
00156                 newDateOrder = myextra[1];
00157             free(subval);
00158             free(subextra);
00159         }
00160         else
00161         {
00162             GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
00163             pfree(rawstring);
00164             list_free(elemlist);
00165             return false;
00166         }
00167     }
00168 
00169     pfree(rawstring);
00170     list_free(elemlist);
00171 
00172     if (!ok)
00173     {
00174         GUC_check_errdetail("Conflicting \"datestyle\" specifications.");
00175         return false;
00176     }
00177 
00178     /*
00179      * Prepare the canonical string to return.  GUC wants it malloc'd.
00180      */
00181     result = (char *) malloc(32);
00182     if (!result)
00183         return false;
00184 
00185     switch (newDateStyle)
00186     {
00187         case USE_ISO_DATES:
00188             strcpy(result, "ISO");
00189             break;
00190         case USE_SQL_DATES:
00191             strcpy(result, "SQL");
00192             break;
00193         case USE_GERMAN_DATES:
00194             strcpy(result, "German");
00195             break;
00196         default:
00197             strcpy(result, "Postgres");
00198             break;
00199     }
00200     switch (newDateOrder)
00201     {
00202         case DATEORDER_YMD:
00203             strcat(result, ", YMD");
00204             break;
00205         case DATEORDER_DMY:
00206             strcat(result, ", DMY");
00207             break;
00208         default:
00209             strcat(result, ", MDY");
00210             break;
00211     }
00212 
00213     free(*newval);
00214     *newval = result;
00215 
00216     /*
00217      * Set up the "extra" struct actually used by assign_datestyle.
00218      */
00219     myextra = (int *) malloc(2 * sizeof(int));
00220     if (!myextra)
00221         return false;
00222     myextra[0] = newDateStyle;
00223     myextra[1] = newDateOrder;
00224     *extra = (void *) myextra;
00225 
00226     return true;
00227 }
00228 
00229 /*
00230  * assign_datestyle: GUC assign_hook for datestyle
00231  */
00232 void
00233 assign_datestyle(const char *newval, void *extra)
00234 {
00235     int        *myextra = (int *) extra;
00236 
00237     DateStyle = myextra[0];
00238     DateOrder = myextra[1];
00239 }
00240 
00241 
00242 /*
00243  * TIMEZONE
00244  */
00245 
00246 typedef struct
00247 {
00248     pg_tz      *session_timezone;
00249     int         CTimeZone;
00250     bool        HasCTZSet;
00251 } timezone_extra;
00252 
00253 /*
00254  * check_timezone: GUC check_hook for timezone
00255  */
00256 bool
00257 check_timezone(char **newval, void **extra, GucSource source)
00258 {
00259     timezone_extra myextra;
00260     char       *endptr;
00261     double      hours;
00262 
00263     /*
00264      * Initialize the "extra" struct that will be passed to assign_timezone.
00265      * We don't want to change any of the three global variables except as
00266      * specified by logic below.  To avoid leaking memory during failure
00267      * returns, we set up the struct contents in a local variable, and only
00268      * copy it to *extra at the end.
00269      */
00270     myextra.session_timezone = session_timezone;
00271     myextra.CTimeZone = CTimeZone;
00272     myextra.HasCTZSet = HasCTZSet;
00273 
00274     if (pg_strncasecmp(*newval, "interval", 8) == 0)
00275     {
00276         /*
00277          * Support INTERVAL 'foo'.  This is for SQL spec compliance, not
00278          * because it has any actual real-world usefulness.
00279          */
00280         const char *valueptr = *newval;
00281         char       *val;
00282         Interval   *interval;
00283 
00284         valueptr += 8;
00285         while (isspace((unsigned char) *valueptr))
00286             valueptr++;
00287         if (*valueptr++ != '\'')
00288             return false;
00289         val = pstrdup(valueptr);
00290         /* Check and remove trailing quote */
00291         endptr = strchr(val, '\'');
00292         if (!endptr || endptr[1] != '\0')
00293         {
00294             pfree(val);
00295             return false;
00296         }
00297         *endptr = '\0';
00298 
00299         /*
00300          * Try to parse it.  XXX an invalid interval format will result in
00301          * ereport(ERROR), which is not desirable for GUC.  We did what we
00302          * could to guard against this in flatten_set_variable_args, but a
00303          * string coming in from postgresql.conf might contain anything.
00304          */
00305         interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
00306                                                          CStringGetDatum(val),
00307                                                 ObjectIdGetDatum(InvalidOid),
00308                                                          Int32GetDatum(-1)));
00309 
00310         pfree(val);
00311         if (interval->month != 0)
00312         {
00313             GUC_check_errdetail("Cannot specify months in time zone interval.");
00314             pfree(interval);
00315             return false;
00316         }
00317         if (interval->day != 0)
00318         {
00319             GUC_check_errdetail("Cannot specify days in time zone interval.");
00320             pfree(interval);
00321             return false;
00322         }
00323 
00324         /* Here we change from SQL to Unix sign convention */
00325 #ifdef HAVE_INT64_TIMESTAMP
00326         myextra.CTimeZone = -(interval->time / USECS_PER_SEC);
00327 #else
00328         myextra.CTimeZone = -interval->time;
00329 #endif
00330         myextra.HasCTZSet = true;
00331 
00332         pfree(interval);
00333     }
00334     else
00335     {
00336         /*
00337          * Try it as a numeric number of hours (possibly fractional).
00338          */
00339         hours = strtod(*newval, &endptr);
00340         if (endptr != *newval && *endptr == '\0')
00341         {
00342             /* Here we change from SQL to Unix sign convention */
00343             myextra.CTimeZone = -hours * SECS_PER_HOUR;
00344             myextra.HasCTZSet = true;
00345         }
00346         else
00347         {
00348             /*
00349              * Otherwise assume it is a timezone name, and try to load it.
00350              */
00351             pg_tz      *new_tz;
00352 
00353             new_tz = pg_tzset(*newval);
00354 
00355             if (!new_tz)
00356             {
00357                 /* Doesn't seem to be any great value in errdetail here */
00358                 return false;
00359             }
00360 
00361             if (!pg_tz_acceptable(new_tz))
00362             {
00363                 GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
00364                                  *newval);
00365                 GUC_check_errdetail("PostgreSQL does not support leap seconds.");
00366                 return false;
00367             }
00368 
00369             myextra.session_timezone = new_tz;
00370             myextra.HasCTZSet = false;
00371         }
00372     }
00373 
00374     /*
00375      * Prepare the canonical string to return.  GUC wants it malloc'd.
00376      *
00377      * Note: the result string should be something that we'd accept as input.
00378      * We use the numeric format for interval cases, because it's simpler to
00379      * reload.  In the named-timezone case, *newval is already OK and need not
00380      * be changed; it might not have the canonical casing, but that's taken
00381      * care of by show_timezone.
00382      */
00383     if (myextra.HasCTZSet)
00384     {
00385         char       *result = (char *) malloc(64);
00386 
00387         if (!result)
00388             return false;
00389         snprintf(result, 64, "%.5f",
00390                  (double) (-myextra.CTimeZone) / (double) SECS_PER_HOUR);
00391         free(*newval);
00392         *newval = result;
00393     }
00394 
00395     /*
00396      * Pass back data for assign_timezone to use
00397      */
00398     *extra = malloc(sizeof(timezone_extra));
00399     if (!*extra)
00400         return false;
00401     memcpy(*extra, &myextra, sizeof(timezone_extra));
00402 
00403     return true;
00404 }
00405 
00406 /*
00407  * assign_timezone: GUC assign_hook for timezone
00408  */
00409 void
00410 assign_timezone(const char *newval, void *extra)
00411 {
00412     timezone_extra *myextra = (timezone_extra *) extra;
00413 
00414     session_timezone = myextra->session_timezone;
00415     CTimeZone = myextra->CTimeZone;
00416     HasCTZSet = myextra->HasCTZSet;
00417 }
00418 
00419 /*
00420  * show_timezone: GUC show_hook for timezone
00421  *
00422  * We wouldn't need this, except that historically interval values have been
00423  * shown without an INTERVAL prefix, so the display format isn't what would
00424  * be accepted as input.  Otherwise we could have check_timezone return the
00425  * preferred string to begin with.
00426  */
00427 const char *
00428 show_timezone(void)
00429 {
00430     const char *tzn;
00431 
00432     if (HasCTZSet)
00433     {
00434         Interval    interval;
00435 
00436         interval.month = 0;
00437         interval.day = 0;
00438 #ifdef HAVE_INT64_TIMESTAMP
00439         interval.time = -(CTimeZone * USECS_PER_SEC);
00440 #else
00441         interval.time = -CTimeZone;
00442 #endif
00443 
00444         tzn = DatumGetCString(DirectFunctionCall1(interval_out,
00445                                               IntervalPGetDatum(&interval)));
00446     }
00447     else
00448         tzn = pg_get_timezone_name(session_timezone);
00449 
00450     if (tzn != NULL)
00451         return tzn;
00452 
00453     return "unknown";
00454 }
00455 
00456 
00457 /*
00458  * LOG_TIMEZONE
00459  *
00460  * For log_timezone, we don't support the interval-based methods of setting a
00461  * zone, which are only there for SQL spec compliance not because they're
00462  * actually useful.
00463  */
00464 
00465 /*
00466  * check_log_timezone: GUC check_hook for log_timezone
00467  */
00468 bool
00469 check_log_timezone(char **newval, void **extra, GucSource source)
00470 {
00471     pg_tz      *new_tz;
00472 
00473     /*
00474      * Assume it is a timezone name, and try to load it.
00475      */
00476     new_tz = pg_tzset(*newval);
00477 
00478     if (!new_tz)
00479     {
00480         /* Doesn't seem to be any great value in errdetail here */
00481         return false;
00482     }
00483 
00484     if (!pg_tz_acceptable(new_tz))
00485     {
00486         GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
00487                          *newval);
00488         GUC_check_errdetail("PostgreSQL does not support leap seconds.");
00489         return false;
00490     }
00491 
00492     /*
00493      * Pass back data for assign_log_timezone to use
00494      */
00495     *extra = malloc(sizeof(pg_tz *));
00496     if (!*extra)
00497         return false;
00498     memcpy(*extra, &new_tz, sizeof(pg_tz *));
00499 
00500     return true;
00501 }
00502 
00503 /*
00504  * assign_log_timezone: GUC assign_hook for log_timezone
00505  */
00506 void
00507 assign_log_timezone(const char *newval, void *extra)
00508 {
00509     log_timezone = *((pg_tz **) extra);
00510 }
00511 
00512 /*
00513  * show_log_timezone: GUC show_hook for log_timezone
00514  */
00515 const char *
00516 show_log_timezone(void)
00517 {
00518     const char *tzn;
00519 
00520     tzn = pg_get_timezone_name(log_timezone);
00521 
00522     if (tzn != NULL)
00523         return tzn;
00524 
00525     return "unknown";
00526 }
00527 
00528 
00529 /*
00530  * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE
00531  *
00532  * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and
00533  * we also always allow changes from read-write to read-only.  However,
00534  * read-only may be changed to read-write only when in a top-level transaction
00535  * that has not yet taken an initial snapshot.  Can't do it in a hot standby
00536  * slave, either.
00537  *
00538  * If we are not in a transaction at all, just allow the change; it means
00539  * nothing since XactReadOnly will be reset by the next StartTransaction().
00540  * The IsTransactionState() test protects us against trying to check
00541  * RecoveryInProgress() in contexts where shared memory is not accessible.
00542  */
00543 bool
00544 check_transaction_read_only(bool *newval, void **extra, GucSource source)
00545 {
00546     if (*newval == false && XactReadOnly && IsTransactionState())
00547     {
00548         /* Can't go to r/w mode inside a r/o transaction */
00549         if (IsSubTransaction())
00550         {
00551             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00552             GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
00553             return false;
00554         }
00555         /* Top level transaction can't change to r/w after first snapshot. */
00556         if (FirstSnapshotSet)
00557         {
00558             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00559             GUC_check_errmsg("transaction read-write mode must be set before any query");
00560             return false;
00561         }
00562         /* Can't go to r/w mode while recovery is still active */
00563         if (RecoveryInProgress())
00564         {
00565             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00566             GUC_check_errmsg("cannot set transaction read-write mode during recovery");
00567             return false;
00568         }
00569     }
00570 
00571     return true;
00572 }
00573 
00574 /*
00575  * SET TRANSACTION ISOLATION LEVEL
00576  *
00577  * We allow idempotent changes at any time, but otherwise this can only be
00578  * changed in a toplevel transaction that has not yet taken a snapshot.
00579  *
00580  * As in check_transaction_read_only, allow it if not inside a transaction.
00581  */
00582 bool
00583 check_XactIsoLevel(char **newval, void **extra, GucSource source)
00584 {
00585     int         newXactIsoLevel;
00586 
00587     if (strcmp(*newval, "serializable") == 0)
00588     {
00589         newXactIsoLevel = XACT_SERIALIZABLE;
00590     }
00591     else if (strcmp(*newval, "repeatable read") == 0)
00592     {
00593         newXactIsoLevel = XACT_REPEATABLE_READ;
00594     }
00595     else if (strcmp(*newval, "read committed") == 0)
00596     {
00597         newXactIsoLevel = XACT_READ_COMMITTED;
00598     }
00599     else if (strcmp(*newval, "read uncommitted") == 0)
00600     {
00601         newXactIsoLevel = XACT_READ_UNCOMMITTED;
00602     }
00603     else if (strcmp(*newval, "default") == 0)
00604     {
00605         newXactIsoLevel = DefaultXactIsoLevel;
00606     }
00607     else
00608         return false;
00609 
00610     if (newXactIsoLevel != XactIsoLevel && IsTransactionState())
00611     {
00612         if (FirstSnapshotSet)
00613         {
00614             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00615             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
00616             return false;
00617         }
00618         /* We ignore a subtransaction setting it to the existing value. */
00619         if (IsSubTransaction())
00620         {
00621             GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00622             GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
00623             return false;
00624         }
00625         /* Can't go to serializable mode while recovery is still active */
00626         if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
00627         {
00628             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00629             GUC_check_errmsg("cannot use serializable mode in a hot standby");
00630             GUC_check_errhint("You can use REPEATABLE READ instead.");
00631             return false;
00632         }
00633     }
00634 
00635     *extra = malloc(sizeof(int));
00636     if (!*extra)
00637         return false;
00638     *((int *) *extra) = newXactIsoLevel;
00639 
00640     return true;
00641 }
00642 
00643 void
00644 assign_XactIsoLevel(const char *newval, void *extra)
00645 {
00646     XactIsoLevel = *((int *) extra);
00647 }
00648 
00649 const char *
00650 show_XactIsoLevel(void)
00651 {
00652     /* We need this because we don't want to show "default". */
00653     switch (XactIsoLevel)
00654     {
00655         case XACT_READ_UNCOMMITTED:
00656             return "read uncommitted";
00657         case XACT_READ_COMMITTED:
00658             return "read committed";
00659         case XACT_REPEATABLE_READ:
00660             return "repeatable read";
00661         case XACT_SERIALIZABLE:
00662             return "serializable";
00663         default:
00664             return "bogus";
00665     }
00666 }
00667 
00668 /*
00669  * SET TRANSACTION [NOT] DEFERRABLE
00670  */
00671 
00672 bool
00673 check_transaction_deferrable(bool *newval, void **extra, GucSource source)
00674 {
00675     if (IsSubTransaction())
00676     {
00677         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00678         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
00679         return false;
00680     }
00681     if (FirstSnapshotSet)
00682     {
00683         GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
00684         GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
00685         return false;
00686     }
00687 
00688     return true;
00689 }
00690 
00691 /*
00692  * Random number seed
00693  *
00694  * We can't roll back the random sequence on error, and we don't want
00695  * config file reloads to affect it, so we only want interactive SET SEED
00696  * commands to set it.  We use the "extra" storage to ensure that rollbacks
00697  * don't try to do the operation again.
00698  */
00699 
00700 bool
00701 check_random_seed(double *newval, void **extra, GucSource source)
00702 {
00703     *extra = malloc(sizeof(int));
00704     if (!*extra)
00705         return false;
00706     /* Arm the assign only if source of value is an interactive SET */
00707     *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
00708 
00709     return true;
00710 }
00711 
00712 void
00713 assign_random_seed(double newval, void *extra)
00714 {
00715     /* We'll do this at most once for any setting of the GUC variable */
00716     if (*((int *) extra))
00717         DirectFunctionCall1(setseed, Float8GetDatum(newval));
00718     *((int *) extra) = 0;
00719 }
00720 
00721 const char *
00722 show_random_seed(void)
00723 {
00724     return "unavailable";
00725 }
00726 
00727 
00728 /*
00729  * SET CLIENT_ENCODING
00730  */
00731 
00732 bool
00733 check_client_encoding(char **newval, void **extra, GucSource source)
00734 {
00735     int         encoding;
00736     const char *canonical_name;
00737 
00738     /* Look up the encoding by name */
00739     encoding = pg_valid_client_encoding(*newval);
00740     if (encoding < 0)
00741         return false;
00742 
00743     /* Get the canonical name (no aliases, uniform case) */
00744     canonical_name = pg_encoding_to_char(encoding);
00745 
00746     /*
00747      * If we are not within a transaction then PrepareClientEncoding will not
00748      * be able to look up the necessary conversion procs.  If we are still
00749      * starting up, it will return "OK" anyway, and InitializeClientEncoding
00750      * will fix things once initialization is far enough along.  After
00751      * startup, we'll fail.  This would only happen if someone tries to change
00752      * client_encoding in postgresql.conf and then SIGHUP existing sessions.
00753      * It seems like a bad idea for client_encoding to change that way anyhow,
00754      * so we don't go out of our way to support it.
00755      *
00756      * Note: in the postmaster, or any other process that never calls
00757      * InitializeClientEncoding, PrepareClientEncoding will always succeed,
00758      * and so will SetClientEncoding; but they won't do anything, which is OK.
00759      */
00760     if (PrepareClientEncoding(encoding) < 0)
00761     {
00762         if (IsTransactionState())
00763         {
00764             /* Must be a genuine no-such-conversion problem */
00765             GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
00766             GUC_check_errdetail("Conversion between %s and %s is not supported.",
00767                                 canonical_name,
00768                                 GetDatabaseEncodingName());
00769         }
00770         else
00771         {
00772             /* Provide a useful complaint */
00773             GUC_check_errdetail("Cannot change \"client_encoding\" now.");
00774         }
00775         return false;
00776     }
00777 
00778     /*
00779      * Replace the user-supplied string with the encoding's canonical name.
00780      * This gets rid of aliases and case-folding variations.
00781      *
00782      * XXX Although canonicalizing seems like a good idea in the abstract, it
00783      * breaks pre-9.1 JDBC drivers, which expect that if they send "UNICODE"
00784      * as the client_encoding setting then it will read back the same way. As
00785      * a workaround, don't replace the string if it's "UNICODE".  Remove that
00786      * hack when pre-9.1 JDBC drivers are no longer in use.
00787      */
00788     if (strcmp(*newval, canonical_name) != 0 &&
00789         strcmp(*newval, "UNICODE") != 0)
00790     {
00791         free(*newval);
00792         *newval = strdup(canonical_name);
00793         if (!*newval)
00794             return false;
00795     }
00796 
00797     /*
00798      * Save the encoding's ID in *extra, for use by assign_client_encoding.
00799      */
00800     *extra = malloc(sizeof(int));
00801     if (!*extra)
00802         return false;
00803     *((int *) *extra) = encoding;
00804 
00805     return true;
00806 }
00807 
00808 void
00809 assign_client_encoding(const char *newval, void *extra)
00810 {
00811     int         encoding = *((int *) extra);
00812 
00813     /* We do not expect an error if PrepareClientEncoding succeeded */
00814     if (SetClientEncoding(encoding) < 0)
00815         elog(LOG, "SetClientEncoding(%d) failed", encoding);
00816 }
00817 
00818 
00819 /*
00820  * SET SESSION AUTHORIZATION
00821  */
00822 
00823 typedef struct
00824 {
00825     /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
00826     Oid         roleid;
00827     bool        is_superuser;
00828 } role_auth_extra;
00829 
00830 bool
00831 check_session_authorization(char **newval, void **extra, GucSource source)
00832 {
00833     HeapTuple   roleTup;
00834     Oid         roleid;
00835     bool        is_superuser;
00836     role_auth_extra *myextra;
00837 
00838     /* Do nothing for the boot_val default of NULL */
00839     if (*newval == NULL)
00840         return true;
00841 
00842     if (!IsTransactionState())
00843     {
00844         /*
00845          * Can't do catalog lookups, so fail.  The result of this is that
00846          * session_authorization cannot be set in postgresql.conf, which seems
00847          * like a good thing anyway, so we don't work hard to avoid it.
00848          */
00849         return false;
00850     }
00851 
00852     /* Look up the username */
00853     roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
00854     if (!HeapTupleIsValid(roleTup))
00855     {
00856         GUC_check_errmsg("role \"%s\" does not exist", *newval);
00857         return false;
00858     }
00859 
00860     roleid = HeapTupleGetOid(roleTup);
00861     is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
00862 
00863     ReleaseSysCache(roleTup);
00864 
00865     /* Set up "extra" struct for assign_session_authorization to use */
00866     myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
00867     if (!myextra)
00868         return false;
00869     myextra->roleid = roleid;
00870     myextra->is_superuser = is_superuser;
00871     *extra = (void *) myextra;
00872 
00873     return true;
00874 }
00875 
00876 void
00877 assign_session_authorization(const char *newval, void *extra)
00878 {
00879     role_auth_extra *myextra = (role_auth_extra *) extra;
00880 
00881     /* Do nothing for the boot_val default of NULL */
00882     if (!myextra)
00883         return;
00884 
00885     SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
00886 }
00887 
00888 
00889 /*
00890  * SET ROLE
00891  *
00892  * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
00893  * a translation of "none" to InvalidOid.  Otherwise this is much like
00894  * SET SESSION AUTHORIZATION.
00895  */
00896 extern char *role_string;       /* in guc.c */
00897 
00898 bool
00899 check_role(char **newval, void **extra, GucSource source)
00900 {
00901     HeapTuple   roleTup;
00902     Oid         roleid;
00903     bool        is_superuser;
00904     role_auth_extra *myextra;
00905 
00906     if (strcmp(*newval, "none") == 0)
00907     {
00908         /* hardwired translation */
00909         roleid = InvalidOid;
00910         is_superuser = false;
00911     }
00912     else
00913     {
00914         if (!IsTransactionState())
00915         {
00916             /*
00917              * Can't do catalog lookups, so fail.  The result of this is that
00918              * role cannot be set in postgresql.conf, which seems like a good
00919              * thing anyway, so we don't work hard to avoid it.
00920              */
00921             return false;
00922         }
00923 
00924         /* Look up the username */
00925         roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
00926         if (!HeapTupleIsValid(roleTup))
00927         {
00928             GUC_check_errmsg("role \"%s\" does not exist", *newval);
00929             return false;
00930         }
00931 
00932         roleid = HeapTupleGetOid(roleTup);
00933         is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
00934 
00935         ReleaseSysCache(roleTup);
00936 
00937         /*
00938          * Verify that session user is allowed to become this role
00939          */
00940         if (!is_member_of_role(GetSessionUserId(), roleid))
00941         {
00942             GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
00943             GUC_check_errmsg("permission denied to set role \"%s\"",
00944                              *newval);
00945             return false;
00946         }
00947     }
00948 
00949     /* Set up "extra" struct for assign_role to use */
00950     myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
00951     if (!myextra)
00952         return false;
00953     myextra->roleid = roleid;
00954     myextra->is_superuser = is_superuser;
00955     *extra = (void *) myextra;
00956 
00957     return true;
00958 }
00959 
00960 void
00961 assign_role(const char *newval, void *extra)
00962 {
00963     role_auth_extra *myextra = (role_auth_extra *) extra;
00964 
00965     SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
00966 }
00967 
00968 const char *
00969 show_role(void)
00970 {
00971     /*
00972      * Check whether SET ROLE is active; if not return "none".  This is a
00973      * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
00974      * resets SET ROLE to NONE, but we cannot set the GUC role variable from
00975      * assign_session_authorization (because we haven't got enough info to
00976      * call set_config_option).
00977      */
00978     if (!OidIsValid(GetCurrentRoleId()))
00979         return "none";
00980 
00981     /* Otherwise we can just use the GUC string */
00982     return role_string ? role_string : "none";
00983 }