diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 92dac7c60b..4d8fd20c1c 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -72,7 +72,9 @@ The minmax operator classes store the minimum and the maximum values appearing - in the indexed column within the range. + in the indexed column within the range. The inclusion + operator classes store a value which includes the values in the indexed + column within the range. @@ -251,6 +253,18 @@ > + + inet_inclusion_ops + inet + + && + >> + >>= + << + <<= + = + + bpchar_minmax_ops character @@ -372,6 +386,25 @@ > + + range_inclusion_ops + any range type + + && + &> + &< + >> + << + <@ + = + @> + < + <= + = + >= + > + + pg_lsn_minmax_ops pg_lsn @@ -383,6 +416,24 @@ > + + box_inclusion_ops + box + + && + &> + &< + >> + << + <@ + ~= + @> + &>| + |&< + >>| + |<< + +
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index ac44fcdee3..f4572d80a8 100644 --- a/src/backend/access/brin/Makefile +++ b/src/backend/access/brin/Makefile @@ -13,6 +13,6 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \ - brin_minmax.o + brin_minmax.o brin_inclusion.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index 2b5fb8dce9..199512551e 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -105,11 +105,6 @@ brininsert(PG_FUNCTION_ARGS) BrinMemTuple *dtup; BlockNumber heapBlk; int keyno; -#ifdef USE_ASSERT_CHECKING - BrinTuple *tmptup; - BrinMemTuple *tmpdtup; - Size tmpsiz; -#endif CHECK_FOR_INTERRUPTS(); @@ -137,45 +132,6 @@ brininsert(PG_FUNCTION_ARGS) dtup = brin_deform_tuple(bdesc, brtup); -#ifdef USE_ASSERT_CHECKING - { - /* - * When assertions are enabled, we use this as an opportunity to - * test the "union" method, which would otherwise be used very - * rarely: first create a placeholder tuple, and addValue the - * value we just got into it. Then union the existing index tuple - * with the updated placeholder tuple. The tuple resulting from - * that union should be identical to the one resulting from the - * regular operation (straight addValue) below. - * - * Here we create the tuple to compare with; the actual comparison - * is below. - */ - tmptup = brin_form_placeholder_tuple(bdesc, heapBlk, &tmpsiz); - tmpdtup = brin_deform_tuple(bdesc, tmptup); - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - BrinValues *bval; - FmgrInfo *addValue; - - bval = &tmpdtup->bt_columns[keyno]; - addValue = index_getprocinfo(idxRel, keyno + 1, - BRIN_PROCNUM_ADDVALUE); - FunctionCall4Coll(addValue, - idxRel->rd_indcollation[keyno], - PointerGetDatum(bdesc), - PointerGetDatum(bval), - values[keyno], - nulls[keyno]); - } - - union_tuples(bdesc, tmpdtup, brtup); - - tmpdtup->bt_placeholder = dtup->bt_placeholder; - tmptup = brin_form_tuple(bdesc, heapBlk, tmpdtup, &tmpsiz); - } -#endif - /* * Compare the key values of the new tuple to the stored index values; * our deformed tuple will get updated if the new tuple doesn't fit @@ -202,20 +158,6 @@ brininsert(PG_FUNCTION_ARGS) need_insert |= DatumGetBool(result); } -#ifdef USE_ASSERT_CHECKING - { - /* - * Now we can compare the tuple produced by the union function - * with the one from plain addValue. - */ - BrinTuple *cmptup; - Size cmpsz; - - cmptup = brin_form_tuple(bdesc, heapBlk, dtup, &cmpsz); - Assert(brin_tuples_equal(tmptup, tmpsiz, cmptup, cmpsz)); - } -#endif - if (!need_insert) { /* @@ -323,8 +265,6 @@ brinbeginscan(PG_FUNCTION_ARGS) * If a TID from the revmap is read as InvalidTID, we know that range is * unsummarized. Pages in those ranges need to be returned regardless of scan * keys. - * - * XXX see _bt_first on what to do about sk_subtype. */ Datum bringetbitmap(PG_FUNCTION_ARGS) @@ -340,7 +280,6 @@ bringetbitmap(PG_FUNCTION_ARGS) BlockNumber nblocks; BlockNumber heapBlk; int totalpages = 0; - int keyno; FmgrInfo *consistentFn; MemoryContext oldcxt; MemoryContext perRangeCxt; @@ -359,18 +298,11 @@ bringetbitmap(PG_FUNCTION_ARGS) heap_close(heapRel, AccessShareLock); /* - * Obtain consistent functions for all indexed column. Maybe it'd be - * possible to do this lazily only the first time we see a scan key that - * involves each particular attribute. + * Make room for the consistent support procedures of indexed columns. We + * don't look them up here; we do that lazily the first time we see a scan + * key reference each of them. We rely on zeroing fn_oid to InvalidOid. */ - consistentFn = palloc(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); - for (keyno = 0; keyno < bdesc->bd_tupdesc->natts; keyno++) - { - FmgrInfo *tmp; - - tmp = index_getprocinfo(idxRel, keyno + 1, BRIN_PROCNUM_CONSISTENT); - fmgr_info_copy(&consistentFn[keyno], tmp, CurrentMemoryContext); - } + consistentFn = palloc0(sizeof(FmgrInfo) * bdesc->bd_tupdesc->natts); /* * Setup and use a per-range memory context, which is reset every time we @@ -418,7 +350,6 @@ bringetbitmap(PG_FUNCTION_ARGS) else { BrinMemTuple *dtup; - int keyno; dtup = brin_deform_tuple(bdesc, tup); if (dtup->bt_placeholder) @@ -431,6 +362,8 @@ bringetbitmap(PG_FUNCTION_ARGS) } else { + int keyno; + /* * Compare scan keys with summary values stored for the range. * If scan keys are matched, the page range must be added to @@ -456,6 +389,17 @@ bringetbitmap(PG_FUNCTION_ARGS) (key->sk_collation == bdesc->bd_tupdesc->attrs[keyattno - 1]->attcollation)); + /* First time this column? look up consistent function */ + if (consistentFn[keyattno - 1].fn_oid == InvalidOid) + { + FmgrInfo *tmp; + + tmp = index_getprocinfo(idxRel, keyattno, + BRIN_PROCNUM_CONSISTENT); + fmgr_info_copy(&consistentFn[keyattno - 1], tmp, + CurrentMemoryContext); + } + /* * Check whether the scan key is consistent with the page * range values; if so, have the pages in the range added diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c new file mode 100644 index 0000000000..1f0bc7fdb1 --- /dev/null +++ b/src/backend/access/brin/brin_inclusion.c @@ -0,0 +1,696 @@ +/* + * brin_inclusion.c + * Implementation of inclusion opclasses for BRIN + * + * This module provides framework BRIN support functions for the "inclusion" + * operator classes. A few SQL-level support functions are also required for + * each opclass. + * + * The "inclusion" BRIN strategy is useful for types that support R-Tree + * operations. This implementation is a straight mapping of those operations + * to the block-range nature of BRIN, with two exceptions: (a) we explicitly + * support "empty" elements: at least with range types, we need to consider + * emptiness separately from regular R-Tree strategies; and (b) we need to + * consider "unmergeable" elements, that is, a set of elements for whose union + * no representation exists. The only case where that happens as of this + * writing is the INET type, where IPv6 values cannot be merged with IPv4 + * values. + * + * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/brin/brin_inclusion.c + */ +#include "postgres.h" + +#include "access/brin_internal.h" +#include "access/brin_tuple.h" +#include "access/genam.h" +#include "access/skey.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_type.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + + +/* + * Additional SQL level support functions + * + * Procedure numbers must not use values reserved for BRIN itself; see + * brin_internal.h. + */ +#define INCLUSION_MAX_PROCNUMS 4 /* maximum support procs we need */ +#define PROCNUM_MERGE 11 /* required */ +#define PROCNUM_MERGEABLE 12 /* optional */ +#define PROCNUM_CONTAINS 13 /* optional */ +#define PROCNUM_EMPTY 14 /* optional */ + + +/* + * Subtract this from procnum to obtain index in InclusionOpaque arrays + * (Must be equal to minimum of private procnums). + */ +#define PROCNUM_BASE 11 + +/*- + * The values stored in the bv_values arrays correspond to: + * + * 0 - the union of the values in the block range + * 1 - whether an empty value is present in any tuple in the block range + * 2 - whether the values in the block range cannot be merged (e.g. an IPv6 + * address amidst IPv4 addresses). + */ +#define INCLUSION_UNION 0 +#define INCLUSION_UNMERGEABLE 1 +#define INCLUSION_CONTAINS_EMPTY 2 + + +typedef struct InclusionOpaque +{ + FmgrInfo extra_procinfos[INCLUSION_MAX_PROCNUMS]; + bool extra_proc_missing[INCLUSION_MAX_PROCNUMS]; + Oid cached_subtype; + FmgrInfo strategy_procinfos[RTMaxStrategyNumber]; +} InclusionOpaque; + +Datum brin_inclusion_opcinfo(PG_FUNCTION_ARGS); +Datum brin_inclusion_add_value(PG_FUNCTION_ARGS); +Datum brin_inclusion_consistent(PG_FUNCTION_ARGS); +Datum brin_inclusion_union(PG_FUNCTION_ARGS); +static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, + uint16 procnum); +static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, + Oid subtype, uint16 strategynum); + + +/* + * BRIN inclusion OpcInfo function + */ +Datum +brin_inclusion_opcinfo(PG_FUNCTION_ARGS) +{ + Oid typoid = PG_GETARG_OID(0); + BrinOpcInfo *result; + TypeCacheEntry *bool_typcache = lookup_type_cache(BOOLOID, 0); + + /* + * All members of opaque are initialized lazily; both procinfo arrays + * start out as non-initialized by having fn_oid be InvalidOid, and + * "missing" to false, by zeroing here. strategy_procinfos elements can + * be invalidated when cached_subtype changes by zeroing fn_oid. + * extra_procinfo entries are never invalidated, but if a lookup fails + * (which is expected), extra_proc_missing is set to true, indicating not + * to look it up again. + */ + result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); + result->oi_nstored = 3; + result->oi_opaque = (InclusionOpaque *) + MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); + + /* the union */ + result->oi_typcache[INCLUSION_UNION] = + lookup_type_cache(typoid, 0); + + /* includes elements that are not mergeable */ + result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache; + + /* includes the empty element */ + result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache; + + PG_RETURN_POINTER(result); +} + +/* + * BRIN inclusion add value function + * + * Examine the given index tuple (which contains partial status of a certain + * page range) by comparing it to the given value that comes from another heap + * tuple. If the new value is outside the union specified by the existing + * tuple values, update the index tuple and return true. Otherwise, return + * false and do not modify in this case. + */ +Datum +brin_inclusion_add_value(PG_FUNCTION_ARGS) +{ + BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); + BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); + Datum newval = PG_GETARG_DATUM(2); + bool isnull = PG_GETARG_BOOL(3); + Oid colloid = PG_GET_COLLATION(); + FmgrInfo *finfo; + Datum result; + bool new = false; + AttrNumber attno; + Form_pg_attribute attr; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + attno = column->bv_attno; + attr = bdesc->bd_tupdesc->attrs[attno - 1]; + + /* + * If the recorded value is null, copy the new value (which we know to be + * not null), and we're almost done. + */ + if (column->bv_allnulls) + { + column->bv_values[INCLUSION_UNION] = + datumCopy(newval, attr->attbyval, attr->attlen); + column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false); + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false); + column->bv_allnulls = false; + new = true; + } + + /* + * No need for further processing if the block range is marked as + * containing unmergeable values. + */ + if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) + PG_RETURN_BOOL(false); + + /* + * If the opclass supports the concept of empty values, test the passed + * new value for emptiness; if it returns true, we need to set the + * "contains empty" flag in the element (unless already set). + */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY); + if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval))) + { + if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) + { + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); + } + + if (new) + PG_RETURN_BOOL(true); + + /* Check if the new value is already contained. */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS); + if (finfo != NULL && + DatumGetBool(FunctionCall2Coll(finfo, colloid, + column->bv_values[INCLUSION_UNION], + newval))) + PG_RETURN_BOOL(false); + + /* + * Check if the new value is mergeable to the existing union. If it is + * not, mark the value as containing unmergeable elements and get out. + * + * Note: at this point we could remove the value from the union, since + * it's not going to be used any longer. However, the BRIN framework + * doesn't allow for the value not being present. Improve someday. + */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); + if (finfo != NULL && + !DatumGetBool(FunctionCall2Coll(finfo, colloid, + column->bv_values[INCLUSION_UNION], + newval))) + { + column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + /* Finally, merge the new value to the existing union. */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); + Assert(finfo != NULL); + result = FunctionCall2Coll(finfo, colloid, + column->bv_values[INCLUSION_UNION], newval); + if (!attr->attbyval) + pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION])); + column->bv_values[INCLUSION_UNION] = result; + + PG_RETURN_BOOL(true); +} + +/* + * BRIN inclusion consistent function + * + * All of the strategies are optional. + */ +Datum +brin_inclusion_consistent(PG_FUNCTION_ARGS) +{ + BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); + BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); + ScanKey key = (ScanKey) PG_GETARG_POINTER(2); + Oid colloid = PG_GET_COLLATION(), + subtype; + Datum unionval; + AttrNumber attno; + Datum query; + FmgrInfo *finfo; + Datum result; + + Assert(key->sk_attno == column->bv_attno); + + /* Handle IS NULL/IS NOT NULL tests. */ + if (key->sk_flags & SK_ISNULL) + { + if (key->sk_flags & SK_SEARCHNULL) + { + if (column->bv_allnulls || column->bv_hasnulls) + PG_RETURN_BOOL(true); + PG_RETURN_BOOL(false); + } + + /* + * For IS NOT NULL, we can only skip ranges that are known to have + * only nulls. + */ + Assert(key->sk_flags & SK_SEARCHNOTNULL); + PG_RETURN_BOOL(!column->bv_allnulls); + } + + /* If it is all nulls, it cannot possibly be consistent. */ + if (column->bv_allnulls) + PG_RETURN_BOOL(false); + + /* It has to be checked, if it contains elements that are not mergeable. */ + if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) + PG_RETURN_BOOL(true); + + attno = key->sk_attno; + subtype = key->sk_subtype; + query = key->sk_argument; + unionval = column->bv_values[INCLUSION_UNION]; + switch (key->sk_strategy) + { + /* + * Placement strategies + * + * These are implemented by logically negating the result of the + * converse placement operator; for this to work, the converse operator + * must be part of the opclass. An error will be thrown by + * inclusion_get_strategy_procinfo() if the required strategy is not + * part of the opclass. + * + * These all return false if either argument is empty, so there is + * no need to check for empty elements. + */ + + case RTLeftStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverLeftStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverRightStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTRightStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTBelowStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverAboveStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverBelowStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAboveStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverAboveStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTBelowStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTAboveStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverBelowStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + /* + * Overlap and contains strategies + * + * These strategies are simple enough that we can simply call the + * operator and return its result. Empty elements don't change + * the result. + */ + + case RTOverlapStrategyNumber: + case RTContainsStrategyNumber: + case RTOldContainsStrategyNumber: + case RTContainsElemStrategyNumber: + case RTSubStrategyNumber: + case RTSubEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_DATUM(result); + + /* + * Contained by strategies + * + * We cannot just call the original operator for the contained by + * strategies because some elements can be contained even though + * the union is not; instead we use the overlap operator. + * + * We check for empty elements separately as they are not merged to + * the union but contained by everything. + */ + + case RTContainedByStrategyNumber: + case RTOldContainedByStrategyNumber: + case RTSuperStrategyNumber: + case RTSuperEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + /* + * Adjacent strategy + * + * We test for overlap first but to be safe we need to call + * the actual adjacent operator also. + * + * An empty element cannot be adjacent to any other, so there is + * no need to check for it. + */ + + case RTAdjacentStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAdjacentStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_DATUM(result); + + /* + * Basic comparison strategies + * + * It is straightforward to support the equality strategies with + * the contains operator. Generally, inequality strategies do not + * make much sense for the types which will be used with the + * inclusion BRIN family of opclasses, but is is possible to + * implement them with logical negation of the left-of and right-of + * operators. + * + * NB: These strategies cannot be used with geometric datatypes + * that use comparison of areas! The only exception is the "same" + * strategy. + * + * Empty elements are considered to be less than the others. We + * cannot use the empty support function to check the query is an + * empty element, because the query can be another data type than + * the empty support function argument. So we will return true, + * if there is a possibility that empty elements will change the + * result. + */ + + case RTLessStrategyNumber: + case RTLessEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (!DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTSameStrategyNumber: + case RTEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTContainsStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTGreaterEqualStrategyNumber: + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + if (!DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTGreaterStrategyNumber: + /* no need to check for empty elements */ + finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(finfo, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + default: + /* shouldn't happen */ + elog(ERROR, "invalid strategy number %d", key->sk_strategy); + PG_RETURN_BOOL(false); + } +} + +/* + * BRIN inclusion union function + * + * Given two BrinValues, update the first of them as a union of the summary + * values contained in both. The second one is untouched. + */ +Datum +brin_inclusion_union(PG_FUNCTION_ARGS) +{ + BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); + BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); + BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); + Oid colloid = PG_GET_COLLATION(); + AttrNumber attno; + Form_pg_attribute attr; + FmgrInfo *finfo; + Datum result; + + Assert(col_a->bv_attno == col_b->bv_attno); + + /* Adjust "hasnulls". */ + if (!col_a->bv_hasnulls && col_b->bv_hasnulls) + col_a->bv_hasnulls = true; + + /* If there are no values in B, there's nothing left to do. */ + if (col_b->bv_allnulls) + PG_RETURN_VOID(); + + attno = col_a->bv_attno; + attr = bdesc->bd_tupdesc->attrs[attno - 1]; + + /* + * Adjust "allnulls". If A doesn't have values, just copy the values from + * B into A, and we're done. We cannot run the operators in this case, + * because values in A might contain garbage. Note we already established + * that B contains values. + */ + if (col_a->bv_allnulls) + { + col_a->bv_allnulls = false; + col_a->bv_values[INCLUSION_UNION] = + datumCopy(col_b->bv_values[INCLUSION_UNION], + attr->attbyval, attr->attlen); + col_a->bv_values[INCLUSION_UNMERGEABLE] = + col_b->bv_values[INCLUSION_UNMERGEABLE]; + col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = + col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; + PG_RETURN_VOID(); + } + + /* If B includes empty elements, mark A similarly, if needed. */ + if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && + DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) + col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); + + /* Check if A includes elements that are not mergeable. */ + if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE])) + PG_RETURN_VOID(); + + /* If B includes elements that are not mergeable, mark A similarly. */ + if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE])) + { + col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); + PG_RETURN_VOID(); + } + + /* Check if A and B are mergeable; if not, mark A unmergeable. */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); + if (finfo != NULL && + !DatumGetBool(FunctionCall2Coll(finfo, colloid, + col_a->bv_values[INCLUSION_UNION], + col_b->bv_values[INCLUSION_UNION]))) + { + col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); + PG_RETURN_VOID(); + } + + /* Finally, merge B to A. */ + finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); + Assert(finfo != NULL); + result = FunctionCall2Coll(finfo, colloid, + col_a->bv_values[INCLUSION_UNION], + col_b->bv_values[INCLUSION_UNION]); + if (!attr->attbyval) + pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION])); + col_a->bv_values[INCLUSION_UNION] = result; + + PG_RETURN_VOID(); +} + +/* + * Cache and return inclusion opclass support procedure + * + * Return the procedure corresponding to the given function support number + * or null if it is not exists. + */ +static FmgrInfo * +inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) +{ + InclusionOpaque *opaque; + uint16 basenum = procnum - PROCNUM_BASE; + + /* + * We cache these in the opaque struct, to avoid repetitive syscache + * lookups. + */ + opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * If we already searched for this proc and didn't find it, don't bother + * searching again. + */ + if (opaque->extra_proc_missing[basenum]) + return NULL; + + if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid) + { + if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno, + procnum))) + { + fmgr_info_copy(&opaque->extra_procinfos[basenum], + index_getprocinfo(bdesc->bd_index, attno, procnum), + bdesc->bd_context); + } + else + { + opaque->extra_proc_missing[basenum] = true; + return NULL; + } + } + + return &opaque->extra_procinfos[basenum]; +} + +/* + * Cache and return the procedure of the given strategy + * + * Return the procedure corresponding to the given sub-type and strategy + * number. The data type of the index will be used as the left hand side of + * the operator and the given sub-type will be used as the right hand side. + * Throws an error if the pg_amop row does not exist, but that should not + * happen with a properly configured opclass. + * + * It always throws an error when the data type of the opclass is different + * from the data type of the column or the expression. That happens when the + * column data type has implicit cast to the opclass data type. We don't + * bother casting types, because this situation can easily be avoided by + * setting storage data type to that of the opclass. The same problem does not + * apply to the data type of the right hand side, because the type in the + * ScanKey always matches the opclass' one. + * + * Note: this function mirrors minmax_get_strategy_procinfo; if changes are + * made here, see that function too. + */ +static FmgrInfo * +inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, + uint16 strategynum) +{ + InclusionOpaque *opaque; + + Assert(strategynum >= 1 && + strategynum <= RTMaxStrategyNumber); + + opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; + + /* + * We cache the procedures for the last sub-type in the opaque struct, to + * avoid repetitive syscache lookups. If the sub-type is changed, + * invalidate all the cached entries. + */ + if (opaque->cached_subtype != subtype) + { + uint16 i; + + for (i = 1; i <= RTMaxStrategyNumber; i++) + opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid; + opaque->cached_subtype = subtype; + } + + if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid) + { + Form_pg_attribute attr; + HeapTuple tuple; + Oid opfamily, + oprid; + bool isNull; + + opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; + attr = bdesc->bd_tupdesc->attrs[attno - 1]; + tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), + ObjectIdGetDatum(attr->atttypid), + ObjectIdGetDatum(subtype), + Int16GetDatum(strategynum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + strategynum, attr->atttypid, subtype, opfamily); + + oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, + Anum_pg_amop_amopopr, &isNull)); + ReleaseSysCache(tuple); + Assert(!isNull && RegProcedureIsValid(oprid)); + + fmgr_info_cxt(get_opcode(oprid), + &opaque->strategy_procinfos[strategynum - 1], + bdesc->bd_context); + } + + return &opaque->strategy_procinfos[strategynum - 1]; +} diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c index 1175649a6d..b105f980ec 100644 --- a/src/backend/access/brin/brin_minmax.c +++ b/src/backend/access/brin/brin_minmax.c @@ -28,6 +28,10 @@ typedef struct MinmaxOpaque FmgrInfo strategy_procinfos[BTMaxStrategyNumber]; } MinmaxOpaque; +Datum brin_minmax_opcinfo(PG_FUNCTION_ARGS); +Datum brin_minmax_add_value(PG_FUNCTION_ARGS); +Datum brin_minmax_consistent(PG_FUNCTION_ARGS); +Datum brin_minmax_union(PG_FUNCTION_ARGS); static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum); @@ -302,6 +306,9 @@ brin_minmax_union(PG_FUNCTION_ARGS) /* * Cache and return the procedure for the given strategy. + * + * Note: this function mirrors inclusion_get_strategy_procinfo; see notes + * there. If changes are made here, see that function too. */ static FmgrInfo * minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, diff --git a/src/backend/utils/adt/network_gist.c b/src/backend/utils/adt/network_gist.c index 2e3ee1e8ba..0fdb17f947 100644 --- a/src/backend/utils/adt/network_gist.c +++ b/src/backend/utils/adt/network_gist.c @@ -62,9 +62,9 @@ #define INETSTRAT_GT RTGreaterStrategyNumber #define INETSTRAT_GE RTGreaterEqualStrategyNumber #define INETSTRAT_SUB RTSubStrategyNumber -#define INETSTRAT_SUBEQ RTSubOrEqualStrategyNumber +#define INETSTRAT_SUBEQ RTSubEqualStrategyNumber #define INETSTRAT_SUP RTSuperStrategyNumber -#define INETSTRAT_SUPEQ RTSuperOrEqualStrategyNumber +#define INETSTRAT_SUPEQ RTSuperEqualStrategyNumber /* diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h index 1486d04693..6be199e591 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -86,10 +86,4 @@ extern BrinDesc *brin_build_desc(Relation rel); extern void brin_free_desc(BrinDesc *bdesc); extern Datum brin_summarize_new_values(PG_FUNCTION_ARGS); -/* brin_minmax.c */ -extern Datum brin_minmax_opcinfo(PG_FUNCTION_ARGS); -extern Datum brin_minmax_add_value(PG_FUNCTION_ARGS); -extern Datum brin_minmax_consistent(PG_FUNCTION_ARGS); -extern Datum brin_minmax_union(PG_FUNCTION_ARGS); - #endif /* BRIN_INTERNAL_H */ diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h index 458f4dc888..a372be81e2 100644 --- a/src/include/access/stratnum.h +++ b/src/include/access/stratnum.h @@ -65,9 +65,9 @@ typedef uint16 StrategyNumber; #define RTGreaterStrategyNumber 22 /* for > */ #define RTGreaterEqualStrategyNumber 23 /* for >= */ #define RTSubStrategyNumber 24 /* for inet >> */ -#define RTSubOrEqualStrategyNumber 25 /* for inet <<= */ +#define RTSubEqualStrategyNumber 25 /* for inet <<= */ #define RTSuperStrategyNumber 26 /* for inet << */ -#define RTSuperOrEqualStrategyNumber 27 /* for inet >>= */ +#define RTSuperEqualStrategyNumber 27 /* for inet >>= */ #define RTMaxStrategyNumber 27 diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index b6a6da9a10..bb8a9b08e8 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201505151 +#define CATALOG_VERSION_NO 201505152 #endif diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 79609f7774..8a28b8ea81 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -132,7 +132,8 @@ DESCR("GIN index access method"); DATA(insert OID = 4000 ( spgist 0 5 f f f f f t f t f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions )); DESCR("SP-GiST index access method"); #define SPGIST_AM_OID 4000 -DATA(insert OID = 3580 ( brin 5 14 f f f f t t f t t f f 0 brininsert brinbeginscan - bringetbitmap brinrescan brinendscan brinmarkpos brinrestrpos brinbuild brinbuildempty brinbulkdelete brinvacuumcleanup - brincostestimate brinoptions )); +DATA(insert OID = 3580 ( brin 0 15 f f f f t t f t t f f 0 brininsert brinbeginscan - bringetbitmap brinrescan brinendscan brinmarkpos brinrestrpos brinbuild brinbuildempty brinbulkdelete brinvacuumcleanup - brincostestimate brinoptions )); +DESCR("block range index (BRIN) access method"); #define BRIN_AM_OID 3580 #endif /* PG_AM_H */ diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 9b8294fd94..657ec07059 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -977,6 +977,13 @@ DATA(insert ( 4075 869 869 2 s 1204 3580 0 )); DATA(insert ( 4075 869 869 3 s 1201 3580 0 )); DATA(insert ( 4075 869 869 4 s 1206 3580 0 )); DATA(insert ( 4075 869 869 5 s 1205 3580 0 )); +/* inclusion inet */ +DATA(insert ( 4102 869 869 3 s 3552 3580 0 )); +DATA(insert ( 4102 869 869 7 s 934 3580 0 )); +DATA(insert ( 4102 869 869 8 s 932 3580 0 )); +DATA(insert ( 4102 869 869 18 s 1201 3580 0 )); +DATA(insert ( 4102 869 869 24 s 933 3580 0 )); +DATA(insert ( 4102 869 869 26 s 931 3580 0 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 s 1058 3580 0 )); DATA(insert ( 4076 1042 1042 2 s 1059 3580 0 )); @@ -1072,11 +1079,41 @@ DATA(insert ( 4081 2950 2950 2 s 2976 3580 0 )); DATA(insert ( 4081 2950 2950 3 s 2972 3580 0 )); DATA(insert ( 4081 2950 2950 4 s 2977 3580 0 )); DATA(insert ( 4081 2950 2950 5 s 2975 3580 0 )); +/* inclusion range types */ +DATA(insert ( 4103 3831 3831 1 s 3893 3580 0 )); +DATA(insert ( 4103 3831 3831 2 s 3895 3580 0 )); +DATA(insert ( 4103 3831 3831 3 s 3888 3580 0 )); +DATA(insert ( 4103 3831 3831 4 s 3896 3580 0 )); +DATA(insert ( 4103 3831 3831 5 s 3894 3580 0 )); +DATA(insert ( 4103 3831 3831 7 s 3890 3580 0 )); +DATA(insert ( 4103 3831 3831 8 s 3892 3580 0 )); +DATA(insert ( 4103 3831 2283 16 s 3889 3580 0 )); +DATA(insert ( 4103 3831 3831 17 s 3897 3580 0 )); +DATA(insert ( 4103 3831 3831 18 s 3882 3580 0 )); +DATA(insert ( 4103 3831 3831 20 s 3884 3580 0 )); +DATA(insert ( 4103 3831 3831 21 s 3885 3580 0 )); +DATA(insert ( 4103 3831 3831 22 s 3887 3580 0 )); +DATA(insert ( 4103 3831 3831 23 s 3886 3580 0 )); /* minmax pg_lsn */ DATA(insert ( 4082 3220 3220 1 s 3224 3580 0 )); DATA(insert ( 4082 3220 3220 2 s 3226 3580 0 )); DATA(insert ( 4082 3220 3220 3 s 3222 3580 0 )); DATA(insert ( 4082 3220 3220 4 s 3227 3580 0 )); DATA(insert ( 4082 3220 3220 5 s 3225 3580 0 )); +/* inclusion box */ +DATA(insert ( 4104 603 603 1 s 493 3580 0 )); +DATA(insert ( 4104 603 603 2 s 494 3580 0 )); +DATA(insert ( 4104 603 603 3 s 500 3580 0 )); +DATA(insert ( 4104 603 603 4 s 495 3580 0 )); +DATA(insert ( 4104 603 603 5 s 496 3580 0 )); +DATA(insert ( 4104 603 603 6 s 499 3580 0 )); +DATA(insert ( 4104 603 603 7 s 498 3580 0 )); +DATA(insert ( 4104 603 603 8 s 497 3580 0 )); +DATA(insert ( 4104 603 603 9 s 2571 3580 0 )); +DATA(insert ( 4104 603 603 10 s 2570 3580 0 )); +DATA(insert ( 4104 603 603 11 s 2573 3580 0 )); +DATA(insert ( 4104 603 603 12 s 2572 3580 0 )); +/* we could, but choose not to, supply entries for strategies 13 and 14 */ +DATA(insert ( 4104 603 600 7 s 433 3580 0 )); #endif /* PG_AMOP_H */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 3111d6f4ad..f22e9a61ef 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -551,6 +551,14 @@ DATA(insert ( 4075 869 869 1 3383 )); DATA(insert ( 4075 869 869 2 3384 )); DATA(insert ( 4075 869 869 3 3385 )); DATA(insert ( 4075 869 869 4 3386 )); +/* inclusion inet */ +DATA(insert ( 4102 869 869 1 4105 )); +DATA(insert ( 4102 869 869 2 4106 )); +DATA(insert ( 4102 869 869 3 4107 )); +DATA(insert ( 4102 869 869 4 4108 )); +DATA(insert ( 4102 869 869 11 4063 )); +DATA(insert ( 4102 869 869 12 4071 )); +DATA(insert ( 4102 869 869 13 930 )); /* minmax character */ DATA(insert ( 4076 1042 1042 1 3383 )); DATA(insert ( 4076 1042 1042 2 3384 )); @@ -631,10 +639,25 @@ DATA(insert ( 4081 2950 2950 1 3383 )); DATA(insert ( 4081 2950 2950 2 3384 )); DATA(insert ( 4081 2950 2950 3 3385 )); DATA(insert ( 4081 2950 2950 4 3386 )); +/* inclusion range types */ +DATA(insert ( 4103 3831 3831 1 4105 )); +DATA(insert ( 4103 3831 3831 2 4106 )); +DATA(insert ( 4103 3831 3831 3 4107 )); +DATA(insert ( 4103 3831 3831 4 4108 )); +DATA(insert ( 4103 3831 3831 11 4057 )); +DATA(insert ( 4103 3831 3831 13 3859 )); +DATA(insert ( 4103 3831 3831 14 3850 )); /* minmax pg_lsn */ DATA(insert ( 4082 3220 3220 1 3383 )); DATA(insert ( 4082 3220 3220 2 3384 )); DATA(insert ( 4082 3220 3220 3 3385 )); DATA(insert ( 4082 3220 3220 4 3386 )); +/* inclusion box */ +DATA(insert ( 4104 603 603 1 4105 )); +DATA(insert ( 4104 603 603 2 4106 )); +DATA(insert ( 4104 603 603 3 4107 )); +DATA(insert ( 4104 603 603 4 4108 )); +DATA(insert ( 4104 603 603 11 4067 )); +DATA(insert ( 4104 603 603 13 187 )); #endif /* PG_AMPROC_H */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index f9469375be..a13e082800 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -253,6 +253,7 @@ DATA(insert ( 3580 abstime_minmax_ops PGNSP PGUID 4072 702 t 702 )); DATA(insert ( 3580 reltime_minmax_ops PGNSP PGUID 4073 703 t 703 )); DATA(insert ( 3580 macaddr_minmax_ops PGNSP PGUID 4074 829 t 829 )); DATA(insert ( 3580 inet_minmax_ops PGNSP PGUID 4075 869 f 869 )); +DATA(insert ( 3580 inet_inclusion_ops PGNSP PGUID 4102 869 t 869 )); DATA(insert ( 3580 bpchar_minmax_ops PGNSP PGUID 4076 1042 t 1042 )); DATA(insert ( 3580 time_minmax_ops PGNSP PGUID 4077 1083 t 1083 )); DATA(insert ( 3580 date_minmax_ops PGNSP PGUID 4059 1082 t 1082 )); @@ -265,7 +266,10 @@ DATA(insert ( 3580 varbit_minmax_ops PGNSP PGUID 4080 1562 t 1562 )); DATA(insert ( 3580 numeric_minmax_ops PGNSP PGUID 4055 1700 t 1700 )); /* no brin opclass for record, anyarray */ DATA(insert ( 3580 uuid_minmax_ops PGNSP PGUID 4081 2950 t 2950 )); +DATA(insert ( 3580 range_inclusion_ops PGNSP PGUID 4103 3831 t 3831 )); DATA(insert ( 3580 pg_lsn_minmax_ops PGNSP PGUID 4082 3220 t 3220 )); -/* no brin opclass for enum, tsvector, tsquery, jsonb, range */ +/* no brin opclass for enum, tsvector, tsquery, jsonb */ +DATA(insert ( 3580 box_inclusion_ops PGNSP PGUID 4104 603 t 603 )); +/* no brin opclass for the geometric types except box */ #endif /* PG_OPCLASS_H */ diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index 97ffa327cf..acbc1002b4 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -172,12 +172,15 @@ DATA(insert OID = 4072 ( 3580 abstime_minmax_ops PGNSP PGUID )); DATA(insert OID = 4073 ( 3580 reltime_minmax_ops PGNSP PGUID )); DATA(insert OID = 4074 ( 3580 macaddr_minmax_ops PGNSP PGUID )); DATA(insert OID = 4075 ( 3580 network_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4102 ( 3580 network_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4076 ( 3580 bpchar_minmax_ops PGNSP PGUID )); DATA(insert OID = 4077 ( 3580 time_minmax_ops PGNSP PGUID )); DATA(insert OID = 4078 ( 3580 interval_minmax_ops PGNSP PGUID )); DATA(insert OID = 4079 ( 3580 bit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4080 ( 3580 varbit_minmax_ops PGNSP PGUID )); DATA(insert OID = 4081 ( 3580 uuid_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4103 ( 3580 range_inclusion_ops PGNSP PGUID )); DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); +DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); #endif /* PG_OPFAMILY_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c2185bd9ad..b5b93450ec 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4225,6 +4225,16 @@ DESCR("BRIN minmax support"); DATA(insert OID = 3386 ( brin_minmax_union PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_union _null_ _null_ _null_ )); DESCR("BRIN minmax support"); +/* BRIN inclusion */ +DATA(insert OID = 4105 ( brin_inclusion_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_opcinfo _null_ _null_ _null_ )); +DESCR("BRIN inclusion support"); +DATA(insert OID = 4106 ( brin_inclusion_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_add_value _null_ _null_ _null_ )); +DESCR("BRIN inclusion support"); +DATA(insert OID = 4107 ( brin_inclusion_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_consistent _null_ _null_ _null_ )); +DESCR("BRIN inclusion support"); +DATA(insert OID = 4108 ( brin_inclusion_union PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_union _null_ _null_ _null_ )); +DESCR("BRIN inclusion support"); + /* 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_ _null_ pg_advisory_lock_int8 _null_ _null_ _null_ )); DESCR("obtain exclusive advisory lock"); diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out index 4fe6f07194..8ba961a662 100644 --- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -23,7 +23,9 @@ CREATE TABLE brintest (byteacol bytea, varbitcol bit varying(16), numericcol numeric, uuidcol uuid, - lsncol pg_lsn + int4rangecol int4range, + lsncol pg_lsn, + boxcol box ) WITH (fillfactor=10, autovacuum_enabled=off); INSERT INTO brintest SELECT repeat(stringu1, 8)::bytea, @@ -50,12 +52,15 @@ INSERT INTO brintest SELECT tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 LIMIT 25; + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) +FROM tenk1 LIMIT 100; -- throw in some NULL's and different values -INSERT INTO brintest (inetcol, cidrcol) SELECT +INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, - cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous, + 'empty'::int4range FROM tenk1 LIMIT 25; CREATE INDEX brinidx ON brintest USING brin ( byteacol, @@ -70,6 +75,7 @@ CREATE INDEX brinidx ON brintest USING brin ( float4col, float8col, macaddrcol, + inetcol inet_inclusion_ops, inetcol inet_minmax_ops, bpcharcol, datecol, @@ -82,7 +88,9 @@ CREATE INDEX brinidx ON brintest USING brin ( varbitcol, numericcol, uuidcol, - lsncol + int4rangecol, + lsncol, + boxcol ) with (pages_per_range = 1); CREATE TABLE brinopers (colname name, typ text, op text[], value text[], check (cardinality(op) = cardinality(value))); @@ -128,7 +136,12 @@ INSERT INTO brinopers VALUES ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}'), ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}'), ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}'), - ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'); + ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}'), + ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}'), + ('int4rangecol', 'int4', '{@>}', '{1500}'), + ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'), + ('boxcol', 'point', '{@>}', '{"(500,43)"}'), + ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}'); DO $x$ DECLARE r record; @@ -222,7 +235,9 @@ INSERT INTO brintest SELECT tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) FROM tenk1 LIMIT 5 OFFSET 5; SELECT brin_summarize_new_values('brinidx'::regclass); brin_summarize_new_values diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 6b248f2801..0d2fce9f87 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1657,10 +1657,33 @@ ORDER BY 1, 2, 3; 2742 | 10 | ?| 2742 | 11 | ?& 3580 | 1 | < + 3580 | 1 | << + 3580 | 2 | &< 3580 | 2 | <= + 3580 | 3 | && 3580 | 3 | = + 3580 | 4 | &> 3580 | 4 | >= 3580 | 5 | > + 3580 | 5 | >> + 3580 | 6 | ~= + 3580 | 7 | >>= + 3580 | 7 | @> + 3580 | 8 | <<= + 3580 | 8 | <@ + 3580 | 9 | &<| + 3580 | 10 | <<| + 3580 | 11 | |>> + 3580 | 12 | |&> + 3580 | 16 | @> + 3580 | 17 | -|- + 3580 | 18 | = + 3580 | 20 | < + 3580 | 21 | <= + 3580 | 22 | > + 3580 | 23 | >= + 3580 | 24 | >> + 3580 | 26 | << 4000 | 1 | << 4000 | 1 | ~<~ 4000 | 2 | &< @@ -1683,7 +1706,7 @@ ORDER BY 1, 2, 3; 4000 | 15 | > 4000 | 16 | @> 4000 | 18 | = -(85 rows) +(108 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql index 6a695bbd20..e92717f6ff 100644 --- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -23,7 +23,9 @@ CREATE TABLE brintest (byteacol bytea, varbitcol bit varying(16), numericcol numeric, uuidcol uuid, - lsncol pg_lsn + int4rangecol int4range, + lsncol pg_lsn, + boxcol box ) WITH (fillfactor=10, autovacuum_enabled=off); INSERT INTO brintest SELECT @@ -51,13 +53,16 @@ INSERT INTO brintest SELECT tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn -FROM tenk1 LIMIT 25; + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) +FROM tenk1 LIMIT 100; -- throw in some NULL's and different values -INSERT INTO brintest (inetcol, cidrcol) SELECT +INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT inet 'fe80::6e40:8ff:fea9:8c46' + tenthous, - cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous + cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous, + 'empty'::int4range FROM tenk1 LIMIT 25; CREATE INDEX brinidx ON brintest USING brin ( @@ -73,6 +78,7 @@ CREATE INDEX brinidx ON brintest USING brin ( float4col, float8col, macaddrcol, + inetcol inet_inclusion_ops, inetcol inet_minmax_ops, bpcharcol, datecol, @@ -85,7 +91,9 @@ CREATE INDEX brinidx ON brintest USING brin ( varbitcol, numericcol, uuidcol, - lsncol + int4rangecol, + lsncol, + boxcol ) with (pages_per_range = 1); CREATE TABLE brinopers (colname name, typ text, op text[], value text[], @@ -133,7 +141,12 @@ INSERT INTO brinopers VALUES ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}'), ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}'), ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}'), - ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'); + ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}'), + ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}'), + ('int4rangecol', 'int4', '{@>}', '{1500}'), + ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'), + ('boxcol', 'point', '{@>}', '{"(500,43)"}'), + ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}'); DO $x$ DECLARE @@ -229,7 +242,9 @@ INSERT INTO brintest SELECT tenthous::bit(16)::varbit, tenthous::numeric(36,30) * fivethous * even / (hundred + 1), format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid, - format('%s/%s%s', odd, even, tenthous)::pg_lsn + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) FROM tenk1 LIMIT 5 OFFSET 5; SELECT brin_summarize_new_values('brinidx'::regclass);