From b7056b832444696c931d59af057b0a345f5ae178 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Wed, 23 Nov 2011 17:13:02 -0500 Subject: [PATCH] Adjust range_adjacent to support different canonicalization rules. The original coding would not work for discrete ranges in which the canonicalization rule is to produce symmetric boundaries (either [] or () style), as noted by Jeff Davis. Florian Pflug pointed out that we could fix that by invoking the canonicalization function to see if the range "between" the two given ranges normalizes to empty. This implementation of Florian's idea is a tad slower than the original code, but only in the case where there actually is a canonicalization function --- if not, it's essentially the same logic as before. --- src/backend/utils/adt/rangetypes.c | 60 ++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c index 4b27d4317fb..d0f890c28aa 100644 --- a/src/backend/utils/adt/rangetypes.c +++ b/src/backend/utils/adt/rangetypes.c @@ -699,6 +699,8 @@ range_adjacent(PG_FUNCTION_ARGS) upper2; bool empty1, empty2; + RangeType *r3; + int cmp; /* Different types should be prevented by ANYRANGE matching rules */ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2)) @@ -714,23 +716,59 @@ range_adjacent(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); /* - * For two ranges to be adjacent, the lower boundary of one range has to - * match the upper boundary of the other. However, the inclusivity of - * those two boundaries must also be different. + * Given two ranges A..B and C..D, where B < C, the ranges are adjacent + * if and only if the range B..C is empty, where inclusivity of these two + * bounds is inverted compared to the original bounds. For discrete + * ranges, we have to rely on the canonicalization function to normalize + * B..C to empty if it contains no elements of the subtype. (If there is + * no canonicalization function, it's impossible for such a range to + * normalize to empty, so we needn't bother to try.) + * + * If B == C, the ranges are adjacent only if these bounds have different + * inclusive flags (i.e., exactly one of the ranges includes the common + * boundary point). * - * The semantics for range_cmp_bounds aren't quite what we need here, so - * we do the comparison more directly. + * And if B > C then the ranges cannot be adjacent in this order, but we + * must consider the other order (i.e., check D <= A). */ - if (lower1.inclusive != upper2.inclusive) + cmp = range_cmp_bound_values(typcache, &upper1, &lower2); + if (cmp < 0) + { + /* in a continuous subtype, there are assumed to be points between */ + if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid)) + PG_RETURN_BOOL(false); + /* flip the inclusion flags */ + upper1.inclusive = !upper1.inclusive; + lower2.inclusive = !lower2.inclusive; + /* change upper/lower labels to avoid Assert failures */ + upper1.lower = true; + lower2.lower = false; + r3 = make_range(typcache, &upper1, &lower2, false); + PG_RETURN_BOOL(RangeIsEmpty(r3)); + } + if (cmp == 0) { - if (range_cmp_bound_values(typcache, &lower1, &upper2) == 0) - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(upper1.inclusive != lower2.inclusive); } - if (upper1.inclusive != lower2.inclusive) + cmp = range_cmp_bound_values(typcache, &upper2, &lower1); + if (cmp < 0) + { + /* in a continuous subtype, there are assumed to be points between */ + if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid)) + PG_RETURN_BOOL(false); + /* flip the inclusion flags */ + upper2.inclusive = !upper2.inclusive; + lower1.inclusive = !lower1.inclusive; + /* change upper/lower labels to avoid Assert failures */ + upper2.lower = true; + lower1.lower = false; + r3 = make_range(typcache, &upper2, &lower1, false); + PG_RETURN_BOOL(RangeIsEmpty(r3)); + } + if (cmp == 0) { - if (range_cmp_bound_values(typcache, &upper1, &lower2) == 0) - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(upper2.inclusive != lower1.inclusive); } PG_RETURN_BOOL(false); -- GitLab