@@ -160,13 +160,20 @@ date_in(PG_FUNCTION_ARGS)
160
160
break ;
161
161
}
162
162
163
+ /* Prevent overflow in Julian-day routines */
163
164
if (!IS_VALID_JULIAN (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ))
164
165
ereport (ERROR ,
165
166
(errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
166
167
errmsg ("date out of range: \"%s\"" , str )));
167
168
168
169
date = date2j (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ) - POSTGRES_EPOCH_JDATE ;
169
170
171
+ /* Now check for just-out-of-range dates */
172
+ if (!IS_VALID_DATE (date ))
173
+ ereport (ERROR ,
174
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
175
+ errmsg ("date out of range: \"%s\"" , str )));
176
+
170
177
PG_RETURN_DATEADT (date );
171
178
}
172
179
@@ -209,8 +216,7 @@ date_recv(PG_FUNCTION_ARGS)
209
216
/* Limit to the same range that date_in() accepts. */
210
217
if (DATE_NOT_FINITE (result ))
211
218
/* ok */ ;
212
- else if (result < - POSTGRES_EPOCH_JDATE ||
213
- result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE )
219
+ else if (!IS_VALID_DATE (result ))
214
220
ereport (ERROR ,
215
221
(errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
216
222
errmsg ("date out of range" )));
@@ -258,6 +264,7 @@ make_date(PG_FUNCTION_ARGS)
258
264
errmsg ("date field value out of range: %d-%02d-%02d" ,
259
265
tm .tm_year , tm .tm_mon , tm .tm_mday )));
260
266
267
+ /* Prevent overflow in Julian-day routines */
261
268
if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
262
269
ereport (ERROR ,
263
270
(errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
@@ -266,6 +273,13 @@ make_date(PG_FUNCTION_ARGS)
266
273
267
274
date = date2j (tm .tm_year , tm .tm_mon , tm .tm_mday ) - POSTGRES_EPOCH_JDATE ;
268
275
276
+ /* Now check for just-out-of-range dates */
277
+ if (!IS_VALID_DATE (date ))
278
+ ereport (ERROR ,
279
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
280
+ errmsg ("date out of range: %d-%02d-%02d" ,
281
+ tm .tm_year , tm .tm_mon , tm .tm_mday )));
282
+
269
283
PG_RETURN_DATEADT (date );
270
284
}
271
285
@@ -427,11 +441,21 @@ date_pli(PG_FUNCTION_ARGS)
427
441
{
428
442
DateADT dateVal = PG_GETARG_DATEADT (0 );
429
443
int32 days = PG_GETARG_INT32 (1 );
444
+ DateADT result ;
430
445
431
446
if (DATE_NOT_FINITE (dateVal ))
432
- days = 0 ; /* can't change infinity */
447
+ PG_RETURN_DATEADT (dateVal ); /* can't change infinity */
448
+
449
+ result = dateVal + days ;
450
+
451
+ /* Check for integer overflow and out-of-allowed-range */
452
+ if ((days >= 0 ? (result < dateVal ) : (result > dateVal )) ||
453
+ !IS_VALID_DATE (result ))
454
+ ereport (ERROR ,
455
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
456
+ errmsg ("date out of range" )));
433
457
434
- PG_RETURN_DATEADT (dateVal + days );
458
+ PG_RETURN_DATEADT (result );
435
459
}
436
460
437
461
/* Subtract a number of days from a date, giving a new date.
@@ -441,11 +465,21 @@ date_mii(PG_FUNCTION_ARGS)
441
465
{
442
466
DateADT dateVal = PG_GETARG_DATEADT (0 );
443
467
int32 days = PG_GETARG_INT32 (1 );
468
+ DateADT result ;
444
469
445
470
if (DATE_NOT_FINITE (dateVal ))
446
- days = 0 ; /* can't change infinity */
471
+ PG_RETURN_DATEADT (dateVal ); /* can't change infinity */
472
+
473
+ result = dateVal - days ;
474
+
475
+ /* Check for integer overflow and out-of-allowed-range */
476
+ if ((days >= 0 ? (result > dateVal ) : (result < dateVal )) ||
477
+ !IS_VALID_DATE (result ))
478
+ ereport (ERROR ,
479
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
480
+ errmsg ("date out of range" )));
447
481
448
- PG_RETURN_DATEADT (dateVal - days );
482
+ PG_RETURN_DATEADT (result );
449
483
}
450
484
451
485
/*
@@ -464,14 +498,18 @@ date2timestamp(DateADT dateVal)
464
498
TIMESTAMP_NOEND (result );
465
499
else
466
500
{
467
- #ifdef HAVE_INT64_TIMESTAMP
468
- /* date is days since 2000, timestamp is microseconds since same... */
469
- result = dateVal * USECS_PER_DAY ;
470
- /* Date's range is wider than timestamp's, so check for overflow */
471
- if (result / USECS_PER_DAY != dateVal )
501
+ /*
502
+ * Date's range is wider than timestamp's, so check for boundaries.
503
+ * Since dates have the same minimum values as timestamps, only upper
504
+ * boundary need be checked for overflow.
505
+ */
506
+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
472
507
ereport (ERROR ,
473
508
(errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
474
509
errmsg ("date out of range for timestamp" )));
510
+ #ifdef HAVE_INT64_TIMESTAMP
511
+ /* date is days since 2000, timestamp is microseconds since same... */
512
+ result = dateVal * USECS_PER_DAY ;
475
513
#else
476
514
/* date is days since 2000, timestamp is seconds since same... */
477
515
result = dateVal * (double ) SECS_PER_DAY ;
@@ -495,6 +533,16 @@ date2timestamptz(DateADT dateVal)
495
533
TIMESTAMP_NOEND (result );
496
534
else
497
535
{
536
+ /*
537
+ * Date's range is wider than timestamp's, so check for boundaries.
538
+ * Since dates have the same minimum values as timestamps, only upper
539
+ * boundary need be checked for overflow.
540
+ */
541
+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
542
+ ereport (ERROR ,
543
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
544
+ errmsg ("date out of range for timestamp" )));
545
+
498
546
j2date (dateVal + POSTGRES_EPOCH_JDATE ,
499
547
& (tm -> tm_year ), & (tm -> tm_mon ), & (tm -> tm_mday ));
500
548
tm -> tm_hour = 0 ;
@@ -504,14 +552,18 @@ date2timestamptz(DateADT dateVal)
504
552
505
553
#ifdef HAVE_INT64_TIMESTAMP
506
554
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC ;
507
- /* Date's range is wider than timestamp's, so check for overflow */
508
- if ((result - tz * USECS_PER_SEC ) / USECS_PER_DAY != dateVal )
509
- ereport (ERROR ,
510
- (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
511
- errmsg ("date out of range for timestamp" )));
512
555
#else
513
556
result = dateVal * (double ) SECS_PER_DAY + tz ;
514
557
#endif
558
+
559
+ /*
560
+ * Since it is possible to go beyond allowed timestamptz range because
561
+ * of time zone, check for allowed timestamp range after adding tz.
562
+ */
563
+ if (!IS_VALID_TIMESTAMP (result ))
564
+ ereport (ERROR ,
565
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
566
+ errmsg ("date out of range for timestamp" )));
515
567
}
516
568
517
569
return result ;
@@ -1053,7 +1105,17 @@ abstime_date(PG_FUNCTION_ARGS)
1053
1105
1054
1106
default :
1055
1107
abstime2tm (abstime , & tz , tm , NULL );
1108
+ /* Prevent overflow in Julian-day routines */
1109
+ if (!IS_VALID_JULIAN (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ))
1110
+ ereport (ERROR ,
1111
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1112
+ errmsg ("abstime out of range for date" )));
1056
1113
result = date2j (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ) - POSTGRES_EPOCH_JDATE ;
1114
+ /* Now check for just-out-of-range dates */
1115
+ if (!IS_VALID_DATE (result ))
1116
+ ereport (ERROR ,
1117
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1118
+ errmsg ("abstime out of range for date" )));
1057
1119
break ;
1058
1120
}
1059
1121
@@ -1678,7 +1740,13 @@ datetime_timestamp(PG_FUNCTION_ARGS)
1678
1740
1679
1741
result = date2timestamp (date );
1680
1742
if (!TIMESTAMP_NOT_FINITE (result ))
1743
+ {
1681
1744
result += time ;
1745
+ if (!IS_VALID_TIMESTAMP (result ))
1746
+ ereport (ERROR ,
1747
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1748
+ errmsg ("timestamp out of range" )));
1749
+ }
1682
1750
1683
1751
PG_RETURN_TIMESTAMP (result );
1684
1752
}
@@ -2550,11 +2618,29 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
2550
2618
TIMESTAMP_NOEND (result );
2551
2619
else
2552
2620
{
2621
+ /*
2622
+ * Date's range is wider than timestamp's, so check for boundaries.
2623
+ * Since dates have the same minimum values as timestamps, only upper
2624
+ * boundary need be checked for overflow.
2625
+ */
2626
+ if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
2627
+ ereport (ERROR ,
2628
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
2629
+ errmsg ("date out of range for timestamp" )));
2553
2630
#ifdef HAVE_INT64_TIMESTAMP
2554
2631
result = date * USECS_PER_DAY + time -> time + time -> zone * USECS_PER_SEC ;
2555
2632
#else
2556
2633
result = date * (double ) SECS_PER_DAY + time -> time + time -> zone ;
2557
2634
#endif
2635
+
2636
+ /*
2637
+ * Since it is possible to go beyond allowed timestamptz range because
2638
+ * of time zone, check for allowed timestamp range after adding tz.
2639
+ */
2640
+ if (!IS_VALID_TIMESTAMP (result ))
2641
+ ereport (ERROR ,
2642
+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
2643
+ errmsg ("date out of range for timestamp" )));
2558
2644
}
2559
2645
2560
2646
PG_RETURN_TIMESTAMP (result );
0 commit comments