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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers