diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/utils/adt/geo_ops.c | 39 | ||||
-rw-r--r-- | src/backend/utils/adt/network.c | 52 | ||||
-rw-r--r-- | src/backend/utils/adt/rangetypes.c | 54 |
3 files changed, 132 insertions, 13 deletions
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 6cb6be5c5fd..39a78552410 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -4227,6 +4227,45 @@ box_div(PG_FUNCTION_ARGS) PG_RETURN_BOX_P(result); } +/* + * Convert point to empty box + */ +Datum +point_box(PG_FUNCTION_ARGS) +{ + Point *pt = PG_GETARG_POINT_P(0); + BOX *box; + + box = (BOX *) palloc(sizeof(BOX)); + + box->high.x = pt->x; + box->low.x = pt->x; + box->high.y = pt->y; + box->low.y = pt->y; + + PG_RETURN_BOX_P(box); +} + +/* + * Smallest bounding box that includes both of the given boxes + */ +Datum +boxes_bound_box(PG_FUNCTION_ARGS) +{ + BOX *box1 = PG_GETARG_BOX_P(0), + *box2 = PG_GETARG_BOX_P(1), + *container; + + container = (BOX *) palloc(sizeof(BOX)); + + container->high.x = Max(box1->high.x, box2->high.x); + container->low.x = Min(box1->low.x, box2->low.x); + container->high.y = Max(box1->high.y, box2->high.y); + container->low.y = Min(box1->low.y, box2->low.y); + + PG_RETURN_BOX_P(container); +} + /*********************************************************************** ** diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index 3a705da6197..1f8469a2cbc 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -888,6 +888,58 @@ network_hostmask(PG_FUNCTION_ARGS) } /* + * Returns true if the addresses are from the same family, or false. Used to + * check that we can create a network which contains both of the networks. + */ +Datum +inet_same_family(PG_FUNCTION_ARGS) +{ + inet *a1 = PG_GETARG_INET_PP(0); + inet *a2 = PG_GETARG_INET_PP(1); + + PG_RETURN_BOOL(ip_family(a1) == ip_family(a2)); +} + +/* + * Returns the smallest CIDR which contains both of the inputs. + */ +Datum +inet_merge(PG_FUNCTION_ARGS) +{ + inet *a1 = PG_GETARG_INET_PP(0), + *a2 = PG_GETARG_INET_PP(1), + *result; + int commonbits; + + if (ip_family(a1) != ip_family(a2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot merge addresses from different families"))); + + commonbits = bitncommon(ip_addr(a1), ip_addr(a2), + Min(ip_bits(a1), ip_bits(a2))); + + /* Make sure any unused bits are zeroed. */ + result = (inet *) palloc0(sizeof(inet)); + + ip_family(result) = ip_family(a1); + ip_bits(result) = commonbits; + + /* Clone appropriate bytes of the address. */ + if (commonbits > 0) + memcpy(ip_addr(result), ip_addr(a1), (commonbits + 7) / 8); + + /* Clean any unwanted bits in the last partial byte. */ + if (commonbits % 8 != 0) + ip_addr(result)[commonbits / 8] &= ~(0xFF >> (commonbits % 8)); + + /* Set varlena header correctly. */ + SET_INET_VARSIZE(result); + + PG_RETURN_INET_P(result); +} + +/* * Convert a value of a network datatype to an approximate scalar value. * This is used for estimating selectivities of inequality operators * involving network types. diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c index c037b05f933..aaf4cb68109 100644 --- a/src/backend/utils/adt/rangetypes.c +++ b/src/backend/utils/adt/rangetypes.c @@ -1006,13 +1006,14 @@ range_minus(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } -/* set union */ -Datum -range_union(PG_FUNCTION_ARGS) +/* + * Set union. If strict is true, it is an error that the two input ranges + * are not adjacent or overlapping. + */ +static RangeType * +range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2, + bool strict) { - RangeType *r1 = PG_GETARG_RANGE(0); - RangeType *r2 = PG_GETARG_RANGE(1); - TypeCacheEntry *typcache; RangeBound lower1, lower2; RangeBound upper1, @@ -1026,19 +1027,18 @@ range_union(PG_FUNCTION_ARGS) if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2)) elog(ERROR, "range types do not match"); - typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); - range_deserialize(typcache, r1, &lower1, &upper1, &empty1); range_deserialize(typcache, r2, &lower2, &upper2, &empty2); /* if either is empty, the other is the correct answer */ if (empty1) - PG_RETURN_RANGE(r2); + return r2; if (empty2) - PG_RETURN_RANGE(r1); + return r1; - if (!DatumGetBool(range_overlaps(fcinfo)) && - !DatumGetBool(range_adjacent(fcinfo))) + if (strict && + !DatumGetBool(range_overlaps_internal(typcache, r1, r2)) && + !DatumGetBool(range_adjacent_internal(typcache, r1, r2))) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("result of range union would not be contiguous"))); @@ -1053,7 +1053,35 @@ range_union(PG_FUNCTION_ARGS) else result_upper = &upper2; - PG_RETURN_RANGE(make_range(typcache, result_lower, result_upper, false)); + return make_range(typcache, result_lower, result_upper, false); +} + +Datum +range_union(PG_FUNCTION_ARGS) +{ + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + TypeCacheEntry *typcache; + + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); + + PG_RETURN_RANGE(range_union_internal(typcache, r1, r2, true)); +} + +/* + * range merge: like set union, except also allow and account for non-adjacent + * input ranges. + */ +Datum +range_merge(PG_FUNCTION_ARGS) +{ + RangeType *r1 = PG_GETARG_RANGE(0); + RangeType *r2 = PG_GETARG_RANGE(1); + TypeCacheEntry *typcache; + + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); + + PG_RETURN_RANGE(range_union_internal(typcache, r1, r2, false)); } /* set intersection */ |