[PATCH] Support empty ranges with bounds information

From: "Joel Jacobson" <joel(at)compiler(dot)org>
To: pgsql-hackers(at)lists(dot)postgresql(dot)org, "Tom Lane" <tgl(at)sss(dot)pgh(dot)pa(dot)us>, "Mark Dilger" <mark(dot)dilger(at)enterprisedb(dot)com>, "Isaac Morland" <isaac(dot)morland(at)gmail(dot)com>
Subject: [PATCH] Support empty ranges with bounds information
Date: 2021-03-02 13:20:45
Message-ID: a4bd2562-bc02-4105-bca9-442eeeb39670@www.fastmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

As discussed in the separate thread "[PATCH] regexp_positions ( string text, pattern text, flags text ) → setof int4range[]" [1]
it's currently not possible to create an empty range with bounds information.

This patch tries to improve the situation by keeping the bounds information,
and allow accessing it via lower() and upper().

No other semantics have been changed.
All tests passes without any changes.

All examples below give the same result before/after this patch:

SELECT int4range(6,6,'[)');
empty
SELECT isempty(int4range(6,6,'[)'));
TRUE
SELECT int4range(6,6,'[)') = int4range(7,7,'[)');
TRUE
SELECT 'empty'::int4range;
empty
SELECT lower('empty'::int4range);
NULL
SELECT upper('empty'::int4range);
NULL
SELECT isempty('empty'::int4range);
TRUE
SELECT 'empty'::int4range = 'empty'::int4range;
TRUE
SELECT int4range(6,6,'[)') = 'empty'::int4range;
TRUE

The only semantic change is lower() and upper()
now returns the lower and upper bounds
for empty ranges created with bounds:

SELECT lower(int4range(6,6,'[)'));
6
SELECT upper(int4range(6,6,'[)'));
6

Isaac Morland asked an interesting question in the other thread [1]:
>Doing this would introduce numerous questions which would have to be resolved.
>For example, where/when is the empty range resulting from an intersection operation?

The result of intersection is with this patch unchanged,
the resulting empty range has no bounds information, e.g:

SELECT lower(int4range(10,10,'[)') * int4range(20,20,'[)'));
NULL

Patch explained below:

I've made use of the two previously not used null flags:

-#define RANGE_LB_NULL 0x20 /* lower bound is null (NOT USED) */
-#define RANGE_UB_NULL 0x40 /* upper bound is null (NOT USED) */
+#define RANGE_LB_NULL 0x20 /* lower bound is null */
+#define RANGE_UB_NULL 0x40 /* upper bound is null */

I've changed the RANGE_HAS_LBOUND and RANGE_HAS_UBOUND macros
to not look at RANGE_EMPTY:

-#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \
- RANGE_LB_NULL | \
- RANGE_LB_INF)))
+#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_LB_NULL | RANGE_LB_INF)))

The definition for RANGE_HAS_UBOUND has been changed in the same way.

These NULL-flags are now set to explicitly indicate there is no bounds information,
when parsing a text string containing the "empty" literal in range_parse(),
or when the caller of make_range() passes empty=true:

- flags |= RANGE_EMPTY;
+ flags |= RANGE_EMPTY | RANGE_LB_NULL | RANGE_UB_NULL;

In the range_lower() and range_upper() functions,
the RANGE_HAS_...BOUND() macros are used,
instead of the old hard-coded expression, e.g.:

- if (empty || lower.infinite)
+ if (!RANGE_HAS_LBOUND(flags))

Finally, in range_recv() we must not mask out the NULL flags,
since they are now used:

flags &= (RANGE_EMPTY |
RANGE_LB_INC |
RANGE_LB_INF |
+ RANGE_LB_NULL |
RANGE_UB_INC |
- RANGE_UB_INF);
+ RANGE_UB_INF |
+ RANGE_UB_NULL);

That's all of it.

I think this little change would make range types more intuitive useful in practise.

/Joel

[1] https://www.postgresql.org/message-id/5eae8911-241a-4432-accc-80e6ffecedfa%40www.fastmail.com

Attachment Content-Type Size
empty-ranges-with-bounds-information.patch application/octet-stream 3.2 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Dian M Fay 2021-03-02 13:34:50 Re: [PATCH] postgres-fdw: column option to override foreign types
Previous Message Peter Eisentraut 2021-03-02 13:20:40 Re: pg_upgrade version checking questions