From 6c547f79a91554960aa08ea978d4da8b0637581b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 4 Sep 2006 01:26:28 +0000 Subject: [PATCH] Fix interval input parser so that fractional weeks and months are cascaded first to days and only what is leftover into seconds. This seems to satisfy the principle of least surprise given the general conversion to three-part interval values --- it was an oversight that these cases weren't dealt with in 8.1. Michael Glaesemann --- src/backend/utils/adt/datetime.c | 42 +++++++++++++++-------- src/interfaces/ecpg/pgtypeslib/interval.c | 42 +++++++++++++++-------- src/test/regress/expected/interval.out | 12 +++++++ src/test/regress/sql/interval.sql | 2 ++ 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index ae2cfd5481..4183a94c90 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -2920,16 +2920,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, tm->tm_mday += val * 7; if (fval != 0) { - int sec; - - fval *= 7 * SECS_PER_DAY; - sec = fval; - tm->tm_sec += sec; + int extra_days; + fval *= 7; + extra_days = (int32) fval; + tm->tm_mday += extra_days; + fval -= extra_days; + if (fval != 0) + { + int sec; + fval *= SECS_PER_DAY; + sec = fval; + tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP - *fsec += (fval - sec) * 1000000; + *fsec += (fval - sec) * 1000000; #else - *fsec += fval - sec; + *fsec += fval - sec; #endif + } } tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; @@ -2938,16 +2945,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, tm->tm_mon += val; if (fval != 0) { - int sec; - - fval *= DAYS_PER_MONTH * SECS_PER_DAY; - sec = fval; - tm->tm_sec += sec; + int day; + fval *= DAYS_PER_MONTH; + day = fval; + tm->tm_mday += day; + fval -= day; + if (fval != 0) + { + int sec; + fval *= SECS_PER_DAY; + sec = fval; + tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP - *fsec += (fval - sec) * 1000000; + *fsec += (fval - sec) * 1000000; #else - *fsec += fval - sec; + *fsec += fval - sec; #endif + } } tmask = DTK_M(MONTH); break; diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c index bf058df43d..ae955bd88b 100644 --- a/src/interfaces/ecpg/pgtypeslib/interval.c +++ b/src/interfaces/ecpg/pgtypeslib/interval.c @@ -307,16 +307,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse tm->tm_mday += val * 7; if (fval != 0) { - int sec; - - fval *= 7 * SECS_PER_DAY; - sec = fval; - tm->tm_sec += sec; + int extra_days; + fval *= 7; + extra_days = (int32) fval; + tm->tm_mday += extra_days; + fval -= extra_days; + if (fval != 0) + { + int sec; + fval *= SECS_PER_DAY; + sec = fval; + tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP - *fsec += (fval - sec) * 1000000; + *fsec += (fval - sec) * 1000000; #else - *fsec += fval - sec; + *fsec += fval - sec; #endif + } } tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; @@ -325,16 +332,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse tm->tm_mon += val; if (fval != 0) { - int sec; - - fval *= DAYS_PER_MONTH * SECS_PER_DAY; - sec = fval; - tm->tm_sec += sec; + int day; + fval *= DAYS_PER_MONTH; + day = fval; + tm->tm_mday += day; + fval -= day; + if (fval != 0) + { + int sec; + fval *= SECS_PER_DAY; + sec = fval; + tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP - *fsec += (fval - sec) * 1000000; + *fsec += (fval - sec) * 1000000; #else - *fsec += fval - sec; + *fsec += fval - sec; #endif + } } tmask = DTK_M(MONTH); break; diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index 0adda4a981..3a5fb12cbc 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -39,6 +39,18 @@ SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; -1 days +02:03:00 (1 row) +SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours"; + Ten days twelve hours +----------------------- + 10 days 12:00:00 +(1 row) + +SELECT INTERVAL '1.5 months' AS "One month 15 days"; + One month 15 days +------------------- + 1 mon 15 days +(1 row) + SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years..."; 9 years... ---------------------------------- diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index bc384d0121..f38b01e45d 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -11,6 +11,8 @@ SELECT INTERVAL '-08:00' AS "Eight hours"; SELECT INTERVAL '-05' AS "Five hours"; SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; +SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours"; +SELECT INTERVAL '1.5 months' AS "One month 15 days"; SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years..."; CREATE TABLE INTERVAL_TBL (f1 interval); -- 2.39.5