Get rid of postgres.c's separate parsing logic for PGDATESTYLE env.
authorTom Lane
Sat, 19 Feb 2000 22:10:47 +0000 (22:10 +0000)
committerTom Lane
Sat, 19 Feb 2000 22:10:47 +0000 (22:10 +0000)
variable, instead calling same code in variable.c that is used to parse
SET DATESTYLE.  Fix bug: although backend's startup datestyle had been
changed to ISO, 'RESET DATESTYLE' and 'SET DATESTYLE TO DEFAULT' didn't
know about it.  For consistency I have made the latter two reset to the
PGDATESTYLE-defined initial value, which may not be the same as the
compiled-in default of ISO.

src/backend/commands/variable.c
src/backend/tcop/postgres.c
src/include/commands/variable.h

index 718a62a118d98d79413ceb1fc5acfb4e889f1f82..ca3f96c063d20dcc86c247a8ad3a146c1c5bd4d9 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.29 2000/02/15 20:49:08 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.30 2000/02/19 22:10:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,157 +40,165 @@ extern bool _use_keyset_query_optimizer;
 
 static bool show_date(void);
 static bool reset_date(void);
-static bool parse_date(const char *);
+static bool parse_date(char *);
 static bool show_timezone(void);
 static bool reset_timezone(void);
-static bool parse_timezone(const char *);
+static bool parse_timezone(char *);
 static bool show_effective_cache_size(void);
 static bool reset_effective_cache_size(void);
-static bool parse_effective_cache_size(const char *);
+static bool parse_effective_cache_size(char *);
 static bool show_random_page_cost(void);
 static bool reset_random_page_cost(void);
-static bool parse_random_page_cost(const char *);
+static bool parse_random_page_cost(char *);
 static bool show_cpu_tuple_cost(void);
 static bool reset_cpu_tuple_cost(void);
-static bool parse_cpu_tuple_cost(const char *);
+static bool parse_cpu_tuple_cost(char *);
 static bool show_cpu_index_tuple_cost(void);
 static bool reset_cpu_index_tuple_cost(void);
-static bool parse_cpu_index_tuple_cost(const char *);
+static bool parse_cpu_index_tuple_cost(char *);
 static bool show_cpu_operator_cost(void);
 static bool reset_cpu_operator_cost(void);
-static bool parse_cpu_operator_cost(const char *);
+static bool parse_cpu_operator_cost(char *);
 static bool reset_enable_seqscan(void);
 static bool show_enable_seqscan(void);
-static bool parse_enable_seqscan(const char *);
+static bool parse_enable_seqscan(char *);
 static bool reset_enable_indexscan(void);
 static bool show_enable_indexscan(void);
-static bool parse_enable_indexscan(const char *);
+static bool parse_enable_indexscan(char *);
 static bool reset_enable_tidscan(void);
 static bool show_enable_tidscan(void);
-static bool parse_enable_tidscan(const char *);
+static bool parse_enable_tidscan(char *);
 static bool reset_enable_sort(void);
 static bool show_enable_sort(void);
-static bool parse_enable_sort(const char *);
+static bool parse_enable_sort(char *);
 static bool reset_enable_nestloop(void);
 static bool show_enable_nestloop(void);
-static bool parse_enable_nestloop(const char *);
+static bool parse_enable_nestloop(char *);
 static bool reset_enable_mergejoin(void);
 static bool show_enable_mergejoin(void);
-static bool parse_enable_mergejoin(const char *);
+static bool parse_enable_mergejoin(char *);
 static bool reset_enable_hashjoin(void);
 static bool show_enable_hashjoin(void);
-static bool parse_enable_hashjoin(const char *);
+static bool parse_enable_hashjoin(char *);
 static bool reset_geqo(void);
 static bool show_geqo(void);
-static bool parse_geqo(const char *);
+static bool parse_geqo(char *);
 static bool show_ksqo(void);
 static bool reset_ksqo(void);
-static bool parse_ksqo(const char *);
+static bool parse_ksqo(char *);
 static bool show_XactIsoLevel(void);
 static bool reset_XactIsoLevel(void);
-static bool parse_XactIsoLevel(const char *);
+static bool parse_XactIsoLevel(char *);
 
 /*
+ * get_token
+ *     Obtain the next item in a comma-separated list of items,
+ *     where each item can be either "word" or "word=word".
+ *     The "word=word" form is only accepted if 'val' is not NULL.
+ *     Words are any sequences not containing whitespace, ',', or '='.
+ *     Whitespace can appear between the words and punctuation.
  *
- * Get_Token
+ * 'tok': receives a pointer to first word of item, or NULL if none.
+ * 'val': if not NULL, receives a pointer to second word, or NULL if none.
+ * 'str': start of input string.
  *
+ * Returns NULL if input string contained no more words, else pointer
+ * to just past this item, which can be used as 'str' for next call.
+ * (If this is the last item, returned pointer will point at a null char,
+ * so caller can alternatively check for that instead of calling again.)
+ *
+ * NB: input string is destructively modified by placing null characters
+ * at ends of words!
+ *
+ * A former version of this code avoided modifying the input string by
+ * returning palloc'd copies of the words.  However, we want to use this
+ * code early in backend startup to parse the PGDATESTYLE environment var,
+ * and palloc/pfree aren't initialized at that point.  Cleanest answer
+ * seems to be to palloc in SetPGVariable() so that we can treat the string
+ * as modifiable here.
  */
-static const char *
-get_token(char **tok, char **val, const char *str)
+static char *
+get_token(char **tok, char **val, char *str)
 {
-   const char *start;
-   int         len = 0;
+   char        ch;
 
    *tok = NULL;
    if (val != NULL)
        *val = NULL;
 
-   if (!(*str))
+   if (!str || *str == '\0')
        return NULL;
 
-   /* skip white spaces */
+   /* skip leading white space */
    while (isspace(*str))
        str++;
-   if (*str == ',' || *str == '=')
-       elog(ERROR, "Syntax error near (%s): empty setting", str);
 
    /* end of string? then return NULL */
-   if (!(*str))
+   if (*str == '\0')
        return NULL;
 
-   /* OK, at beginning of non-NULL string... */
-   start = str;
+   if (*str == ',' || *str == '=')
+       elog(ERROR, "Syntax error near \"%s\": empty setting", str);
 
-   /*
-    * count chars in token until we hit white space or comma or '=' or
-    * end of string
-    */
-   while (*str && (!isspace(*str))
-          && *str != ',' && *str != '=')
-   {
+   /* OK, at beginning of non-empty item */
+   *tok = str;
+
+   /* Advance to end of word */
+   while (*str && !isspace(*str) && *str != ',' && *str != '=')
        str++;
-       len++;
-   }
 
-   *tok = (char *) palloc(len + 1);
-   StrNCpy(*tok, start, len + 1);
+   /* Terminate word string for caller */
+   ch = *str;
+   *str = '\0';
 
-   /* skip white spaces */
-   while (isspace(*str))
-       str++;
+   /* Skip any whitespace */
+   while (isspace(ch))
+       ch = *(++str);
 
    /* end of string? */
-   if (!(*str))
-   {
+   if (ch == '\0')
        return str;
-
-       /* delimiter? */
-   }
-   else if (*str == ',')
-   {
+   /* delimiter? */
+   if (ch == ',')
        return ++str;
 
-   }
-   else if ((val == NULL) || (*str != '='))
-   {
-       elog(ERROR, "Syntax error near (%s)", str);
-   };
+   /* Had better be '=', and caller must be expecting it */
+   if (val == NULL || ch != '=')
+       elog(ERROR, "Syntax error near \"%s\"", str);
 
-   str++;                      /* '=': get value */
-   len = 0;
+   /* '=': get the value */
+   str++;
 
-   /* skip white spaces */
+   /* skip whitespace after '=' */
    while (isspace(*str))
        str++;
 
-   if (*str == ',' || !(*str))
-       elog(ERROR, "Syntax error near (=%s)", str);
+   if (*str == ',' || *str == '\0')
+       elog(ERROR, "Syntax error near \"=%s\"", str);
 
-   start = str;
+   /* OK, at beginning of non-empty value */
+   *val = str;
 
-   /*
-    * count chars in token's value until we hit white space or comma or
-    * end of string
-    */
-   while (*str && (!isspace(*str)) && *str != ',')
-   {
+   /* Advance to end of word */
+   while (*str && !isspace(*str) && *str != ',')
        str++;
-       len++;
-   }
 
-   *val = (char *) palloc(len + 1);
-   StrNCpy(*val, start, len + 1);
+   /* Terminate word string for caller */
+   ch = *str;
+   *str = '\0';
 
-   /* skip white spaces */
-   while (isspace(*str))
-       str++;
+   /* Skip any whitespace */
+   while (isspace(ch))
+       ch = *(++str);
 
-   if (!(*str))
-       return NULL;
-   if (*str == ',')
+   /* end of string? */
+   if (ch == '\0')
+       return str;
+   /* delimiter? */
+   if (ch == ',')
        return ++str;
 
-   elog(ERROR, "Syntax error near (%s)", str);
+   elog(ERROR, "Syntax error near \"%s\"", str);
 
    return str;
 }
@@ -199,7 +207,7 @@ get_token(char **tok, char **val, const char *str)
  * Generic parse routine for boolean ON/OFF variables
  */
 static bool
-parse_boolean_var(const char *value,
+parse_boolean_var(char *value,
                  bool *variable, const char *varname, bool defaultval)
 {
    if (value == NULL)
@@ -222,7 +230,7 @@ parse_boolean_var(const char *value,
  * ENABLE_SEQSCAN
  */
 static bool
-parse_enable_seqscan(const char *value)
+parse_enable_seqscan(char *value)
 {
    return parse_boolean_var(value, &enable_seqscan,
                             "ENABLE_SEQSCAN", true);
@@ -247,7 +255,7 @@ reset_enable_seqscan()
  * ENABLE_INDEXSCAN
  */
 static bool
-parse_enable_indexscan(const char *value)
+parse_enable_indexscan(char *value)
 {
    return parse_boolean_var(value, &enable_indexscan,
                             "ENABLE_INDEXSCAN", true);
@@ -272,7 +280,7 @@ reset_enable_indexscan()
  * ENABLE_TIDSCAN
  */
 static bool
-parse_enable_tidscan(const char *value)
+parse_enable_tidscan(char *value)
 {
    return parse_boolean_var(value, &enable_tidscan,
                             "ENABLE_TIDSCAN", true);
@@ -297,7 +305,7 @@ reset_enable_tidscan()
  * ENABLE_SORT
  */
 static bool
-parse_enable_sort(const char *value)
+parse_enable_sort(char *value)
 {
    return parse_boolean_var(value, &enable_sort,
                             "ENABLE_SORT", true);
@@ -322,7 +330,7 @@ reset_enable_sort()
  * ENABLE_NESTLOOP
  */
 static bool
-parse_enable_nestloop(const char *value)
+parse_enable_nestloop(char *value)
 {
    return parse_boolean_var(value, &enable_nestloop,
                             "ENABLE_NESTLOOP", true);
@@ -347,7 +355,7 @@ reset_enable_nestloop()
  * ENABLE_MERGEJOIN
  */
 static bool
-parse_enable_mergejoin(const char *value)
+parse_enable_mergejoin(char *value)
 {
    return parse_boolean_var(value, &enable_mergejoin,
                             "ENABLE_MERGEJOIN", true);
@@ -372,7 +380,7 @@ reset_enable_mergejoin()
  * ENABLE_HASHJOIN
  */
 static bool
-parse_enable_hashjoin(const char *value)
+parse_enable_hashjoin(char *value)
 {
    return parse_boolean_var(value, &enable_hashjoin,
                             "ENABLE_HASHJOIN", true);
@@ -399,11 +407,11 @@ reset_enable_hashjoin()
  *
  */
 static bool
-parse_geqo(const char *value)
+parse_geqo(char *value)
 {
-   const char *rest;
    char       *tok,
-              *val;
+              *val,
+              *rest;
 
    if (value == NULL)
    {
@@ -412,11 +420,12 @@ parse_geqo(const char *value)
    }
 
    rest = get_token(&tok, &val, value);
+
+   /* expect one and only one item */
    if (tok == NULL)
        elog(ERROR, "Value undefined");
-
-   if ((rest) && (*rest != '\0'))
-       elog(ERROR, "Unable to parse '%s'", value);
+   if (rest && *rest != '\0')
+       elog(ERROR, "Unable to parse '%s'", rest);
 
    if (strcasecmp(tok, "on") == 0)
    {
@@ -427,21 +436,19 @@ parse_geqo(const char *value)
            new_geqo_rels = pg_atoi(val, sizeof(int), '\0');
            if (new_geqo_rels <= 1)
                elog(ERROR, "Bad value for # of relations (%s)", val);
-           pfree(val);
        }
        enable_geqo = true;
        geqo_rels = new_geqo_rels;
    }
    else if (strcasecmp(tok, "off") == 0)
    {
-       if ((val != NULL) && (*val != '\0'))
+       if (val != NULL)
            elog(ERROR, "%s does not allow a parameter", tok);
        enable_geqo = false;
    }
    else
        elog(ERROR, "Bad value for GEQO (%s)", value);
 
-   pfree(tok);
    return TRUE;
 }
 
@@ -471,7 +478,7 @@ reset_geqo(void)
  * EFFECTIVE_CACHE_SIZE
  */
 static bool
-parse_effective_cache_size(const char *value)
+parse_effective_cache_size(char *value)
 {
    float64     res;
 
@@ -506,7 +513,7 @@ reset_effective_cache_size()
  * RANDOM_PAGE_COST
  */
 static bool
-parse_random_page_cost(const char *value)
+parse_random_page_cost(char *value)
 {
    float64     res;
 
@@ -540,7 +547,7 @@ reset_random_page_cost()
  * CPU_TUPLE_COST
  */
 static bool
-parse_cpu_tuple_cost(const char *value)
+parse_cpu_tuple_cost(char *value)
 {
    float64     res;
 
@@ -574,7 +581,7 @@ reset_cpu_tuple_cost()
  * CPU_INDEX_TUPLE_COST
  */
 static bool
-parse_cpu_index_tuple_cost(const char *value)
+parse_cpu_index_tuple_cost(char *value)
 {
    float64     res;
 
@@ -608,7 +615,7 @@ reset_cpu_index_tuple_cost()
  * CPU_OPERATOR_COST
  */
 static bool
-parse_cpu_operator_cost(const char *value)
+parse_cpu_operator_cost(char *value)
 {
    float64     res;
 
@@ -639,12 +646,19 @@ reset_cpu_operator_cost()
 }
 
 /*
- *
  * DATE_STYLE
  *
+ * NOTE: set_default_datestyle() is called during backend startup to check
+ * if the PGDATESTYLE environment variable is set.  We want the env var
+ * to determine the value that "RESET DateStyle" will reset to!
  */
+
+/* These get initialized from the "master" values in init/globals.c */
+static int DefaultDateStyle;
+static bool DefaultEuroDates;
+
 static bool
-parse_date(const char *value)
+parse_date(char *value)
 {
    char       *tok;
    int         dcnt = 0,
@@ -698,13 +712,12 @@ parse_date(const char *value)
        }
        else if (!strcasecmp(tok, "DEFAULT"))
        {
-           DateStyle = USE_POSTGRES_DATES;
-           EuroDates = FALSE;
+           DateStyle = DefaultDateStyle;
+           EuroDates = DefaultEuroDates;
            ecnt++;
        }
        else
            elog(ERROR, "Bad value for date style (%s)", tok);
-       pfree(tok);
    }
 
    if (dcnt > 1 || ecnt > 1)
@@ -746,12 +759,45 @@ show_date()
 static bool
 reset_date()
 {
-   DateStyle = USE_POSTGRES_DATES;
-   EuroDates = FALSE;
+   DateStyle = DefaultDateStyle;
+   EuroDates = DefaultEuroDates;
 
    return TRUE;
 }
 
+void
+set_default_datestyle(void)
+{
+   char       *DBDate;
+
+   /* Initialize from compile-time defaults in init/globals.c.
+    * NB: this is a necessary step; consider PGDATESTYLE="DEFAULT".
+    */
+   DefaultDateStyle = DateStyle;
+   DefaultEuroDates = EuroDates;
+
+   /* If the environment var is set, override compiled-in values */
+   DBDate = getenv("PGDATESTYLE");
+   if (DBDate == NULL)
+       return;
+
+   /* Make a modifiable copy --- overwriting the env var doesn't seem
+    * like a good idea, even though we currently won't look at it again.
+    * Note that we cannot use palloc at this early stage of initialization.
+    */
+   DBDate = strdup(DBDate);
+
+   /* Parse desired setting into DateStyle/EuroDates */
+   parse_date(DBDate);
+
+   free(DBDate);
+
+   /* And make it the default for future RESETs */
+   DefaultDateStyle = DateStyle;
+   DefaultEuroDates = EuroDates;
+}
+
+
 /* Timezone support
  * Working storage for strings is allocated with an arbitrary size of 64 bytes.
  */
@@ -771,7 +817,7 @@ static char tzbuf[64];
  * - thomas 1997-11-10
  */
 static bool
-parse_timezone(const char *value)
+parse_timezone(char *value)
 {
    char       *tok;
 
@@ -801,7 +847,6 @@ parse_timezone(const char *value)
            elog(ERROR, "Unable to set TZ environment variable to %s", tok);
 
        tzset();
-       pfree(tok);
    }
 
    return TRUE;
@@ -869,7 +914,7 @@ See optimizer/prep/prepkeyset.c for more on this.
    [email protected]    6/16/98
 -----------------------------------------------------------------------*/
 static bool
-parse_ksqo(const char *value)
+parse_ksqo(char *value)
 {
    return parse_boolean_var(value, &_use_keyset_query_optimizer,
                             "KSQO", false);
@@ -893,7 +938,7 @@ reset_ksqo()
 /* SET TRANSACTION */
 
 static bool
-parse_XactIsoLevel(const char *value)
+parse_XactIsoLevel(char *value)
 {
 
    if (value == NULL)
@@ -949,7 +994,7 @@ reset_XactIsoLevel()
  * Pg_options
  */
 static bool
-parse_pg_options(const char *value)
+parse_pg_options(char *value)
 {
    if (!superuser()) {
        elog(ERROR, "Only users with superuser privilege can set pg_options");
@@ -978,10 +1023,10 @@ reset_pg_options(void)
 
 /*-----------------------------------------------------------------------*/
 
-struct VariableParsers
+static struct VariableParsers
 {
    const char *name;
-   bool        (*parser) (const char *);
+   bool        (*parser) (char *);
    bool        (*show) ();
    bool        (*reset) ();
 }          VariableParsers[] =
@@ -1071,11 +1116,15 @@ bool
 SetPGVariable(const char *name, const char *value)
 {
    struct VariableParsers *vp;
+   char       *val;
+
+   /* Make a modifiable copy for convenience of get_token */
+   val = pstrdup(value);
 
    for (vp = VariableParsers; vp->name; vp++)
    {
        if (!strcasecmp(vp->name, name))
-           return (vp->parser) (value);
+           return (vp->parser) (val);
    }
 
    elog(NOTICE, "Unrecognized variable %s", name);
index 77422deb386c1c535cbab82f3ab215d771878be5..2415ef3c96d68a8d7d5a29553a95f94e644e3a0f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.142 2000/02/18 09:29:27 inoue Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.143 2000/02/19 22:10:47 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -40,6 +40,7 @@
 
 #include "commands/async.h"
 #include "commands/trigger.h"
+#include "commands/variable.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
@@ -891,7 +892,6 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
    char       *remote_host = "";
    unsigned short remote_port = 0;
 
-   char       *DBDate = NULL;
    extern int  optind;
    extern char *optarg;
    extern int  DebugLvl;
@@ -912,30 +912,8 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
 
    SetProcessingMode(InitProcessing);
 
-   /*
-    * Try to get initial values for date styles and formats. Does not do
-    * a complete job, but should be good enough for backend. Cannot call
-    * parse_date() since palloc/pfree memory is not set up yet.
-    */
-   DBDate = getenv("PGDATESTYLE");
-   if (DBDate != NULL)
-   {
-       if (strcasecmp(DBDate, "ISO") == 0)
-           DateStyle = USE_ISO_DATES;
-       else if (strcasecmp(DBDate, "SQL") == 0)
-           DateStyle = USE_SQL_DATES;
-       else if (strcasecmp(DBDate, "POSTGRES") == 0)
-           DateStyle = USE_POSTGRES_DATES;
-       else if (strcasecmp(DBDate, "GERMAN") == 0)
-       {
-           DateStyle = USE_GERMAN_DATES;
-           EuroDates = TRUE;
-       }
-       else if (strcasecmp(DBDate, "NONEURO") == 0)
-           EuroDates = FALSE;
-       else if (strcasecmp(DBDate, "EURO") == 0)
-           EuroDates = TRUE;
-   }
+   /* Check for PGDATESTYLE environment variable */
+   set_default_datestyle();
 
    /*
     * Read default pg_options from file $DATADIR/pg_options.
@@ -1525,7 +1503,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.142 $ $Date: 2000/02/18 09:29:27 $\n");
+       puts("$Revision: 1.143 $ $Date: 2000/02/19 22:10:47 $\n");
    }
 
    /*
index a5110e883f2b2eaa285502982632eadd6e72d8f8..c9b39d93cb5f65f6b4638d172d346c3d6a8ae839 100644 (file)
@@ -2,32 +2,16 @@
  * Headers for handling of 'SET var TO', 'SHOW var' and 'RESET var'
  * statements
  *
- * $Id: variable.h,v 1.8 1998/10/08 18:30:27 momjian Exp $
+ * $Id: variable.h,v 1.9 2000/02/19 22:10:43 tgl Exp $
  *
  */
 #ifndef VARIABLE_H
 #define VARIABLE_H 1
 
-enum DateFormat
-{
-   Date_Postgres, Date_SQL, Date_ISO
-};
+extern bool SetPGVariable(const char *name, const char *value);
+extern bool GetPGVariable(const char *name);
+extern bool ResetPGVariable(const char *name);
 
-/*-----------------------------------------------------------------------*/
-struct PGVariables
-{
-   struct
-   {
-       bool        euro;
-       enum DateFormat format;
-   }           date;
-};
-
-extern struct PGVariables PGVariables;
-
-/*-----------------------------------------------------------------------*/
-bool       SetPGVariable(const char *, const char *);
-bool       GetPGVariable(const char *);
-bool       ResetPGVariable(const char *);
+extern void set_default_datestyle(void);
 
 #endif  /* VARIABLE_H */