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

Commitf0774ab

Browse files
committed
Fix interval_transform so it doesn't throw away non-no-op casts.
interval_transform() contained two separate bugs that caused it tosometimes mistakenly decide that a cast from interval to restrictedinterval is a no-op and throw it away.First, it was wrong to rely on dt.h's field type macros to have anordering consistent with the field's significance; in one case they donot. This led to mistakenly treating YEAR as less significant than MONTH,so that a cast from INTERVAL MONTH to INTERVAL YEAR was incorrectlydiscarded.Second, fls(1<<k) produces k+1 not k, so comparing its output directlyto SECOND was wrong. This led to supposing that a cast to INTERVALMINUTE was really a cast to INTERVAL SECOND and so could be discarded.To fix, get rid of the use of fls(), and make a function based onintervaltypmodout to produce a field ID code adapted to the need here.Per bug #14479 from Piotr Stefaniak. Back-patch to 9.2 where transformfunctions were introduced, because this code was born broken.Discussion:https://postgr.es/m/20161227172307.10135.7747@wrigleys.postgresql.org
1 parent71f996d commitf0774ab

File tree

3 files changed

+105
-29
lines changed

3 files changed

+105
-29
lines changed

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

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,59 @@ intervaltypmodout(PG_FUNCTION_ARGS)
12501250
PG_RETURN_CSTRING(res);
12511251
}
12521252

1253+
/*
1254+
* Given an interval typmod value, return a code for the least-significant
1255+
* field that the typmod allows to be nonzero, for instance given
1256+
* INTERVAL DAY TO HOUR we want to identify "hour".
1257+
*
1258+
* The results should be ordered by field significance, which means
1259+
* we can't use the dt.h macros YEAR etc, because for some odd reason
1260+
* they aren't ordered that way. Instead, arbitrarily represent
1261+
* SECOND = 0, MINUTE = 1, HOUR = 2, DAY = 3, MONTH = 4, YEAR = 5.
1262+
*/
1263+
staticint
1264+
intervaltypmodleastfield(int32typmod)
1265+
{
1266+
if (typmod<0)
1267+
return0;/* SECOND */
1268+
1269+
switch (INTERVAL_RANGE(typmod))
1270+
{
1271+
caseINTERVAL_MASK(YEAR):
1272+
return5;/* YEAR */
1273+
caseINTERVAL_MASK(MONTH):
1274+
return4;/* MONTH */
1275+
caseINTERVAL_MASK(DAY):
1276+
return3;/* DAY */
1277+
caseINTERVAL_MASK(HOUR):
1278+
return2;/* HOUR */
1279+
caseINTERVAL_MASK(MINUTE):
1280+
return1;/* MINUTE */
1281+
caseINTERVAL_MASK(SECOND):
1282+
return0;/* SECOND */
1283+
caseINTERVAL_MASK(YEAR) |INTERVAL_MASK(MONTH):
1284+
return4;/* MONTH */
1285+
caseINTERVAL_MASK(DAY) |INTERVAL_MASK(HOUR):
1286+
return2;/* HOUR */
1287+
caseINTERVAL_MASK(DAY) |INTERVAL_MASK(HOUR) |INTERVAL_MASK(MINUTE):
1288+
return1;/* MINUTE */
1289+
caseINTERVAL_MASK(DAY) |INTERVAL_MASK(HOUR) |INTERVAL_MASK(MINUTE) |INTERVAL_MASK(SECOND):
1290+
return0;/* SECOND */
1291+
caseINTERVAL_MASK(HOUR) |INTERVAL_MASK(MINUTE):
1292+
return1;/* MINUTE */
1293+
caseINTERVAL_MASK(HOUR) |INTERVAL_MASK(MINUTE) |INTERVAL_MASK(SECOND):
1294+
return0;/* SECOND */
1295+
caseINTERVAL_MASK(MINUTE) |INTERVAL_MASK(SECOND):
1296+
return0;/* SECOND */
1297+
caseINTERVAL_FULL_RANGE:
1298+
return0;/* SECOND */
1299+
default:
1300+
elog(ERROR,"invalid INTERVAL typmod: 0x%x",typmod);
1301+
break;
1302+
}
1303+
return0;/* can't get here, but keep compiler quiet */
1304+
}
1305+
12531306

12541307
/* interval_transform()
12551308
* Flatten superfluous calls to interval_scale(). The interval typmod is
@@ -1271,39 +1324,39 @@ interval_transform(PG_FUNCTION_ARGS)
12711324
if (IsA(typmod,Const)&&!((Const*)typmod)->constisnull)
12721325
{
12731326
Node*source= (Node*)linitial(expr->args);
1274-
int32old_typmod=exprTypmod(source);
12751327
int32new_typmod=DatumGetInt32(((Const*)typmod)->constvalue);
1276-
intold_range;
1277-
intold_precis;
1278-
intnew_range=INTERVAL_RANGE(new_typmod);
1279-
intnew_precis=INTERVAL_PRECISION(new_typmod);
1280-
intnew_range_fls;
1281-
intold_range_fls;
1282-
1283-
if (old_typmod<0)
1284-
{
1285-
old_range=INTERVAL_FULL_RANGE;
1286-
old_precis=INTERVAL_FULL_PRECISION;
1287-
}
1328+
boolnoop;
1329+
1330+
if (new_typmod<0)
1331+
noop= true;
12881332
else
12891333
{
1290-
old_range=INTERVAL_RANGE(old_typmod);
1291-
old_precis=INTERVAL_PRECISION(old_typmod);
1292-
}
1334+
int32old_typmod=exprTypmod(source);
1335+
intold_least_field;
1336+
intnew_least_field;
1337+
intold_precis;
1338+
intnew_precis;
1339+
1340+
old_least_field=intervaltypmodleastfield(old_typmod);
1341+
new_least_field=intervaltypmodleastfield(new_typmod);
1342+
if (old_typmod<0)
1343+
old_precis=INTERVAL_FULL_PRECISION;
1344+
else
1345+
old_precis=INTERVAL_PRECISION(old_typmod);
1346+
new_precis=INTERVAL_PRECISION(new_typmod);
12931347

1294-
/*
1295-
* Temporally-smaller fields occupy higher positions in the range
1296-
* bitmap. Since only the temporally-smallest bit matters for length
1297-
* coercion purposes, we compare the last-set bits in the ranges.
1298-
* Precision, which is to say, sub-second precision, only affects
1299-
* ranges that include SECOND.
1300-
*/
1301-
new_range_fls=fls(new_range);
1302-
old_range_fls=fls(old_range);
1303-
if (new_typmod<0||
1304-
((new_range_fls >=SECOND||new_range_fls >=old_range_fls)&&
1305-
(old_range_fls<SECOND||new_precis >=MAX_INTERVAL_PRECISION||
1306-
new_precis >=old_precis)))
1348+
/*
1349+
* Cast is a no-op if least field stays the same or decreases
1350+
* while precision stays the same or increases. But precision,
1351+
* which is to say, sub-second precision, only affects ranges that
1352+
* include SECOND.
1353+
*/
1354+
noop= (new_least_field <=old_least_field)&&
1355+
(old_least_field>0/* SECOND */||
1356+
new_precis >=MAX_INTERVAL_PRECISION||
1357+
new_precis >=old_precis);
1358+
}
1359+
if (noop)
13071360
ret=relabel_to_typmod(source,new_typmod);
13081361
}
13091362

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,24 @@ SELECT interval '1 2:03:04.5678' minute to second(2);
682682
1 day 02:03:04.57
683683
(1 row)
684684

685+
-- test casting to restricted precision (bug #14479)
686+
SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes",
687+
(f1 + INTERVAL '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years"
688+
FROM interval_tbl;
689+
f1 | minutes | years
690+
-----------------+-----------------+----------
691+
00:01:00 | 00:01:00 | 00:00:00
692+
05:00:00 | 05:00:00 | 00:00:00
693+
10 days | 10 days | 00:00:00
694+
34 years | 34 years | 34 years
695+
3 mons | 3 mons | 00:00:00
696+
-00:00:14 | 00:00:00 | 00:00:00
697+
1 day 02:03:04 | 1 day 02:03:00 | 00:00:00
698+
6 years | 6 years | 6 years
699+
5 mons | 5 mons | 00:00:00
700+
5 mons 12:00:00 | 5 mons 12:00:00 | 00:00:00
701+
(10 rows)
702+
685703
-- test inputting and outputting SQL standard interval literals
686704
SET IntervalStyle TO sql_standard;
687705
SELECT interval '0' AS "zero",

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ SELECT interval '1 2.3456' minute to second(2);
196196
SELECT interval'1 2:03.5678' minute to second(2);
197197
SELECT interval'1 2:03:04.5678' minute to second(2);
198198

199+
-- test casting to restricted precision (bug #14479)
200+
SELECT f1, f1::INTERVAL DAY TO MINUTEAS"minutes",
201+
(f1+ INTERVAL'1 month')::INTERVAL MONTH::INTERVAL YEARAS"years"
202+
FROM interval_tbl;
203+
199204
-- test inputting and outputting SQL standard interval literals
200205
SET IntervalStyle TO sql_standard;
201206
SELECT interval'0'AS"zero",

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp