Add BRIN infrastructure for "inclusion" opclasses
authorAlvaro Herrera <[email protected]>
Fri, 15 May 2015 21:05:22 +0000 (18:05 -0300)
committerAlvaro Herrera <[email protected]>
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);