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

Commitded5ede

Browse files
committed
Fix parsing of ISO-8601 interval fields with exponential notation.
Historically we've accepted interval input like 'P.1e10D'. Thisis probably an accident of having used strtod() to do the parsing,rather than something anyone intended, but it's been that way fora long time. Commite39f990 broke this by trying to parse theinteger and fractional parts separately, without accounting forthe possibility of an exponent. In principle that coding allowedfor precise conversions of field values wider than 15 decimaldigits, but that does not seem like a goal worth sweating bulletsfor. So, rather than trying to manage an exponent on top of theexisting complexity, let's just revert to the previous coding thatused strtod() by itself. We can still improve on the old code tothe extent of allowing the value to range up to 1.0e15 rather thanonly INT_MAX. (Allowing more than that risks creating problemsdue to precision loss: the converted fractional part might haveabsolute value more than 1. Perhaps that could be dealt with insome way, but it really does not seem worth additional effort.)Per bug #17795 from Alexander Lakhin. Back-patch to v15 wherethe faulty code came in.Discussion:https://postgr.es/m/17795-748d6db3ed95d313@postgresql.org
1 parente6d8639 commitded5ede

File tree

3 files changed

+37
-38
lines changed

3 files changed

+37
-38
lines changed

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

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3691,46 +3691,38 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
36913691
staticint
36923692
ParseISO8601Number(char*str,char**endptr,int64*ipart,double*fpart)
36933693
{
3694-
intsign=1;
3694+
doubleval;
36953695

3696-
*ipart=0;
3697-
*fpart=0.0;
3698-
3699-
/* Parse sign if there is any */
3700-
if (*str=='-')
3701-
{
3702-
sign=-1;
3703-
str++;
3704-
}
3705-
3706-
*endptr=str;
3696+
/*
3697+
* Historically this has accepted anything that strtod() would take,
3698+
* notably including "e" notation, so continue doing that. This is
3699+
* slightly annoying because the precision of double is less than that of
3700+
* int64, so we would lose accuracy for inputs larger than 2^53 or so.
3701+
* However, historically we rejected inputs outside the int32 range,
3702+
* making that concern moot. What we do now is reject abs(val) above
3703+
* 1.0e15 (a round number a bit less than 2^50), so that any accepted
3704+
* value will have an exact integer part, and thereby a fraction part with
3705+
* abs(*fpart) less than 1. In the absence of field complaints it doesn't
3706+
* seem worth working harder.
3707+
*/
3708+
if (!(isdigit((unsignedchar)*str)||*str=='-'||*str=='.'))
3709+
returnDTERR_BAD_FORMAT;
37073710
errno=0;
3708-
3709-
/* Parse int64 part if there is any */
3710-
if (isdigit((unsignedchar)**endptr))
3711-
*ipart=strtoi64(*endptr,endptr,10)*sign;
3712-
3713-
/* Parse fractional part if there is any */
3714-
if (**endptr=='.')
3715-
{
3716-
/*
3717-
* As in ParseFraction, some versions of strtod insist on seeing some
3718-
* digits after '.', but some don't. We want to allow zero digits
3719-
* after '.' as long as there were some before it.
3720-
*/
3721-
if (isdigit((unsignedchar)*(*endptr+1)))
3722-
*fpart=strtod(*endptr,endptr)*sign;
3723-
else
3724-
{
3725-
(*endptr)++;/* advance over '.' */
3726-
str++;/* so next test will fail if no digits */
3727-
}
3728-
}
3729-
3730-
/* did we not see anything that looks like a number? */
3711+
val=strtod(str,endptr);
3712+
/* did we not see anything that looks like a double? */
37313713
if (*endptr==str||errno!=0)
37323714
returnDTERR_BAD_FORMAT;
3733-
3715+
/* watch out for overflow, including infinities; reject NaN too */
3716+
if (isnan(val)||val<-1.0e15||val>1.0e15)
3717+
returnDTERR_FIELD_OVERFLOW;
3718+
/* be very sure we truncate towards zero (cf dtrunc()) */
3719+
if (val >=0)
3720+
*ipart= (int64)floor(val);
3721+
else
3722+
*ipart= (int64)-floor(-val);
3723+
*fpart=val-*ipart;
3724+
/* Callers expect this to hold */
3725+
Assert(*fpart>-1.0&&*fpart<1.0);
37343726
return0;
37353727
}
37363728

‎src/test/regress/expected/interval.out

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,12 @@ select interval 'P.1Y0M3DT4H5M6S';
944944
1 mon 3 days 04:05:06
945945
(1 row)
946946

947+
select interval 'P10.5e4Y'; -- not per spec, but we've historically taken it
948+
interval
949+
--------------
950+
105000 years
951+
(1 row)
952+
947953
select interval 'P.Y0M3DT4H5M6S'; -- error
948954
ERROR: invalid input syntax for type interval: "P.Y0M3DT4H5M6S"
949955
LINE 1: select interval 'P.Y0M3DT4H5M6S';
@@ -1050,13 +1056,13 @@ select interval 'PT2562047788:00:54.775807';
10501056
select interval 'PT2562047788.0152155019444';
10511057
interval
10521058
-----------------------------------
1053-
@ 2562047788 hours 54.775807 secs
1059+
@ 2562047788 hours 54.775429 secs
10541060
(1 row)
10551061

10561062
select interval 'PT-2562047788.0152155022222';
10571063
interval
10581064
---------------------------------------
1059-
@ 2562047788 hours 54.775808 secs ago
1065+
@ 2562047788 hours 54.775429 secs ago
10601066
(1 row)
10611067

10621068
-- overflow each date/time field

‎src/test/regress/sql/interval.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ select interval 'P1.0Y0M3DT4H5M6S';
321321
select interval'P1.1Y0M3DT4H5M6S';
322322
select interval'P1.Y0M3DT4H5M6S';
323323
select interval'P.1Y0M3DT4H5M6S';
324+
select interval'P10.5e4Y';-- not per spec, but we've historically taken it
324325
select interval'P.Y0M3DT4H5M6S';-- error
325326

326327
-- test a couple rounding cases that changed since 8.3 w/ HAVE_INT64_TIMESTAMP.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp