This lets BRIN be used with R-Tree-like indexing strategies.
Also provided are operator classes for range types, box and inet/cidr.
The infrastructure provided here should be sufficient to create operator
classes for similar datatypes; for instance, opclasses for PostGIS
geometries should be doable, though we didn't try to implement one.
(A box/point opclass was also submitted, but we ripped it out before
commit because the handling of floating point comparisons in existing
code is inconsistent and would generate corrupt indexes.)
Author: Emre Hasegeli. Cosmetic changes by me
Review: Andreas Karlsson
<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">
<literal>></literal>
</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>
<literal>></literal>
</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>
<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>
</tbody>
</tgroup>
</table>
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
BrinMemTuple *dtup;
BlockNumber heapBlk;
int keyno;
-#ifdef USE_ASSERT_CHECKING
- BrinTuple *tmptup;
- BrinMemTuple *tmpdtup;
- Size tmpsiz;
-#endif
CHECK_FOR_INTERRUPTS();
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
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)
{
/*
* 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)
BlockNumber nblocks;
BlockNumber heapBlk;
int totalpages = 0;
- int keyno;
FmgrInfo *consistentFn;
MemoryContext oldcxt;
MemoryContext perRangeCxt;
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
else
{
BrinMemTuple *dtup;
- int keyno;
dtup = brin_deform_tuple(bdesc, tup);
if (dtup->bt_placeholder)
}
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
(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
--- /dev/null
+/*
+ * 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];
+}
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);
/*
* 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,
#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
/*
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 */
#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
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201505151
+#define CATALOG_VERSION_NO 201505152
#endif
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 */
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 ));
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 */
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 ));
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 */
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 ));
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 */
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 */
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");
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,
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,
float4col,
float8col,
macaddrcol,
+ inetcol inet_inclusion_ops,
inetcol inet_minmax_ops,
bpcharcol,
datecol,
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)));
('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;
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
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 | &<
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
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
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 (
float4col,
float8col,
macaddrcol,
+ inetcol inet_inclusion_ops,
inetcol inet_minmax_ops,
bpcharcol,
datecol,
varbitcol,
numericcol,
uuidcol,
- lsncol
+ int4rangecol,
+ lsncol,
+ boxcol
) with (pages_per_range = 1);
CREATE TABLE brinopers (colname name, typ text, op text[], value text[],
('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
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);