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.
WIN32_DEV
Tom Lane 23 years ago
parent 828822bc96
commit cb23b8415b
  1. 11
      src/backend/postmaster/postmaster.c
  2. 79
      src/backend/utils/adt/datetime.c
  3. 8
      src/include/utils/datetime.h
  4. 6
      src/test/regress/expected/timestamp.out
  5. 6
      src/test/regress/expected/timestamptz.out

@ -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.

@ -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 <ctype.h>
#include <math.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#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;
}

@ -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 <time.h>
#include <math.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#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 */

@ -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');

@ -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');

Loading…
Cancel
Save