]> granicus.if.org Git - postgresql/commitdiff
Fix crash caused by log_timezone patch if we attempt to emit any elog messages
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 19:29:25 +0000 (19:29 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 19:29:25 +0000 (19:29 +0000)
between the setting of log_line_prefix and the setting of log_timezone.  We
can't realistically set log_timezone any earlier than we do now, so the best
behavior seems to be to use GMT zone if any timestamps are to be logged during
early startup.  Create a dummy zone variable with a minimal definition of GMT
(in particular it will never know about leap seconds), so that we can set it
up without reference to any external files.

src/backend/utils/error/elog.c
src/backend/utils/misc/guc.c
src/include/pgtime.h
src/timezone/localtime.c
src/timezone/pgtz.c

index d51dd0bf1af250cf3bd4b2320b597a801e1dde09..44d02752080ea6a253f44d9d0844e1b445612652 100644 (file)
@@ -42,7 +42,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.192 2007/08/04 01:26:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.193 2007/08/04 19:29:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1497,16 +1497,25 @@ log_line_prefix(StringInfo buf)
                                {
                                        struct timeval tv;
                                        pg_time_t       stamp_time;
+                                       pg_tz      *tz;
                                        char            strfbuf[128],
                                                                msbuf[8];
 
                                        gettimeofday(&tv, NULL);
                                        stamp_time = (pg_time_t) tv.tv_sec;
 
+                                       /*
+                                        * Normally we print log timestamps in log_timezone, but
+                                        * during startup we could get here before that's set.
+                                        * If so, fall back to gmt_timezone (which guc.c ensures
+                                        * is set up before Log_line_prefix can become nonempty).
+                                        */
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                /* leave room for milliseconds... */
                                                                "%Y-%m-%d %H:%M:%S     %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
 
                                        /* 'paste' milliseconds into place... */
                                        sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
@@ -1518,22 +1527,28 @@ log_line_prefix(StringInfo buf)
                        case 't':
                                {
                                        pg_time_t       stamp_time = (pg_time_t) time(NULL);
+                                       pg_tz      *tz;
                                        char            strfbuf[128];
 
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                "%Y-%m-%d %H:%M:%S %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
                                        appendStringInfoString(buf, strfbuf);
                                }
                                break;
                        case 's':
                                {
                                        pg_time_t       stamp_time = (pg_time_t) MyStartTime;
+                                       pg_tz      *tz;
                                        char            strfbuf[128];
 
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                "%Y-%m-%d %H:%M:%S %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
                                        appendStringInfoString(buf, strfbuf);
                                }
                                break;
index fc35b14cf3f3b58d80b23c6ab7e343cfe9b483a4..45fef6d7a3128e3502481cbb97171210486f4396 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.409 2007/08/04 01:26:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.410 2007/08/04 19:29:25 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -2926,6 +2926,12 @@ InitializeGUCOptions(void)
        char       *env;
        long            stack_rlimit;
 
+       /*
+        * Before log_line_prefix could possibly receive a nonempty setting,
+        * make sure that timezone processing is minimally alive (see elog.c).
+        */
+       pg_timezone_pre_initialize();
+
        /*
         * Build sorted array of all GUC variables.
         */
index b1ff5eac44d813e70e43d6a9d5c0b1ed853e7967..c8db2702d1248960f33bd7431c2b7cd4f453977d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/include/pgtime.h,v 1.16 2007/08/04 01:26:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/include/pgtime.h,v 1.17 2007/08/04 19:29:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,6 +52,7 @@ extern int pg_next_dst_boundary(const pg_time_t *timep,
 extern size_t pg_strftime(char *s, size_t max, const char *format,
                        const struct pg_tm * tm);
 
+extern void pg_timezone_pre_initialize(void);
 extern void pg_timezone_initialize(void);
 extern pg_tz *pg_tzset(const char *tzname);
 extern bool tz_acceptable(pg_tz *tz);
@@ -64,6 +65,7 @@ extern void pg_tzenumerate_end(pg_tzenum *dir);
 
 extern pg_tz *session_timezone;
 extern pg_tz *log_timezone;
+extern pg_tz *gmt_timezone;
 
 /* Maximum length of a timezone name (not including trailing null) */
 #define TZ_STRLEN_MAX 255
index fca5ebac643fa32409146ca1f3bcfe8c5ebe6c99..774d83a2f6f46371e5e1724041ecbda6e6263239 100644 (file)
@@ -3,7 +3,7 @@
  * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.16 2006/10/18 16:43:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.17 2007/08/04 19:29:25 tgl Exp $
  */
 
 /*
@@ -88,7 +88,6 @@ static void timesub(const pg_time_t *timep, long offset,
                const struct state * sp, struct pg_tm * tmp);
 static pg_time_t transtime(pg_time_t janfirst, int year,
                  const struct rule * rulep, long offset);
-int                    tzparse(const char *name, struct state * sp, int lastditch);
 
 /* GMT timezone */
 static struct state gmtmem;
@@ -549,6 +548,12 @@ tzparse(const char *name, struct state * sp, int lastditch)
                if (stdlen >= sizeof sp->chars)
                        stdlen = (sizeof sp->chars) - 1;
                stdoffset = 0;
+               /*
+                * Unlike the original zic library, do NOT invoke tzload() here;
+                * we can't assume pg_open_tzfile() is sane yet, and we don't
+                * care about leap seconds anyway.
+                */
+               load_result = -1;
        }
        else
        {
@@ -561,8 +566,8 @@ tzparse(const char *name, struct state * sp, int lastditch)
                name = getoffset(name, &stdoffset);
                if (name == NULL)
                        return -1;
+               load_result = tzload(TZDEFRULES, NULL, sp);
        }
-       load_result = tzload(TZDEFRULES, NULL, sp);
        if (load_result != 0)
                sp->leapcnt = 0;                /* so, we're off a little */
        if (*name != '\0')
index 26ff1ea1129db64803b2cd432f7854e65358a037..ad599c70936d65cdbc725d610e1d0d08ba3ef3bd 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.52 2007/08/04 01:26:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.53 2007/08/04 19:29:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,10 @@ pg_tz           *session_timezone = NULL;
 /* Current log timezone (controlled by log_timezone GUC) */
 pg_tz     *log_timezone = NULL;
 
+/* Fallback GMT timezone for last-ditch error message formatting */
+pg_tz     *gmt_timezone = NULL;
+static pg_tz gmt_timezone_data;
+
 
 static char tzdir[MAXPGPATH];
 static bool done_tzdir = false;
@@ -1251,6 +1255,31 @@ select_default_timezone(void)
        return NULL;                            /* keep compiler quiet */
 }
 
+
+/*
+ * Pre-initialize timezone library
+ *
+ * This is called before GUC variable initialization begins.  Its purpose
+ * is to ensure that elog.c has a pgtz variable available to format timestamps
+ * with, in case log_line_prefix is set to a value requiring that.  We cannot
+ * set log_timezone yet.
+ */
+void
+pg_timezone_pre_initialize(void)
+{
+       /*
+        * We can't use tzload() because we may not know where PGSHAREDIR
+        * is (in particular this is true in an EXEC_BACKEND subprocess).
+        * Since this timezone variable will only be used for emergency
+        * fallback purposes, it seems OK to just use the "lastditch" case
+        * provided by tzparse().
+        */
+       if (tzparse("GMT", &gmt_timezone_data.state, TRUE) != 0)
+               elog(FATAL, "could not initialize GMT timezone");
+       strcpy(gmt_timezone_data.TZname, "GMT");
+       gmt_timezone = &gmt_timezone_data;
+}
+
 /*
  * Initialize timezone library
  *