While looking at the GIN's partial match logic, I got an idea to let the generic index code do what opclass-specific comparePartial() functions do. It can be achieved if range type is accepted as key entry.
In this patch I add ANYRANGEARRAY pseudotype (note that changes in parse_coerce.c are rather ad hoc so far) and a set of cross-type operators like ANYARRAY @> ANYRANGEARRAY The semantics is that the range array is contained in the array iff each range element has a matching (non-range) element in the left array. For example: postgres=# SELECT ARRAY[-2, 5, 0.1, 10]::numeric[] @> ARRAY['[-10,-1]', '[7,10]']::numrange[]; ?column? ---------- t (1 row) postgres=# SELECT ARRAY[-2, 5, 0.1, 10]::numeric[] @> ARRAY['[-10,-1]', '[7,10)']::numrange[]; ?column? ---------- f (1 row) The other operators also correspond to those (ANYARRAY, ANYARRAY), except that array elements are matched using ANYRANGE @> ANYELEMENT The patch just adds the matching logic to GIN. It does not remove the original partial match because text search depends on it. Subtopic: GIN and cross-type operators -------------------------------------- So far all the in-core operators in the GIN's opfamilies have oprleft equal to oprright. When I tried to implement the (ANYARRAY, ANYRANGEARRAY) operators I had to do some changes in the core code: 1. While GIN_COMPARE_PROC and GIN_EXTRACTVALUE_PROC support functions depend on pg_opclass(opckeytype) and pg_opclass(opcintype) respectively (and thus are universial for the whole opclass), the other ones can be specific for pg_amproc(amproclefttype, amprocrighttype). That's why I moved some code from ginbeginscan() to ginrescan(). (I think it'd make sense to only store GIN_COMPARE_PROC and GIN_EXTRACTVALUE_PROC once per opclass, but that would require changes in CREATE OPERATOR CLASS command.) 2. To let the GIN code find the appropriate support functions for cross-type operators, I had to ensure that scan key's sk_subtype contains OID of particular type as opposed to that of the pseudotype. Is there any misconception in this patch proposal? // Antonin Houska (Tony)
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index cb17d38..9e1c665 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -194,7 +194,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, /* Initialize empty bitmap result */ scanEntry->matchBitmap = tbm_create(work_mem * 1024L); - /* Null query cannot partial-match anything */ + /* Null query cannot partial/range-match anything */ if (scanEntry->isPartialMatch && scanEntry->queryCategory != GIN_CAT_NORM_KEY) return true; @@ -263,8 +263,34 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, stack->off++; continue; } - } - else if (scanEntry->searchMode == GIN_SEARCH_MODE_ALL) + } else if (scanEntry->isRange) { + int32 cmp; + Datum lower = scanEntry->rangeLower.val; + bool lowerIncl = scanEntry->rangeLower.inclusive; + Datum upper = scanEntry->rangeUpper.val; + bool upperIncl = scanEntry->rangeUpper.inclusive; + + cmp = DatumGetInt32(FunctionCall2Coll(&btree->ginstate->compareFn[attnum - 1], + btree->ginstate->supportCollation[attnum - 1], + idatum, lower)); + + if ((lowerIncl && cmp < 0) || (!lowerIncl && cmp <= 0)) { + /* Matching idatum not reached yet. */ + stack->off++; + continue; + } + + cmp = DatumGetInt32(FunctionCall2Coll(&btree->ginstate->compareFn[attnum - 1], + btree->ginstate->supportCollation[attnum - 1], + idatum, upper)); + + if ((upperIncl && cmp > 0) || (!upperIncl && cmp >= 0)) + /* + * We're past the upper bound, no more matches + * for the current range. + */ + return true; + } else if (scanEntry->searchMode == GIN_SEARCH_MODE_ALL) { /* * In ALL mode, we are not interested in null items, so we can @@ -392,7 +418,7 @@ restartScanEntry: entry->isFinished = TRUE; - if (entry->isPartialMatch || + if (entry->isPartialMatch || entry->isRange || entry->queryCategory == GIN_CAT_EMPTY_QUERY) { /* @@ -1531,7 +1557,11 @@ gingetbitmap(PG_FUNCTION_ARGS) * to scan the main index before the pending list, since concurrent * cleanup could then make us miss entries entirely. */ - scanPendingInsert(scan, tbm, &ntids); + /* + * TODO + * enable when the match logic is adjusted. + */ + //scanPendingInsert(scan, tbm, &ntids); /* * Now scan the main index. diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c index afee2db..b085f75 100644 --- a/src/backend/access/gin/ginscan.c +++ b/src/backend/access/gin/ginscan.c @@ -17,6 +17,7 @@ #include "access/gin_private.h" #include "access/relscan.h" #include "pgstat.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -59,11 +60,98 @@ static GinScanEntry ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum queryKey, GinNullCategory queryCategory, - bool isPartialMatch, Pointer extra_data) + bool isPartialMatch, bool isRange, Pointer extra_data) { GinState *ginstate = &so->ginstate; GinScanEntry scanEntry; uint32 i; + RangeBound boundLower, boundUpper; + + /* + * TODO + * Load FmgrInfo for all the range functions as soon as we + * hit the first range entry and stop calling those functions + * by Oid (anywhere in the patch). + */ + + /* + * Neither NULL nor empty range can match anything - treat it as a NULL + * query. + * + * We should eliminate the empty ranges here, before looking for an + * existing entry. There might be many empty but distinct ranges and + * we don't want to create a separate entry for each. + */ + if (isRange) + { + Assert(queryCategory == GIN_CAT_NORM_KEY || + queryCategory == GIN_CAT_NULL_KEY); + + if (queryCategory == GIN_CAT_NORM_KEY) { + /* range_empty() */ + if (DatumGetBool(OidFunctionCall1(3850, queryKey))) { + queryCategory = GIN_CAT_NULL_KEY; + } + } + + if (queryCategory == GIN_CAT_NULL_KEY) { + isRange = false; + /* This shouldn't be necessary, anyway ... */ + queryKey = (Datum) 0; + } + } + + /* Extract attributes of the ranges, we may need them many times. */ + if (isRange) { + /* Probably not necessary, but setting them doesn't hurt. */ + boundLower.lower = true; + boundUpper.lower = false; + + /* range_lower_inf() */ + boundLower.infinite = DatumGetBool(OidFunctionCall1(3853, queryKey)); + /* range_lower_inc() */ + boundLower.inclusive = DatumGetBool(OidFunctionCall1(3851, queryKey)); + /* range_lower() */ + boundLower.val = OidFunctionCall1(3848, queryKey); + + /* range_upper_inf() */ + boundUpper.infinite = DatumGetBool(OidFunctionCall1(3854, queryKey)); + /* range_upper_inc() */ + boundUpper.inclusive = DatumGetBool(OidFunctionCall1(3852, queryKey)); + /* range_upper() */ + boundUpper.val = OidFunctionCall1(3849, queryKey); + + /* + * TODO + * Lift the following restrictions. + */ + if (boundLower.infinite) { + elog(ERROR, "the lower value of the range must not be infinity"); + } + if (boundUpper.infinite) { + elog(ERROR, "the upper value of the range must not be infinity"); + } + + if (!boundLower.infinite && !boundUpper.infinite && + boundLower.inclusive && boundUpper.inclusive) + { + GinState *ginstate = &so->ginstate; + int cmp; + + cmp = DatumGetInt32(FunctionCall2Coll( + &ginstate->compareFn[attnum - 1], + ginstate->supportCollation[attnum - 1], + boundLower.val, boundUpper.val)); + + if (cmp == 0) { + /* Range degenerated to a normal entry. */ + isRange = false; + queryKey = boundLower.val; + } + } + } + + /* Range might degenerate to a normal entry in some cases. */ /* * Look for an existing equivalent entry. @@ -81,22 +169,47 @@ ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, prevEntry->isPartialMatch == isPartialMatch && prevEntry->strategy == strategy && prevEntry->searchMode == searchMode && - prevEntry->attnum == attnum && - ginCompareEntries(ginstate, attnum, + prevEntry->isRange == isRange && + prevEntry->attnum == attnum) + { + + if (isRange) + { + if (queryCategory == prevEntry->queryCategory) { + if (queryCategory == GIN_CAT_NULL_KEY) + /* An entry for NULL range exists. */ + return prevEntry; + + /* Two regular ranges, need to compare. */ + if (DatumGetBool(OidFunctionCall2(3855, queryKey, + prevEntry->range))) + return prevEntry; + } + } else if (ginCompareEntries(ginstate, attnum, prevEntry->queryKey, prevEntry->queryCategory, queryKey, queryCategory) == 0) - { - /* Successful match */ - return prevEntry; + return prevEntry; } } } /* Nope, create a new entry */ scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData)); - scanEntry->queryKey = queryKey; + + scanEntry->isRange = isRange; + if (isRange) { + memcpy(&scanEntry->rangeLower, &boundLower, sizeof(RangeBound)); + memcpy(&scanEntry->rangeUpper, &boundUpper, sizeof(RangeBound)); + scanEntry->range = queryKey; + /* The lower bound is what we look for in the entry tree. */ + scanEntry->queryKey = scanEntry->rangeLower.val; + } else { + scanEntry->range = (Datum) 0; + scanEntry->queryKey = queryKey; + } + scanEntry->queryCategory = queryCategory; scanEntry->isPartialMatch = isPartialMatch; scanEntry->extra_data = extra_data; @@ -135,7 +248,8 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum query, uint32 nQueryValues, Datum *queryValues, GinNullCategory *queryCategories, - bool *partial_matches, Pointer *extra_data) + bool *partial_matches, Pointer *extra_data, + bool ranges) { GinScanKey key = &(so->keys[so->nkeys++]); GinState *ginstate = &so->ginstate; @@ -169,6 +283,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, Datum queryKey; GinNullCategory queryCategory; bool isPartialMatch; + Oid isRange; Pointer this_extra; if (i < nUserQueryValues) @@ -179,6 +294,8 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, isPartialMatch = (ginstate->canPartialMatch[attnum - 1] && partial_matches) ? partial_matches[i] : false; + /* All entries within a key are ranges or none. */ + isRange = ranges; this_extra = (extra_data) ? extra_data[i] : NULL; } else @@ -202,6 +319,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, break; } isPartialMatch = false; + isRange = false; this_extra = NULL; /* @@ -217,7 +335,8 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, key->scanEntry[i] = ginFillScanEntry(so, attnum, strategy, searchMode, queryKey, queryCategory, - isPartialMatch, this_extra); + isPartialMatch, isRange, + this_extra); } } @@ -268,6 +387,7 @@ ginNewScanKey(IndexScanDesc scan) GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; bool hasNullQuery = false; + Relation rel = scan->indexRelation; /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */ so->keys = (GinScanKey) @@ -291,6 +411,7 @@ ginNewScanKey(IndexScanDesc scan) Pointer *extra_data = NULL; bool *nullFlags = NULL; int32 searchMode = GIN_SEARCH_MODE_DEFAULT; + bool ranges = false; /* * We assume that GIN-indexable operators are strict, so a null query @@ -364,11 +485,42 @@ ginNewScanKey(IndexScanDesc scan) } /* now we can use the nullFlags as category codes */ + if (skey->sk_subtype != rel->rd_opcintype[i] && + skey->sk_subtype != InvalidOid) + { + Oid subType1 = InvalidOid; + Oid subType2 = InvalidOid; + + /* + * A special case of cross-type operator is + * ANYARRAY op ANYRANGEARRAY + */ + subType1 = get_element_type(rel->rd_opcintype[i]); + if (OidIsValid(subType1)) + /* The left is an array. Is the right too? */ + subType2 = get_element_type(skey->sk_subtype); + if (OidIsValid(subType1) && OidIsValid(subType2)) { + /* Both are arrays. */ + subType2 = get_range_subtype(subType2); + /* + * Do non-range elements of the left array + * match the subtype of the right array's range + * elements? + */ + if (subType1 == subType2) + ranges = true; + else + /* Should not happen as long as coercions work fine. */ + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("array element type %u does not match range subtype %u", + subType1, subType2))); + } + } ginFillScanKey(so, skey->sk_attno, skey->sk_strategy, searchMode, skey->sk_argument, nQueryValues, queryValues, (GinNullCategory *) nullFlags, - partial_matches, extra_data); + partial_matches, extra_data, ranges); } /* @@ -381,7 +533,7 @@ ginNewScanKey(IndexScanDesc scan) ginFillScanKey(so, FirstOffsetNumber, InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING, (Datum) 0, 0, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, InvalidOid); } /* @@ -405,6 +557,59 @@ ginNewScanKey(IndexScanDesc scan) pgstat_count_index_scan(scan->indexRelation); } +/* + * Retrieve the desired support function, either from relcache or catcache. + * + * Return TRUE on success, FALSE when GIN_COMPARE_PARTIAL_PROC does not + * exist or error if any other kind is not there. + */ +static bool +loadSupportFunction(GinState *state, Relation indRel, ScanKey scankey, + uint16 procnum, FmgrInfo *fn, bool required) +{ + AttrNumber attno = scankey->sk_attno; + bool defaultOperator; + FmgrInfo *procinfo; + RegProcedure proc = InvalidOid;; + + /* + * The "default" operators should be in relcache, however the cross-type + * ones need to be retrieved from catalog. + */ + defaultOperator = scankey->sk_subtype == indRel->rd_opcintype[attno - 1] || + scankey->sk_subtype == InvalidOid; + + if (defaultOperator) { + if (!required) { + proc = index_getprocid(indRel, attno, procnum); + if (!RegProcedureIsValid(proc)) + return false; + } + + procinfo = index_getprocinfo(indRel, attno, procnum); + fmgr_info_copy(fn, procinfo, CurrentMemoryContext); + } else { + proc = get_opfamily_proc(indRel->rd_opfamily[attno - 1], + indRel->rd_opcintype[attno - 1], scankey->sk_subtype, + procnum); + + if (!RegProcedureIsValid(proc)) + { + if (!required) + return false; + else + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("missing support function %d(%u,%u) for attribute %d of index \"%s\"", + procnum, indRel->rd_opcintype[attno - 1], + scankey->sk_subtype, + scankey->sk_attno, + RelationGetRelationName(indRel)))); + } + fmgr_info_cxt(proc, fn, CurrentMemoryContext); + } + return true; +} + Datum ginrescan(PG_FUNCTION_ARGS) { @@ -413,13 +618,57 @@ ginrescan(PG_FUNCTION_ARGS) /* remaining arguments are ignored */ GinScanOpaque so = (GinScanOpaque) scan->opaque; + Relation rel = scan->indexRelation; freeScanKeys(so); if (scankey && scan->numberOfKeys > 0) { + int i; + GinState *state; + memmove(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData)); + + state = &so->ginstate; + scankey = scan->keyData; + + /* + * Now that we have the keys, load the support functions. + * + * In most cases the left and right operand are equal, so we + * can get the functions from relation cache. However that's + * not always true. + */ + /* + * TODO + * Consider flags to recognize whether the operator at given + * key position changed to cross-type or back. + * In some cases it may not be necessary to reload some/all functions. + */ + for (i = 0; i < scan->numberOfKeys; i++) + { + /* + * While GIN_COMPARE_PROC and GIN_EXTRACTVALUE_PROC + * depend on opckeytpye and ocintype of the corresponding + * opclass respectively, the other support functions can + * be different for different righttype of the operator. + */ + loadSupportFunction(state, rel, scankey, + GIN_EXTRACTQUERY_PROC, + &(state->extractQueryFn[i]), true); + loadSupportFunction(state, rel, scankey, + GIN_CONSISTENT_PROC, + &(state->consistentFn[i]), true); + /* Partial match is not mandatory, FALSE can be returned. */ + state->canPartialMatch[i] = loadSupportFunction(state, + rel, scankey, + GIN_COMPARE_PARTIAL_PROC, + &(state->comparePartialFn[i]), false); + + scankey++; + } + } PG_RETURN_VOID(); diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 8a71681..e0146ee 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -58,34 +58,23 @@ initGinState(GinState *state, Relation index) origTupdesc->attrs[i]->attcollation); } + /* + * There should in fact be only one GIN_COMPARE_PROC throughout + * the whole opclass. This function always deals with + * pg_opclass(opckeytype). + */ fmgr_info_copy(&(state->compareFn[i]), - index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), - CurrentMemoryContext); - fmgr_info_copy(&(state->extractValueFn[i]), - index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), - CurrentMemoryContext); - fmgr_info_copy(&(state->extractQueryFn[i]), - index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC), - CurrentMemoryContext); - fmgr_info_copy(&(state->consistentFn[i]), - index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC), - CurrentMemoryContext); - + index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), + CurrentMemoryContext); /* - * Check opclass capability to do partial match. + * Likewise, all operators within the opclass can use the same + * GIN_EXTRACTVALUE_PROC. The only type we need to process is + * pg_opclass(opcintype). */ - if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid) - { - fmgr_info_copy(&(state->comparePartialFn[i]), - index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC), - CurrentMemoryContext); - state->canPartialMatch[i] = true; - } - else - { - state->canPartialMatch[i] = false; - } - + fmgr_info_copy(&(state->extractValueFn[i]), + index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), + CurrentMemoryContext); + /* * If the index column has a specified collation, we should honor that * while doing comparisons. However, we may have a collatable storage diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index f1062f1..ef43ed9 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -794,6 +794,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; + op_righttype = ((Const *) rightop)->consttype; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } @@ -831,7 +832,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, flags, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ - op_righttype, /* strategy subtype */ + op_righttype, /* strategy subtype */ ((OpExpr *) clause)->inputcollid, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index b6df2c6..e3eebc3 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -159,13 +159,23 @@ coerce_type(ParseState *pstate, Node *node, Node *result; CoercionPathType pathtype; Oid funcId; - if (targetTypeId == inputTypeId || node == NULL) { /* no conversion needed */ return node; } + if (targetTypeId == ANYRANGEARRAYOID) { + Oid elemtype, rngsubtype; + + elemtype = get_element_type(inputTypeId); + if (OidIsValid(elemtype)) { + rngsubtype = get_range_subtype(elemtype); + if (OidIsValid(rngsubtype)) { + return node; + } + } + } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || targetTypeId == ANYNONARRAYOID) @@ -1352,6 +1362,7 @@ check_generic_type_consistency(Oid *actual_arg_types, Oid array_typeid = InvalidOid; Oid array_typelem; Oid range_typeid = InvalidOid; + Oid rangearray_typeid = InvalidOid; Oid range_typelem; bool have_anyelement = false; bool have_anynonarray = false; @@ -1399,6 +1410,15 @@ check_generic_type_consistency(Oid *actual_arg_types, return false; range_typeid = actual_type; } + else if (decl_type == ANYRANGEARRAYOID) + { + if (actual_type == UNKNOWNOID) + continue; + actual_type = getBaseType(actual_type); /* flatten domains */ + if (OidIsValid(rangearray_typeid) && actual_type != rangearray_typeid) + return false; + rangearray_typeid = actual_type; + } } /* Get the element type based on the array type, if we have one */ @@ -1451,6 +1471,31 @@ check_generic_type_consistency(Oid *actual_arg_types, } } + if (OidIsValid(rangearray_typeid)) + { + array_typelem = get_element_type(rangearray_typeid); + if (!OidIsValid(array_typelem)) + return false; /* should be an array, but isn't */ + + range_typelem = get_range_subtype(array_typelem); + if (!OidIsValid(range_typelem)) + return false; /* should be a range, but isn't */ + + + if (!OidIsValid(elem_typeid)) + { + /* + * if we don't have an element type yet, use the one we just got + */ + elem_typeid = range_typelem; + } + else if (range_typelem != elem_typeid) + { + /* otherwise, they better match */ + return false; + } + } + if (have_anynonarray) { /* require the element type to not be an array or domain over array */ diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 438c3d0..b1b74a5 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -3137,13 +3137,11 @@ array_eq(PG_FUNCTION_ARGS) int ndims2 = ARR_NDIM(array2); int *dims1 = ARR_DIMS(array1); int *dims2 = ARR_DIMS(array2); - Oid element_type = ARR_ELEMTYPE(array1); + Oid element_type1 = ARR_ELEMTYPE(array1); + Oid element_type2 = ARR_ELEMTYPE(array2); bool result = true; int nitems; - TypeCacheEntry *typentry; - int typlen; - bool typbyval; - char typalign; + TypeCacheEntry *typentry, *typentry_rng, *typentry1, *typentry2; char *ptr1; char *ptr2; bits8 *bitmap1; @@ -3151,11 +3149,44 @@ array_eq(PG_FUNCTION_ARGS) int bitmask; int i; FunctionCallInfoData locfcinfo; + bool eltype1_is_range, eltype2_is_range; + Oid rng_type, base_type; - if (element_type != ARR_ELEMTYPE(array2)) + eltype1_is_range = type_is_range(element_type1); + eltype2_is_range = type_is_range(element_type2); + + if (eltype1_is_range && eltype2_is_range) ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot compare arrays of different element types"))); + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("at maximum one array may contain ranges"))); + + rng_type = InvalidOid; + + if (!eltype1_is_range && !eltype2_is_range) { + /* No range, so the type OIDs must be equal. */ + if (element_type1 != element_type2) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot compare arrays of different non-range element types"))); + + base_type = element_type1; + } else { + Oid rng_subtype; + + if (eltype1_is_range) { + rng_type = element_type1; + base_type = element_type2; + } else { + rng_type = element_type2; + base_type = element_type1; + } + + rng_subtype = get_range_subtype(rng_type); + if (base_type != rng_subtype) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("non-range type must be equal to sub-type of the range type"))); + } /* fast path if the arrays do not have the same dimensionality */ if (ndims1 != ndims2 || @@ -3170,27 +3201,64 @@ array_eq(PG_FUNCTION_ARGS) * as an index support function. */ typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; - if (typentry == NULL || - typentry->type_id != element_type) + + if (typentry == NULL || typentry->type_id != base_type) { - typentry = lookup_type_cache(element_type, - TYPECACHE_EQ_OPR_FINFO); - if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) - ereport(ERROR, + typentry = lookup_type_cache(base_type, TYPECACHE_EQ_OPR_FINFO); + + if (!eltype1_is_range && !eltype2_is_range && + !OidIsValid(typentry->eq_opr_finfo.fn_oid)) + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("could not identify an equality operator for type %s", - format_type_be(element_type)))); + errmsg("could not identify an equality operator for type %s", + format_type_be(element_type1)))); fcinfo->flinfo->fn_extra = (void *) typentry; - } - typlen = typentry->typlen; - typbyval = typentry->typbyval; - typalign = typentry->typalign; + } /* - * apply the operator to each pair of array elements. + * TODO + * Find out if/how typentry_rng can be saved across calls. */ - InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2, - collation, NULL, NULL); + typentry_rng = NULL; + + if (OidIsValid(rng_type) && + (typentry_rng == NULL || typentry_rng->type_id != rng_type)) + { + /* + * One array contains ranges - containment function needed. + */ + typentry_rng = lookup_type_cache(rng_type, TYPECACHE_RANGE_INFO); + + if (!OidIsValid(typentry_rng->rng_cont_proc_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a containment operator for type %s", + format_type_be(rng_type)))); + + } + + if (eltype1_is_range) { + typentry1 = typentry_rng; + typentry2 = typentry; + } else if (eltype2_is_range) { + typentry2 = typentry_rng; + typentry1 = typentry; + } else { + /* No range, both types are the same. */ + typentry1 = typentry2 = typentry; + } + + if (!eltype1_is_range && !eltype2_is_range) + /* + * Apply the comparison operator to each pair of array elements. + */ + InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, + 2, collation, NULL, NULL); + else + /* Apply containment function instead. */ + InitFunctionCallInfoData(locfcinfo, + &typentry_rng->rng_cont_proc_finfo, 2, + collation, NULL, NULL); /* Loop over source data */ nitems = ArrayGetNItems(ndims1, dims1); @@ -3217,9 +3285,13 @@ array_eq(PG_FUNCTION_ARGS) else { isnull1 = false; - elt1 = fetch_att(ptr1, typbyval, typlen); - ptr1 = att_addlength_pointer(ptr1, typlen, ptr1); - ptr1 = (char *) att_align_nominal(ptr1, typalign); + elt1 = fetch_att(ptr1, + typentry1->typbyval, + typentry1->typlen); + ptr1 = att_addlength_pointer(ptr1, + typentry1->typlen, ptr1); + ptr1 = (char *) att_align_nominal(ptr1, + typentry1->typalign); } if (bitmap2 && (*bitmap2 & bitmask) == 0) @@ -3230,9 +3302,13 @@ array_eq(PG_FUNCTION_ARGS) else { isnull2 = false; - elt2 = fetch_att(ptr2, typbyval, typlen); - ptr2 = att_addlength_pointer(ptr2, typlen, ptr2); - ptr2 = (char *) att_align_nominal(ptr2, typalign); + elt2 = fetch_att(ptr2, + typentry2->typbyval, + typentry2->typlen); + ptr2 = att_addlength_pointer(ptr2, + typentry2->typlen, ptr2); + ptr2 = (char *) att_align_nominal(ptr2, + typentry2->typalign); } /* advance bitmap pointers if any */ @@ -3260,8 +3336,17 @@ array_eq(PG_FUNCTION_ARGS) /* * Apply the operator to the element pair */ - locfcinfo.arg[0] = elt1; - locfcinfo.arg[1] = elt2; + if (eltype2_is_range) { + /* + * The containment operator expects the range + * to be the first argument. + */ + locfcinfo.arg[0] = elt2; + locfcinfo.arg[1] = elt1; + } else { + locfcinfo.arg[0] = elt1; + locfcinfo.arg[1] = elt2; + } locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.isnull = false; @@ -3661,63 +3746,131 @@ array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation, bool matchall, void **fn_extra) { bool result = matchall; - Oid element_type = ARR_ELEMTYPE(array1); - TypeCacheEntry *typentry; + Oid element_type1 = ARR_ELEMTYPE(array1); + Oid element_type2 = ARR_ELEMTYPE(array2); + TypeCacheEntry *typentry, *typentry_rng, *typentry1, *typentry2; int nelems1; Datum *values2; bool *nulls2; int nelems2; - int typlen; - bool typbyval; - char typalign; char *ptr1; bits8 *bitmap1; int bitmask; int i; int j; FunctionCallInfoData locfcinfo; + bool eltype1_is_range, eltype2_is_range; + Oid rng_type, base_type; - if (element_type != ARR_ELEMTYPE(array2)) + eltype1_is_range = type_is_range(element_type1); + eltype2_is_range = type_is_range(element_type2); + + if (eltype1_is_range && eltype2_is_range) ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot compare arrays of different element types"))); + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("at maximum one array may contain ranges"))); + + rng_type = InvalidOid; + + if (!eltype1_is_range && !eltype2_is_range) { + /* No range, so the type OIDs must be equal. */ + if (element_type1 != element_type2) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot compare arrays of different non-range element types"))); + + base_type = element_type1; + } else { + Oid rng_subtype; + + if (eltype1_is_range) { + rng_type = element_type1; + base_type = element_type2; + } else { + rng_type = element_type2; + base_type = element_type1; + } + + rng_subtype = get_range_subtype(rng_type); + if (base_type != rng_subtype) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("non-range type must be equal to sub-type of the range type"))); + } /* - * We arrange to look up the equality function only once per series of + * We arrange to look up the match function only once per series of * calls, assuming the element type doesn't change underneath us. The * typcache is used so that we have no memory leakage when being used as * an index support function. */ - typentry = (TypeCacheEntry *) *fn_extra; - if (typentry == NULL || - typentry->type_id != element_type) + typentry = (TypeCacheEntry *) *fn_extra; + + if (typentry == NULL || typentry->type_id != base_type) { - typentry = lookup_type_cache(element_type, - TYPECACHE_EQ_OPR_FINFO); - if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) + typentry = lookup_type_cache(base_type, TYPECACHE_EQ_OPR_FINFO); + + if (!eltype1_is_range && !eltype2_is_range && + !OidIsValid(typentry->eq_opr_finfo.fn_oid)) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), + (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify an equality operator for type %s", - format_type_be(element_type)))); + format_type_be(element_type1)))); *fn_extra = (void *) typentry; } - typlen = typentry->typlen; - typbyval = typentry->typbyval; - typalign = typentry->typalign; + + /* + * TODO + * Find out if/how typentry_rng can be saved across calls. + */ + typentry_rng = NULL; + + if (OidIsValid(rng_type) && + (typentry_rng == NULL || typentry_rng->type_id != rng_type)) + { + /* + * One array contains ranges - containment function needed. + */ + typentry_rng = lookup_type_cache(rng_type, TYPECACHE_RANGE_INFO); + + if (!OidIsValid(typentry_rng->rng_cont_proc_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a containment operator for type %s", + format_type_be(rng_type)))); + } + + + if (eltype1_is_range) { + typentry1 = typentry_rng; + typentry2 = typentry; + } else if (eltype2_is_range) { + typentry2 = typentry_rng; + typentry1 = typentry; + } else { + /* No range, both types are the same. */ + typentry1 = typentry2 = typentry; + } /* * Since we probably will need to scan array2 multiple times, it's * worthwhile to use deconstruct_array on it. We scan array1 the hard way * however, since we very likely won't need to look at all of it. */ - deconstruct_array(array2, element_type, typlen, typbyval, typalign, - &values2, &nulls2, &nelems2); + deconstruct_array(array2, element_type2, + typentry2->typlen, typentry2->typbyval, typentry2->typalign, + &values2, &nulls2, &nelems2); - /* - * Apply the comparison operator to each pair of array elements. - */ - InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2, + if (!eltype1_is_range && !eltype2_is_range) + /* + * Apply the comparison operator to each pair of array elements. + */ + InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2, collation, NULL, NULL); + else + /* Apply containment function instead. */ + InitFunctionCallInfoData(locfcinfo, &typentry_rng->rng_cont_proc_finfo, 2, + collation, NULL, NULL); /* Loop over source data */ nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1)); @@ -3739,9 +3892,9 @@ array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation, else { isnull1 = false; - elt1 = fetch_att(ptr1, typbyval, typlen); - ptr1 = att_addlength_pointer(ptr1, typlen, ptr1); - ptr1 = (char *) att_align_nominal(ptr1, typalign); + elt1 = fetch_att(ptr1, typentry1->typbyval, typentry1->typlen); + ptr1 = att_addlength_pointer(ptr1, typentry1->typlen, ptr1); + ptr1 = (char *) att_align_nominal(ptr1, typentry1->typalign); } /* advance bitmap pointer if any */ @@ -3780,11 +3933,21 @@ array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation, /* * Apply the operator to the element pair */ - locfcinfo.arg[0] = elt1; - locfcinfo.arg[1] = elt2; + if (eltype2_is_range) { + /* + * The containment operator expects the range + * to be the first argument. + */ + locfcinfo.arg[0] = elt2; + locfcinfo.arg[1] = elt1; + } else { + locfcinfo.arg[0] = elt1; + locfcinfo.arg[1] = elt2; + } locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.isnull = false; + oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo)); if (oprresult) break; diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 04650d8..41fd4ec 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -213,6 +213,30 @@ anyrange_out(PG_FUNCTION_ARGS) } /* + * anyrangearray_in - input routine for pseudo-type ANYRANGEARRAY. + */ +Datum +anyrangearray_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type anyrangearray"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * anyrangearray_out - output routine for pseudo-type ANYRANGEARRAY. + * + * We may as well allow this, since array_out will in fact work. + */ +Datum +anyrangearray_out(PG_FUNCTION_ARGS) +{ + return array_out(fcinfo); +} + +/* * void_in - input routine for pseudo-type VOID. * * We allow this so that PL functions can return VOID without any special diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index da66f34..addc5fa 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6750,7 +6750,7 @@ find_index_column(Node *op, IndexOptInfo *index) */ static bool gincost_pattern(IndexOptInfo *index, int indexcol, - Oid clause_op, Datum query, + Oid clause_op, Datum query, Oid queryType, GinQualCounts *counts) { Oid extractProcOid; @@ -6775,14 +6775,14 @@ gincost_pattern(IndexOptInfo *index, int indexcol, &strategy_op, &lefttype, &righttype); /* - * GIN always uses the "default" support functions, which are those with - * lefttype == righttype == the opclass' opcintype (see - * IndexSupportInitialize in relcache.c). - */ + * The particular types are needed so that we can retrieve any support + * function. + */ + op_input_types(clause_op, &lefttype, &righttype); + extractProcOid = get_opfamily_proc(index->opfamily[indexcol], - index->opcintype[indexcol], - index->opcintype[indexcol], - GIN_EXTRACTQUERY_PROC); + index->opcintype[indexcol], queryType, + GIN_EXTRACTQUERY_PROC); if (!OidIsValid(extractProcOid)) { @@ -6897,6 +6897,7 @@ gincost_opexpr(IndexOptInfo *index, OpExpr *clause, GinQualCounts *counts) /* Otherwise, apply extractQuery and get the actual term counts */ return gincost_pattern(index, indexcol, clause_op, ((Const *) operand)->constvalue, + ((Const *) operand)->consttype, counts); } @@ -6982,6 +6983,7 @@ gincost_scalararrayopexpr(IndexOptInfo *index, ScalarArrayOpExpr *clause, memset(&elemcounts, 0, sizeof(elemcounts)); if (gincost_pattern(index, indexcol, clause_op, elemValues[i], + ARR_ELEMTYPE(arrayval), &elemcounts)) { /* We ignore array elements that are unsatisfiable patterns */ diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 04cb74c..7301f03 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -52,6 +52,7 @@ #include "catalog/indexing.h" #include "catalog/pg_enum.h" #include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" #include "catalog/pg_range.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -539,6 +540,8 @@ load_rangetype_info(TypeCacheEntry *typentry) /* set up cached fmgrinfo structs */ fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo, CacheMemoryContext); + fmgr_info_cxt(OID_RANGE_CONTAINS_ELEM_PROC, &typentry->rng_cont_proc_finfo, + CacheMemoryContext); if (OidIsValid(canonicalOid)) fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo, CacheMemoryContext); diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index c603521..a67ca72 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -15,6 +15,7 @@ #include "access/itup.h" #include "fmgr.h" #include "storage/bufmgr.h" +#include "utils/rangetypes.h" #include "utils/rbtree.h" @@ -583,6 +584,9 @@ typedef struct GinScanKeyData /* array of GinScanEntry pointers, one per extracted search condition */ GinScanEntry *scanEntry; + /* TRUE means that all entries are ranges instead of simple values. */ + bool ranges; + /* array of check flags, reported to consistentFn */ bool *entryRes; @@ -620,6 +624,12 @@ typedef struct GinScanEntryData int32 searchMode; OffsetNumber attnum; + bool isRange; + Datum range; + /* Cache the bounds for easier use. */ + RangeBound rangeLower; + RangeBound rangeUpper; + /* Current page in posting tree */ Buffer buffer; diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index d200348..8f19c53 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -657,6 +657,12 @@ DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 )); DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 )); DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 )); +/* ANYARRAY op ANYRANGEARRAY */ +DATA(insert ( 2745 2277 3846 1 s 3968 2742 0 )); +DATA(insert ( 2745 2277 3846 2 s 3970 2742 0 )); +DATA(insert ( 2745 2277 3846 3 s 3972 2742 0 )); +DATA(insert ( 2745 2277 3846 4 s 3974 2742 0 )); + /* * btree enum_ops */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 7155cb2..a3c04a0 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -355,6 +355,30 @@ DATA(insert ( 3919 3831 3831 4 3878 )); DATA(insert ( 3919 3831 3831 5 3879 )); DATA(insert ( 3919 3831 3831 6 3880 )); DATA(insert ( 3919 3831 3831 7 3881 )); +DATA(insert ( 2745 1007 3905 1 351 )); +DATA(insert ( 2745 1007 3905 2 2743 )); +DATA(insert ( 2745 1007 3905 3 2774 )); +DATA(insert ( 2745 1007 3905 4 2744 )); +DATA(insert ( 2745 1231 3907 1 1769 )); +DATA(insert ( 2745 1231 3907 2 2743 )); +DATA(insert ( 2745 1231 3907 3 2774 )); +DATA(insert ( 2745 1231 3907 4 2744 )); +DATA(insert ( 2745 1115 3909 1 2045 )); +DATA(insert ( 2745 1115 3909 2 2743 )); +DATA(insert ( 2745 1115 3909 3 2774 )); +DATA(insert ( 2745 1115 3909 4 2744 )); +DATA(insert ( 2745 1185 3911 1 1314 )); +DATA(insert ( 2745 1185 3911 2 2743 )); +DATA(insert ( 2745 1185 3911 3 2774 )); +DATA(insert ( 2745 1185 3911 4 2744 )); +DATA(insert ( 2745 1182 3913 1 1092 )); +DATA(insert ( 2745 1182 3913 2 2743 )); +DATA(insert ( 2745 1182 3913 3 2774 )); +DATA(insert ( 2745 1182 3913 4 2744 )); +DATA(insert ( 2745 1016 3927 1 842 )); +DATA(insert ( 2745 1016 3927 2 2743 )); +DATA(insert ( 2745 1016 3927 3 2774 )); +DATA(insert ( 2745 1016 3927 4 2744 )); /* sp-gist */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 5f28fc3..43f5372 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1736,7 +1736,26 @@ DATA(insert OID = 3966 ( "#>" PGNSP PGUID b f f 114 1009 114 0 0 json_extrac DESCR("get value from json with path elements"); DATA(insert OID = 3967 ( "#>>" PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - )); DESCR("get value from json as text with path elements"); - +/* + * TODO + * oprrest, oprjoin + */ +DATA(insert OID = 3968 ( "&&" PGNSP PGUID b f f 2277 3846 16 3969 0 array_overlap_rngarray - - )); +DESCR("array overlaps range array"); +DATA(insert OID = 3969 ( "&&" PGNSP PGUID b f f 3846 2277 16 3968 0 rngarray_overlap_array - - )); +DESCR("range array overlaps array"); +DATA(insert OID = 3970 ( "@>" PGNSP PGUID b f f 2277 3846 16 3973 0 array_contains_rngarray - - )); +DESCR("array contains range array"); +DATA(insert OID = 3971 ( "@>" PGNSP PGUID b f f 3846 2277 16 3972 0 rngarray_contains_array - - )); +DESCR("range array contains array"); +DATA(insert OID = 3972 ( "<@" PGNSP PGUID b f f 2277 3846 16 3971 0 array_contained_by_rngarray - - )); +DESCR("array is contained by range array"); +DATA(insert OID = 3973 ( "<@" PGNSP PGUID b f f 3846 2277 16 3970 0 rngarray_contained_by_array - - )); +DESCR("range array is contained by array"); +DATA(insert OID = 3974 ( "=" PGNSP PGUID b f f 2277 3846 16 3975 0 array_equals_rngarray - - )); +DESCR("range array is equal to array"); +DATA(insert OID = 3975 ( "=" PGNSP PGUID b f f 3846 2277 16 3974 0 rngarray_equals_array - - )); +DESCR("range array is equal to array"); /* diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 90aff3d..fd31744 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3514,6 +3514,10 @@ DATA(insert OID = 3116 ( fdw_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 DESCR("I/O"); DATA(insert OID = 3117 ( fdw_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3115" _null_ _null_ _null_ _null_ fdw_handler_out _null_ _null_ _null_ )); DESCR("I/O"); +DATA(insert OID = 3186 ( anyrangearray_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3846 "2275" _null_ _null_ _null_ _null_ anyrangearray_in _null_ _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 3187 ( anyrangearray_out PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "3846" _null_ _null_ _null_ _null_ anyrangearray_out _null_ _null_ _null_ )); +DESCR("I/O"); /* cryptographic */ DATA(insert OID = 2311 ( md5 PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ )); @@ -3976,6 +3980,15 @@ DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ )); DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ )); +DATA(insert OID = 3178 ( array_overlap_rngarray PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 3846" _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ )); +DATA(insert OID = 3179 ( rngarray_overlap_array PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3846 2277" _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ )); +DATA(insert OID = 3180 ( array_contains_rngarray PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 3846" _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ )); +DATA(insert OID = 3181 ( rngarray_contains_array PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3846 2277" _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ )); +DATA(insert OID = 3182 ( array_contained_by_rngarray PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 3846" _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ )); +DATA(insert OID = 3183 ( rngarray_contained_by_array PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3846 2277" _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ )); +DATA(insert OID = 3184 ( array_equals_rngarray PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 3846" _null_ _null_ _null_ _null_ array_eq _null_ _null_ _null_ )); +DATA(insert OID = 3185 ( rngarray_equals_array PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3846 2277" _null_ _null_ _null_ _null_ array_eq _null_ _null_ _null_ )); + /* userlock replacements */ DATA(insert OID = 2880 ( pg_advisory_lock PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "20" _null_ _null_ _null_ _null_ pg_advisory_lock_int8 _null_ _null_ _null_ )); DESCR("obtain exclusive advisory lock"); @@ -4555,6 +4568,7 @@ DATA(insert OID = 3857 ( range_overlaps PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 DESCR("implementation of && operator"); DATA(insert OID = 3858 ( range_contains_elem PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3831 2283" _null_ _null_ _null_ _null_ range_contains_elem _null_ _null_ _null_ )); DESCR("implementation of @> operator"); +#define OID_RANGE_CONTAINS_ELEM_PROC 3858 DATA(insert OID = 3859 ( range_contains PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contains _null_ _null_ _null_ )); DESCR("implementation of @> operator"); DATA(insert OID = 3860 ( elem_contained_by_range PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2283 3831" _null_ _null_ _null_ _null_ elem_contained_by_range _null_ _null_ _null_ )); diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h index f9e3373..a262ba0 100644 --- a/src/include/catalog/pg_range.h +++ b/src/include/catalog/pg_range.h @@ -16,6 +16,11 @@ * * XXX do NOT break up DATA() statements into multiple lines! * the scripts are not as smart as you might think... + * + * XXX When adding a new range for a subtype array of which already + * has default opclass for GIN, don't forget to add the appropriate + * cross-type entries to pg_amproc. + * * *------------------------------------------------------------------------- */ diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index e3822fa..b048c7a 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -669,7 +669,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han #define FDW_HANDLEROID 3115 DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); #define ANYRANGEOID 3831 - +DATA(insert OID = 3846 ( anyrangearray PGNSP PGUID -1 f p P f t \054 0 0 0 anyrangearray_in anyrangearray_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +#define ANYRANGEARRAYOID 3846 /* * macros @@ -704,6 +705,7 @@ DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange (typid) == ANYARRAYOID || \ (typid) == ANYNONARRAYOID || \ (typid) == ANYENUMOID || \ - (typid) == ANYRANGEOID) + (typid) == ANYRANGEOID || \ + (typid) == ANYRANGEARRAYOID) #endif /* PG_TYPE_H */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 667c58b..25e2ea9 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -531,6 +531,8 @@ extern Datum anyenum_in(PG_FUNCTION_ARGS); extern Datum anyenum_out(PG_FUNCTION_ARGS); extern Datum anyrange_in(PG_FUNCTION_ARGS); extern Datum anyrange_out(PG_FUNCTION_ARGS); +extern Datum anyrangearray_in(PG_FUNCTION_ARGS); +extern Datum anyrangearray_out(PG_FUNCTION_ARGS); extern Datum void_in(PG_FUNCTION_ARGS); extern Datum void_out(PG_FUNCTION_ARGS); extern Datum void_recv(PG_FUNCTION_ARGS); diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h index 20f510d..d6fbdab 100644 --- a/src/include/utils/typcache.h +++ b/src/include/utils/typcache.h @@ -81,6 +81,7 @@ typedef struct TypeCacheEntry struct TypeCacheEntry *rngelemtype; /* range's element type */ Oid rng_collation; /* collation for comparisons, if any */ FmgrInfo rng_cmp_proc_finfo; /* comparison function */ + FmgrInfo rng_cont_proc_finfo; /* range_contains_elem() function */ FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */ FmgrInfo rng_subdiff_finfo; /* difference function, if any */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers