From f46571165d0d0e82b36e1f4ca2a10efb7255e3e6 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 19 Feb 2000 22:10:47 +0000
Subject: [PATCH] Get rid of postgres.c's separate parsing logic for
 PGDATESTYLE env. 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 | 281 +++++++++++++++++++-------------
 src/backend/tcop/postgres.c     |  32 +---
 src/include/commands/variable.h |  26 +--
 3 files changed, 175 insertions(+), 164 deletions(-)

diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 718a62a118d..ca3f96c063d 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -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.
 	daveh@insightdist.com	 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);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 77422deb386..2415ef3c96d 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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");
 	}
 
 	/*
diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h
index a5110e883f2..c9b39d93cb5 100644
--- a/src/include/commands/variable.h
+++ b/src/include/commands/variable.h
@@ -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 */
-- 
GitLab