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

Commitf0d0394

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 parentf6db76c commitf0d0394

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
@@ -3702,46 +3702,38 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
37023702
staticint
37033703
ParseISO8601Number(char*str,char**endptr,int64*ipart,double*fpart)
37043704
{
3705-
intsign=1;
3705+
doubleval;
37063706

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

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

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

978+
select interval 'P10.5e4Y'; -- not per spec, but we've historically taken it
979+
interval
980+
--------------
981+
105000 years
982+
(1 row)
983+
978984
select interval 'P.Y0M3DT4H5M6S'; -- error
979985
ERROR: invalid input syntax for type interval: "P.Y0M3DT4H5M6S"
980986
LINE 1: select interval 'P.Y0M3DT4H5M6S';
@@ -1081,13 +1087,13 @@ select interval 'PT2562047788:00:54.775807';
10811087
select interval 'PT2562047788.0152155019444';
10821088
interval
10831089
-----------------------------------
1084-
@ 2562047788 hours 54.775807 secs
1090+
@ 2562047788 hours 54.775429 secs
10851091
(1 row)
10861092

10871093
select interval 'PT-2562047788.0152155022222';
10881094
interval
10891095
---------------------------------------
1090-
@ 2562047788 hours 54.775808 secs ago
1096+
@ 2562047788 hours 54.775429 secs ago
10911097
(1 row)
10921098

10931099
-- 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
@@ -328,6 +328,7 @@ select interval 'P1.0Y0M3DT4H5M6S';
328328
select interval'P1.1Y0M3DT4H5M6S';
329329
select interval'P1.Y0M3DT4H5M6S';
330330
select interval'P.1Y0M3DT4H5M6S';
331+
select interval'P10.5e4Y';-- not per spec, but we've historically taken it
331332
select interval'P.Y0M3DT4H5M6S';-- error
332333

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

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp