> Once again, many thanks for the review.  Here's a new version.  I have
> added operator classes for int8, text, and actually everything that btree
> supports except:
>     bool
>     record
>     oidvector
>     anyarray
>     tsvector
>     tsquery
>     jsonb
>     range
> 
> since I'm not sure that it makes sense to have opclasses for any of
> these -- at least not regular minmax opclasses.  There are some
> interesting possibilities, for example for range types, whereby we store
> in the index tuple the union of all the range in the block range.

I thought we can do better than minmax for the inet data type,
and ended up with a generalized opclass supporting both inet and range
types.  Patch based on minmax-v20 attached.  It works well except
a few small problems.  I will improve the patch and add into
a commitfest after BRIN framework is committed.

To support more operators I needed to change amstrategies and
amsupport on the catalog.  It would be nice if amsupport can be set
to 0 like amstrategies.

Inet data types accept IP version 4 and version 6.  It is not possible
to represent union of addresses from different versions with a valid
inet type.  So, I made the union function return NULL in this case.
Then, I tried to store if returned value is NULL or not, in
column->values[] as boolean, but it failed on the pfree() inside
brin_dtuple_initilize().  It doesn't seem right to free the values
based on attr->attbyval.

I think the same opclass can be used for geometric types.  I can
rename it to inclusion_ops instead of range_ops.  The GiST opclasses
for the geometric types use bounding boxes.  It wouldn't be possible
to use a different data type in a generic oplass.  Maybe STORAGE
parameter can be used for that purpose.

> (I had an opclass for anyenum too, but on further thought I removed it
> because it is going to be pointless in nearly all cases.)

It can be useful in some circumstances.  We wouldn't lose anything
by supporting more types.  I think we should even add an operator
class for boolean.
diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml
index 12ba3f4..7663113 100644
--- a/doc/src/sgml/brin.sgml
+++ b/doc/src/sgml/brin.sgml
@@ -249,6 +249,18 @@
      </entry>
     </row>
     <row>
+     <entry><literal>inet_range_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>=</>
+     </entry>
+    </row>
+    <row>
      <entry><literal>bpchar_minmax_ops</literal></entry>
      <entry><type>character</type></entry>
      <entry>
@@ -370,6 +382,23 @@
      </entry>
     </row>
     <row>
+     <entry><literal>range_ops</></entry>
+     <entry>any range type</entry>
+     <entry>
+      <literal>&amp;&amp;</>
+      <literal>&amp;&gt;</>
+      <literal>&amp;&lt;</>
+      <literal>&gt;&gt;</>
+      <literal>&lt;&lt;</>
+      <literal>&lt;@</>
+      <literal>=</>
+      <literal>@&gt;</>
+      <literal>@&gt;</>
+     </entry>
+     <entry>
+     </entry>
+    </row>
+    <row>
      <entry><literal>pg_lsn_minmax_ops</literal></entry>
      <entry><type>pg_lsn</type></entry>
      <entry>
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index ac44fcd..019c582 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/brin
 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
+OBJS = brin.o brin_pageops.o brin_range.o brin_revmap.o brin_tuple.o \
+	   brin_xlog.o brin_minmax.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/brin/brin_range.c b/src/backend/access/brin/brin_range.c
new file mode 100644
index 0000000..b63b80a
--- /dev/null
+++ b/src/backend/access/brin/brin_range.c
@@ -0,0 +1,323 @@
+/*
+ * brin_range.c
+ *		Implementation of range opclass for BRIN
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/brin/brin_range.c
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/brin_internal.h"
+#include "access/brin_tuple.h"
+#include "access/skey.h"
+#include "catalog/pg_type.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Procedure numbers must not collide with BRIN_PROCNUM defines in
+ * brin_internal.h.  Note we only need inequality functions.
+ */
+#define		RANGE_NUM_PROCNUMS		10	/* # support procs */
+#define		PROCNUM_CONTAINS		5
+#define		PROCNUM_UNION			6
+#define		PROCNUM_BEFORE			7	/* required for overright strategy */
+#define		PROCNUM_OVERLEFT		8	/* required for after strategy */
+#define		PROCNUM_OVERLAPS		9	/* required for overlaps strategy */
+#define		PROCNUM_OVERRIGHT		10	/* required for before strategy */
+#define		PROCNUM_AFTER			11	/* required for after strategy */
+#define		PROCNUM_ADJACENT		12	/* required for adjacent strategy */
+#define		PROCNUM_CONTAINS_ELEM	13	/* required for contains element strategy */
+#define		PROCNUM_CONTAINS_NOTEQ	14	/* required for contains but not equals strategy */
+
+/*
+ * Subtract this from procnum to obtain index in RangeOpaque arrays
+ * (Must be equal to minimum of private procnums)
+ */
+#define		PROCNUM_BASE			5
+
+static FmgrInfo *range_get_procinfo(BrinDesc *bdesc, uint16 attno,
+					uint16 procnum);
+
+PG_FUNCTION_INFO_V1(rangeOpcInfo);
+PG_FUNCTION_INFO_V1(rangeAddValue);
+PG_FUNCTION_INFO_V1(rangeConsistent);
+PG_FUNCTION_INFO_V1(rangeUnion);
+
+
+typedef struct RangeOpaque
+{
+	FmgrInfo	operators[RANGE_NUM_PROCNUMS];
+	bool		inited[RANGE_NUM_PROCNUMS];
+} RangeOpaque;
+
+Datum
+rangeOpcInfo(PG_FUNCTION_ARGS)
+{
+	Oid		typoid = PG_GETARG_OID(0);
+	BrinOpcInfo *result;
+
+	/*
+	 * opaque->operators is initialized lazily, as indicated by 'inited'
+	 * which is initialized to all false by palloc0.
+	 */
+
+	result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) + sizeof(RangeOpaque));
+	result->oi_nstored = 1;
+	result->oi_opaque = (RangeOpaque *)
+		MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
+	result->oi_typids[0] = typoid;
+
+	PG_RETURN_POINTER(result);
+}
+
+/*
+ * 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 min/max range specified by the
+ * existing tuple values, update the index tuple and return true.  Otherwise,
+ * return false and do not modify in this case.
+ */
+Datum
+rangeAddValue(PG_FUNCTION_ARGS)
+{
+	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+	BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
+	Datum		newval = PG_GETARG_DATUM(2);
+	bool		isnull = PG_GETARG_BOOL(3);
+	Oid			colloid = PG_GET_COLLATION();
+	FmgrInfo   *frmg;
+	Datum		result;
+	AttrNumber	attno;
+	Form_pg_attribute attr;
+	FunctionCallInfoData newfcinfo;
+
+	/*
+	 * 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->hasnulls)
+			PG_RETURN_BOOL(false);
+
+		column->hasnulls = true;
+		PG_RETURN_BOOL(true);
+	}
+
+	attno = column->attno;
+	attr = bdesc->bd_tupdesc->attrs[attno - 1];
+
+	/*
+	 * If the recorded value is null, store the new value (which we know to be
+	 * not null) as both minimum and maximum, and we're done.
+	 */
+	if (column->allnulls)
+	{
+		column->values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
+		column->allnulls = false;
+		PG_RETURN_BOOL(true);
+	}
+
+	frmg = range_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
+	result = FunctionCall2Coll(frmg, colloid, column->values[0], newval);
+	if (DatumGetBool(result))
+		PG_RETURN_BOOL(false);
+
+	frmg = range_get_procinfo(bdesc, attno, PROCNUM_UNION);
+
+	/* FunctionCall2Coll() cannot be used because NULL is expected. */
+	InitFunctionCallInfoData(newfcinfo, frmg, 2, colloid, NULL, NULL);
+	newfcinfo.arg[0] = column->values[0];
+	newfcinfo.arg[1] = newval;
+	newfcinfo.argnull[0] = false;
+	newfcinfo.argnull[1] = false;
+	result = FunctionCallInvoke(&newfcinfo);
+
+	if (!attr->attbyval)
+		pfree(DatumGetPointer(column->values[0]));
+
+	column->values[0] = result;
+
+	PG_RETURN_BOOL(true);
+}
+
+/*
+ * Given an index tuple corresponding to a certain page range and a scan key,
+ * return whether the scan key is consistent with the index tuple's min/max
+ * values.  Return true if so, false otherwise.
+ */
+Datum
+rangeConsistent(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();
+	AttrNumber	attno;
+	Datum		query;
+	FmgrInfo   *frmg;
+	Datum		result;
+
+	Assert(key->sk_attno == column->attno);
+
+	/* handle IS NULL/IS NOT NULL tests */
+	if (key->sk_flags & SK_ISNULL)
+	{
+		if (key->sk_flags & SK_SEARCHNULL)
+		{
+			if (column->allnulls || column->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->allnulls);
+	}
+
+	attno = key->sk_attno;
+	query = key->sk_argument;
+	switch (key->sk_strategy)
+	{
+		case RANGESTRAT_CONTAINS:
+		case RANGESTRAT_EQ:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_DATUM(result);
+
+		/* Remaining are the optional strategies. */
+
+		case RANGESTRAT_BEFORE:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_OVERRIGHT);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_BOOL(!DatumGetBool(result));
+
+		case RANGESTRAT_OVERLEFT:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_AFTER);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_BOOL(!DatumGetBool(result));
+
+		case RANGESTRAT_OVERLAPS:
+		case RANGESTRAT_CONTAINED_BY:
+		case RANGESTRAT_CONTAINED_BY_NOTEQ:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_OVERLAPS);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_DATUM(result);
+
+		case RANGESTRAT_OVERRIGHT:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_BEFORE);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_BOOL(!DatumGetBool(result));
+
+		case RANGESTRAT_AFTER:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_OVERLEFT);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_BOOL(!DatumGetBool(result));
+
+		case RANGESTRAT_ADJACENT:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_OVERLAPS);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			if (DatumGetBool(result))
+				PG_RETURN_BOOL(true);
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_ADJACENT);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_DATUM(result);
+
+		case RANGESTRAT_CONTAINS_ELEM:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_CONTAINS_ELEM);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_DATUM(result);
+
+		case RANGESTRAT_CONTAINS_NOTEQ:
+			frmg = range_get_procinfo(bdesc, attno, PROCNUM_CONTAINS_NOTEQ);
+			result = FunctionCall2Coll(frmg, colloid, column->values[0], query);
+			PG_RETURN_DATUM(result);
+
+		default:
+			/* shouldn't happen */
+			elog(ERROR, "invalid strategy number %d", key->sk_strategy);
+			PG_RETURN_BOOL(false);	/* keep compiler quiet */
+	}
+}
+
+/*
+ * Given two BrinValues, update the first of them as a union of the summary
+ * values contained in both.  The second one is untouched.
+ */
+Datum
+rangeUnion(PG_FUNCTION_ARGS)
+{
+	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
+	BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
+	BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
+	Oid			colloid = PG_GET_COLLATION();
+	AttrNumber	attno;
+	Form_pg_attribute attr;
+	FmgrInfo   *frmg;
+	FunctionCallInfoData newfcinfo;
+	Datum		result;
+
+	Assert(col_a->attno == col_b->attno);
+
+	attno = col_a->attno;
+	attr = bdesc->bd_tupdesc->attrs[attno - 1];
+
+	/* Adjust null flags */
+	if (col_a->allnulls && !col_b->allnulls)
+		col_a->allnulls = false;
+	if (!col_a->hasnulls && col_b->hasnulls)
+		col_a->hasnulls = true;
+
+	frmg = range_get_procinfo(bdesc, attno, PROCNUM_UNION);
+
+	/* FunctionCall2Coll() cannot be used because NULL is expected. */
+	InitFunctionCallInfoData(newfcinfo, frmg, 2, colloid, NULL, NULL);
+	newfcinfo.arg[0] = col_a->values[0];
+	newfcinfo.arg[1] = col_b->values[0];
+	newfcinfo.argnull[0] = false;
+	newfcinfo.argnull[1] = false;
+	result = FunctionCallInvoke(&newfcinfo);
+
+	if (!attr->attbyval)
+		pfree(DatumGetPointer(col_a->values[0]));
+
+	col_a->values[0] = result;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Return the procedure corresponding to the given function support number.
+ */
+static FmgrInfo *
+range_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
+{
+	RangeOpaque *opaque;
+	uint16		basenum = procnum - PROCNUM_BASE;
+
+	opaque = (RangeOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
+
+	/*
+	 * We cache these in the opaque struct, to avoid repetitive syscache
+	 * lookups.
+	 */
+	if (!opaque->inited[basenum])
+	{
+		fmgr_info_copy(&opaque->operators[basenum],
+					   index_getprocinfo(bdesc->bd_index, attno, procnum),
+					   bdesc->bd_context);
+		opaque->inited[basenum] = true;
+	}
+
+	return &opaque->operators[basenum];
+}
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 3a705da..485d932 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -888,6 +888,45 @@ network_hostmask(PG_FUNCTION_ARGS)
 }
 
 /*
+ * Returns the network which contain both of the inputs.  Note that return
+ * value isn't trimmed.  It wouldn't be nice to display it directly.  Use
+ * inet_to_cidr() to get the network address.  Returns null for inputs which
+ * are not from the same IP family.
+ */
+Datum
+inet_merge(PG_FUNCTION_ARGS)
+{
+	inet	   *a1 = PG_GETARG_INET_PP(0),
+			   *a2 = PG_GETARG_INET_PP(1),
+			   *result;
+	int			commonbits;
+
+	if (ip_family(a1) != ip_family(a2))
+	{
+		elog(LOG, "bok %d %d", ip_family(a1), ip_family(a2));
+		PG_RETURN_NULL();
+	}
+
+	commonbits = bitncommon(ip_addr(a1), ip_addr(a2),
+							Min(ip_bits(a1), ip_bits(a2)));
+
+	/* Make sure any unused bits are zeroed. */
+	result = (inet *) palloc0(sizeof(inet));
+
+	ip_family(result) = ip_family(a1);
+	ip_bits(result) = commonbits;
+
+	/* Clone appropriate bytes of the address. */
+	if (commonbits > 0)
+		memcpy(ip_addr(result), ip_addr(a1), (commonbits + 7) / 8);
+
+	/* Set varlena header correctly. */
+	SET_INET_VARSIZE(result);
+
+	PG_RETURN_INET_P(result);
+}
+
+/*
  * Convert a value of a network datatype to an approximate scalar value.
  * This is used for estimating selectivities of inequality operators
  * involving network types.
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index c1c3091..f96ddab 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -1007,12 +1007,10 @@ range_minus(PG_FUNCTION_ARGS)
 }
 
 /* set union */
-Datum
-range_union(PG_FUNCTION_ARGS)
+static RangeType *
+range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
+					 bool strict)
 {
-	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeType  *r2 = PG_GETARG_RANGE(1);
-	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -1026,19 +1024,18 @@ range_union(PG_FUNCTION_ARGS)
 	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
-	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
 	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
 	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
 	/* if either is empty, the other is the correct answer */
 	if (empty1)
-		PG_RETURN_RANGE(r2);
+		return r2;
 	if (empty2)
-		PG_RETURN_RANGE(r1);
+		return r1;
 
-	if (!DatumGetBool(range_overlaps(fcinfo)) &&
-		!DatumGetBool(range_adjacent(fcinfo)))
+	if (strict &&
+		!DatumGetBool(range_overlaps_internal(typcache, r1, r2)) &&
+		!DatumGetBool(range_adjacent_internal(typcache, r1, r2)))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_EXCEPTION),
 				 errmsg("result of range union would not be contiguous")));
@@ -1053,7 +1050,31 @@ range_union(PG_FUNCTION_ARGS)
 	else
 		result_upper = &upper2;
 
-	PG_RETURN_RANGE(make_range(typcache, result_lower, result_upper, false));
+	return make_range(typcache, result_lower, result_upper, false);
+}
+
+Datum
+range_union(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE(0);
+	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	PG_RETURN_RANGE(range_union_internal(typcache, r1, r2, true));
+}
+
+Datum
+range_merge(PG_FUNCTION_ARGS)
+{
+	RangeType  *r1 = PG_GETARG_RANGE(0);
+	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	PG_RETURN_RANGE(range_union_internal(typcache, r1, r2, false));
 }
 
 /* set intersection */
diff --git a/src/include/access/skey.h b/src/include/access/skey.h
index bb96808..fec0aea 100644
--- a/src/include/access/skey.h
+++ b/src/include/access/skey.h
@@ -40,6 +40,22 @@ typedef uint16 StrategyNumber;
 #define BTMaxStrategyNumber				5
 
 
+/* Operator strategy numbers used in the GiST and SP-GiST range opclasses */
+/* Numbers are chosen to match up operator names with existing usages */
+#define RANGESTRAT_BEFORE				1
+#define RANGESTRAT_OVERLEFT				2
+#define RANGESTRAT_OVERLAPS				3
+#define RANGESTRAT_OVERRIGHT			4
+#define RANGESTRAT_AFTER				5
+#define RANGESTRAT_ADJACENT				6
+#define RANGESTRAT_CONTAINS				7
+#define RANGESTRAT_CONTAINED_BY			8
+#define RANGESTRAT_CONTAINS_NOTEQ		9
+#define RANGESTRAT_CONTAINED_BY_NOTEQ	10
+#define RANGESTRAT_CONTAINS_ELEM		16
+#define RANGESTRAT_EQ					18
+
+
 /*
  * A ScanKey represents the application of a comparison operator between
  * a table or index column and a constant.  When it's part of an array of
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index b03e5d4..5416707 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -132,7 +132,7 @@ 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 8 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 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 ));
 #define BRIN_AM_OID 3580
 
 #endif   /* PG_AM_H */
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index e72cc6c..40b93f2 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -936,6 +936,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 ));
+/* range inet */
+DATA(insert (   4051    869  869 3 s      3552    3580 0 ));
+DATA(insert (   4051    869  869 7 s      934     3580 0 ));
+DATA(insert (   4051    869  869 8 s      932     3580 0 ));
+DATA(insert (   4051    869  869 9 s      933     3580 0 ));
+DATA(insert (   4051    869  869 10 s     931     3580 0 ));
+DATA(insert (   4051    869  869 18 s     1201    3580 0 ));
 /* minmax character */
 DATA(insert (   4076   1042 1042 1 s      1058    3580 0 ));
 DATA(insert (   4076   1042 1042 2 s      1059    3580 0 ));
@@ -1002,6 +1009,17 @@ 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 ));
+/* range types */
+DATA(insert (   4053   3831 3831 1 s      3893    3580 0 ));
+DATA(insert (   4053   3831 3831 2 s      3895    3580 0 ));
+DATA(insert (   4053   3831 3831 3 s      3888    3580 0 ));
+DATA(insert (   4053   3831 3831 4 s      3896    3580 0 ));
+DATA(insert (   4053   3831 3831 5 s      3894    3580 0 ));
+DATA(insert (   4053   3831 3831 6 s      3897    3580 0 ));
+DATA(insert (   4053   3831 3831 7 s      3890    3580 0 ));
+DATA(insert (   4053   3831 3831 8 s      3892    3580 0 ));
+DATA(insert (   4053   3831 2283 16 s     3889    3580 0 ));
+DATA(insert (   4053   3831 3831 18 s     3882    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 ));
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 9ffe1a9..db380fe 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -568,6 +568,15 @@ DATA(insert (   4075   869   869  5   921 ));
 DATA(insert (   4075   869   869  6   922 ));
 DATA(insert (   4075   869   869  7   924 ));
 DATA(insert (   4075   869   869  8   923 ));
+/* range inet */
+DATA(insert (   4051   869   869  1  3387 ));
+DATA(insert (   4051   869   869  2  3388 ));
+DATA(insert (   4051   869   869  3  3389 ));
+DATA(insert (   4051   869   869  4  3390 ));
+DATA(insert (   4051   869   869  5   930 ));
+DATA(insert (   4051   869   869  6  4052 ));
+DATA(insert (   4051   869   869  9  3551 ));
+DATA(insert (   4051   869   869 14   929 ));
 /* minmax character */
 DATA(insert (   4076  1042  1042  1  3383 ));
 DATA(insert (   4076  1042  1042  2  3384 ));
@@ -667,6 +676,20 @@ DATA(insert (   4081  2950  2950  5  2954 ));
 DATA(insert (   4081  2950  2950  6  2955 ));
 DATA(insert (   4081  2950  2950  7  2957 ));
 DATA(insert (   4081  2950  2950  8  2958 ));
+/* range types */
+DATA(insert (   4053  3831  3831  1  3387 ));
+DATA(insert (   4053  3831  3831  2  3388 ));
+DATA(insert (   4053  3831  3831  3  3389 ));
+DATA(insert (   4053  3831  3831  4  3390 ));
+DATA(insert (   4053  3831  3831  5  3859 ));
+DATA(insert (   4053  3831  3831  6  4057 ));
+DATA(insert (   4053  3831  3831  7  3863 ));
+DATA(insert (   4053  3831  3831  8  3865 ));
+DATA(insert (   4053  3831  3831  9  3857 ));
+DATA(insert (   4053  3831  3831 10  3866 ));
+DATA(insert (   4053  3831  3831 11  3864 ));
+DATA(insert (   4053  3831  3831 12  3862 ));
+DATA(insert (   4053  3831  3831 13  3858 ));
 /* minmax pg_lsn */
 DATA(insert (   4082  3220  3220  1  3383 ));
 DATA(insert (   4082  3220  3220  2  3384 ));
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index 595cd7f..d4f50c5 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -252,7 +252,8 @@ DATA(insert (	3580	float8_minmax_ops		PGNSP PGUID 4071   701 t 0 ));
 DATA(insert (	3580	abstime_minmax_ops		PGNSP PGUID 4072   702 t 0 ));
 DATA(insert (	3580	reltime_minmax_ops		PGNSP PGUID 4073   703 t 0 ));
 DATA(insert (	3580	macaddr_minmax_ops		PGNSP PGUID 4074   829 t 0 ));
-DATA(insert (	3580	inet_minmax_ops			PGNSP PGUID 4075   869 t 0 ));
+DATA(insert (	3580	inet_minmax_ops			PGNSP PGUID 4075   869 f 0 ));
+DATA(insert (	3580	inet_range_ops			PGNSP PGUID 4051   869 t 0 ));
 DATA(insert (	3580	bpchar_minmax_ops		PGNSP PGUID 4076  1042 t 0 ));
 DATA(insert (	3580	date_minmax_ops			PGNSP PGUID 4061  1082 t 0 ));
 DATA(insert (	3580	time_minmax_ops			PGNSP PGUID 4077  1083 t 0 ));
@@ -265,7 +266,8 @@ DATA(insert (	3580	varbit_minmax_ops		PGNSP PGUID 4080  1562 t 0 ));
 DATA(insert (	3580	numeric_minmax_ops		PGNSP PGUID 4055  1700 t 0 ));
 /* no brin opclass for record, anyarray */
 DATA(insert (	3580	uuid_minmax_ops			PGNSP PGUID 4081  2950 t 0 ));
+DATA(insert (	3580	range_ops				PGNSP PGUID 4053  3831 t 0 ));
 DATA(insert (	3580	pg_lsn_minmax_ops		PGNSP PGUID 4082  3220 t 0 ));
-/* no brin opclass for enum, tsvector, tsquery, jsonb, range */
+/* no brin opclass for enum, tsvector, tsquery, jsonb */
 
 #endif   /* PG_OPCLASS_H */
diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h
index 2d8af76..a574a9f 100644
--- a/src/include/catalog/pg_opfamily.h
+++ b/src/include/catalog/pg_opfamily.h
@@ -147,6 +147,7 @@ DATA(insert OID = 3901 (	403		range_ops		PGNSP PGUID ));
 DATA(insert OID = 3903 (	405		range_ops		PGNSP PGUID ));
 DATA(insert OID = 3919 (	783		range_ops		PGNSP PGUID ));
 DATA(insert OID = 3474 (	4000	range_ops		PGNSP PGUID ));
+DATA(insert OID = 4053 (	3580	range_ops		PGNSP PGUID ));
 DATA(insert OID = 4015 (	4000	quad_point_ops	PGNSP PGUID ));
 DATA(insert OID = 4016 (	4000	kd_point_ops	PGNSP PGUID ));
 DATA(insert OID = 4017 (	4000	text_ops		PGNSP PGUID ));
@@ -177,6 +178,7 @@ 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	inet_minmax_ops			PGNSP PGUID ));
+DATA(insert OID = 4051 (	3580	inet_range_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 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 91cb911..6cb8a1e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2210,6 +2210,8 @@ DATA(insert OID = 2630 (  inetpl			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 869
 DATA(insert OID = 2631 (  int8pl_inet		PGNSP PGUID 14 1 0 0 0 f f f f t f i 2 0 869 "20 869" _null_ _null_ _null_ _null_	"select $2 + $1" _null_ _null_ _null_ ));
 DATA(insert OID = 2632 (  inetmi_int8		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 869 "869 20" _null_ _null_ _null_ _null_	inetmi_int8 _null_ _null_ _null_ ));
 DATA(insert OID = 2633 (  inetmi			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 20 "869 869" _null_ _null_ _null_ _null_	inetmi _null_ _null_ _null_ ));
+DATA(insert OID = 4052 (  inet_merge		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 869 "869 869" _null_ _null_ _null_ _null_ inet_merge _null_ _null_ _null_ ));
+DESCR("union of two networks");
 
 /* GiST support for inet and cidr */
 DATA(insert OID = 3553 (  inet_gist_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 869 23 26 2281" _null_ _null_ _null_ _null_ inet_gist_consistent _null_ _null_ _null_ ));
@@ -4116,6 +4118,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_ minmaxUnion _null_ _null_ _null_ ));
 DESCR("BRIN minmax support");
 
+/* BRIN range */
+DATA(insert OID = 3387 ( brin_range_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ rangeOpcInfo _null_ _null_ _null_ ));
+DESCR("BRIN range support");
+DATA(insert OID = 3388 ( brin_range_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_ rangeAddValue _null_ _null_ _null_ ));
+DESCR("BRIN range support");
+DATA(insert OID = 3389 ( brin_range_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_ rangeConsistent _null_ _null_ _null_ ));
+DESCR("BRIN range support");
+DATA(insert OID = 3390 ( brin_range_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_ rangeUnion _null_ _null_ _null_ ));
+DESCR("BRIN range 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_ pg_advisory_lock_int8 _null_ _null_ _null_ ));
 DESCR("obtain exclusive advisory lock");
@@ -4843,6 +4855,8 @@ DATA(insert OID = 3866 (  range_overright	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2
 DESCR("implementation of &> operator");
 DATA(insert OID = 3867 (  range_union		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_union _null_ _null_ _null_ ));
 DESCR("implementation of + operator");
+DATA(insert OID = 4057 (  range_merge		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_merge _null_ _null_ _null_ ));
+DESCR("merge two ranges");
 DATA(insert OID = 3868 (  range_intersect	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_intersect _null_ _null_ _null_ ));
 DESCR("implementation of * operator");
 DATA(insert OID = 3869 (  range_minus		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_minus _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d88e7a3..63fc362 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -944,6 +944,7 @@ extern Datum inetpl(PG_FUNCTION_ARGS);
 extern Datum inetmi_int8(PG_FUNCTION_ARGS);
 extern Datum inetmi(PG_FUNCTION_ARGS);
 extern void clean_ipv6_addr(int addr_family, char *addr);
+extern Datum inet_merge(PG_FUNCTION_ARGS);
 
 /* mac.c */
 extern Datum macaddr_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index b1d17b9..0146166 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -75,19 +75,6 @@ typedef struct
 #define PG_GETARG_RANGE_COPY(n)		DatumGetRangeTypeCopy(PG_GETARG_DATUM(n))
 #define PG_RETURN_RANGE(x)			return RangeTypeGetDatum(x)
 
-/* Operator strategy numbers used in the GiST and SP-GiST range opclasses */
-/* Numbers are chosen to match up operator names with existing usages */
-#define RANGESTRAT_BEFORE				1
-#define RANGESTRAT_OVERLEFT				2
-#define RANGESTRAT_OVERLAPS				3
-#define RANGESTRAT_OVERRIGHT			4
-#define RANGESTRAT_AFTER				5
-#define RANGESTRAT_ADJACENT				6
-#define RANGESTRAT_CONTAINS				7
-#define RANGESTRAT_CONTAINED_BY			8
-#define RANGESTRAT_CONTAINS_ELEM		16
-#define RANGESTRAT_EQ					18
-
 /*
  * prototypes for functions defined in rangetypes.c
  */
@@ -156,6 +143,7 @@ extern bool range_overright_internal(TypeCacheEntry *typcache, RangeType *r1,
 /* range, range -> range */
 extern Datum range_minus(PG_FUNCTION_ARGS);
 extern Datum range_union(PG_FUNCTION_ARGS);
+extern Datum range_merge(PG_FUNCTION_ARGS);
 extern Datum range_intersect(PG_FUNCTION_ARGS);
 
 /* BTree support */
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index d58bf01..e897b2e 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -392,6 +392,91 @@ SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i;
 
 SET enable_seqscan TO on;
 DROP INDEX inet_idx2;
+-- check that brin opclasses work correctly
+CREATE TABLE inet_brin_tbl AS
+SELECT (i::text || '.' || j::text || '/16')::cidr AS addr
+FROM generate_series(0, 255) AS i, generate_series(0, 255) AS j;
+CREATE INDEX inet_brin_idx1 ON inet_brin_tbl using brin (addr inet_minmax_ops);
+SET enable_seqscan TO off;
+SELECT count(*) FROM inet_brin_tbl WHERE addr < '10/8'::cidr;
+ count 
+-------
+  2560
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr <= '10.0/16'::cidr;
+ count 
+-------
+  2561
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr = '10.0/16'::cidr;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr >= '200.0/16'::cidr;
+ count 
+-------
+ 14336
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr > '200.0.0.0'::inet;
+ count 
+-------
+ 14335
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr <> '200.0/16'::cidr;
+ count 
+-------
+ 65535
+(1 row)
+
+SET enable_seqscan TO on;
+DROP INDEX inet_brin_idx1;
+SET enable_seqscan TO off;
+CREATE INDEX inet_brin_idx2 ON inet_brin_tbl using brin (addr inet_range_ops);
+SELECT count(*) FROM inet_brin_tbl WHERE addr << '10/8'::cidr;
+ count 
+-------
+   256
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr <<= '10.0/16'::cidr;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr && '10.0/16'::cidr;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr >>= '200.0/16'::cidr;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr >> '200.0.0.0'::inet;
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM inet_brin_tbl WHERE addr = '200.0/16'::cidr;
+ count 
+-------
+     1
+(1 row)
+
+SET enable_seqscan TO on;
+DROP INDEX inet_brin_idx2;
+DROP TABLE inet_brin_tbl;
 -- simple tests of inet boolean and arithmetic operators
 SELECT i, ~i AS "~i" FROM inet_tbl;
         i         |                     ~i                     
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 932e89a..085c3db 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1659,10 +1659,24 @@ 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 |           16 | @>
+       3580 |           18 | =
        4000 |            1 | <<
        4000 |            1 | ~<~
        4000 |            2 | &<
@@ -1685,7 +1699,7 @@ ORDER BY 1, 2, 3;
        4000 |           15 | >
        4000 |           16 | @>
        4000 |           18 | =
-(85 rows)
+(99 rows)
 
 -- Check that all opclass search operators have selectivity estimators.
 -- This is not absolutely required, but it seems a reasonable thing
@@ -1847,13 +1861,14 @@ WHERE NOT (
   -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
   --   at least one of 4 and 6 must be given.
   -- SP-GiST has five support functions, all mandatory
-  -- BRIN has eight support functions, all mandatory
+  -- BRIN has four mandatory support functions, in-core implementations
+  --   have 2 more mandatory and up to eight more optional
   amname = 'btree' AND procnums @> '{1}' OR
   amname = 'hash' AND procnums = '{1}' OR
   amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
   amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
   amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
-  amname = 'brin' AND procnums = '{1, 2, 3, 4, 5, 6, 7, 8}'
+  amname = 'brin' AND procnums @> '{1, 2, 3, 4, 5, 6}'
 );
  amname | opfname | amproclefttype | amprocrighttype | procnums 
 --------+---------+----------------+-----------------+----------
@@ -1875,7 +1890,7 @@ WHERE NOT (
   amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
   amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
   amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
-  amname = 'brin' AND procnums = '{1, 2, 3, 4, 5, 6, 7, 8}'
+  amname = 'brin' AND procnums @> '{1, 2, 3, 4, 5, 6}'
 );
  amname | opcname | procnums 
 --------+---------+----------
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 39db992..fba7685 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1043,6 +1043,225 @@ select count(*) from test_range_spgist where ir -|- int4range(100,500);
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
+-- test brin that's been built incrementally
+create table test_range_brin(ir int4range);
+create index test_range_brin_idx on test_range_brin using brin (ir);
+insert into test_range_brin select int4range(g, g+10) from generate_series(1,2000) g;
+insert into test_range_brin select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_brin select int4range(g, g+10000) from generate_series(1,1000) g;
+insert into test_range_brin select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_brin select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+insert into test_range_brin select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+insert into test_range_brin select int4range(g, g+10) from generate_series(1,2000) g;
+-- first, verify non-indexed results
+SET enable_seqscan    = t;
+SET enable_bitmapscan = f;
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_brin where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_brin where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_brin where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_brin where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from test_range_brin where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_brin where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_brin where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_brin where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+-- now check same queries using index
+SET enable_seqscan    = f;
+SET enable_bitmapscan = t;
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_brin where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_brin where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_brin where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_brin where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from test_range_brin where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_brin where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_brin where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_brin where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+-- now check same queries using a bulk-loaded index
+drop index test_range_brin_idx;
+create index test_range_brin_idx on test_range_brin using brin (ir);
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_brin where ir = int4range(10,20);
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from test_range_brin where ir @> 10;
+ count 
+-------
+   130
+(1 row)
+
+select count(*) from test_range_brin where ir @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_range_brin where ir && int4range(10,20);
+ count 
+-------
+   158
+(1 row)
+
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+ count 
+-------
+  1062
+(1 row)
+
+select count(*) from test_range_brin where ir << int4range(100,500);
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_brin where ir >> int4range(100,500);
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_brin where ir &< int4range(100,500);
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_brin where ir &> int4range(100,500);
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+ count 
+-------
+     5
+(1 row)
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
 -- test elem <@ range operator
 create table test_range_elem(i int4);
 create index test_range_elem_idx on test_range_elem (i);
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 2c8ec11..325aa03 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -157,6 +157,7 @@ stud_emp|f
 student|f
 tenk1|t
 tenk2|t
+test_range_brin|t
 test_range_excl|t
 test_range_gist|t
 test_range_spgist|t
diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql
index c9792b7..24ddde5 100644
--- a/src/test/regress/sql/inet.sql
+++ b/src/test/regress/sql/inet.sql
@@ -87,6 +87,32 @@ SELECT * FROM inet_tbl WHERE i <> '192.168.1.0/24'::cidr ORDER BY i;
 SET enable_seqscan TO on;
 DROP INDEX inet_idx2;
 
+-- check that brin opclasses work correctly
+CREATE TABLE inet_brin_tbl AS
+SELECT (i::text || '.' || j::text || '/16')::cidr AS addr
+FROM generate_series(0, 255) AS i, generate_series(0, 255) AS j;
+CREATE INDEX inet_brin_idx1 ON inet_brin_tbl using brin (addr inet_minmax_ops);
+SET enable_seqscan TO off;
+SELECT count(*) FROM inet_brin_tbl WHERE addr < '10/8'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr <= '10.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr = '10.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr >= '200.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr > '200.0.0.0'::inet;
+SELECT count(*) FROM inet_brin_tbl WHERE addr <> '200.0/16'::cidr;
+SET enable_seqscan TO on;
+DROP INDEX inet_brin_idx1;
+SET enable_seqscan TO off;
+CREATE INDEX inet_brin_idx2 ON inet_brin_tbl using brin (addr inet_range_ops);
+SELECT count(*) FROM inet_brin_tbl WHERE addr << '10/8'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr <<= '10.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr && '10.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr >>= '200.0/16'::cidr;
+SELECT count(*) FROM inet_brin_tbl WHERE addr >> '200.0.0.0'::inet;
+SELECT count(*) FROM inet_brin_tbl WHERE addr = '200.0/16'::cidr;
+SET enable_seqscan TO on;
+DROP INDEX inet_brin_idx2;
+DROP TABLE inet_brin_tbl;
+
 -- simple tests of inet boolean and arithmetic operators
 SELECT i, ~i AS "~i" FROM inet_tbl;
 SELECT i, c, i & c AS "and" FROM inet_tbl;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index bd83d00..c7d08a1 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1195,13 +1195,14 @@ WHERE NOT (
   -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
   --   at least one of 4 and 6 must be given.
   -- SP-GiST has five support functions, all mandatory
-  -- BRIN has eight support functions, all mandatory
+  -- BRIN has four mandatory support functions, in-core implementations
+  --   have 2 more mandatory and up to eight more optional
   amname = 'btree' AND procnums @> '{1}' OR
   amname = 'hash' AND procnums = '{1}' OR
   amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
   amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
   amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
-  amname = 'brin' AND procnums = '{1, 2, 3, 4, 5, 6, 7, 8}'
+  amname = 'brin' AND procnums @> '{1, 2, 3, 4, 5, 6}'
 );
 
 -- Also, check if there are any pg_opclass entries that don't seem to have
@@ -1221,7 +1222,7 @@ WHERE NOT (
   amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
   amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
   amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
-  amname = 'brin' AND procnums = '{1, 2, 3, 4, 5, 6, 7, 8}'
+  amname = 'brin' AND procnums @> '{1, 2, 3, 4, 5, 6}'
 );
 
 -- Unfortunately, we can't check the amproc link very well because the
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index fad843a..ed998eb 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -286,6 +286,69 @@ RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
 
+-- test brin that's been built incrementally
+create table test_range_brin(ir int4range);
+create index test_range_brin_idx on test_range_brin using brin (ir);
+
+insert into test_range_brin select int4range(g, g+10) from generate_series(1,2000) g;
+insert into test_range_brin select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_brin select int4range(g, g+10000) from generate_series(1,1000) g;
+insert into test_range_brin select 'empty'::int4range from generate_series(1,500) g;
+insert into test_range_brin select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
+insert into test_range_brin select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
+insert into test_range_brin select int4range(g, g+10) from generate_series(1,2000) g;
+
+-- first, verify non-indexed results
+SET enable_seqscan    = t;
+SET enable_bitmapscan = f;
+
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+select count(*) from test_range_brin where ir = int4range(10,20);
+select count(*) from test_range_brin where ir @> 10;
+select count(*) from test_range_brin where ir @> int4range(10,20);
+select count(*) from test_range_brin where ir && int4range(10,20);
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+select count(*) from test_range_brin where ir << int4range(100,500);
+select count(*) from test_range_brin where ir >> int4range(100,500);
+select count(*) from test_range_brin where ir &< int4range(100,500);
+select count(*) from test_range_brin where ir &> int4range(100,500);
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+
+-- now check same queries using index
+SET enable_seqscan    = f;
+SET enable_bitmapscan = t;
+
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+select count(*) from test_range_brin where ir = int4range(10,20);
+select count(*) from test_range_brin where ir @> 10;
+select count(*) from test_range_brin where ir @> int4range(10,20);
+select count(*) from test_range_brin where ir && int4range(10,20);
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+select count(*) from test_range_brin where ir << int4range(100,500);
+select count(*) from test_range_brin where ir >> int4range(100,500);
+select count(*) from test_range_brin where ir &< int4range(100,500);
+select count(*) from test_range_brin where ir &> int4range(100,500);
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+
+-- now check same queries using a bulk-loaded index
+drop index test_range_brin_idx;
+create index test_range_brin_idx on test_range_brin using brin (ir);
+
+select count(*) from test_range_brin where ir @> 'empty'::int4range;
+select count(*) from test_range_brin where ir = int4range(10,20);
+select count(*) from test_range_brin where ir @> 10;
+select count(*) from test_range_brin where ir @> int4range(10,20);
+select count(*) from test_range_brin where ir && int4range(10,20);
+select count(*) from test_range_brin where ir <@ int4range(10,50);
+select count(*) from test_range_brin where ir << int4range(100,500);
+select count(*) from test_range_brin where ir >> int4range(100,500);
+select count(*) from test_range_brin where ir &< int4range(100,500);
+select count(*) from test_range_brin where ir &> int4range(100,500);
+select count(*) from test_range_brin where ir -|- int4range(100,500);
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
 -- test elem <@ range operator
 create table test_range_elem(i int4);
 create index test_range_elem_idx on test_range_elem (i);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to