From cb23b8415bfe9134196ba35a11eb312a0b77068a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 16 Jan 2003 00:26:49 +0000 Subject: [PATCH] Repair an embarrassingly large number of alphabetization mistakes in the 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 | 11 +++- src/backend/utils/adt/datetime.c | 79 +++++++++++++++++------ src/include/utils/datetime.h | 8 ++- src/test/regress/expected/timestamp.out | 6 +- src/test/regress/expected/timestamptz.out | 6 +- 5 files changed, 79 insertions(+), 31 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index f5048372c0..c9334ef514 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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. diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 8173c7476f..55e3e2a198 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -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. * - * The text field is not guaranteed to be NULL-terminated. + * 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. * * 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; +} diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index be7283bf26..3b81770152 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -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 */ diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index e7c8cc8086..73b8d13995 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -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'); diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 403a0bbd4a..ab54a06d8b 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -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');