]> granicus.if.org Git - postgresql/commitdiff
Add BRIN infrastructure for "inclusion" opclasses
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 15 May 2015 21:05:22 +0000 (18:05 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 15 May 2015 21:05:22 +0000 (18:05 -0300)
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

18 files changed:
doc/src/sgml/brin.sgml
src/backend/access/brin/Makefile
src/backend/access/brin/brin.c
src/backend/access/brin/brin_inclusion.c [new file with mode: 0644]
src/backend/access/brin/brin_minmax.c
src/backend/utils/adt/network_gist.c
src/include/access/brin_internal.h
src/include/access/stratnum.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_opfamily.h
src/include/catalog/pg_proc.h
src/test/regress/expected/brin.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/brin.sql

index 92dac7c60b51384ff89823febd684f88abb4cbb6..4d8fd20c1c41936aac67ee4630286e8518f102fd 100644 (file)
@@ -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">
       <literal>&gt;</literal>
      </entry>
     </row>
+    <row>
+     <entry><literal>inet_inclusion_ops</literal></entry>
+     <entry><type>inet</type></entry>
+     <entry>
+      <literal>&amp;&amp;</>
+      <literal>&gt;&gt;</>
+      <literal>&gt;&gt;=</>
+      <literal>&lt;&lt;</literal>
+      <literal>&lt;&lt;=</literal>
+      <literal>=</literal>
+     </entry>
+    </row>
     <row>
      <entry><literal>bpchar_minmax_ops</literal></entry>
      <entry><type>character</type></entry>
       <literal>&gt;</literal>
      </entry>
     </row>
+    <row>
+     <entry><literal>range_inclusion_ops</></entry>
+     <entry><type>any range type</type></entry>
+     <entry>
+      <literal>&amp;&amp;</>
+      <literal>&amp;&gt;</>
+      <literal>&amp;&lt;</>
+      <literal>&gt;&gt;</>
+      <literal>&lt;&lt;</>
+      <literal>&lt;@</>
+      <literal>=</>
+      <literal>@&gt;</>
+      <literal>&lt;</literal>
+      <literal>&lt;=</literal>
+      <literal>=</literal>
+      <literal>&gt;=</literal>
+      <literal>&gt;</literal>
+     </entry>
+    </row>
     <row>
      <entry><literal>pg_lsn_minmax_ops</literal></entry>
      <entry><type>pg_lsn</type></entry>
       <literal>&gt;</literal>
      </entry>
     </row>
+    <row>
+     <entry><literal>box_inclusion_ops</></entry>
+     <entry><type>box</type></entry>
+     <entry>
+      <literal>&amp;&amp;</>
+      <literal>&amp;&gt;</>
+      <literal>&amp;&lt;</>
+      <literal>&gt;&gt;</>
+      <literal>&lt;&lt;</>
+      <literal>&lt;@</>
+      <literal>~=</>
+      <literal>@&gt;</>
+      <literal>&amp;&gt;|</>
+      <literal>|&amp;&lt;</>
+      <literal>&gt;&gt;|</>
+      <literal>|&lt;&lt;</literal>
+     </entry>
+    </row>
    </tbody>
   </tgroup>
  </table>
index ac44fcdee39e13e0cce59cc9f5c184c1a45adbc0..f4572d80a898d2181b90f305ca580f9e373ffa7b 100644 (file)
@@ -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
index 2b5fb8dce9b2eb1dbaaf0f2b58bf324de528a74f..199512551e5d381765d9daa349b3c207e8e21086 100644 (file)
@@ -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 (file)
index 0000000..1f0bc7f
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * brin_inclusion.c
+ *             Implementation of inclusion opclasses for BRIN
+ *
+ * This module provides framework BRIN support functions for the "inclusion"
+ * operator classes.  A few SQL-level support functions are also required for
+ * each opclass.
+ *
+ * The "inclusion" BRIN strategy is useful for types that support R-Tree
+ * operations.  This implementation is a straight mapping of those operations
+ * to the block-range nature of BRIN, with two exceptions: (a) we explicitly
+ * support "empty" elements: at least with range types, we need to consider
+ * emptiness separately from regular R-Tree strategies; and (b) we need to
+ * consider "unmergeable" elements, that is, a set of elements for whose union
+ * no representation exists.  The only case where that happens as of this
+ * writing is the INET type, where IPv6 values cannot be merged with IPv4
+ * values.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/access/brin/brin_inclusion.c
+ */
+#include "postgres.h"
+
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/genam.h"
+#include "access/skey.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_type.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Additional SQL level support functions
+ *
+ * Procedure numbers must not use values reserved for BRIN itself; see
+ * brin_internal.h.
+ */
+#define                INCLUSION_MAX_PROCNUMS  4       /* maximum support procs we need */
+#define                PROCNUM_MERGE                   11      /* required */
+#define                PROCNUM_MERGEABLE               12      /* optional */
+#define                PROCNUM_CONTAINS                13      /* optional */
+#define                PROCNUM_EMPTY                   14      /* optional */
+
+
+/*
+ * Subtract this from procnum to obtain index in InclusionOpaque arrays
+ * (Must be equal to minimum of private procnums).
+ */
+#define                PROCNUM_BASE                    11
+
+/*-
+ * The values stored in the bv_values arrays correspond to:
+ *
+ * 0 - the union of the values in the block range
+ * 1 - whether an empty value is present in any tuple in the block range
+ * 2 - whether the values in the block range cannot be merged (e.g. an IPv6
+ *     address amidst IPv4 addresses).
+ */
+#define        INCLUSION_UNION                         0
+#define        INCLUSION_UNMERGEABLE           1
+#define        INCLUSION_CONTAINS_EMPTY        2
+
+
+typedef struct InclusionOpaque
+{
+       FmgrInfo        extra_procinfos[INCLUSION_MAX_PROCNUMS];
+       bool            extra_proc_missing[INCLUSION_MAX_PROCNUMS];
+       Oid                     cached_subtype;
+       FmgrInfo        strategy_procinfos[RTMaxStrategyNumber];
+} InclusionOpaque;
+
+Datum          brin_inclusion_opcinfo(PG_FUNCTION_ARGS);
+Datum          brin_inclusion_add_value(PG_FUNCTION_ARGS);
+Datum          brin_inclusion_consistent(PG_FUNCTION_ARGS);
+Datum          brin_inclusion_union(PG_FUNCTION_ARGS);
+static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
+                                          uint16 procnum);
+static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
+                                                               Oid subtype, uint16 strategynum);
+
+
+/*
+ * BRIN inclusion OpcInfo function
+ */
+Datum
+brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
+{
+       Oid                     typoid = PG_GETARG_OID(0);
+       BrinOpcInfo *result;
+       TypeCacheEntry *bool_typcache = lookup_type_cache(BOOLOID, 0);
+
+       /*
+        * All members of opaque are initialized lazily; both procinfo arrays
+        * start out as non-initialized by having fn_oid be InvalidOid, and
+        * "missing" to false, by zeroing here.  strategy_procinfos elements can
+        * be invalidated when cached_subtype changes by zeroing fn_oid.
+        * extra_procinfo entries are never invalidated, but if a lookup fails
+        * (which is expected), extra_proc_missing is set to true, indicating not
+        * to look it up again.
+        */
+       result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
+       result->oi_nstored = 3;
+       result->oi_opaque = (InclusionOpaque *)
+               MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
+
+       /* the union */
+       result->oi_typcache[INCLUSION_UNION] =
+               lookup_type_cache(typoid, 0);
+
+       /* includes elements that are not mergeable */
+       result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
+
+       /* includes the empty element */
+       result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
+
+       PG_RETURN_POINTER(result);
+}
+
+/*
+ * BRIN inclusion add value function
+ *
+ * Examine the given index tuple (which contains partial status of a certain
+ * page range) by comparing it to the given value that comes from another heap
+ * tuple.  If the new value is outside the union specified by the existing
+ * tuple values, update the index tuple and return true.  Otherwise, return
+ * false and do not modify in this case.
+ */
+Datum
+brin_inclusion_add_value(PG_FUNCTION_ARGS)
+{
+       BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+       BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+       Datum           newval = PG_GETARG_DATUM(2);
+       bool            isnull = PG_GETARG_BOOL(3);
+       Oid                     colloid = PG_GET_COLLATION();
+       FmgrInfo   *finfo;
+       Datum           result;
+       bool            new = false;
+       AttrNumber      attno;
+       Form_pg_attribute attr;
+
+       /*
+        * If the new value is null, we record that we saw it if it's the first
+        * one; otherwise, there's nothing to do.
+        */
+       if (isnull)
+       {
+               if (column->bv_hasnulls)
+                       PG_RETURN_BOOL(false);
+
+               column->bv_hasnulls = true;
+               PG_RETURN_BOOL(true);
+       }
+
+       attno = column->bv_attno;
+       attr = bdesc->bd_tupdesc->attrs[attno - 1];
+
+       /*
+        * If the recorded value is null, copy the new value (which we know to be
+        * not null), and we're almost done.
+        */
+       if (column->bv_allnulls)
+       {
+               column->bv_values[INCLUSION_UNION] =
+                       datumCopy(newval, attr->attbyval, attr->attlen);
+               column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
+               column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
+               column->bv_allnulls = false;
+               new = true;
+       }
+
+       /*
+        * No need for further processing if the block range is marked as
+        * containing unmergeable values.
+        */
+       if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
+               PG_RETURN_BOOL(false);
+
+       /*
+        * If the opclass supports the concept of empty values, test the passed
+        * new value for emptiness; if it returns true, we need to set the
+        * "contains empty" flag in the element (unless already set).
+        */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
+       if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
+       {
+               if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
+               {
+                       column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
+                       PG_RETURN_BOOL(true);
+               }
+
+               PG_RETURN_BOOL(false);
+       }
+
+       if (new)
+               PG_RETURN_BOOL(true);
+
+       /* Check if the new value is already contained. */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
+       if (finfo != NULL &&
+               DatumGetBool(FunctionCall2Coll(finfo, colloid,
+                                                                          column->bv_values[INCLUSION_UNION],
+                                                                          newval)))
+               PG_RETURN_BOOL(false);
+
+       /*
+        * Check if the new value is mergeable to the existing union.  If it is
+        * not, mark the value as containing unmergeable elements and get out.
+        *
+        * Note: at this point we could remove the value from the union, since
+        * it's not going to be used any longer.  However, the BRIN framework
+        * doesn't allow for the value not being present.  Improve someday.
+        */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
+       if (finfo != NULL &&
+               !DatumGetBool(FunctionCall2Coll(finfo, colloid,
+                                                                               column->bv_values[INCLUSION_UNION],
+                                                                               newval)))
+       {
+               column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
+               PG_RETURN_BOOL(true);
+       }
+
+       /* Finally, merge the new value to the existing union. */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
+       Assert(finfo != NULL);
+       result = FunctionCall2Coll(finfo, colloid,
+                                                          column->bv_values[INCLUSION_UNION], newval);
+       if (!attr->attbyval)
+               pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
+       column->bv_values[INCLUSION_UNION] = result;
+
+       PG_RETURN_BOOL(true);
+}
+
+/*
+ * BRIN inclusion consistent function
+ *
+ * All of the strategies are optional.
+ */
+Datum
+brin_inclusion_consistent(PG_FUNCTION_ARGS)
+{
+       BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+       BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+       ScanKey         key = (ScanKey) PG_GETARG_POINTER(2);
+       Oid                     colloid = PG_GET_COLLATION(),
+                               subtype;
+       Datum           unionval;
+       AttrNumber      attno;
+       Datum           query;
+       FmgrInfo   *finfo;
+       Datum           result;
+
+       Assert(key->sk_attno == column->bv_attno);
+
+       /* Handle IS NULL/IS NOT NULL tests. */
+       if (key->sk_flags & SK_ISNULL)
+       {
+               if (key->sk_flags & SK_SEARCHNULL)
+               {
+                       if (column->bv_allnulls || column->bv_hasnulls)
+                               PG_RETURN_BOOL(true);
+                       PG_RETURN_BOOL(false);
+               }
+
+               /*
+                * For IS NOT NULL, we can only skip ranges that are known to have
+                * only nulls.
+                */
+               Assert(key->sk_flags & SK_SEARCHNOTNULL);
+               PG_RETURN_BOOL(!column->bv_allnulls);
+       }
+
+       /* If it is all nulls, it cannot possibly be consistent. */
+       if (column->bv_allnulls)
+               PG_RETURN_BOOL(false);
+
+       /* It has to be checked, if it contains elements that are not mergeable. */
+       if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
+               PG_RETURN_BOOL(true);
+
+       attno = key->sk_attno;
+       subtype = key->sk_subtype;
+       query = key->sk_argument;
+       unionval = column->bv_values[INCLUSION_UNION];
+       switch (key->sk_strategy)
+       {
+               /*
+                * Placement strategies
+                *
+                * These are implemented by logically negating the result of the
+                * converse placement operator; for this to work, the converse operator
+                * must be part of the opclass.  An error will be thrown by
+                * inclusion_get_strategy_procinfo() if the required strategy is not
+                * part of the opclass.
+                *
+                * These all return false if either argument is empty, so there is
+                * no need to check for empty elements.
+                */
+
+               case RTLeftStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverRightStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTOverLeftStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTRightStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTOverRightStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTLeftStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTRightStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverLeftStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTBelowStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverAboveStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTOverBelowStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTAboveStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTOverAboveStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTBelowStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               case RTAboveStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverBelowStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+                       /*
+                        * Overlap and contains strategies
+                        *
+                        * These strategies are simple enough that we can simply call the
+                        * operator and return its result.  Empty elements don't change
+                        * the result.
+                        */
+
+               case RTOverlapStrategyNumber:
+               case RTContainsStrategyNumber:
+               case RTOldContainsStrategyNumber:
+               case RTContainsElemStrategyNumber:
+               case RTSubStrategyNumber:
+               case RTSubEqualStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       key->sk_strategy);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_DATUM(result);
+
+                       /*
+                        * Contained by strategies
+                        *
+                        * We cannot just call the original operator for the contained by
+                        * strategies because some elements can be contained even though
+                        * the union is not; instead we use the overlap operator.
+                        *
+                        * We check for empty elements separately as they are not merged to
+                        * the union but contained by everything.
+                        */
+
+               case RTContainedByStrategyNumber:
+               case RTOldContainedByStrategyNumber:
+               case RTSuperStrategyNumber:
+               case RTSuperEqualStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverlapStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       if (DatumGetBool(result))
+                               PG_RETURN_BOOL(true);
+
+                       PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+
+                       /*
+                        * Adjacent strategy
+                        *
+                        * We test for overlap first but to be safe we need to call
+                        * the actual adjacent operator also.
+                        *
+                        * An empty element cannot be adjacent to any other, so there is
+                        * no need to check for it.
+                        */
+
+               case RTAdjacentStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTOverlapStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       if (DatumGetBool(result))
+                               PG_RETURN_BOOL(true);
+
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTAdjacentStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_DATUM(result);
+
+                       /*
+                        * Basic comparison strategies
+                        *
+                        * It is straightforward to support the equality strategies with
+                        * the contains operator.  Generally, inequality strategies do not
+                        * make much sense for the types which will be used with the
+                        * inclusion BRIN family of opclasses, but is is possible to
+                        * implement them with logical negation of the left-of and right-of
+                        * operators.
+                        *
+                        * NB: These strategies cannot be used with geometric datatypes
+                        * that use comparison of areas!  The only exception is the "same"
+                        * strategy.
+                        *
+                        * Empty elements are considered to be less than the others.  We
+                        * cannot use the empty support function to check the query is an
+                        * empty element, because the query can be another data type than
+                        * the empty support function argument.  So we will return true,
+                        * if there is a possibility that empty elements will change the
+                        * result.
+                        */
+
+               case RTLessStrategyNumber:
+               case RTLessEqualStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTRightStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       if (!DatumGetBool(result))
+                               PG_RETURN_BOOL(true);
+
+                       PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+
+               case RTSameStrategyNumber:
+               case RTEqualStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTContainsStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       if (DatumGetBool(result))
+                               PG_RETURN_BOOL(true);
+
+                       PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+
+               case RTGreaterEqualStrategyNumber:
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTLeftStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       if (!DatumGetBool(result))
+                               PG_RETURN_BOOL(true);
+
+                       PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
+
+               case RTGreaterStrategyNumber:
+                       /* no need to check for empty elements */
+                       finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
+                                                                                                       RTLeftStrategyNumber);
+                       result = FunctionCall2Coll(finfo, colloid, unionval, query);
+                       PG_RETURN_BOOL(!DatumGetBool(result));
+
+               default:
+                       /* shouldn't happen */
+                       elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+                       PG_RETURN_BOOL(false);
+       }
+}
+
+/*
+ * BRIN inclusion union function
+ *
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both.  The second one is untouched.
+ */
+Datum
+brin_inclusion_union(PG_FUNCTION_ARGS)
+{
+       BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+       BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+       BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+       Oid                     colloid = PG_GET_COLLATION();
+       AttrNumber      attno;
+       Form_pg_attribute attr;
+       FmgrInfo   *finfo;
+       Datum           result;
+
+       Assert(col_a->bv_attno == col_b->bv_attno);
+
+       /* Adjust "hasnulls". */
+       if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
+               col_a->bv_hasnulls = true;
+
+       /* If there are no values in B, there's nothing left to do. */
+       if (col_b->bv_allnulls)
+               PG_RETURN_VOID();
+
+       attno = col_a->bv_attno;
+       attr = bdesc->bd_tupdesc->attrs[attno - 1];
+
+       /*
+        * Adjust "allnulls".  If A doesn't have values, just copy the values from
+        * B into A, and we're done.  We cannot run the operators in this case,
+        * because values in A might contain garbage.  Note we already established
+        * that B contains values.
+        */
+       if (col_a->bv_allnulls)
+       {
+               col_a->bv_allnulls = false;
+               col_a->bv_values[INCLUSION_UNION] =
+                       datumCopy(col_b->bv_values[INCLUSION_UNION],
+                                         attr->attbyval, attr->attlen);
+               col_a->bv_values[INCLUSION_UNMERGEABLE] =
+                       col_b->bv_values[INCLUSION_UNMERGEABLE];
+               col_a->bv_values[INCLUSION_CONTAINS_EMPTY] =
+                       col_b->bv_values[INCLUSION_CONTAINS_EMPTY];
+               PG_RETURN_VOID();
+       }
+
+       /* If B includes empty elements, mark A similarly, if needed. */
+       if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
+               DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
+               col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
+
+       /* Check if A includes elements that are not mergeable. */
+       if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
+               PG_RETURN_VOID();
+
+       /* If B includes elements that are not mergeable, mark A similarly. */
+       if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
+       {
+               col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
+               PG_RETURN_VOID();
+       }
+
+       /* Check if A and B are mergeable; if not, mark A unmergeable. */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
+       if (finfo != NULL &&
+               !DatumGetBool(FunctionCall2Coll(finfo, colloid,
+                                                                               col_a->bv_values[INCLUSION_UNION],
+                                                                               col_b->bv_values[INCLUSION_UNION])))
+       {
+               col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
+               PG_RETURN_VOID();
+       }
+
+       /* Finally, merge B to A. */
+       finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
+       Assert(finfo != NULL);
+       result = FunctionCall2Coll(finfo, colloid,
+                                                          col_a->bv_values[INCLUSION_UNION],
+                                                          col_b->bv_values[INCLUSION_UNION]);
+       if (!attr->attbyval)
+               pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
+       col_a->bv_values[INCLUSION_UNION] = result;
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * Cache and return inclusion opclass support procedure
+ *
+ * Return the procedure corresponding to the given function support number
+ * or null if it is not exists.
+ */
+static FmgrInfo *
+inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+       InclusionOpaque *opaque;
+       uint16          basenum = procnum - PROCNUM_BASE;
+
+       /*
+        * We cache these in the opaque struct, to avoid repetitive syscache
+        * lookups.
+        */
+       opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+       /*
+        * If we already searched for this proc and didn't find it, don't bother
+        * searching again.
+        */
+       if (opaque->extra_proc_missing[basenum])
+               return NULL;
+
+       if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
+       {
+               if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
+                                                                                               procnum)))
+               {
+                       fmgr_info_copy(&opaque->extra_procinfos[basenum],
+                                                  index_getprocinfo(bdesc->bd_index, attno, procnum),
+                                                  bdesc->bd_context);
+               }
+               else
+               {
+                       opaque->extra_proc_missing[basenum] = true;
+                       return NULL;
+               }
+       }
+
+       return &opaque->extra_procinfos[basenum];
+}
+
+/*
+ * Cache and return the procedure of the given strategy
+ *
+ * Return the procedure corresponding to the given sub-type and strategy
+ * number.  The data type of the index will be used as the left hand side of
+ * the operator and the given sub-type will be used as the right hand side.
+ * Throws an error if the pg_amop row does not exist, but that should not
+ * happen with a properly configured opclass.
+ *
+ * It always throws an error when the data type of the opclass is different
+ * from the data type of the column or the expression.  That happens when the
+ * column data type has implicit cast to the opclass data type.  We don't
+ * bother casting types, because this situation can easily be avoided by
+ * setting storage data type to that of the opclass.  The same problem does not
+ * apply to the data type of the right hand side, because the type in the
+ * ScanKey always matches the opclass' one.
+ *
+ * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
+ * made here, see that function too.
+ */
+static FmgrInfo *
+inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
+                                                               uint16 strategynum)
+{
+       InclusionOpaque *opaque;
+
+       Assert(strategynum >= 1 &&
+                  strategynum <= RTMaxStrategyNumber);
+
+       opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+       /*
+        * We cache the procedures for the last sub-type in the opaque struct, to
+        * avoid repetitive syscache lookups.  If the sub-type is changed,
+        * invalidate all the cached entries.
+        */
+       if (opaque->cached_subtype != subtype)
+       {
+               uint16          i;
+
+               for (i = 1; i <= RTMaxStrategyNumber; i++)
+                       opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
+               opaque->cached_subtype = subtype;
+       }
+
+       if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
+       {
+               Form_pg_attribute attr;
+               HeapTuple       tuple;
+               Oid                     opfamily,
+                                       oprid;
+               bool            isNull;
+
+               opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
+               attr = bdesc->bd_tupdesc->attrs[attno - 1];
+               tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+                                                               ObjectIdGetDatum(attr->atttypid),
+                                                               ObjectIdGetDatum(subtype),
+                                                               Int16GetDatum(strategynum));
+
+               if (!HeapTupleIsValid(tuple))
+                       elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+                                strategynum, attr->atttypid, subtype, opfamily);
+
+               oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
+                                                                                        Anum_pg_amop_amopopr, &isNull));
+               ReleaseSysCache(tuple);
+               Assert(!isNull && RegProcedureIsValid(oprid));
+
+               fmgr_info_cxt(get_opcode(oprid),
+                                         &opaque->strategy_procinfos[strategynum - 1],
+                                         bdesc->bd_context);
+       }
+
+       return &opaque->strategy_procinfos[strategynum - 1];
+}
index 1175649a6d65d4ebc43c51cda20ebed0dac5303e..b105f980eca50388cab299ef71253cb4fcb57261 100644 (file)
@@ -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,
index 2e3ee1e8ba7d0d25bfa86ba94ddaa889c58118cd..0fdb17f947fdbb8817dba7ba814579c59e09f6d1 100644 (file)
@@ -62,9 +62,9 @@
 #define INETSTRAT_GT                   RTGreaterStrategyNumber
 #define INETSTRAT_GE                   RTGreaterEqualStrategyNumber
 #define INETSTRAT_SUB                  RTSubStrategyNumber
-#define INETSTRAT_SUBEQ                        RTSubOrEqualStrategyNumber
+#define INETSTRAT_SUBEQ                        RTSubEqualStrategyNumber
 #define INETSTRAT_SUP                  RTSuperStrategyNumber
-#define INETSTRAT_SUPEQ                        RTSuperOrEqualStrategyNumber
+#define INETSTRAT_SUPEQ                        RTSuperEqualStrategyNumber
 
 
 /*
index 1486d046935c120f27d83fa23fba6f77bdcebb82..6be199e591c4768d321cb3fe1e0a4acc7c78bcca 100644 (file)
@@ -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 */
index 458f4dc888d6e13eab2a8ee539c6b4d4bb1586d3..a372be81e21b5c27bfceb05ee91ab869426fd1a1 100644 (file)
@@ -65,9 +65,9 @@ typedef uint16 StrategyNumber;
 #define RTGreaterStrategyNumber                        22              /* for > */
 #define RTGreaterEqualStrategyNumber   23              /* for >= */
 #define RTSubStrategyNumber                            24              /* for inet >> */
-#define RTSubOrEqualStrategyNumber             25              /* for inet <<= */
+#define RTSubEqualStrategyNumber               25              /* for inet <<= */
 #define RTSuperStrategyNumber                  26              /* for inet << */
-#define RTSuperOrEqualStrategyNumber   27              /* for inet >>= */
+#define RTSuperEqualStrategyNumber             27              /* for inet >>= */
 
 #define RTMaxStrategyNumber                            27
 
index b6a6da9a10a78d330de9f6be40fb558f3e4ed5f4..bb8a9b08e8a0fa3919110c9d0561de0b252d0cea 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201505151
+#define CATALOG_VERSION_NO     201505152
 
 #endif
index 79609f7774c82a6081e4a645fa8dbf83d613b0da..8a28b8ea810983de8c3083e342aa24af3f9e77f6 100644 (file)
@@ -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 */
index 9b8294fd94c0f464bcfabcef54fab934778be80c..657ec07059cfc3fc1e10bd6eb27014a27f43ceed 100644 (file)
@@ -977,6 +977,13 @@ DATA(insert (   4075    869  869 2 s      1204    3580 0 ));
 DATA(insert (   4075    869  869 3 s      1201    3580 0 ));
 DATA(insert (   4075    869  869 4 s      1206    3580 0 ));
 DATA(insert (   4075    869  869 5 s      1205    3580 0 ));
+/* inclusion inet */
+DATA(insert (   4102    869  869 3 s      3552    3580 0 ));
+DATA(insert (   4102    869  869 7 s       934    3580 0 ));
+DATA(insert (   4102    869  869 8 s       932    3580 0 ));
+DATA(insert (   4102    869  869 18 s     1201    3580 0 ));
+DATA(insert (   4102    869  869 24 s      933    3580 0 ));
+DATA(insert (   4102    869  869 26 s      931    3580 0 ));
 /* minmax character */
 DATA(insert (   4076   1042 1042 1 s      1058    3580 0 ));
 DATA(insert (   4076   1042 1042 2 s      1059    3580 0 ));
@@ -1072,11 +1079,41 @@ DATA(insert (   4081   2950 2950 2 s      2976    3580 0 ));
 DATA(insert (   4081   2950 2950 3 s      2972    3580 0 ));
 DATA(insert (   4081   2950 2950 4 s      2977    3580 0 ));
 DATA(insert (   4081   2950 2950 5 s      2975    3580 0 ));
+/* inclusion range types */
+DATA(insert (   4103   3831 3831  1 s     3893    3580 0 ));
+DATA(insert (   4103   3831 3831  2 s     3895    3580 0 ));
+DATA(insert (   4103   3831 3831  3 s     3888    3580 0 ));
+DATA(insert (   4103   3831 3831  4 s     3896    3580 0 ));
+DATA(insert (   4103   3831 3831  5 s     3894    3580 0 ));
+DATA(insert (   4103   3831 3831  7 s     3890    3580 0 ));
+DATA(insert (   4103   3831 3831  8 s     3892    3580 0 ));
+DATA(insert (   4103   3831 2283 16 s     3889    3580 0 ));
+DATA(insert (   4103   3831 3831 17 s     3897    3580 0 ));
+DATA(insert (   4103   3831 3831 18 s     3882    3580 0 ));
+DATA(insert (   4103   3831 3831 20 s     3884    3580 0 ));
+DATA(insert (   4103   3831 3831 21 s     3885    3580 0 ));
+DATA(insert (   4103   3831 3831 22 s     3887    3580 0 ));
+DATA(insert (   4103   3831 3831 23 s     3886    3580 0 ));
 /* minmax pg_lsn */
 DATA(insert (   4082   3220 3220 1 s      3224    3580 0 ));
 DATA(insert (   4082   3220 3220 2 s      3226    3580 0 ));
 DATA(insert (   4082   3220 3220 3 s      3222    3580 0 ));
 DATA(insert (   4082   3220 3220 4 s      3227    3580 0 ));
 DATA(insert (   4082   3220 3220 5 s      3225    3580 0 ));
+/* inclusion box */
+DATA(insert (  4104    603      603  1 s          493    3580 0 ));
+DATA(insert (  4104    603  603  2 s      494    3580 0 ));
+DATA(insert (  4104    603  603  3 s      500    3580 0 ));
+DATA(insert (  4104    603  603  4 s      495    3580 0 ));
+DATA(insert (  4104    603  603  5 s      496    3580 0 ));
+DATA(insert (  4104    603  603  6 s      499    3580 0 ));
+DATA(insert (  4104    603  603  7 s      498    3580 0 ));
+DATA(insert (  4104    603  603  8 s      497    3580 0 ));
+DATA(insert (  4104    603  603  9 s     2571    3580 0 ));
+DATA(insert (  4104    603  603 10 s     2570    3580 0 ));
+DATA(insert (  4104    603  603 11 s     2573    3580 0 ));
+DATA(insert (  4104    603  603 12 s     2572    3580 0 ));
+/* we could, but choose not to, supply entries for strategies 13 and 14 */
+DATA(insert (  4104    603  600  7 s      433    3580 0 ));
 
 #endif   /* PG_AMOP_H */
index 3111d6f4ad17b484bf9982a0034606701a796600..f22e9a61ef62cd4ff928917e67f1a943869b5fa4 100644 (file)
@@ -551,6 +551,14 @@ DATA(insert (   4075   869   869  1  3383 ));
 DATA(insert (   4075   869   869  2  3384 ));
 DATA(insert (   4075   869   869  3  3385 ));
 DATA(insert (   4075   869   869  4  3386 ));
+/* inclusion inet */
+DATA(insert (   4102   869   869  1  4105 ));
+DATA(insert (   4102   869   869  2  4106 ));
+DATA(insert (   4102   869   869  3  4107 ));
+DATA(insert (   4102   869   869  4  4108 ));
+DATA(insert (   4102   869   869 11  4063 ));
+DATA(insert (   4102   869   869 12  4071 ));
+DATA(insert (   4102   869   869 13   930 ));
 /* minmax character */
 DATA(insert (   4076  1042  1042  1  3383 ));
 DATA(insert (   4076  1042  1042  2  3384 ));
@@ -631,10 +639,25 @@ DATA(insert (   4081  2950  2950  1  3383 ));
 DATA(insert (   4081  2950  2950  2  3384 ));
 DATA(insert (   4081  2950  2950  3  3385 ));
 DATA(insert (   4081  2950  2950  4  3386 ));
+/* inclusion range types */
+DATA(insert (   4103  3831  3831  1  4105 ));
+DATA(insert (   4103  3831  3831  2  4106 ));
+DATA(insert (   4103  3831  3831  3  4107 ));
+DATA(insert (   4103  3831  3831  4  4108 ));
+DATA(insert (   4103  3831  3831  11 4057 ));
+DATA(insert (   4103  3831  3831  13 3859 ));
+DATA(insert (   4103  3831  3831  14 3850 ));
 /* minmax pg_lsn */
 DATA(insert (   4082  3220  3220  1  3383 ));
 DATA(insert (   4082  3220  3220  2  3384 ));
 DATA(insert (   4082  3220  3220  3  3385 ));
 DATA(insert (   4082  3220  3220  4  3386 ));
+/* inclusion box */
+DATA(insert (   4104   603   603  1  4105 ));
+DATA(insert (   4104   603   603  2  4106 ));
+DATA(insert (   4104   603   603  3  4107 ));
+DATA(insert (   4104   603   603  4  4108 ));
+DATA(insert (   4104   603   603  11 4067 ));
+DATA(insert (   4104   603   603  13  187 ));
 
 #endif   /* PG_AMPROC_H */
index f9469375beac8dfcda9ea88cb81b3d9dd2cfb97b..a13e08280057f0d0837d8f9539d3ae424a7937a4 100644 (file)
@@ -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 */
index 97ffa327cfcd429aab610052886e078cb13cbb60..acbc1002b4c82b91f39b1e8ffc955f05b38f9436 100644 (file)
@@ -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 */
index c2185bd9add337665aef134530bfa56b727985fe..b5b93450ec36dbfb3330ab5b2f11f758f6c12ab2 100644 (file)
@@ -4225,6 +4225,16 @@ DESCR("BRIN minmax support");
 DATA(insert OID = 3386 ( brin_minmax_union             PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_union _null_ _null_ _null_ ));
 DESCR("BRIN minmax support");
 
+/* BRIN inclusion */
+DATA(insert OID = 4105 ( brin_inclusion_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_opcinfo _null_ _null_ _null_ ));
+DESCR("BRIN inclusion support");
+DATA(insert OID = 4106 ( brin_inclusion_add_value PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 16 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_add_value _null_ _null_ _null_ ));
+DESCR("BRIN inclusion support");
+DATA(insert OID = 4107 ( brin_inclusion_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_consistent _null_ _null_ _null_ ));
+DESCR("BRIN inclusion support");
+DATA(insert OID = 4108 ( brin_inclusion_union  PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 16 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brin_inclusion_union _null_ _null_ _null_ ));
+DESCR("BRIN inclusion support");
+
 /* userlock replacements */
 DATA(insert OID = 2880 (  pg_advisory_lock                             PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "20" _null_ _null_ _null_ _null_ _null_ pg_advisory_lock_int8 _null_ _null_ _null_ ));
 DESCR("obtain exclusive advisory lock");
index 4fe6f071942b6d7f403ddaf00c4cb726d425a674..8ba961a662b325ead7b589334f562edb9498129f 100644 (file)
@@ -23,7 +23,9 @@ CREATE TABLE brintest (byteacol bytea,
        varbitcol bit varying(16),
        numericcol numeric,
        uuidcol uuid,
-       lsncol pg_lsn
+       int4rangecol int4range,
+       lsncol pg_lsn,
+       boxcol box
 ) WITH (fillfactor=10, autovacuum_enabled=off);
 INSERT INTO brintest SELECT
        repeat(stringu1, 8)::bytea,
@@ -50,12 +52,15 @@ INSERT INTO brintest SELECT
        tenthous::bit(16)::varbit,
        tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
        format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
-       format('%s/%s%s', odd, even, tenthous)::pg_lsn
-FROM tenk1 LIMIT 25;
+       int4range(thousand, twothousand),
+       format('%s/%s%s', odd, even, tenthous)::pg_lsn,
+       box(point(odd, even), point(thousand, twothousand))
+FROM tenk1 LIMIT 100;
 -- throw in some NULL's and different values
-INSERT INTO brintest (inetcol, cidrcol) SELECT
+INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT
        inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
-       cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+       cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+       'empty'::int4range
 FROM tenk1 LIMIT 25;
 CREATE INDEX brinidx ON brintest USING brin (
        byteacol,
@@ -70,6 +75,7 @@ CREATE INDEX brinidx ON brintest USING brin (
        float4col,
        float8col,
        macaddrcol,
+       inetcol inet_inclusion_ops,
        inetcol inet_minmax_ops,
        bpcharcol,
        datecol,
@@ -82,7 +88,9 @@ CREATE INDEX brinidx ON brintest USING brin (
        varbitcol,
        numericcol,
        uuidcol,
-       lsncol
+       int4rangecol,
+       lsncol,
+       boxcol
 ) with (pages_per_range = 1);
 CREATE TABLE brinopers (colname name, typ text, op text[], value text[],
        check (cardinality(op) = cardinality(value)));
@@ -128,7 +136,12 @@ INSERT INTO brinopers VALUES
        ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}'),
        ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}'),
        ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}'),
-       ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}');
+       ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}'),
+       ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}'),
+       ('int4rangecol', 'int4', '{@>}', '{1500}'),
+       ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'),
+       ('boxcol', 'point', '{@>}', '{"(500,43)"}'),
+       ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}');
 DO $x$
 DECLARE
        r record;
@@ -222,7 +235,9 @@ INSERT INTO brintest SELECT
        tenthous::bit(16)::varbit,
        tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
        format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
-       format('%s/%s%s', odd, even, tenthous)::pg_lsn
+       int4range(thousand, twothousand),
+       format('%s/%s%s', odd, even, tenthous)::pg_lsn,
+       box(point(odd, even), point(thousand, twothousand))
 FROM tenk1 LIMIT 5 OFFSET 5;
 SELECT brin_summarize_new_values('brinidx'::regclass);
  brin_summarize_new_values 
index 6b248f2801af9c14ba651071ac0996defbca1f9e..0d2fce9f87bad798564aa5a3f014c69462a7851e 100644 (file)
@@ -1657,10 +1657,33 @@ ORDER BY 1, 2, 3;
        2742 |           10 | ?|
        2742 |           11 | ?&
        3580 |            1 | <
+       3580 |            1 | <<
+       3580 |            2 | &<
        3580 |            2 | <=
+       3580 |            3 | &&
        3580 |            3 | =
+       3580 |            4 | &>
        3580 |            4 | >=
        3580 |            5 | >
+       3580 |            5 | >>
+       3580 |            6 | ~=
+       3580 |            7 | >>=
+       3580 |            7 | @>
+       3580 |            8 | <<=
+       3580 |            8 | <@
+       3580 |            9 | &<|
+       3580 |           10 | <<|
+       3580 |           11 | |>>
+       3580 |           12 | |&>
+       3580 |           16 | @>
+       3580 |           17 | -|-
+       3580 |           18 | =
+       3580 |           20 | <
+       3580 |           21 | <=
+       3580 |           22 | >
+       3580 |           23 | >=
+       3580 |           24 | >>
+       3580 |           26 | <<
        4000 |            1 | <<
        4000 |            1 | ~<~
        4000 |            2 | &<
@@ -1683,7 +1706,7 @@ ORDER BY 1, 2, 3;
        4000 |           15 | >
        4000 |           16 | @>
        4000 |           18 | =
-(85 rows)
+(108 rows)
 
 -- Check that all opclass search operators have selectivity estimators.
 -- This is not absolutely required, but it seems a reasonable thing
index 6a695bbd204412f32dfc568d28a61e2c488dd7b2..e92717f6ff033173aa2a4287c3d8426cfe974959 100644 (file)
@@ -23,7 +23,9 @@ CREATE TABLE brintest (byteacol bytea,
        varbitcol bit varying(16),
        numericcol numeric,
        uuidcol uuid,
-       lsncol pg_lsn
+       int4rangecol int4range,
+       lsncol pg_lsn,
+       boxcol box
 ) WITH (fillfactor=10, autovacuum_enabled=off);
 
 INSERT INTO brintest SELECT
@@ -51,13 +53,16 @@ INSERT INTO brintest SELECT
        tenthous::bit(16)::varbit,
        tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
        format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
-       format('%s/%s%s', odd, even, tenthous)::pg_lsn
-FROM tenk1 LIMIT 25;
+       int4range(thousand, twothousand),
+       format('%s/%s%s', odd, even, tenthous)::pg_lsn,
+       box(point(odd, even), point(thousand, twothousand))
+FROM tenk1 LIMIT 100;
 
 -- throw in some NULL's and different values
-INSERT INTO brintest (inetcol, cidrcol) SELECT
+INSERT INTO brintest (inetcol, cidrcol, int4rangecol) SELECT
        inet 'fe80::6e40:8ff:fea9:8c46' + tenthous,
-       cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous
+       cidr 'fe80::6e40:8ff:fea9:8c46' + tenthous,
+       'empty'::int4range
 FROM tenk1 LIMIT 25;
 
 CREATE INDEX brinidx ON brintest USING brin (
@@ -73,6 +78,7 @@ CREATE INDEX brinidx ON brintest USING brin (
        float4col,
        float8col,
        macaddrcol,
+       inetcol inet_inclusion_ops,
        inetcol inet_minmax_ops,
        bpcharcol,
        datecol,
@@ -85,7 +91,9 @@ CREATE INDEX brinidx ON brintest USING brin (
        varbitcol,
        numericcol,
        uuidcol,
-       lsncol
+       int4rangecol,
+       lsncol,
+       boxcol
 ) with (pages_per_range = 1);
 
 CREATE TABLE brinopers (colname name, typ text, op text[], value text[],
@@ -133,7 +141,12 @@ INSERT INTO brinopers VALUES
        ('varbitcol', 'varbit(16)', '{>, >=, =, <=, <}', '{0000000000000100, 0000000000000100, 0001010001100110, 1111111111111000, 1111111111111000}'),
        ('numericcol', 'numeric', '{>, >=, =, <=, <}', '{0.00, 0.01, 2268164.347826086956521739130434782609, 99470151.9, 99470151.9}'),
        ('uuidcol', 'uuid', '{>, >=, =, <=, <}', '{00040004-0004-0004-0004-000400040004, 00040004-0004-0004-0004-000400040004, 52225222-5222-5222-5222-522252225222, 99989998-9998-9998-9998-999899989998, 99989998-9998-9998-9998-999899989998}'),
-       ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}');
+       ('int4rangecol', 'int4range', '{<<, &<, &&, &>, >>, @>, <@, =, <, <=, >, >=}', '{"[10000,)","[10000,)","(,]","[3,4)","[36,44)","(1500,1501]","[3,4)","[222,1222)","[36,44)","[43,1043)","[367,4466)","[519,)"}'),
+       ('int4rangecol', 'int4range', '{@>, <@, =, <=, >, >=}', '{empty, empty, empty, empty, empty, empty}'),
+       ('int4rangecol', 'int4', '{@>}', '{1500}'),
+       ('lsncol', 'pg_lsn', '{>, >=, =, <=, <, IS, IS NOT}', '{0/1200, 0/1200, 44/455222, 198/1999799, 198/1999799, NULL, NULL}'),
+       ('boxcol', 'point', '{@>}', '{"(500,43)"}'),
+       ('boxcol', 'box', '{<<, &<, &&, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=}', '{"((1000,2000),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3000,4000))","((1,2),(3,4))","((1000,2000),(3000,4000))","((1,2000),(3,4000))","((1000,2),(3000,4))","((1,2),(3,4))","((1,2),(300,400))","((1,2),(3000,4000))","((222,1222),(44,45))"}');
 
 DO $x$
 DECLARE
@@ -229,7 +242,9 @@ INSERT INTO brintest SELECT
        tenthous::bit(16)::varbit,
        tenthous::numeric(36,30) * fivethous * even / (hundred + 1),
        format('%s%s-%s-%s-%s-%s%s%s', to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'), to_char(tenthous, 'FM0000'))::uuid,
-       format('%s/%s%s', odd, even, tenthous)::pg_lsn
+       int4range(thousand, twothousand),
+       format('%s/%s%s', odd, even, tenthous)::pg_lsn,
+       box(point(odd, even), point(thousand, twothousand))
 FROM tenk1 LIMIT 5 OFFSET 5;
 
 SELECT brin_summarize_new_values('brinidx'::regclass);