Repair an embarrassingly large number of alphabetization mistakes in the
authorTom Lane
Thu, 16 Jan 2003 00:26:49 +0000 (00:26 +0000)
committerTom Lane
Thu, 16 Jan 2003 00:26:49 +0000 (00:26 +0000)
datetime token tables.  Even more embarrassing, the regression tests
revealed some of the problems --- but evidently the bogus output wasn't
questioned.  Add code to postmaster startup to directly check the tables
for correct ordering, in hopes of not being embarrassed like this again.

src/backend/postmaster/postmaster.c
src/backend/utils/adt/datetime.c
src/include/utils/datetime.h
src/test/regress/expected/timestamp.out
src/test/regress/expected/timestamptz.out

index f5048372c03b70d50a1e66acadb97b7ae9991194..c9334ef514a2c9655f5c7f102d5b9b95a70ba107 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.304 2003/01/07 18:48:13 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.305 2003/01/16 00:26:44 tgl Exp $
  *
  * NOTES
  *
@@ -594,6 +594,15 @@ PostmasterMain(int argc, char *argv[])
        ExitPostmaster(1);
    }
 
+   /*
+    * Other one-time internal sanity checks can go here.
+    */
+   if (!CheckDateTokenTables())
+   {
+       postmaster_error("Invalid datetoken tables, please fix.");
+       ExitPostmaster(1);
+   }
+
    /*
     * Now that we are done processing the postmaster arguments, reset
     * getopt(3) library so that it will work correctly in subprocesses.
index 8173c7476f9c93e7034e35f41a4d0d737a1e0826..55e3e2a198450e770c4cbd3a1312e573c27c5b5b 100644 (file)
@@ -8,21 +8,21 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.97 2002/11/13 17:24:05 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.98 2003/01/16 00:26:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include 
-#include 
 #include 
 #include 
 #include 
+#include 
 
 #include "miscadmin.h"
-#include "utils/guc.h"
 #include "utils/datetime.h"
+#include "utils/guc.h"
 
 
 static int DecodeNumber(int flen, char *field,
@@ -37,7 +37,7 @@ static int    DecodeTimezone(char *str, int *tzp);
 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
 static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
 static int DecodePosixTimezone(char *str, int *val);
-void       TrimTrailingZeros(char *str);
+static void TrimTrailingZeros(char *str);
 
 
 int            day_tab[2][13] = {
@@ -69,14 +69,16 @@ char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
 #define TOVAL(tp, v)   ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
 
 /*
- * datetktbl holds date/time keywords. Note that this table must be strictly
- * ordered to allow an O(ln(N)) search algorithm.
+ * datetktbl holds date/time keywords.
+ *
+ * Note that this table must be strictly alphabetically ordered to allow an
+ * O(ln(N)) search algorithm to be used.
  *
- * The text field is not guaranteed to be NULL-terminated.
+ * The text field is NOT guaranteed to be NULL-terminated.
  *
  * To keep this table reasonably small, we divide the lexval for TZ and DTZ
  * entries by 15 (so they are on 15 minute boundaries) and truncate the text
- * field at MAXTOKLEN characters.
+ * field at TOKMAXLEN characters.
  * Formerly, we divided by 10 rather than 15 but there are a few time zones
  * which are 30 or 45 minutes away from an even hour, most are on an hour
  * boundary, and none on other boundaries.
@@ -88,11 +90,11 @@ char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
 static datetkn datetktbl[] = {
 /* text, token, lexval */
    {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
+   {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
    {"acsst", DTZ, POS(42)},    /* Cent. Australia */
    {"acst", DTZ, NEG(16)},     /* Atlantic/Porto Acre */
    {"act", TZ, NEG(20)},       /* Atlantic/Porto Acre */
    {DA_D, ADBC, AD},           /* "ad" for years >= 0 */
-   {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
    {"adt", DTZ, NEG(12)},      /* Atlantic Daylight Time */
    {"aesst", DTZ, POS(44)},    /* E. Australia */
    {"aest", TZ, POS(40)},      /* Australia Eastern Std Time */
@@ -101,16 +103,18 @@ static datetkn datetktbl[] = {
    {"akdt", DTZ, NEG(32)},     /* Alaska Daylight Time */
    {"akst", DTZ, NEG(36)},     /* Alaska Standard Time */
    {"allballs", RESERV, DTK_ZULU},     /* 00:00:00 */
-   {"almt", TZ, POS(24)},      /* Almaty Time */
    {"almst", TZ, POS(28)},     /* Almaty Savings Time */
+   {"almt", TZ, POS(24)},      /* Almaty Time */
    {"am", AMPM, AM},
    {"amst", DTZ, POS(20)},     /* Armenia Summer Time (Yerevan) */
-   {"amt", TZ, POS(16)},       /* Armenia Time (Yerevan) */
 #if 0
    {"amst", DTZ, NEG(12)},     /* Porto Velho */
 #endif
+   {"amt", TZ, POS(16)},       /* Armenia Time (Yerevan) */
    {"anast", DTZ, POS(52)},    /* Anadyr Summer Time (Russia) */
    {"anat", TZ, POS(48)},      /* Anadyr Time (Russia) */
+   {"apr", MONTH, 4},
+   {"april", MONTH, 4},
 #if 0
    aqtst
    aqtt
@@ -122,8 +126,6 @@ static datetkn datetktbl[] = {
    ast                         /* Atlantic Standard Time, Arabia Standard
                                 * Time, Acre Standard Time */
 #endif
-   {"apr", MONTH, 4},
-   {"april", MONTH, 4},
    {"ast", TZ, NEG(16)},       /* Atlantic Std Time (Canada) */
    {"at", IGNORE_DTF, 0},      /* "at" (throwaway) */
    {"aug", MONTH, 8},
@@ -181,12 +183,12 @@ static datetkn datetktbl[] = {
 #endif
    {"cot", TZ, NEG(20)},       /* Columbia Time */
    {"cst", TZ, NEG(24)},       /* Central Standard Time */
+   {DCURRENT, RESERV, DTK_CURRENT},    /* "current" is always now */
 #if 0
    cvst
 #endif
    {"cvt", TZ, POS(28)},       /* Christmas Island Time (Indian Ocean) */
    {"cxt", TZ, POS(28)},       /* Christmas Island Time (Indian Ocean) */
-   {DCURRENT, RESERV, DTK_CURRENT},    /* "current" is always now */
    {"d", UNITS, DTK_DAY},      /* "day of month" for ISO input */
    {"davt", TZ, POS(28)},      /* Davis Time (Antarctica) */
    {"ddut", TZ, POS(40)},      /* Dumont-d'Urville Time (Antarctica) */
@@ -414,8 +416,8 @@ static datetkn datetktbl[] = {
    syot
 #endif
    {"t", ISOTIME, DTK_TIME},   /* Filler for ISO time fields */
-   {"that", TZ, NEG(40)},      /* Tahiti Time */
    {"tft", TZ, POS(20)},       /* Kerguelen Time */
+   {"that", TZ, NEG(40)},      /* Tahiti Time */
    {"thu", DOW, 4},
    {"thur", DOW, 4},
    {"thurs", DOW, 4},
@@ -516,9 +518,9 @@ static datetkn deltatktbl[] = {
    {DDAY, UNITS, DTK_DAY},     /* "day" relative */
    {"days", UNITS, DTK_DAY},   /* "days" relative */
    {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
-   {"decs", UNITS, DTK_DECADE},    /* "decades" relative */
    {DDECADE, UNITS, DTK_DECADE},       /* "decade" relative */
    {"decades", UNITS, DTK_DECADE},     /* "decades" relative */
+   {"decs", UNITS, DTK_DECADE},    /* "decades" relative */
    {"h", UNITS, DTK_HOUR},     /* "hour" relative */
    {DHOUR, UNITS, DTK_HOUR},   /* "hour" relative */
    {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
@@ -534,7 +536,6 @@ static datetkn deltatktbl[] = {
    {"mils", UNITS, DTK_MILLENNIUM},    /* "millennia" relative */
    {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
    {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
-   {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
    {DMINUTE, UNITS, DTK_MINUTE},       /* "minute" relative */
    {"minutes", UNITS, DTK_MINUTE},     /* "minutes" relative */
    {"mon", UNITS, DTK_MONTH},  /* "months" relative */
@@ -555,7 +556,6 @@ static datetkn deltatktbl[] = {
    {"seconds", UNITS, DTK_SECOND},
    {"secs", UNITS, DTK_SECOND},
    {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
-   {"timezone", UNITS, DTK_TZ},    /* "timezone" time offset */
    {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
    {"timezone_m", UNITS, DTK_TZ_MINUTE},       /* timezone minutes units */
    {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
@@ -576,9 +576,9 @@ static datetkn deltatktbl[] = {
 
 static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
 
-datetkn    *datecache[MAXDATEFIELDS] = {NULL};
+static datetkn    *datecache[MAXDATEFIELDS] = {NULL};
 
-datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
+static datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
 
 
 /*
@@ -653,7 +653,7 @@ j2day(int date)
 /* TrimTrailingZeros()
  * ... resulting from printing numbers with full precision.
  */
-void
+static void
 TrimTrailingZeros(char *str)
 {
    int         len = strlen(str);
@@ -3690,3 +3690,40 @@ ClearDateCache(bool newval, bool doit, bool interactive)
 
    return true;
 }
+
+/*
+ * We've been burnt by stupid errors in the ordering of the datetkn tables
+ * once too often.  Arrange to check them during postmaster start.
+ */
+static bool
+CheckDateTokenTable(const char *tablename, datetkn *base, unsigned int nel)
+{
+   bool        ok = true;
+   unsigned int i;
+
+   for (i = 1; i < nel; i++)
+   {
+       if (strncmp(base[i-1].token, base[i].token, TOKMAXLEN) >= 0)
+       {
+           elog(LOG, "Ordering error in %s table: \"%.*s\" >= \"%.*s\"",
+                tablename,
+                TOKMAXLEN, base[i-1].token,
+                TOKMAXLEN, base[i].token);
+           ok = false;
+       }
+   }
+   return ok;
+}
+
+bool
+CheckDateTokenTables(void)
+{
+   bool        ok = true;
+
+   ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
+   ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
+   ok &= CheckDateTokenTable("australian_datetktbl",
+                             australian_datetktbl,
+                             australian_szdatetktbl);
+   return ok;
+}
index be7283bf264d9ca1aef41c4f002082405a3871e4..3b81770152d7eec4b3e6ddbe8733136f22200636 100644 (file)
@@ -9,16 +9,16 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: datetime.h,v 1.33 2002/09/04 20:31:45 momjian Exp $
+ * $Id: datetime.h,v 1.34 2003/01/16 00:26:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef DATETIME_H
 #define DATETIME_H
 
-#include 
-#include 
 #include 
+#include 
+#include 
 
 #include "utils/timestamp.h"
 
@@ -293,4 +293,6 @@ extern bool ClearDateCache(bool, bool, bool);
 
 extern int j2day(int jd);
 
+extern bool CheckDateTokenTables(void);
+
 #endif   /* DATETIME_H */
index e7c8cc8086b7c42a0e63058eabcf367e1ed80ba2..73b8d1399573864d414321872656d9062ea9c469 100644 (file)
@@ -11,7 +11,7 @@ CREATE TABLE TIMESTAMP_TBL ( d1 timestamp(2) without time zone);
 -- statements.
 INSERT INTO TIMESTAMP_TBL VALUES ('now');
 INSERT INTO TIMESTAMP_TBL VALUES ('current');
-ERROR:  Bad timestamp external representation 'current'
+ERROR:  'CURRENT' is no longer supported
 INSERT INTO TIMESTAMP_TBL VALUES ('today');
 INSERT INTO TIMESTAMP_TBL VALUES ('yesterday');
 INSERT INTO TIMESTAMP_TBL VALUES ('tomorrow');
@@ -64,9 +64,9 @@ ERROR:  TIMESTAMP 'invalid' no longer supported
 -- Postgres v6.0 standard output format
 INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST');
 INSERT INTO TIMESTAMP_TBL VALUES ('Invalid Abstime');
-ERROR:  Bad timestamp external representation 'Invalid Abstime'
+ERROR:  TIMESTAMP 'Invalid Abstime' no longer supported
 INSERT INTO TIMESTAMP_TBL VALUES ('Undefined Abstime');
-ERROR:  Bad timestamp external representation 'Undefined Abstime'
+ERROR:  TIMESTAMP 'Undefined Abstime' no longer supported
 -- Variations on Postgres v6.1 standard output format
 INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST');
 INSERT INTO TIMESTAMP_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST');
index 403a0bbd4afc94cf72ab52a32e343d6a72b3f704..ab54a06d8bfd994a6feda558b416718a1d760872 100644 (file)
@@ -6,7 +6,7 @@ SET australian_timezones = 'off';
 CREATE TABLE TIMESTAMPTZ_TBL ( d1 timestamp(2) with time zone);
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('now');
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('current');
-ERROR:  Bad timestamp external representation 'current'
+ERROR:  'CURRENT' is no longer supported
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('today');
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('yesterday');
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('tomorrow');
@@ -59,9 +59,9 @@ ERROR:  TIMESTAMP WITH TIME ZONE 'invalid' no longer supported
 -- Postgres v6.0 standard output format
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01 1997 PST');
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('Invalid Abstime');
-ERROR:  Bad timestamp external representation 'Invalid Abstime'
+ERROR:  TIMESTAMP WITH TIME ZONE 'Invalid Abstime' no longer supported
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('Undefined Abstime');
-ERROR:  Bad timestamp external representation 'Undefined Abstime'
+ERROR:  TIMESTAMP WITH TIME ZONE 'Undefined Abstime' no longer supported
 -- Variations on Postgres v6.1 standard output format
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.000001 1997 PST');
 INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mon Feb 10 17:32:01.999999 1997 PST');