Remember the source GucContext for each GUC parameter.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Oct 2011 20:13:16 +0000 (16:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Oct 2011 20:13:50 +0000 (16:13 -0400)
We used to just remember the GucSource, but saving GucContext too provides
a little more information --- notably, whether a SET was done by a
superuser or regular user.  This allows us to rip out the fairly dodgy code
that define_custom_variable used to use to try to infer the context to
re-install a pre-existing setting with.  In particular, it now works for
a superuser to SET a extension's SUSET custom variable before loading the
associated extension, because GUC can remember whether the SET was done as
a superuser or not.  The plperl regression tests contain an example where
this is useful.

src/backend/utils/misc/guc-file.l
src/backend/utils/misc/guc.c
src/include/utils/guc_tables.h
src/pl/plperl/expected/plperl_init.out
src/pl/plperl/expected/plperl_shared.out
src/pl/plperl/expected/plperlu.out
src/pl/plperl/sql/plperl_init.sql
src/pl/plperl/sql/plperl_shared.sql
src/pl/plperl/sql/plperlu.sql

index 7728544c5452650187e242d06d7b6c3353ef99d5..b91da01e86804d484d165345065d874248f80794 100644 (file)
@@ -296,11 +296,7 @@ ProcessConfigFile(GucContext context)
                                  GUC_ACTION_SET, true);
        if (scres > 0)
        {
-           /* variable was updated, so remember the source location */
-           set_config_sourcefile(item->name, item->filename,
-                                 item->sourceline);
-
-           /* and log the change if appropriate */
+           /* variable was updated, so log the change if appropriate */
            if (pre_value)
            {
                const char *post_value = GetConfigOption(item->name, true, false);
@@ -315,7 +311,17 @@ ProcessConfigFile(GucContext context)
        }
        else if (scres == 0)
            error = true;
-       /* else no error but variable was not changed, do nothing */
+       /* else no error but variable's active value was not changed */
+
+       /*
+        * We should update source location unless there was an error, since
+        * even if the active value didn't change, the reset value might have.
+        * (In the postmaster, there won't be a difference, but it does matter
+        * in backends.)
+        */
+       if (scres != 0)
+           set_config_sourcefile(item->name, item->filename,
+                                 item->sourceline);
 
        if (pre_value)
            pfree(pre_value);
index 2fd4867d253ca97f1c1d10a31686f450a289b48e..84702aa46ed45c1ae86b13491fa3fc3527376bf6 100644 (file)
@@ -3861,8 +3861,10 @@ static void
 InitializeOneGUCOption(struct config_generic * gconf)
 {
    gconf->status = 0;
-   gconf->reset_source = PGC_S_DEFAULT;
    gconf->source = PGC_S_DEFAULT;
+   gconf->reset_source = PGC_S_DEFAULT;
+   gconf->scontext = PGC_INTERNAL;
+   gconf->reset_scontext = PGC_INTERNAL;
    gconf->stack = NULL;
    gconf->extra = NULL;
    gconf->sourcefile = NULL;
@@ -4213,6 +4215,7 @@ ResetAllOptions(void)
        }
 
        gconf->source = gconf->reset_source;
+       gconf->scontext = gconf->reset_scontext;
 
        if (gconf->flags & GUC_REPORT)
            ReportGUCOption(gconf);
@@ -4254,6 +4257,7 @@ push_old_value(struct config_generic * gconf, GucAction action)
                if (stack->state == GUC_SET)
                {
                    /* SET followed by SET LOCAL, remember SET's value */
+                   stack->masked_scontext = gconf->scontext;
                    set_stack_value(gconf, &stack->masked);
                    stack->state = GUC_SET_LOCAL;
                }
@@ -4291,6 +4295,7 @@ push_old_value(struct config_generic * gconf, GucAction action)
            break;
    }
    stack->source = gconf->source;
+   stack->scontext = gconf->scontext;
    set_stack_value(gconf, &stack->prior);
 
    gconf->stack = stack;
@@ -4431,6 +4436,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                        if (prev->state == GUC_SET)
                        {
                            /* LOCAL migrates down */
+                           prev->masked_scontext = stack->scontext;
                            prev->masked = stack->prior;
                            prev->state = GUC_SET_LOCAL;
                        }
@@ -4445,6 +4451,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                        /* prior state at this level no longer wanted */
                        discard_stack_value(gconf, &stack->prior);
                        /* copy down the masked state */
+                       prev->masked_scontext = stack->masked_scontext;
                        if (prev->state == GUC_SET_LOCAL)
                            discard_stack_value(gconf, &prev->masked);
                        prev->masked = stack->masked;
@@ -4460,16 +4467,19 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                /* Perform appropriate restoration of the stacked value */
                config_var_value newvalue;
                GucSource   newsource;
+               GucContext  newscontext;
 
                if (restoreMasked)
                {
                    newvalue = stack->masked;
                    newsource = PGC_S_SESSION;
+                   newscontext = stack->masked_scontext;
                }
                else
                {
                    newvalue = stack->prior;
                    newsource = stack->source;
+                   newscontext = stack->scontext;
                }
 
                switch (gconf->vartype)
@@ -4581,7 +4591,9 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                set_extra_field(gconf, &(stack->prior.extra), NULL);
                set_extra_field(gconf, &(stack->masked.extra), NULL);
 
+               /* And restore source information */
                gconf->source = newsource;
+               gconf->scontext = newscontext;
            }
 
            /* Finish popping the state stack */
@@ -5255,6 +5267,7 @@ set_config_option(const char *name, const char *value,
                    newval = conf->reset_val;
                    newextra = conf->reset_extra;
                    source = conf->gen.reset_source;
+                   context = conf->gen.reset_scontext;
                }
 
                if (prohibitValueChange)
@@ -5282,6 +5295,7 @@ set_config_option(const char *name, const char *value,
                    set_extra_field(&conf->gen, &conf->gen.extra,
                                    newextra);
                    conf->gen.source = source;
+                   conf->gen.scontext = context;
                }
                if (makeDefault)
                {
@@ -5293,6 +5307,7 @@ set_config_option(const char *name, const char *value,
                        set_extra_field(&conf->gen, &conf->reset_extra,
                                        newextra);
                        conf->gen.reset_source = source;
+                       conf->gen.reset_scontext = context;
                    }
                    for (stack = conf->gen.stack; stack; stack = stack->prev)
                    {
@@ -5302,6 +5317,7 @@ set_config_option(const char *name, const char *value,
                            set_extra_field(&conf->gen, &stack->prior.extra,
                                            newextra);
                            stack->source = source;
+                           stack->scontext = context;
                        }
                    }
                }
@@ -5355,6 +5371,7 @@ set_config_option(const char *name, const char *value,
                    newval = conf->reset_val;
                    newextra = conf->reset_extra;
                    source = conf->gen.reset_source;
+                   context = conf->gen.reset_scontext;
                }
 
                if (prohibitValueChange)
@@ -5382,6 +5399,7 @@ set_config_option(const char *name, const char *value,
                    set_extra_field(&conf->gen, &conf->gen.extra,
                                    newextra);
                    conf->gen.source = source;
+                   conf->gen.scontext = context;
                }
                if (makeDefault)
                {
@@ -5393,6 +5411,7 @@ set_config_option(const char *name, const char *value,
                        set_extra_field(&conf->gen, &conf->reset_extra,
                                        newextra);
                        conf->gen.reset_source = source;
+                       conf->gen.reset_scontext = context;
                    }
                    for (stack = conf->gen.stack; stack; stack = stack->prev)
                    {
@@ -5402,6 +5421,7 @@ set_config_option(const char *name, const char *value,
                            set_extra_field(&conf->gen, &stack->prior.extra,
                                            newextra);
                            stack->source = source;
+                           stack->scontext = context;
                        }
                    }
                }
@@ -5452,6 +5472,7 @@ set_config_option(const char *name, const char *value,
                    newval = conf->reset_val;
                    newextra = conf->reset_extra;
                    source = conf->gen.reset_source;
+                   context = conf->gen.reset_scontext;
                }
 
                if (prohibitValueChange)
@@ -5479,6 +5500,7 @@ set_config_option(const char *name, const char *value,
                    set_extra_field(&conf->gen, &conf->gen.extra,
                                    newextra);
                    conf->gen.source = source;
+                   conf->gen.scontext = context;
                }
                if (makeDefault)
                {
@@ -5490,6 +5512,7 @@ set_config_option(const char *name, const char *value,
                        set_extra_field(&conf->gen, &conf->reset_extra,
                                        newextra);
                        conf->gen.reset_source = source;
+                       conf->gen.reset_scontext = context;
                    }
                    for (stack = conf->gen.stack; stack; stack = stack->prev)
                    {
@@ -5499,6 +5522,7 @@ set_config_option(const char *name, const char *value,
                            set_extra_field(&conf->gen, &stack->prior.extra,
                                            newextra);
                            stack->source = source;
+                           stack->scontext = context;
                        }
                    }
                }
@@ -5567,6 +5591,7 @@ set_config_option(const char *name, const char *value,
                    newval = conf->reset_val;
                    newextra = conf->reset_extra;
                    source = conf->gen.reset_source;
+                   context = conf->gen.reset_scontext;
                }
 
                if (prohibitValueChange)
@@ -5596,6 +5621,7 @@ set_config_option(const char *name, const char *value,
                    set_extra_field(&conf->gen, &conf->gen.extra,
                                    newextra);
                    conf->gen.source = source;
+                   conf->gen.scontext = context;
                }
 
                if (makeDefault)
@@ -5608,6 +5634,7 @@ set_config_option(const char *name, const char *value,
                        set_extra_field(&conf->gen, &conf->reset_extra,
                                        newextra);
                        conf->gen.reset_source = source;
+                       conf->gen.reset_scontext = context;
                    }
                    for (stack = conf->gen.stack; stack; stack = stack->prev)
                    {
@@ -5618,6 +5645,7 @@ set_config_option(const char *name, const char *value,
                            set_extra_field(&conf->gen, &stack->prior.extra,
                                            newextra);
                            stack->source = source;
+                           stack->scontext = context;
                        }
                    }
                }
@@ -5673,6 +5701,7 @@ set_config_option(const char *name, const char *value,
                    newval = conf->reset_val;
                    newextra = conf->reset_extra;
                    source = conf->gen.reset_source;
+                   context = conf->gen.reset_scontext;
                }
 
                if (prohibitValueChange)
@@ -5700,6 +5729,7 @@ set_config_option(const char *name, const char *value,
                    set_extra_field(&conf->gen, &conf->gen.extra,
                                    newextra);
                    conf->gen.source = source;
+                   conf->gen.scontext = context;
                }
                if (makeDefault)
                {
@@ -5711,6 +5741,7 @@ set_config_option(const char *name, const char *value,
                        set_extra_field(&conf->gen, &conf->reset_extra,
                                        newextra);
                        conf->gen.reset_source = source;
+                       conf->gen.reset_scontext = context;
                    }
                    for (stack = conf->gen.stack; stack; stack = stack->prev)
                    {
@@ -5720,6 +5751,7 @@ set_config_option(const char *name, const char *value,
                            set_extra_field(&conf->gen, &stack->prior.extra,
                                            newextra);
                            stack->source = source;
+                           stack->scontext = context;
                        }
                    }
                }
@@ -6252,7 +6284,6 @@ define_custom_variable(struct config_generic * variable)
    const char **nameAddr = &name;
    const char *value;
    struct config_string *pHolder;
-   GucContext  phcontext;
    struct config_generic **res;
 
    /*
@@ -6298,56 +6329,6 @@ define_custom_variable(struct config_generic * variable)
     */
    *res = variable;
 
-   /*
-    * Infer context for assignment based on source of existing value. We
-    * can't tell this with exact accuracy, but we can at least do something
-    * reasonable in typical cases.
-    */
-   switch (pHolder->gen.source)
-   {
-       case PGC_S_DEFAULT:
-       case PGC_S_DYNAMIC_DEFAULT:
-       case PGC_S_ENV_VAR:
-       case PGC_S_FILE:
-       case PGC_S_ARGV:
-
-           /*
-            * If we got past the check in init_custom_variable, we can safely
-            * assume that any existing value for a PGC_POSTMASTER variable
-            * was set in postmaster context.
-            */
-           if (variable->context == PGC_POSTMASTER)
-               phcontext = PGC_POSTMASTER;
-           else
-               phcontext = PGC_SIGHUP;
-           break;
-
-       case PGC_S_DATABASE:
-       case PGC_S_USER:
-       case PGC_S_DATABASE_USER:
-
-           /*
-            * The existing value came from an ALTER ROLE/DATABASE SET
-            * command. We can assume that at the time the command was issued,
-            * we checked that the issuing user was superuser if the variable
-            * requires superuser privileges to set.  So it's safe to use
-            * SUSET context here.
-            */
-           phcontext = PGC_SUSET;
-           break;
-
-       case PGC_S_CLIENT:
-       case PGC_S_SESSION:
-       default:
-
-           /*
-            * We must assume that the value came from an untrusted user, even
-            * if the current_user is a superuser.
-            */
-           phcontext = PGC_USERSET;
-           break;
-   }
-
    /*
     * Assign the string value stored in the placeholder to the real variable.
     *
@@ -6360,8 +6341,8 @@ define_custom_variable(struct config_generic * variable)
    if (value)
    {
        if (set_config_option(name, value,
-                             phcontext, pHolder->gen.source,
-                             GUC_ACTION_SET, true) > 0)
+                             pHolder->gen.scontext, pHolder->gen.source,
+                             GUC_ACTION_SET, true) != 0)
        {
            /* Also copy over any saved source-location information */
            if (pHolder->gen.sourcefile)
@@ -7284,6 +7265,7 @@ _ShowOption(struct config_generic * record, bool use_units)
  *     variable name, string, null terminated
  *     variable value, string, null terminated
  *     variable source, integer
+ *     variable scontext, integer
  */
 static void
 write_one_nondefault_variable(FILE *fp, struct config_generic * gconf)
@@ -7319,8 +7301,7 @@ write_one_nondefault_variable(FILE *fp, struct config_generic * gconf)
            {
                struct config_real *conf = (struct config_real *) gconf;
 
-               /* Could lose precision here? */
-               fprintf(fp, "%f", *conf->variable);
+               fprintf(fp, "%.17g", *conf->variable);
            }
            break;
 
@@ -7344,7 +7325,8 @@ write_one_nondefault_variable(FILE *fp, struct config_generic * gconf)
 
    fputc(0, fp);
 
-   fwrite(&gconf->source, sizeof(gconf->source), 1, fp);
+   fwrite(&gconf->source, 1, sizeof(gconf->source), fp);
+   fwrite(&gconf->scontext, 1, sizeof(gconf->scontext), fp);
 }
 
 void
@@ -7436,7 +7418,8 @@ read_nondefault_variables(void)
    FILE       *fp;
    char       *varname,
               *varvalue;
-   int         varsource;
+   GucSource   varsource;
+   GucContext  varscontext;
 
    /*
     * Open file
@@ -7464,11 +7447,14 @@ read_nondefault_variables(void)
            elog(FATAL, "failed to locate variable %s in exec config params file", varname);
        if ((varvalue = read_string_with_null(fp)) == NULL)
            elog(FATAL, "invalid format of exec config params file");
-       if (fread(&varsource, sizeof(varsource), 1, fp) == 0)
+       if (fread(&varsource, 1, sizeof(varsource), fp) != sizeof(varsource))
+           elog(FATAL, "invalid format of exec config params file");
+       if (fread(&varscontext, 1, sizeof(varscontext), fp) != sizeof(varscontext))
            elog(FATAL, "invalid format of exec config params file");
 
-       (void) set_config_option(varname, varvalue, record->context,
-                                varsource, GUC_ACTION_SET, true);
+       (void) set_config_option(varname, varvalue,
+                                varscontext, varsource,
+                                GUC_ACTION_SET, true);
        free(varname);
        free(varvalue);
    }
index 84e9fb54d2fe87817988a9e4261f841678e0c2eb..21342f59acd9927518c011db9b0a8d1f94881fe9 100644 (file)
@@ -118,9 +118,11 @@ typedef struct guc_stack
    int         nest_level;     /* nesting depth at which we made entry */
    GucStackState state;        /* see enum above */
    GucSource   source;         /* source of the prior value */
+   /* masked value's source must be PGC_S_SESSION, so no need to store it */
+   GucContext  scontext;       /* context that set the prior value */
+   GucContext  masked_scontext; /* context that set the masked value */
    config_var_value prior;     /* previous value of variable */
    config_var_value masked;    /* SET value in a GUC_SET_LOCAL entry */
-   /* masked value's source must be PGC_S_SESSION, so no need to store it */
 } GucStack;
 
 /*
@@ -143,21 +145,21 @@ struct config_generic
    enum config_group group;    /* to help organize variables by function */
    const char *short_desc;     /* short desc. of this variable's purpose */
    const char *long_desc;      /* long desc. of this variable's purpose */
-   int         flags;          /* flag bits, see below */
+   int         flags;          /* flag bits, see guc.h */
    /* variable fields, initialized at runtime: */
    enum config_type vartype;   /* type of variable (set only at startup) */
    int         status;         /* status bits, see below */
-   GucSource   reset_source;   /* source of the reset_value */
    GucSource   source;         /* source of the current actual value */
+   GucSource   reset_source;   /* source of the reset_value */
+   GucContext  scontext;       /* context that set the current value */
+   GucContext  reset_scontext; /* context that set the reset value */
    GucStack   *stack;          /* stacked prior values */
    void       *extra;          /* "extra" pointer for current actual value */
    char       *sourcefile;     /* file current setting is from (NULL if not
-                                * file) */
+                                * set in config file) */
    int         sourceline;     /* line in source file */
 };
 
-/* bit values in flags field are defined in guc.h */
-
 /* bit values in status field */
 #define GUC_IS_IN_FILE     0x0001      /* found it in config file */
 /*
index e8a8e9bd83d6dff6c034d154965c86be55b1303d..a21ea0b6214b891bad9a27999a0454a90836a456 100644 (file)
@@ -1,5 +1,5 @@
 -- test plperl.on_plperl_init errors are fatal
--- Must load plperl before we can set on_plperl_init
+-- This test tests setting on_plperl_init after loading plperl
 LOAD 'plperl';
 SET SESSION plperl.on_plperl_init = ' system("/nonesuch") ';
 SHOW plperl.on_plperl_init;
index 67478ab454b4176955ca36e236edfd704222972b..464e22090c328b9cd92552f51ba5da21734eaef8 100644 (file)
@@ -1,7 +1,6 @@
 -- test plperl.on_plperl_init via the shared hash
 -- (must be done before plperl is first used)
--- Must load plperl before we can set on_plperl_init
-LOAD 'plperl';
+-- This test tests setting on_plperl_init before loading plperl
 -- testing on_plperl_init gets run, and that it can alter %_SHARED
 SET plperl.on_plperl_init = '$_SHARED{on_init} = 42';
 -- test the shared hash
index 1ba07eed9dc835579e52bc50217075f64991dbbc..3daf4ced86fbd2d29fa1ecc3e1c84cde65abf2e2 100644 (file)
@@ -1,6 +1,6 @@
 -- Use ONLY plperlu tests here. For plperl/plerlu combined tests
 -- see plperl_plperlu.sql
--- Must load plperl before we can set on_plperlu_init
+-- This test tests setting on_plperlu_init after loading plperl
 LOAD 'plperl';
 -- Test plperl.on_plperlu_init gets run
 SET plperl.on_plperlu_init = '$_SHARED{init} = 42';
index 51ac9192bdfc4f77c08a841f0106304ee34e9e56..d60268d033ecf8cc6ef636ed933a5d6983fd1bab 100644 (file)
@@ -1,6 +1,6 @@
 -- test plperl.on_plperl_init errors are fatal
 
--- Must load plperl before we can set on_plperl_init
+-- This test tests setting on_plperl_init after loading plperl
 LOAD 'plperl';
 
 SET SESSION plperl.on_plperl_init = ' system("/nonesuch") ';
index d2fa8cbf93e7b7f63c49984b1432a93de65089a7..b60e114fafc7619b981ec9138d81eea0024c8170 100644 (file)
@@ -1,8 +1,7 @@
 -- test plperl.on_plperl_init via the shared hash
 -- (must be done before plperl is first used)
 
--- Must load plperl before we can set on_plperl_init
-LOAD 'plperl';
+-- This test tests setting on_plperl_init before loading plperl
 
 -- testing on_plperl_init gets run, and that it can alter %_SHARED
 SET plperl.on_plperl_init = '$_SHARED{on_init} = 42';
index 831b8f44604e1829e058394fed9c67ccf6e75c7f..be43df5d90a6e1cc275609cda6035d937781b1f6 100644 (file)
@@ -1,7 +1,7 @@
 -- Use ONLY plperlu tests here. For plperl/plerlu combined tests
 -- see plperl_plperlu.sql
 
--- Must load plperl before we can set on_plperlu_init
+-- This test tests setting on_plperlu_init after loading plperl
 LOAD 'plperl';
 
 -- Test plperl.on_plperlu_init gets run