Emre Hasegeli wrote: > > I pushed patches 04 and 07, as well as adopting some of the changes to > > the regression test in 06. I'm afraid I caused a bit of merge pain for > > you -- sorry about that. > > No problem. I rebased the remaining ones.
Thanks! After some back-and-forth between Emre and me, here's an updated patch. My changes are cosmetic; for a detailed rundown, see https://github.com/alvherre/postgres/commits/brin-inclusion Note that datatype point was removed: it turns out that unless we get box_contain_pt changed to use FPlt() et al, indexes created with this opclass would be corrupt. And we cannot simply change box_contain_pt, because that would break existing GiST and SP-GiST indexes that use it today and pg_upgrade to 9.5! So that needs to be considered separately. Also, removing point support means remove the CAST support procedure, because there is no use for it in the supported types. Also, patch 05 in the previous submissions goes away completely because there's no need for those (box,point) operators anymore. There's nothing Earth-shattering here that hasn't been seen in previous submissions by Emre. One item of note is that this patch is blindly removing the assert-only blocks as previously discussed, without any replacement. Need to think more on how to put something back ... -- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 92dac7c..b26bcf3 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -72,7 +72,9 @@ <para> The <firstterm>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 <firstterm>inclusion</> + operator classes store a value which includes the values in the indexed + column within the range. </para> <table id="brin-builtin-opclasses-table"> @@ -252,6 +254,18 @@ </entry> </row> <row> + <entry><literal>inet_inclusion_ops</literal></entry> + <entry><type>inet</type></entry> + <entry> + <literal>&&</> + <literal>>></> + <literal>>>=</> + <literal><<</literal> + <literal><<=</literal> + <literal>=</literal> + </entry> + </row> + <row> <entry><literal>bpchar_minmax_ops</literal></entry> <entry><type>character</type></entry> <entry> @@ -373,6 +387,25 @@ </entry> </row> <row> + <entry><literal>range_inclusion_ops</></entry> + <entry><type>any range type</type></entry> + <entry> + <literal>&&</> + <literal>&></> + <literal>&<</> + <literal>>></> + <literal><<</> + <literal><@</> + <literal>=</> + <literal>@></> + <literal><</literal> + <literal><=</literal> + <literal>=</literal> + <literal>>=</literal> + <literal>></literal> + </entry> + </row> + <row> <entry><literal>pg_lsn_minmax_ops</literal></entry> <entry><type>pg_lsn</type></entry> <entry> @@ -383,6 +416,43 @@ <literal>></literal> </entry> </row> + <row> + <entry><literal>box_inclusion_ops</></entry> + <entry><type>box</type></entry> + <entry> + <literal>&&</> + <literal>&></> + <literal>&<</> + <literal>>></> + <literal><<</> + <literal><@</> + <literal>~=</> + <literal>@></> + <literal>&>|</> + <literal>|&<</> + <literal>>>|</> + <literal>|<<</literal> + </entry> + </row> + <row> + <entry><literal>point_box_inclusion_ops</></entry> + <entry><type>point</type></entry> + <entry> + <literal>&&</> + <literal>&></> + <literal>&<</> + <literal>>></> + <literal><<</> + <literal><@</> + <literal>~=</> + <literal>&>|</> + <literal>|&<</> + <literal>>>|</> + <literal>|<<</literal> + <literal>>^</> + <literal>|<^</literal> + </entry> + </row> </tbody> </tgroup> </table> diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index ac44fcd..fb36882 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 2b5fb8d..1995125 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 0000000..c6d5b48 --- /dev/null +++ b/src/backend/access/brin/brin_inclusion.c @@ -0,0 +1,697 @@ +/* + * 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 explicitely + * 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 somewhere in the block range + * 2 - whether the values in the block range cannot be merged + */ +#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 *frmg; + 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). + */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY); + if (frmg != NULL && DatumGetBool(FunctionCall1Coll(frmg, 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. */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS); + if (frmg != NULL && + DatumGetBool(FunctionCall2Coll(frmg, 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. + */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); + if (frmg != NULL && + !DatumGetBool(FunctionCall2Coll(frmg, 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. */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); + Assert(frmg != NULL); + result = FunctionCall2Coll(frmg, 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 *frmg; + 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 + * + * They are implemented by using logical negation of the other + * placement operators. So the other strategies are required for + * them to be used. An error will be thrown by + * inclusion_get_strategy_procinfo() if the required strategy does + * not exists. + * + * These all return false if either argument is empty, so there is + * no need to check for empty elements. + */ + + case RTLeftStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverRightStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverLeftStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverRightStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTRightStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverLeftStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTBelowStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverAboveStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverBelowStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAboveStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTOverAboveStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTBelowStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + case RTAboveStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverBelowStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + PG_RETURN_BOOL(!DatumGetBool(result)); + + /* + * Overlaps and contains strategies + * + * These strategies are simple enough that we can simply call the + * operator of the strategy. Empty elements don't change their + * results. + */ + + case RTOverlapStrategyNumber: + case RTContainsStrategyNumber: + case RTOldContainsStrategyNumber: + case RTContainsElemStrategyNumber: + case RTContainsNotEqualStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + key->sk_strategy); + result = FunctionCall2Coll(frmg, 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 thought + * the union is not. + * + * Also, we will check for empty elements as they are not merged + * to the union but contained by everything. + */ + + case RTContainedByStrategyNumber: + case RTOldContainedByStrategyNumber: + case RTContainedByNotEqualStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + /* + * Adjacent strategy + * + * To be adjacent with an element in the query most likely to + * overlap with the union. We will call the actual adjacent + * procedure in case it is not. + * + * An empty element cannot be adjacent to any other, so there is + * no need to check for it. + */ + + case RTAdjacentStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTOverlapStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTAdjacentStrategyNumber); + result = FunctionCall2Coll(frmg, 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: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTRightStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + if (!DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTSameStrategyNumber: + case RTEqualStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTContainsStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + if (DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTGreaterEqualStrategyNumber: + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(frmg, colloid, unionval, query); + if (!DatumGetBool(result)) + PG_RETURN_BOOL(true); + + PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); + + case RTGreaterStrategyNumber: + /* + * This one is different from the ones above others because of the + * assumption that "empty is less than all other values". + */ + frmg = inclusion_get_strategy_procinfo(bdesc, attno, subtype, + RTLeftStrategyNumber); + result = FunctionCall2Coll(frmg, 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 *frmg; + 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. */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); + if (frmg != NULL && + !DatumGetBool(FunctionCall2Coll(frmg, 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. */ + frmg = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); + Assert(frmg != NULL); + result = FunctionCall2Coll(frmg, 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 d64c4f0..d06812d 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/include/access/brin_internal.h b/src/include/access/brin_internal.h index 1486d04..6be199e 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/gist.h b/src/include/access/gist.h index 50261b8..81e559b 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -37,26 +37,6 @@ #define GISTNProcs 9 /* - * strategy numbers for GiST opclasses that want to implement the old - * RTREE behavior. - */ -#define RTLeftStrategyNumber 1 -#define RTOverLeftStrategyNumber 2 -#define RTOverlapStrategyNumber 3 -#define RTOverRightStrategyNumber 4 -#define RTRightStrategyNumber 5 -#define RTSameStrategyNumber 6 -#define RTContainsStrategyNumber 7 /* for @> */ -#define RTContainedByStrategyNumber 8 /* for <@ */ -#define RTOverBelowStrategyNumber 9 -#define RTBelowStrategyNumber 10 -#define RTAboveStrategyNumber 11 -#define RTOverAboveStrategyNumber 12 -#define RTOldContainsStrategyNumber 13 /* for old spelling of @> */ -#define RTOldContainedByStrategyNumber 14 /* for old spelling of <@ */ -#define RTKNNSearchStrategyNumber 15 - -/* * Page opaque data in a GiST index page. */ #define F_LEAF (1 << 0) /* leaf page */ diff --git a/src/include/access/skey.h b/src/include/access/skey.h index eeee1f4..487a928 100644 --- a/src/include/access/skey.h +++ b/src/include/access/skey.h @@ -41,6 +41,40 @@ typedef uint16 StrategyNumber; /* + * Strategy numbers for opclasses that want to implement the old RTREE behavior. + * Operator strategy numbers used in the GiST, SP-GiST, GIN, BRIN opclasses. + * New numbers are chosen to match up operator names with existing usages. + */ +#define RTLeftStrategyNumber 1 /* for << */ +#define RTOverLeftStrategyNumber 2 /* for &< */ +#define RTOverlapStrategyNumber 3 /* for && */ +#define RTOverRightStrategyNumber 4 /* for &> */ +#define RTRightStrategyNumber 5 /* for >> */ +#define RTSameStrategyNumber 6 /* for ~= */ +#define RTContainsStrategyNumber 7 /* for @> */ +#define RTContainedByStrategyNumber 8 /* for <@ */ +#define RTOverBelowStrategyNumber 9 /* for &<| */ +#define RTBelowStrategyNumber 10 /* for <<| */ +#define RTAboveStrategyNumber 11 /* for |>> */ +#define RTOverAboveStrategyNumber 12 /* for |&> */ +#define RTOldContainsStrategyNumber 13 /* for old spelling of @> */ +#define RTOldContainedByStrategyNumber 14 /* for old spelling of <@ */ +#define RTKNNSearchStrategyNumber 15 +#define RTContainsElemStrategyNumber 16 /* for range types @> elem */ +#define RTAdjacentStrategyNumber 17 /* for -|- */ +#define RTEqualStrategyNumber 18 /* for = */ +#define RTNotEqualStrategyNumber 19 /* for != */ +#define RTLessStrategyNumber 20 /* for < */ +#define RTLessEqualStrategyNumber 21 /* for <= */ +#define RTGreaterStrategyNumber 22 /* for > */ +#define RTGreaterEqualStrategyNumber 23 /* for >= */ +#define RTContainsNotEqualStrategyNumber 24 /* for inet >> */ +#define RTContainedByNotEqualStrategyNumber 26 /* for inet << */ + +#define RTMaxStrategyNumber 26 + + +/* * A ScanKey represents the application of a comparison operator between * a table or index column and a constant. When it's part of an array of * ScanKeys, the comparison conditions are implicitly ANDed. The index diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 79609f7..8a28b8e 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 5aab896..94396b3 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -975,6 +975,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 )); @@ -1070,11 +1077,42 @@ 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 )); +DATA(insert ( 4104 603 603 13 s 2863 3580 0 )); +DATA(insert ( 4104 603 603 14 s 2862 3580 0 )); +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 e3de3b5..d24dc5a 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -549,6 +549,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 )); @@ -629,10 +637,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 f946937..a13e082 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 97ffa32..acbc100 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 a1e2442..99a53cd 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4221,6 +4221,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 4fe6f07..8c7a661 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 + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) FROM tenk1 LIMIT 25; -- 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 6b248f2..e545102 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1657,10 +1657,35 @@ 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 | 13 | ~ + 3580 | 14 | @ + 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 +1708,7 @@ ORDER BY 1, 2, 3; 4000 | 15 | > 4000 | 16 | @> 4000 | 18 | = -(85 rows) +(110 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 6a695bb..59ae51a 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 + int4range(thousand, twothousand), + format('%s/%s%s', odd, even, tenthous)::pg_lsn, + box(point(odd, even), point(thousand, twothousand)) FROM tenk1 LIMIT 25; -- 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);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers