Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a303e4d

Browse files
committed
Extend the date type to support infinity and -infinity, analogously to
the timestamp types. Turns out this doesn't even reduce the available range of dates, since the restriction to dates that work for Julian-date arithmetic is much tighter than the int32 range anyway. Per a longstanding TODO item.
1 parent 791359f commit a303e4d

File tree

9 files changed

+206
-65
lines changed

9 files changed

+206
-65
lines changed

doc/src/sgml/datatype.sgml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.229 2008/10/03 15:37:18 petere Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.230 2008/10/14 17:12:32 tgl Exp $ -->
22

33
<chapter id="datatype">
44
<title id="datatype-title">Data Types</title>
@@ -2032,12 +2032,12 @@ January 8 04:05:06 1999 PST
20322032
</row>
20332033
<row>
20342034
<entry><literal>infinity</literal></entry>
2035-
<entry><type>timestamp</type></entry>
2035+
<entry><type>date</type>, <type>timestamp</type></entry>
20362036
<entry>later than all other time stamps</entry>
20372037
</row>
20382038
<row>
20392039
<entry><literal>-infinity</literal></entry>
2040-
<entry><type>timestamp</type></entry>
2040+
<entry><type>date</type>, <type>timestamp</type></entry>
20412041
<entry>earlier than all other time stamps</entry>
20422042
</row>
20432043
<row>

doc/src/sgml/func.sgml

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.449 2008/10/13 16:25:19 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.450 2008/10/14 17:12:32 tgl Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -5912,10 +5912,18 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
59125912
<entry><literal>3</literal></entry>
59135913
</row>
59145914

5915+
<row>
5916+
<entry><literal><function>isfinite</function>(<type>date</type>)</literal></entry>
5917+
<entry><type>boolean</type></entry>
5918+
<entry>Test for finite date (not +/-infinity)</entry>
5919+
<entry><literal>isfinite(date '2001-02-16')</literal></entry>
5920+
<entry><literal>true</literal></entry>
5921+
</row>
5922+
59155923
<row>
59165924
<entry><literal><function>isfinite</function>(<type>timestamp</type>)</literal></entry>
59175925
<entry><type>boolean</type></entry>
5918-
<entry>Test for finite time stamp (not equal to infinity)</entry>
5926+
<entry>Test for finite time stamp (not +/-infinity)</entry>
59195927
<entry><literal>isfinite(timestamp '2001-02-16 21:28:30')</literal></entry>
59205928
<entry><literal>true</literal></entry>
59215929
</row>

src/backend/utils/adt/date.c

+132-55
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.142 2008/07/07 18:09:46 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.143 2008/10/14 17:12:33 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -38,6 +38,7 @@
3838
#endif
3939

4040

41+
static void EncodeSpecialDate(DateADT dt, char *str);
4142
static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
4243
static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
4344
static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
@@ -147,6 +148,14 @@ date_in(PG_FUNCTION_ARGS)
147148
GetEpochTime(tm);
148149
break;
149150

151+
case DTK_LATE:
152+
DATE_NOEND(date);
153+
PG_RETURN_DATEADT(date);
154+
155+
case DTK_EARLY:
156+
DATE_NOBEGIN(date);
157+
PG_RETURN_DATEADT(date);
158+
150159
default:
151160
DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
152161
break;
@@ -174,10 +183,14 @@ date_out(PG_FUNCTION_ARGS)
174183
*tm = &tt;
175184
char buf[MAXDATELEN + 1];
176185

177-
j2date(date + POSTGRES_EPOCH_JDATE,
178-
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
179-
180-
EncodeDateOnly(tm, DateStyle, buf);
186+
if (DATE_NOT_FINITE(date))
187+
EncodeSpecialDate(date, buf);
188+
else
189+
{
190+
j2date(date + POSTGRES_EPOCH_JDATE,
191+
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
192+
EncodeDateOnly(tm, DateStyle, buf);
193+
}
181194

182195
result = pstrdup(buf);
183196
PG_RETURN_CSTRING(result);
@@ -208,6 +221,20 @@ date_send(PG_FUNCTION_ARGS)
208221
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
209222
}
210223

224+
/*
225+
* Convert reserved date values to string.
226+
*/
227+
static void
228+
EncodeSpecialDate(DateADT dt, char *str)
229+
{
230+
if (DATE_IS_NOBEGIN(dt))
231+
strcpy(str, EARLY);
232+
else if (DATE_IS_NOEND(dt))
233+
strcpy(str, LATE);
234+
else /* shouldn't happen */
235+
elog(ERROR, "invalid argument for EncodeSpecialDate");
236+
}
237+
211238

212239
/*
213240
* Comparison functions for dates
@@ -280,6 +307,14 @@ date_cmp(PG_FUNCTION_ARGS)
280307
PG_RETURN_INT32(0);
281308
}
282309

310+
Datum
311+
date_finite(PG_FUNCTION_ARGS)
312+
{
313+
DateADT date = PG_GETARG_DATEADT(0);
314+
315+
PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
316+
}
317+
283318
Datum
284319
date_larger(PG_FUNCTION_ARGS)
285320
{
@@ -306,6 +341,11 @@ date_mi(PG_FUNCTION_ARGS)
306341
DateADT dateVal1 = PG_GETARG_DATEADT(0);
307342
DateADT dateVal2 = PG_GETARG_DATEADT(1);
308343

344+
if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
345+
ereport(ERROR,
346+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
347+
errmsg("cannot subtract infinite dates")));
348+
309349
PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
310350
}
311351

@@ -318,6 +358,9 @@ date_pli(PG_FUNCTION_ARGS)
318358
DateADT dateVal = PG_GETARG_DATEADT(0);
319359
int32 days = PG_GETARG_INT32(1);
320360

361+
if (DATE_NOT_FINITE(dateVal))
362+
days = 0; /* can't change infinity */
363+
321364
PG_RETURN_DATEADT(dateVal + days);
322365
}
323366

@@ -329,6 +372,9 @@ date_mii(PG_FUNCTION_ARGS)
329372
DateADT dateVal = PG_GETARG_DATEADT(0);
330373
int32 days = PG_GETARG_INT32(1);
331374

375+
if (DATE_NOT_FINITE(dateVal))
376+
days = 0; /* can't change infinity */
377+
332378
PG_RETURN_DATEADT(dateVal - days);
333379
}
334380

@@ -342,18 +388,25 @@ date2timestamp(DateADT dateVal)
342388
{
343389
Timestamp result;
344390

391+
if (DATE_IS_NOBEGIN(dateVal))
392+
TIMESTAMP_NOBEGIN(result);
393+
else if (DATE_IS_NOEND(dateVal))
394+
TIMESTAMP_NOEND(result);
395+
else
396+
{
345397
#ifdef HAVE_INT64_TIMESTAMP
346-
/* date is days since 2000, timestamp is microseconds since same... */
347-
result = dateVal * USECS_PER_DAY;
348-
/* Date's range is wider than timestamp's, so must check for overflow */
349-
if (result / USECS_PER_DAY != dateVal)
350-
ereport(ERROR,
351-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
352-
errmsg("date out of range for timestamp")));
398+
/* date is days since 2000, timestamp is microseconds since same... */
399+
result = dateVal * USECS_PER_DAY;
400+
/* Date's range is wider than timestamp's, so check for overflow */
401+
if (result / USECS_PER_DAY != dateVal)
402+
ereport(ERROR,
403+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
404+
errmsg("date out of range for timestamp")));
353405
#else
354-
/* date is days since 2000, timestamp is seconds since same... */
355-
result = dateVal * (double) SECS_PER_DAY;
406+
/* date is days since 2000, timestamp is seconds since same... */
407+
result = dateVal * (double) SECS_PER_DAY;
356408
#endif
409+
}
357410

358411
return result;
359412
}
@@ -366,24 +419,30 @@ date2timestamptz(DateADT dateVal)
366419
*tm = &tt;
367420
int tz;
368421

369-
j2date(dateVal + POSTGRES_EPOCH_JDATE,
370-
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
371-
372-
tm->tm_hour = 0;
373-
tm->tm_min = 0;
374-
tm->tm_sec = 0;
375-
tz = DetermineTimeZoneOffset(tm, session_timezone);
422+
if (DATE_IS_NOBEGIN(dateVal))
423+
TIMESTAMP_NOBEGIN(result);
424+
else if (DATE_IS_NOEND(dateVal))
425+
TIMESTAMP_NOEND(result);
426+
else
427+
{
428+
j2date(dateVal + POSTGRES_EPOCH_JDATE,
429+
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
430+
tm->tm_hour = 0;
431+
tm->tm_min = 0;
432+
tm->tm_sec = 0;
433+
tz = DetermineTimeZoneOffset(tm, session_timezone);
376434

377435
#ifdef HAVE_INT64_TIMESTAMP
378-
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
379-
/* Date's range is wider than timestamp's, so must check for overflow */
380-
if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
381-
ereport(ERROR,
382-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
383-
errmsg("date out of range for timestamp")));
436+
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
437+
/* Date's range is wider than timestamp's, so check for overflow */
438+
if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
439+
ereport(ERROR,
440+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
441+
errmsg("date out of range for timestamp")));
384442
#else
385-
result = dateVal * (double) SECS_PER_DAY + tz;
443+
result = dateVal * (double) SECS_PER_DAY + tz;
386444
#endif
445+
}
387446

388447
return result;
389448
}
@@ -797,15 +856,19 @@ timestamp_date(PG_FUNCTION_ARGS)
797856
*tm = &tt;
798857
fsec_t fsec;
799858

800-
if (TIMESTAMP_NOT_FINITE(timestamp))
801-
PG_RETURN_NULL();
802-
803-
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
804-
ereport(ERROR,
805-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
806-
errmsg("timestamp out of range")));
859+
if (TIMESTAMP_IS_NOBEGIN(timestamp))
860+
DATE_NOBEGIN(result);
861+
else if (TIMESTAMP_IS_NOEND(timestamp))
862+
DATE_NOEND(result);
863+
else
864+
{
865+
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
866+
ereport(ERROR,
867+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
868+
errmsg("timestamp out of range")));
807869

808-
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
870+
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
871+
}
809872

810873
PG_RETURN_DATEADT(result);
811874
}
@@ -840,15 +903,19 @@ timestamptz_date(PG_FUNCTION_ARGS)
840903
int tz;
841904
char *tzn;
842905

843-
if (TIMESTAMP_NOT_FINITE(timestamp))
844-
PG_RETURN_NULL();
845-
846-
if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
847-
ereport(ERROR,
848-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
849-
errmsg("timestamp out of range")));
906+
if (TIMESTAMP_IS_NOBEGIN(timestamp))
907+
DATE_NOBEGIN(result);
908+
else if (TIMESTAMP_IS_NOEND(timestamp))
909+
DATE_NOEND(result);
910+
else
911+
{
912+
if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
913+
ereport(ERROR,
914+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
915+
errmsg("timestamp out of range")));
850916

851-
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
917+
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
918+
}
852919

853920
PG_RETURN_DATEADT(result);
854921
}
@@ -869,16 +936,19 @@ abstime_date(PG_FUNCTION_ARGS)
869936
switch (abstime)
870937
{
871938
case INVALID_ABSTIME:
872-
case NOSTART_ABSTIME:
873-
case NOEND_ABSTIME:
874939
ereport(ERROR,
875940
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
876941
errmsg("cannot convert reserved abstime value to date")));
942+
result = 0; /* keep compiler quiet */
943+
break;
877944

878-
/*
879-
* pretend to drop through to make compiler think that result will
880-
* be set
881-
*/
945+
case NOSTART_ABSTIME:
946+
DATE_NOBEGIN(result);
947+
break;
948+
949+
case NOEND_ABSTIME:
950+
DATE_NOEND(result);
951+
break;
882952

883953
default:
884954
abstime2tm(abstime, &tz, tm, NULL);
@@ -1452,9 +1522,9 @@ datetime_timestamp(PG_FUNCTION_ARGS)
14521522
TimeADT time = PG_GETARG_TIMEADT(1);
14531523
Timestamp result;
14541524

1455-
result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
1456-
DateADTGetDatum(date)));
1457-
result += time;
1525+
result = date2timestamp(date);
1526+
if (!TIMESTAMP_NOT_FINITE(result))
1527+
result += time;
14581528

14591529
PG_RETURN_TIMESTAMP(result);
14601530
}
@@ -2304,11 +2374,18 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
23042374
TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
23052375
TimestampTz result;
23062376

2377+
if (DATE_IS_NOBEGIN(date))
2378+
TIMESTAMP_NOBEGIN(result);
2379+
else if (DATE_IS_NOEND(date))
2380+
TIMESTAMP_NOEND(result);
2381+
else
2382+
{
23072383
#ifdef HAVE_INT64_TIMESTAMP
2308-
result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2384+
result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
23092385
#else
2310-
result = date * (double) SECS_PER_DAY + time->time + time->zone;
2386+
result = date * (double) SECS_PER_DAY + time->time + time->zone;
23112387
#endif
2388+
}
23122389

23132390
PG_RETURN_TIMESTAMP(result);
23142391
}

src/backend/utils/adt/xml.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.78 2008/10/09 15:49:04 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.79 2008/10/14 17:12:33 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -1632,6 +1632,11 @@ map_sql_value_to_xml_value(Datum value, Oid type)
16321632
char buf[MAXDATELEN + 1];
16331633

16341634
date = DatumGetDateADT(value);
1635+
/* XSD doesn't support infinite values */
1636+
if (DATE_NOT_FINITE(date))
1637+
ereport(ERROR,
1638+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1639+
errmsg("date out of range")));
16351640
j2date(date + POSTGRES_EPOCH_JDATE,
16361641
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
16371642
EncodeDateOnly(&tm, USE_XSD_DATES, buf);

src/include/catalog/catversion.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.497 2008/10/13 16:25:19 tgl Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.498 2008/10/14 17:12:33 tgl Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200810131
56+
#define CATALOG_VERSION_NO 200810141
5757

5858
#endif

0 commit comments

Comments
 (0)