From 5e91331955452b03cab76aa52e905d311eaf155e Mon Sep 17 00:00:00 2001 From: Isaac Morland Date: Wed, 31 Mar 2021 03:06:06 +0000 Subject: [PATCH] Add abs(interval) function and related @ operator. --- doc/src/sgml/func.sgml | 32 ++++++++++++++++++++++++++ src/backend/utils/adt/timestamp.c | 31 +++++++++++++++++++++++-- src/include/catalog/pg_operator.dat | 3 +++ src/include/catalog/pg_proc.dat | 6 +++++ src/test/regress/expected/interval.out | 25 ++++++++++++++++++++ src/test/regress/sql/interval.sql | 7 ++++++ 6 files changed, 102 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index fbf6062d0a..62bd8b8375 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -8474,6 +8474,20 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}'); + + + @ interval + interval + + + Find the absolute value of an interval + + + @ interval '-23 hours' + 23:00:00 + + + date - date @@ -8644,6 +8658,24 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}'); + + + + abs + + abs ( interval ) + interval + + + Compute the absolute value of an interval; that is, the greater of the input + and its negative. + + + abs(interval '-23 hours') + 23:00:00 + + + diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index f619b56d6f..1b336195b9 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -3041,10 +3041,21 @@ timestamptz_mi_interval(PG_FUNCTION_ARGS) } +Interval * +interval_um_internal(Interval *interval); + Datum interval_um(PG_FUNCTION_ARGS) { Interval *interval = PG_GETARG_INTERVAL_P(0); + Interval *result = interval_um_internal(interval); + + PG_RETURN_INTERVAL_P(result); +} + +Interval * +interval_um_internal(Interval *interval) +{ Interval *result; result = (Interval *) palloc(sizeof(Interval)); @@ -3066,7 +3077,7 @@ interval_um(PG_FUNCTION_ARGS) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); - PG_RETURN_INTERVAL_P(result); + return result; } @@ -3168,10 +3179,26 @@ interval_mi(PG_FUNCTION_ARGS) } /* - * There is no interval_abs(): it is unclear what value to return: + * Although some strangeness is possible with intervals: * http://archives.postgresql.org/pgsql-general/2009-10/msg01031.php * http://archives.postgresql.org/pgsql-general/2009-11/msg00041.php + * ... there is nevertheless nothing clearly better than the usual + * definition of abs as the greater of the input and its negative. */ +Datum +interval_abs(PG_FUNCTION_ARGS) +{ + Interval *interval = PG_GETARG_INTERVAL_P(0); + Interval *minterval = interval_um_internal(interval); + Interval *result; + + if (interval_cmp_internal(interval, minterval) >= 0) + result = interval; + else + result = minterval; + + PG_RETURN_INTERVAL_P(result); +} Datum interval_mul(PG_FUNCTION_ARGS) diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat index 85395a81ee..4eb28bad6e 100644 --- a/src/include/catalog/pg_operator.dat +++ b/src/include/catalog/pg_operator.dat @@ -1588,6 +1588,9 @@ { oid => '1336', descr => 'negate', oprname => '-', oprkind => 'l', oprleft => '0', oprright => 'interval', oprresult => 'interval', oprcode => 'interval_um' }, +{ oid => '9000', descr => 'absolute value', + oprname => '@', oprkind => 'l', oprleft => '0', oprright => 'interval', + oprresult => 'interval', oprcode => 'interval_abs' }, { oid => '1337', descr => 'add', oprname => '+', oprleft => 'interval', oprright => 'interval', oprresult => 'interval', oprcom => '+(interval,interval)', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bfb89e0575..92718e32a5 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -2330,6 +2330,12 @@ { oid => '1168', proname => 'interval_um', prorettype => 'interval', proargtypes => 'interval', prosrc => 'interval_um' }, +{ oid => '9001', + proname => 'interval_abs', prorettype => 'interval', proargtypes => 'interval', + prosrc => 'interval_abs' }, +{ oid => '9002', descr => 'absolute value', + proname => 'abs', prorettype => 'interval', proargtypes => 'interval', + prosrc => 'interval_abs' }, { oid => '1169', proname => 'interval_pl', prorettype => 'interval', proargtypes => 'interval interval', prosrc => 'interval_pl' }, diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index acc353a0dc..1aaeb2d3f8 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -207,6 +207,31 @@ SELECT r1.*, r2.* 34 years | 6 years (45 rows) +SELECT abs (f1), abs (-f1), @ f1, @ -f1 FROM interval_tbl; + abs | abs | ?column? | ?column? +-----------------+-----------------+-----------------+----------------- + 00:01:00 | 00:01:00 | 00:01:00 | 00:01:00 + 05:00:00 | 05:00:00 | 05:00:00 | 05:00:00 + 10 days | 10 days | 10 days | 10 days + 34 years | 34 years | 34 years | 34 years + 3 mons | 3 mons | 3 mons | 3 mons + 00:00:14 | 00:00:14 | 00:00:14 | 00:00:14 + 1 day 02:03:04 | 1 day 02:03:04 | 1 day 02:03:04 | 1 day 02:03:04 + 6 years | 6 years | 6 years | 6 years + 5 mons | 5 mons | 5 mons | 5 mons + 5 mons 12:00:00 | 5 mons 12:00:00 | 5 mons 12:00:00 | 5 mons 12:00:00 +(10 rows) + +WITH t AS ( + SELECT '1 month -30 days'::interval AS i +) +SELECT i = -i, i = '0'::interval, abs (i), abs (-i) + FROM t; + ?column? | ?column? | abs | abs +----------+----------+----------------+------------------ + t | t | 1 mon -30 days | -1 mons +30 days +(1 row) + -- Test intervals that are large enough to overflow 64 bits in comparisons CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval); INSERT INTO INTERVAL_TBL_OF (f1) VALUES diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 11c1929bef..ebcd5250e3 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -59,6 +59,13 @@ SELECT r1.*, r2.* WHERE r1.f1 > r2.f1 ORDER BY r1.f1, r2.f1; +SELECT abs (f1), abs (-f1), @ f1, @ -f1 FROM interval_tbl; +WITH t AS ( + SELECT '1 month -30 days'::interval AS i +) +SELECT i = -i, i = '0'::interval, abs (i), abs (-i) + FROM t; + -- Test intervals that are large enough to overflow 64 bits in comparisons CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval); INSERT INTO INTERVAL_TBL_OF (f1) VALUES -- 2.25.1