Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit519fc1b

Browse files
committed
Support +/- infinity in the interval data type.
This adds support for infinity to the interval data type, using thesame input/output representation as the other date/time data typesthat support infinity. This allows various arithmetic operations oninfinite dates, timestamps and intervals.The new values are represented by setting all fields of the intervalto INT32/64_MIN for -infinity, and INT32/64_MAX for +infinity. Thisensures that they compare as less/greater than all other intervalvalues, without the need for any special-case comparison code.Note that, since those 2 values were formerly accepted as legal finiteintervals, pg_upgrade and dump/restore from an old database will turnthem from finite to infinite intervals. That seems OK, since thoseexact values should be extremely rare in practice, and they areoutside the documented range supported by the interval type, whichgives us a certain amount of leeway.Bump catalog version.Joseph Koshakow, Jian He, and Ashutosh Bapat, reviewed by me.Discussion:https://postgr.es/m/CAAvxfHea4%2BsPybKK7agDYOMo9N-Z3J6ZXf3BOM79pFsFNcRjwA%40mail.gmail.com
1 parentb41b1a7 commit519fc1b

File tree

25 files changed

+2541
-297
lines changed

25 files changed

+2541
-297
lines changed

‎contrib/btree_gin/btree_gin.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,8 @@ leftmostvalue_interval(void)
306306
{
307307
Interval*v=palloc(sizeof(Interval));
308308

309-
v->time=PG_INT64_MIN;
310-
v->day=PG_INT32_MIN;
311-
v->month=PG_INT32_MIN;
309+
INTERVAL_NOBEGIN(v);
310+
312311
returnIntervalPGetDatum(v);
313312
}
314313

‎doc/src/sgml/datatype.sgml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,12 +2328,12 @@ TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'
23282328
</row>
23292329
<row>
23302330
<entry><literal>infinity</literal></entry>
2331-
<entry><type>date</type>, <type>timestamp</type></entry>
2331+
<entry><type>date</type>, <type>timestamp</type>, <type>interval</type></entry>
23322332
<entry>later than all other time stamps</entry>
23332333
</row>
23342334
<row>
23352335
<entry><literal>-infinity</literal></entry>
2336-
<entry><type>date</type>, <type>timestamp</type></entry>
2336+
<entry><type>date</type>, <type>timestamp</type>, <type>interval</type></entry>
23372337
<entry>earlier than all other time stamps</entry>
23382338
</row>
23392339
<row>

‎doc/src/sgml/func.sgml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9565,7 +9565,7 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
95659565
<returnvalue>boolean</returnvalue>
95669566
</para>
95679567
<para>
9568-
Test for finite interval (currently always true)
9568+
Test for finite interval (not +/-infinity)
95699569
</para>
95709570
<para>
95719571
<literal>isfinite(interval '4 hours')</literal>
@@ -10462,7 +10462,11 @@ SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40');
1046210462
When the input value is +/-Infinity, <function>extract</function> returns
1046310463
+/-Infinity for monotonically-increasing fields (<literal>epoch</literal>,
1046410464
<literal>julian</literal>, <literal>year</literal>, <literal>isoyear</literal>,
10465-
<literal>decade</literal>, <literal>century</literal>, and <literal>millennium</literal>).
10465+
<literal>decade</literal>, <literal>century</literal>, and <literal>millennium</literal>
10466+
for <type>timestamp</type> inputs; <literal>epoch</literal>, <literal>hour</literal>,
10467+
<literal>day</literal>, <literal>year</literal>, <literal>decade</literal>,
10468+
<literal>century</literal>, and <literal>millennium</literal> for
10469+
<type>interval</type> inputs).
1046610470
For other fields, NULL is returned. <productname>PostgreSQL</productname>
1046710471
versions before 9.6 returned zero for all cases of infinite input.
1046810472
</para>

‎src/backend/utils/adt/date.c

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include"access/xact.h"
2525
#include"catalog/pg_type.h"
2626
#include"common/hashfn.h"
27+
#include"common/int.h"
2728
#include"libpq/pqformat.h"
2829
#include"miscadmin.h"
2930
#include"nodes/supportnodes.h"
@@ -2013,6 +2014,11 @@ interval_time(PG_FUNCTION_ARGS)
20132014
Interval*span=PG_GETARG_INTERVAL_P(0);
20142015
TimeADTresult;
20152016

2017+
if (INTERVAL_NOT_FINITE(span))
2018+
ereport(ERROR,
2019+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2020+
errmsg("cannot convert infinite interval to time")));
2021+
20162022
result=span->time %USECS_PER_DAY;
20172023
if (result<0)
20182024
result+=USECS_PER_DAY;
@@ -2049,6 +2055,11 @@ time_pl_interval(PG_FUNCTION_ARGS)
20492055
Interval*span=PG_GETARG_INTERVAL_P(1);
20502056
TimeADTresult;
20512057

2058+
if (INTERVAL_NOT_FINITE(span))
2059+
ereport(ERROR,
2060+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2061+
errmsg("cannot add infinite interval to time")));
2062+
20522063
result=time+span->time;
20532064
result-=result /USECS_PER_DAY*USECS_PER_DAY;
20542065
if (result<INT64CONST(0))
@@ -2067,6 +2078,11 @@ time_mi_interval(PG_FUNCTION_ARGS)
20672078
Interval*span=PG_GETARG_INTERVAL_P(1);
20682079
TimeADTresult;
20692080

2081+
if (INTERVAL_NOT_FINITE(span))
2082+
ereport(ERROR,
2083+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2084+
errmsg("cannot subtract infinite interval from time")));
2085+
20702086
result=time-span->time;
20712087
result-=result /USECS_PER_DAY*USECS_PER_DAY;
20722088
if (result<INT64CONST(0))
@@ -2090,7 +2106,8 @@ in_range_time_interval(PG_FUNCTION_ARGS)
20902106

20912107
/*
20922108
* Like time_pl_interval/time_mi_interval, we disregard the month and day
2093-
* fields of the offset. So our test for negative should too.
2109+
* fields of the offset. So our test for negative should too. This also
2110+
* catches -infinity, so we only need worry about +infinity below.
20942111
*/
20952112
if (offset->time<0)
20962113
ereport(ERROR,
@@ -2100,13 +2117,14 @@ in_range_time_interval(PG_FUNCTION_ARGS)
21002117
/*
21012118
* We can't use time_pl_interval/time_mi_interval here, because their
21022119
* wraparound behavior would give wrong (or at least undesirable) answers.
2103-
* Fortunately the equivalent non-wrapping behavior is trivial, especially
2104-
* since we don't worry about integer overflow.
2120+
* Fortunately the equivalent non-wrapping behavior is trivial, except
2121+
* that adding an infinite (or very large) interval might cause integer
2122+
* overflow. Subtraction cannot overflow here.
21052123
*/
21062124
if (sub)
21072125
sum=base-offset->time;
2108-
else
2109-
sum=base+offset->time;
2126+
elseif (pg_add_s64_overflow(base,offset->time,&sum))
2127+
PG_RETURN_BOOL(less);
21102128

21112129
if (less)
21122130
PG_RETURN_BOOL(val <=sum);
@@ -2581,6 +2599,11 @@ timetz_pl_interval(PG_FUNCTION_ARGS)
25812599
Interval*span=PG_GETARG_INTERVAL_P(1);
25822600
TimeTzADT*result;
25832601

2602+
if (INTERVAL_NOT_FINITE(span))
2603+
ereport(ERROR,
2604+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2605+
errmsg("cannot add infinite interval to time")));
2606+
25842607
result= (TimeTzADT*)palloc(sizeof(TimeTzADT));
25852608

25862609
result->time=time->time+span->time;
@@ -2603,6 +2626,11 @@ timetz_mi_interval(PG_FUNCTION_ARGS)
26032626
Interval*span=PG_GETARG_INTERVAL_P(1);
26042627
TimeTzADT*result;
26052628

2629+
if (INTERVAL_NOT_FINITE(span))
2630+
ereport(ERROR,
2631+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2632+
errmsg("cannot subtract infinite interval from time")));
2633+
26062634
result= (TimeTzADT*)palloc(sizeof(TimeTzADT));
26072635

26082636
result->time=time->time-span->time;
@@ -2630,7 +2658,8 @@ in_range_timetz_interval(PG_FUNCTION_ARGS)
26302658

26312659
/*
26322660
* Like timetz_pl_interval/timetz_mi_interval, we disregard the month and
2633-
* day fields of the offset. So our test for negative should too.
2661+
* day fields of the offset. So our test for negative should too. This
2662+
* also catches -infinity, so we only need worry about +infinity below.
26342663
*/
26352664
if (offset->time<0)
26362665
ereport(ERROR,
@@ -2640,13 +2669,14 @@ in_range_timetz_interval(PG_FUNCTION_ARGS)
26402669
/*
26412670
* We can't use timetz_pl_interval/timetz_mi_interval here, because their
26422671
* wraparound behavior would give wrong (or at least undesirable) answers.
2643-
* Fortunately the equivalent non-wrapping behavior is trivial, especially
2644-
* since we don't worry about integer overflow.
2672+
* Fortunately the equivalent non-wrapping behavior is trivial, except
2673+
* that adding an infinite (or very large) interval might cause integer
2674+
* overflow. Subtraction cannot overflow here.
26452675
*/
26462676
if (sub)
26472677
sum.time=base->time-offset->time;
2648-
else
2649-
sum.time=base->time+offset->time;
2678+
elseif (pg_add_s64_overflow(base->time,offset->time,&sum.time))
2679+
PG_RETURN_BOOL(less);
26502680
sum.zone=base->zone;
26512681

26522682
if (less)
@@ -3096,6 +3126,13 @@ timetz_izone(PG_FUNCTION_ARGS)
30963126
TimeTzADT*result;
30973127
inttz;
30983128

3129+
if (INTERVAL_NOT_FINITE(zone))
3130+
ereport(ERROR,
3131+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3132+
errmsg("interval time zone \"%s\" must be finite",
3133+
DatumGetCString(DirectFunctionCall1(interval_out,
3134+
PointerGetDatum(zone))))));
3135+
30993136
if (zone->month!=0||zone->day!=0)
31003137
ereport(ERROR,
31013138
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),

‎src/backend/utils/adt/datetime.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,6 +3271,9 @@ ClearPgItmIn(struct pg_itm_in *itm_in)
32713271
*
32723272
* Allow ISO-style time span, with implicit units on number of days
32733273
*preceding an hh:mm:ss field. - thomas 1998-04-30
3274+
*
3275+
* itm_in remains undefined for infinite interval values for which dtype alone
3276+
* suffices.
32743277
*/
32753278
int
32763279
DecodeInterval(char**field,int*ftype,intnf,intrange,
@@ -3574,6 +3577,8 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
35743577
if (parsing_unit_val)
35753578
returnDTERR_BAD_FORMAT;
35763579
type=DecodeUnits(i,field[i],&uval);
3580+
if (type==UNKNOWN_FIELD)
3581+
type=DecodeSpecial(i,field[i],&uval);
35773582
if (type==IGNORE_DTF)
35783583
continue;
35793584

@@ -3597,6 +3602,27 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
35973602
type=uval;
35983603
break;
35993604

3605+
caseRESERV:
3606+
tmask= (DTK_DATE_M |DTK_TIME_M);
3607+
3608+
/*
3609+
* Only reserved words corresponding to infinite
3610+
* intervals are accepted.
3611+
*/
3612+
if (uval!=DTK_LATE&&uval!=DTK_EARLY)
3613+
returnDTERR_BAD_FORMAT;
3614+
3615+
/*
3616+
* Infinity cannot be followed by anything else. We
3617+
* could allow "ago" to reverse the sign of infinity
3618+
* but using signed infinity is more intuitive.
3619+
*/
3620+
if (i!=nf-1)
3621+
returnDTERR_BAD_FORMAT;
3622+
3623+
*dtype=uval;
3624+
break;
3625+
36003626
default:
36013627
returnDTERR_BAD_FORMAT;
36023628
}

‎src/backend/utils/adt/formatting.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4127,7 +4127,7 @@ interval_to_char(PG_FUNCTION_ARGS)
41274127
structpg_itmtt,
41284128
*itm=&tt;
41294129

4130-
if (VARSIZE_ANY_EXHDR(fmt) <=0)
4130+
if (VARSIZE_ANY_EXHDR(fmt) <=0||INTERVAL_NOT_FINITE(it))
41314131
PG_RETURN_NULL();
41324132

41334133
ZERO_tmtc(&tmtc);

‎src/backend/utils/adt/selfuncs.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4802,6 +4802,10 @@ convert_timevalue_to_scalar(Datum value, Oid typid, bool *failure)
48024802
* Convert the month part of Interval to days using assumed
48034803
* average month length of 365.25/12.0 days. Not too
48044804
* accurate, but plenty good enough for our purposes.
4805+
*
4806+
* This also works for infinite intervals, which just have all
4807+
* fields set to INT_MIN/INT_MAX, and so will produce a result
4808+
* smaller/larger than any finite interval.
48054809
*/
48064810
returninterval->time+interval->day* (double)USECS_PER_DAY+
48074811
interval->month* ((DAYS_PER_YEAR / (double)MONTHS_PER_YEAR)*USECS_PER_DAY);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp