From fa5c8a055a02e44f446e4593e397c33a572c4d67 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 12 Nov 2003 21:15:59 +0000
Subject: [PATCH] Cross-data-type comparisons are now indexable by btrees,
 pursuant to my pghackers proposal of 8-Nov.  All the existing cross-type
 comparison operators (int2/int4/int8 and float4/float8) have appropriate
 support. The original proposal of storing the right-hand-side datatype as
 part of the primary key for pg_amop and pg_amproc got modified a bit in the
 event; it is easier to store zero as the 'default' case and only store a
 nonzero when the operator is actually cross-type.  Along the way, remove the
 long-since-defunct bigbox_ops operator class.

---
 contrib/dblink/dblink.c                    |   8 +-
 contrib/miscutil/misc_utils.c              |   9 +-
 doc/src/sgml/catalogs.sgml                 |  21 +-
 doc/src/sgml/xindex.sgml                   |  69 ++-
 src/backend/access/common/scankey.c        |  42 +-
 src/backend/access/gist/gistget.c          |  43 +-
 src/backend/access/gist/gistscan.c         |   5 +-
 src/backend/access/heap/tuptoaster.c       |  54 +--
 src/backend/access/nbtree/nbtcompare.c     |  86 +++-
 src/backend/access/nbtree/nbtinsert.c      |  15 +-
 src/backend/access/nbtree/nbtree.c         |  29 +-
 src/backend/access/nbtree/nbtsearch.c      | 261 ++++++----
 src/backend/access/nbtree/nbtsort.c        |  49 +-
 src/backend/access/nbtree/nbtutils.c       | 307 +++++++-----
 src/backend/access/rtree/rtproc.c          |  16 +-
 src/backend/access/rtree/rtscan.c          |  10 +-
 src/backend/catalog/aclchk.c               |  26 +-
 src/backend/catalog/dependency.c           | 120 +++--
 src/backend/catalog/heap.c                 |  75 ++-
 src/backend/catalog/index.c                |  18 +-
 src/backend/catalog/pg_constraint.c        |  42 +-
 src/backend/catalog/pg_conversion.c        |  11 +-
 src/backend/catalog/pg_depend.c            |  35 +-
 src/backend/catalog/pg_largeobject.c       |  19 +-
 src/backend/commands/async.c               |  19 +-
 src/backend/commands/cluster.c             |  11 +-
 src/backend/commands/comment.c             |  84 ++--
 src/backend/commands/dbcommands.c          |  59 ++-
 src/backend/commands/functioncmds.c        |  10 +-
 src/backend/commands/indexcmds.c           |   8 +-
 src/backend/commands/opclasscmds.c         | 289 ++++++++---
 src/backend/commands/tablecmds.c           |  18 +-
 src/backend/commands/trigger.c             |  83 ++--
 src/backend/commands/typecmds.c            |  34 +-
 src/backend/commands/user.c                |  10 +-
 src/backend/commands/vacuum.c              |  19 +-
 src/backend/executor/nodeIndexscan.c       |  18 +-
 src/backend/nodes/copyfuncs.c              |  13 +-
 src/backend/nodes/outfuncs.c               |  12 +-
 src/backend/optimizer/path/indxpath.c      | 132 ++---
 src/backend/optimizer/plan/createplan.c    |  54 ++-
 src/backend/optimizer/util/plancat.c       |  11 +-
 src/backend/parser/parse_func.c            |  10 +-
 src/backend/rewrite/rewriteRemove.c        |  11 +-
 src/backend/storage/large_object/inv_api.c |  42 +-
 src/backend/utils/adt/float.c              |  22 +-
 src/backend/utils/adt/regproc.c            |  34 +-
 src/backend/utils/adt/ruleutils.c          |  18 +-
 src/backend/utils/adt/selfuncs.c           |  11 +-
 src/backend/utils/cache/catcache.c         |   6 +-
 src/backend/utils/cache/lsyscache.c        |  25 +-
 src/backend/utils/cache/relcache.c         | 172 ++++---
 src/backend/utils/cache/syscache.c         |  10 +-
 src/backend/utils/cache/typcache.c         |  15 +-
 src/backend/utils/init/postinit.c          |  11 +-
 src/backend/utils/sort/tuplesort.c         |  23 +-
 src/include/access/nbtree.h                |  13 +-
 src/include/access/skey.h                  |  30 +-
 src/include/catalog/catversion.h           |   4 +-
 src/include/catalog/indexing.h             |  10 +-
 src/include/catalog/namespace.h            |   6 +-
 src/include/catalog/pg_amop.h              | 532 +++++++++++----------
 src/include/catalog/pg_amproc.h            | 178 +++----
 src/include/catalog/pg_opclass.h           |  18 +-
 src/include/catalog/pg_proc.h              |  14 +-
 src/include/nodes/plannodes.h              |   3 +-
 src/include/utils/builtins.h               |  10 +-
 src/include/utils/geo_decls.h              |   3 +-
 src/include/utils/lsyscache.h              |   9 +-
 src/include/utils/rel.h                    |   6 +-
 src/test/regress/expected/create_index.out |   2 +-
 src/test/regress/expected/oidjoins.out     |  16 +
 src/test/regress/expected/opr_sanity.out   | 121 ++++-
 src/test/regress/sql/create_index.sql      |   2 +-
 src/test/regress/sql/oidjoins.sql          |   8 +
 src/test/regress/sql/opr_sanity.sql        |  82 +++-
 76 files changed, 2238 insertions(+), 1493 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 5728682ccc8..bb601c5436a 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1357,10 +1357,10 @@ get_pkey_attnames(Oid relid, int16 *numatts)
 
 	/* use relid to get all related indexes */
 	indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry, 0,
-						   Anum_pg_index_indrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
+	ScanKeyInit(&entry,
+				Anum_pg_index_indrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
 	scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
 
 	while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
diff --git a/contrib/miscutil/misc_utils.c b/contrib/miscutil/misc_utils.c
index bf84f3b3c2e..fb121e83803 100644
--- a/contrib/miscutil/misc_utils.c
+++ b/contrib/miscutil/misc_utils.c
@@ -21,7 +21,6 @@
 #include "access/tupdesc.h"
 #include "catalog/catname.h"
 #include "catalog/pg_listener.h"
-#include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "fmgr.h"
 #include "storage/lmgr.h"
@@ -87,10 +86,10 @@ active_listeners(text *relname)
 		MemSet(listen_name, 0, NAMEDATALEN);
 		len = MIN(VARSIZE(relname) - VARHDRSZ, NAMEDATALEN - 1);
 		memcpy(listen_name, VARDATA(relname), len);
-		ScanKeyEntryInitialize(&key, 0,
-							   Anum_pg_listener_relname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   PointerGetDatum(listen_name), NAMEOID);
+		ScanKeyInit(&key,
+					Anum_pg_listener_relname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					PointerGetDatum(listen_name));
 		sRel = heap_beginscan(lRel, SnapshotNow, 1, &key);
 	}
 	else
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index bd208906dfe..37f8b9cd659 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.78 2003/11/02 12:53:57 petere Exp $
+ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.79 2003/11/12 21:15:42 tgl Exp $
  -->
 
 <chapter id="catalogs">
@@ -499,6 +499,14 @@
       <entry>The index operator class this entry is for</entry>
      </row>
 
+     <row>
+      <entry><structfield>amopsubtype</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+      <entry>Subtype to distinguish multiple entries for one strategy;
+             zero for default</entry>
+     </row>
+
      <row>
       <entry><structfield>amopstrategy</structfield></entry>
       <entry><type>int2</type></entry>
@@ -562,6 +570,13 @@
       <entry>The index operator class this entry is for</entry>
      </row>
 
+     <row>
+      <entry><structfield>amprocsubtype</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+      <entry>Subtype, if cross-type routine, else zero</entry>
+     </row>
+
      <row>
       <entry><structfield>amprocnum</structfield></entry>
       <entry><type>int2</type></entry>
@@ -2435,7 +2450,7 @@
       <entry><structfield>opcintype</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
-      <entry>Input data type of the operator class</entry>
+      <entry>Data type that the operator class indexes</entry>
      </row>
 
      <row>
@@ -2449,7 +2464,7 @@
       <entry><structfield>opckeytype</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
-      <entry>Type of index data, or zero if same as <structfield>opcintype</></entry>
+      <entry>Type of data stored in index, or zero if same as <structfield>opcintype</></entry>
      </row>
 
     </tbody>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 85aba8abe70..877709cee68 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xindex.sgml,v 1.34 2003/11/01 01:56:29 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xindex.sgml,v 1.35 2003/11/12 21:15:45 tgl Exp $
 -->
 
 <sect1 id="xindex">
@@ -80,7 +80,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xindex.sgml,v 1.34 2003/11/01 01:56:29 pete
    The same operator class name
    can be used for several different index methods (for example, both B-tree
    and hash index methods have operator classes named
-   <literal>oid_ops</literal>), but each such class is an independent
+   <literal>int4_ops</literal>), but each such class is an independent
    entity and must be defined separately.
   </para>
  </sect2>
@@ -589,6 +589,71 @@ CREATE OPERATOR CLASS complex_abs_ops
   </para>
  </sect2>
 
+ <sect2 id="xindex-opclass-crosstype">
+  <title>Cross-Data-Type Operator Classes</title>
+
+  <para>
+   So far we have implicitly assumed that an operator class deals with
+   only one data type.  While there certainly can be only one data type in
+   a particular index column, it is often useful to index operations that
+   compare an indexed column to a value of a different data type.  This is
+   presently supported by the B-tree and GiST index methods.
+  </para>
+
+  <para>
+   B-trees require the left-hand operand of each operator to be the indexed
+   data type, but the right-hand operand can be of a different type.  There
+   must be a support function having a matching signature.  For example,
+   the built-in operator class for type <type>bigint</> (<type>int8</>)
+   allows cross-type comparisons to <type>int4</> and <type>int2</>.  It
+   could be duplicated by this definition:
+
+<programlisting>
+CREATE OPERATOR CLASS int8_ops
+DEFAULT FOR TYPE int8 USING btree AS
+  -- standard int8 comparisons
+  OPERATOR 1 &lt; ,
+  OPERATOR 2 &lt;= ,
+  OPERATOR 3 = ,
+  OPERATOR 4 &gt;= ,
+  OPERATOR 5 &gt; ,
+  FUNCTION 1 btint8cmp(int8, int8) ,
+
+  -- cross-type comparisons to int2 (smallint)
+  OPERATOR 1 &lt; (int8, int2) ,
+  OPERATOR 2 &lt;= (int8, int2) ,
+  OPERATOR 3 = (int8, int2) ,
+  OPERATOR 4 &gt;= (int8, int2) ,
+  OPERATOR 5 &gt; (int8, int2) ,
+  FUNCTION 1 btint82cmp(int8, int2) ,
+
+  -- cross-type comparisons to int4 (integer)
+  OPERATOR 1 &lt; (int8, int4) ,
+  OPERATOR 2 &lt;= (int8, int4) ,
+  OPERATOR 3 = (int8, int4) ,
+  OPERATOR 4 &gt;= (int8, int4) ,
+  OPERATOR 5 &gt; (int8, int4) ,
+  FUNCTION 1 btint84cmp(int8, int4) ;
+</programlisting>
+
+   Notice that this definition <quote>overloads</> the operator strategy and
+   support function numbers.  This is allowed (for B-tree operator classes
+   only) so long as each instance of a particular number has a different
+   right-hand data type.  The instances that are not cross-type are the
+   default or primary operators of the operator class.
+  </para>
+
+  <para>
+   GiST indexes do not allow overloading of strategy or support function
+   numbers, but it is still possible to get the effect of supporting
+   multiple right-hand data types, by assigning a distinct strategy number
+   to each operator that needs to be supported.  The <literal>consistent</>
+   support function must determine what it needs to do based on the strategy
+   number, and must be prepared to accept comparison values of the appropriate
+   data types.
+  </para>
+ </sect2>
+
  <sect2 id="xindex-opclass-dependencies">
   <title>System Dependencies on Operator Classes</title>
 
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index 93741cbb849..2fa02b7d91f 100644
--- a/src/backend/access/common/scankey.c
+++ b/src/backend/access/common/scankey.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.23 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.24 2003/11/12 21:15:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,15 +31,43 @@ ScanKeyEntryInitialize(ScanKey entry,
 					   int flags,
 					   AttrNumber attributeNumber,
 					   StrategyNumber strategy,
+					   Oid subtype,
 					   RegProcedure procedure,
-					   Datum argument,
-					   Oid argtype)
+					   Datum argument)
 {
 	entry->sk_flags = flags;
 	entry->sk_attno = attributeNumber;
 	entry->sk_strategy = strategy;
+	entry->sk_subtype = subtype;
+	entry->sk_argument = argument;
+	fmgr_info(procedure, &entry->sk_func);
+}
+
+/*
+ * ScanKeyInit
+ *		Shorthand version of ScanKeyEntryInitialize: flags and subtype
+ *		are assumed to be zero (the usual value).
+ *
+ * This is the recommended version for hardwired lookups in system catalogs.
+ * It cannot handle NULL arguments, unary operators, or nondefault operators,
+ * but we need none of those features for most hardwired lookups.
+ *
+ * Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
+ * itself, because that's what will be used for any subsidiary info attached
+ * to the ScanKey's FmgrInfo record.
+ */
+void
+ScanKeyInit(ScanKey entry,
+			AttrNumber attributeNumber,
+			StrategyNumber strategy,
+			RegProcedure procedure,
+			Datum argument)
+{
+	entry->sk_flags = 0;
+	entry->sk_attno = attributeNumber;
+	entry->sk_strategy = strategy;
+	entry->sk_subtype = InvalidOid;
 	entry->sk_argument = argument;
-	entry->sk_argtype = argtype;
 	fmgr_info(procedure, &entry->sk_func);
 }
 
@@ -57,14 +85,14 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
 							   int flags,
 							   AttrNumber attributeNumber,
 							   StrategyNumber strategy,
+							   Oid subtype,
 							   FmgrInfo *finfo,
-							   Datum argument,
-							   Oid argtype)
+							   Datum argument)
 {
 	entry->sk_flags = flags;
 	entry->sk_attno = attributeNumber;
 	entry->sk_strategy = strategy;
+	entry->sk_subtype = subtype;
 	entry->sk_argument = argument;
-	entry->sk_argtype = argtype;
 	fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext);
 }
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 24168c99269..5db69a6b40a 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gistget.c,v 1.37 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gistget.c,v 1.38 2003/11/12 21:15:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -221,40 +221,50 @@ gistindex_keytest(IndexTuple tuple,
 				  Page p,
 				  OffsetNumber offset)
 {
-	bool		isNull;
-	Datum		datum;
-	Datum		test;
-	GISTENTRY	de;
-
 	IncrIndexProcessed();
 
 	while (scanKeySize > 0)
 	{
+		Datum		datum;
+		bool		isNull;
+		Datum		test;
+		GISTENTRY	de;
+
 		datum = index_getattr(tuple,
-							  key[0].sk_attno,
+							  key->sk_attno,
 							  giststate->tupdesc,
 							  &isNull);
+		/* is the index entry NULL? */
 		if (isNull)
 		{
 			/* XXX eventually should check if SK_ISNULL */
 			return false;
 		}
-
-/* this code from backend/access/common/indexvalid.c. But why and what???
-		if (key[0].sk_flags & SK_ISNULL)
+		/* is the compared-to datum NULL? */
+		if (key->sk_flags & SK_ISNULL)
 			return false;
-*/
-		gistdentryinit(giststate, key[0].sk_attno - 1, &de,
+
+		gistdentryinit(giststate, key->sk_attno - 1, &de,
 					   datum, r, p, offset,
 					   IndexTupleSize(tuple) - sizeof(IndexTupleData),
 					   FALSE, isNull);
 
-		test = FunctionCall3(&key[0].sk_func,
+		/*
+		 * Call the Consistent function to evaluate the test.  The arguments
+		 * are the index datum (as a GISTENTRY*), the comparison datum, and
+		 * the comparison operator's strategy number and subtype from pg_amop.
+		 *
+		 * (Presently there's no need to pass the subtype since it'll always
+		 * be zero, but might as well pass it for possible future use.)
+		 */
+		test = FunctionCall4(&key->sk_func,
 							 PointerGetDatum(&de),
-							 key[0].sk_argument,
-							 Int32GetDatum(key[0].sk_strategy));
+							 key->sk_argument,
+							 Int32GetDatum(key->sk_strategy),
+							 ObjectIdGetDatum(key->sk_subtype));
 
-		if (de.key != datum && !isAttByVal(giststate, key[0].sk_attno - 1))
+		/* if index datum had to be decompressed, free it */
+		if (de.key != datum && !isAttByVal(giststate, key->sk_attno - 1))
 			if (DatumGetPointer(de.key) != NULL)
 				pfree(DatumGetPointer(de.key));
 
@@ -264,6 +274,7 @@ gistindex_keytest(IndexTuple tuple,
 		scanKeySize--;
 		key++;
 	}
+
 	return true;
 }
 
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
index 960d1c2f25b..9b9d1f60341 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gistscan.c,v 1.48 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gistscan.c,v 1.49 2003/11/12 21:15:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,7 +109,8 @@ gistrescan(PG_FUNCTION_ARGS)
 		 * Modify the scan key so that the Consistent function is called
 		 * for all comparisons.  The original operator is passed to the
 		 * Consistent function in the form of its strategy number, which
-		 * is available from the sk_strategy field.
+		 * is available from the sk_strategy field, and its subtype from
+		 * the sk_subtype field.
 		 */
 		for (i = 0; i < s->numberOfKeys; i++)
 		{
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 0bfe3c01b8f..f643906da1f 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.39 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.40 2003/11/12 21:15:46 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -31,7 +31,6 @@
 #include "access/genam.h"
 #include "access/tuptoaster.h"
 #include "catalog/catalog.h"
-#include "catalog/pg_type.h"
 #include "utils/rel.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -968,11 +967,10 @@ toast_delete_datum(Relation rel, Datum value)
 	 * Setup a scan key to fetch from the index by va_valueid (we don't
 	 * particularly care whether we see them in sequence or not)
 	 */
-	ScanKeyEntryInitialize(&toastkey, 0,
-						   (AttrNumber) 1,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(attr->va_content.va_external.va_valueid),
-						   OIDOID);
+	ScanKeyInit(&toastkey,
+				(AttrNumber) 1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
 
 	/*
 	 * Find the chunks by index
@@ -1040,11 +1038,10 @@ toast_fetch_datum(varattrib *attr)
 	/*
 	 * Setup a scan key to fetch from the index by va_valueid
 	 */
-	ScanKeyEntryInitialize(&toastkey, 0,
-						   (AttrNumber) 1,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(attr->va_content.va_external.va_valueid),
-						   OIDOID);
+	ScanKeyInit(&toastkey,
+				(AttrNumber) 1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
 
 	/*
 	 * Read the chunks by index
@@ -1195,33 +1192,32 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	 * Setup a scan key to fetch from the index. This is either two keys
 	 * or three depending on the number of chunks.
 	 */
-	ScanKeyEntryInitialize(&toastkey[0], 0,
-						   (AttrNumber) 1,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(attr->va_content.va_external.va_valueid),
-						   OIDOID);
+	ScanKeyInit(&toastkey[0],
+				(AttrNumber) 1,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
 
 	/*
 	 * Use equality condition for one chunk, a range condition otherwise:
 	 */
 	if (numchunks == 1)
 	{
-		ScanKeyEntryInitialize(&toastkey[1], 0,
-							   (AttrNumber) 2,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(startchunk), INT4OID);
+		ScanKeyInit(&toastkey[1],
+					(AttrNumber) 2,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(startchunk));
 		nscankeys = 2;
 	}
 	else
 	{
-		ScanKeyEntryInitialize(&toastkey[1], 0,
-							   (AttrNumber) 2,
-							   BTGreaterEqualStrategyNumber, F_INT4GE,
-							   Int32GetDatum(startchunk), INT4OID);
-		ScanKeyEntryInitialize(&toastkey[2], 0,
-							   (AttrNumber) 2,
-							   BTLessEqualStrategyNumber, F_INT4LE,
-							   Int32GetDatum(endchunk), INT4OID);
+		ScanKeyInit(&toastkey[1],
+					(AttrNumber) 2,
+					BTGreaterEqualStrategyNumber, F_INT4GE,
+					Int32GetDatum(startchunk));
+		ScanKeyInit(&toastkey[2],
+					(AttrNumber) 2,
+					BTLessEqualStrategyNumber, F_INT4LE,
+					Int32GetDatum(endchunk));
 		nscankeys = 3;
 	}
 
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 79756513abf..bd698858305 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.46 2003/08/04 02:39:57 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.47 2003/11/12 21:15:46 tgl Exp $
  *
  * NOTES
  *
@@ -97,6 +97,90 @@ btint8cmp(PG_FUNCTION_ARGS)
 		PG_RETURN_INT32(-1);
 }
 
+Datum
+btint48cmp(PG_FUNCTION_ARGS)
+{
+	int32		a = PG_GETARG_INT32(0);
+	int64		b = PG_GETARG_INT64(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
+Datum
+btint84cmp(PG_FUNCTION_ARGS)
+{
+	int64		a = PG_GETARG_INT64(0);
+	int32		b = PG_GETARG_INT32(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
+Datum
+btint24cmp(PG_FUNCTION_ARGS)
+{
+	int16		a = PG_GETARG_INT16(0);
+	int32		b = PG_GETARG_INT32(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
+Datum
+btint42cmp(PG_FUNCTION_ARGS)
+{
+	int32		a = PG_GETARG_INT32(0);
+	int16		b = PG_GETARG_INT16(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
+Datum
+btint28cmp(PG_FUNCTION_ARGS)
+{
+	int16		a = PG_GETARG_INT16(0);
+	int64		b = PG_GETARG_INT64(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
+Datum
+btint82cmp(PG_FUNCTION_ARGS)
+{
+	int64		a = PG_GETARG_INT64(0);
+	int16		b = PG_GETARG_INT16(1);
+
+	if (a > b)
+		PG_RETURN_INT32(1);
+	else if (a == b)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
 Datum
 btoidcmp(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 98f3abb511f..719b1e1f0ed 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.107 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.108 2003/11/12 21:15:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1577,26 +1577,27 @@ _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
 
 	for (i = 1; i <= keysz; i++)
 	{
-		ScanKey		entry = &scankey[i - 1];
 		AttrNumber	attno;
 		Datum		datum;
 		bool		isNull;
 		int32		result;
 
-		attno = entry->sk_attno;
+		attno = scankey->sk_attno;
 		Assert(attno == i);
 		datum = index_getattr(itup, attno, itupdesc, &isNull);
 
 		/* NULLs are never equal to anything */
-		if ((entry->sk_flags & SK_ISNULL) || isNull)
+		if (isNull || (scankey->sk_flags & SK_ISNULL))
 			return false;
 
-		result = DatumGetInt32(FunctionCall2(&entry->sk_func,
-											 entry->sk_argument,
-											 datum));
+		result = DatumGetInt32(FunctionCall2(&scankey->sk_func,
+											 datum,
+											 scankey->sk_argument));
 
 		if (result != 0)
 			return false;
+
+		scankey++;
 	}
 
 	/* if we get here, the keys are equal */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3979f79c358..f920d2497ee 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.106 2003/09/29 23:40:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.107 2003/11/12 21:15:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -397,7 +397,6 @@ btrescan(PG_FUNCTION_ARGS)
 			so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
 		else
 			so->keyData = (ScanKey) NULL;
-		so->numberOfKeys = scan->numberOfKeys;
 		scan->opaque = so;
 	}
 
@@ -423,38 +422,14 @@ btrescan(PG_FUNCTION_ARGS)
 	 * _bt_first.	   - vadim 05/05/97
 	 */
 	if (scankey && scan->numberOfKeys > 0)
-	{
 		memmove(scan->keyData,
 				scankey,
 				scan->numberOfKeys * sizeof(ScanKeyData));
-		so->numberOfKeys = scan->numberOfKeys;
-		memmove(so->keyData,
-				scankey,
-				so->numberOfKeys * sizeof(ScanKeyData));
-	}
+	so->numberOfKeys = 0;		/* until _bt_preprocess_keys sets it */
 
 	PG_RETURN_VOID();
 }
 
-void
-btmovescan(IndexScanDesc scan, Datum v)
-{
-	ItemPointer iptr;
-	BTScanOpaque so;
-
-	so = (BTScanOpaque) scan->opaque;
-
-	/* we aren't holding any read locks, but gotta drop the pin */
-	if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
-	{
-		ReleaseBuffer(so->btso_curbuf);
-		so->btso_curbuf = InvalidBuffer;
-		ItemPointerSetInvalid(iptr);
-	}
-
-	so->keyData[0].sk_argument = v;
-}
-
 /*
  *	btendscan() -- close down a scan
  */
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 432a1ab0c2a..87178d12424 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.81 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.82 2003/11/12 21:15:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 #include "access/genam.h"
 #include "access/nbtree.h"
+#include "utils/lsyscache.h"
 
 
 static Buffer _bt_walk_left(Relation rel, Buffer buf);
@@ -325,17 +326,16 @@ _bt_compare(Relation rel,
 	 * (see _bt_first).
 	 */
 
-	for (i = 0; i < keysz; i++)
+	for (i = 1; i <= keysz; i++)
 	{
-		ScanKey		entry = &scankey[i];
 		Datum		datum;
 		bool		isNull;
 		int32		result;
 
-		datum = index_getattr(itup, entry->sk_attno, itupdesc, &isNull);
+		datum = index_getattr(itup, scankey->sk_attno, itupdesc, &isNull);
 
 		/* see comments about NULLs handling in btbuild */
-		if (entry->sk_flags & SK_ISNULL)		/* key is NULL */
+		if (scankey->sk_flags & SK_ISNULL)		/* key is NULL */
 		{
 			if (isNull)
 				result = 0;		/* NULL "=" NULL */
@@ -348,14 +348,28 @@ _bt_compare(Relation rel,
 		}
 		else
 		{
-			result = DatumGetInt32(FunctionCall2(&entry->sk_func,
-												 entry->sk_argument,
-												 datum));
+			/*
+			 * The sk_func needs to be passed the index value as left arg
+			 * and the sk_argument as right arg (they might be of different
+			 * types).  Since it is convenient for callers to think of
+			 * _bt_compare as comparing the scankey to the index item,
+			 * we have to flip the sign of the comparison result.
+			 *
+			 * Note: curious-looking coding is to avoid overflow if
+			 * comparison function returns INT_MIN.  There is no risk of
+			 * overflow for positive results.
+			 */
+			result = DatumGetInt32(FunctionCall2(&scankey->sk_func,
+												 datum,
+												 scankey->sk_argument));
+			result = (result < 0) ? 1 : -result;
 		}
 
 		/* if the keys are unequal, return the difference */
 		if (result != 0)
 			return result;
+
+		scankey++;
 	}
 
 	/* if we get here, the keys are equal */
@@ -448,126 +462,203 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
 	StrategyNumber strat;
 	bool		res;
 	int32		result;
-	bool		scanFromEnd;
 	bool		continuescan;
 	ScanKey		scankeys = NULL;
+	ScanKey	   *startKeys = NULL;
 	int			keysCount = 0;
-	int		   *nKeyIs = NULL;
-	int			i,
-				j;
+	int			i;
 	StrategyNumber strat_total;
 
 	/*
-	 * Order the scan keys in our canonical fashion and eliminate any
-	 * redundant keys.
+	 * Examine the scan keys and eliminate any redundant keys; also
+	 * discover how many keys must be matched to continue the scan.
 	 */
-	_bt_orderkeys(scan);
+	_bt_preprocess_keys(scan);
 
 	/*
-	 * Quit now if _bt_orderkeys() discovered that the scan keys can never
-	 * be satisfied (eg, x == 1 AND x > 2).
+	 * Quit now if _bt_preprocess_keys() discovered that the scan keys can
+	 * never be satisfied (eg, x == 1 AND x > 2).
 	 */
 	if (!so->qual_ok)
 		return false;
 
-	/*
+	/*----------
 	 * Examine the scan keys to discover where we need to start the scan.
+	 *
+	 * We want to identify the keys that can be used as starting boundaries;
+	 * these are =, >, or >= keys for a forward scan or =, <, <= keys for
+	 * a backwards scan.  We can use keys for multiple attributes so long as
+	 * the prior attributes had only =, >= (resp. =, <=) keys.  Once we accept
+	 * a > or < boundary or find an attribute with no boundary (which can be
+	 * thought of as the same as "> -infinity"), we can't use keys for any
+	 * attributes to its right, because it would break our simplistic notion
+	 * of what initial positioning strategy to use.
+	 *
+	 * When the scan keys include non-default operators, _bt_preprocess_keys
+	 * may not be able to eliminate redundant keys; in such cases we will
+	 * arbitrarily pick a usable one for each attribute.  This is correct
+	 * but possibly not optimal behavior.  (For example, with keys like
+	 * "x >= 4 AND x >= 5" we would elect to scan starting at x=4 when
+	 * x=5 would be more efficient.)  Since the situation only arises in
+	 * hokily-worded queries, live with it.
+	 *
+	 * When both equality and inequality keys appear for a single attribute
+	 * (again, only possible when non-default operators appear), we *must*
+	 * select one of the equality keys for the starting point, because
+	 * _bt_checkkeys() will stop the scan as soon as an equality qual fails.
+	 * For example, if we have keys like "x >= 4 AND x = 10" and we elect to
+	 * start at x=4, we will fail and stop before reaching x=10.  If multiple
+	 * equality quals survive preprocessing, however, it doesn't matter which
+	 * one we use --- by definition, they are either redundant or
+	 * contradictory.
+	 *----------
 	 */
-	scanFromEnd = false;
 	strat_total = BTEqualStrategyNumber;
 	if (so->numberOfKeys > 0)
 	{
-		nKeyIs = (int *) palloc(so->numberOfKeys * sizeof(int));
-		for (i = 0; i < so->numberOfKeys; i++)
-		{
-			AttrNumber	attno = so->keyData[i].sk_attno;
-
-			/* ignore keys for already-determined attrs */
-			if (attno <= keysCount)
-				continue;
-			/* if we didn't find a boundary for the preceding attr, quit */
-			if (attno > keysCount + 1)
-				break;
+		AttrNumber	curattr;
+		ScanKey		chosen;
+		ScanKey		cur;
 
-			/*
-			 * Can we use this key as a starting boundary for this attr?
-			 *
-			 * We can use multiple keys if they look like, say, = >= = but we
-			 * have to stop after accepting a > or < boundary.
-			 */
-			strat = so->keyData[i].sk_strategy;
-			if (strat == strat_total ||
-				strat == BTEqualStrategyNumber)
-				nKeyIs[keysCount++] = i;
-			else if (ScanDirectionIsBackward(dir) &&
-					 (strat == BTLessStrategyNumber ||
-					  strat == BTLessEqualStrategyNumber))
+		startKeys = (ScanKey *) palloc(so->numberOfKeys * sizeof(ScanKey));
+		/*
+		 * chosen is the so-far-chosen key for the current attribute, if any.
+		 * We don't cast the decision in stone until we reach keys for the
+		 * next attribute.
+		 */
+		curattr = 1;
+		chosen = NULL;
+		/*
+		 * Loop iterates from 0 to numberOfKeys inclusive; we use the last
+		 * pass to handle after-last-key processing.  Actual exit from the
+		 * loop is at one of the "break" statements below.
+		 */
+		for (cur = so->keyData, i = 0;; cur++, i++)
+		{
+			if (i >= so->numberOfKeys || cur->sk_attno != curattr)
 			{
-				nKeyIs[keysCount++] = i;
-				strat_total = strat;
-				if (strat == BTLessStrategyNumber)
+				/*
+				 * Done looking at keys for curattr.  If we didn't find a
+				 * usable boundary key, quit; else save the boundary key
+				 * pointer in startKeys.
+				 */
+				if (chosen == NULL)
+					break;
+				startKeys[keysCount++] = chosen;
+				/*
+				 * Adjust strat_total, and quit if we have stored a > or < key.
+				 */
+				strat = chosen->sk_strategy;
+				if (strat != BTEqualStrategyNumber)
+				{
+					strat_total = strat;
+					if (strat == BTGreaterStrategyNumber ||
+						strat == BTLessStrategyNumber)
+						break;
+				}
+				/*
+				 * Done if that was the last attribute.
+				 */
+				if (i >= so->numberOfKeys)
 					break;
+				/*
+				 * Reset for next attr, which should be in sequence.
+				 */
+				Assert(cur->sk_attno == curattr + 1);
+				curattr = cur->sk_attno;
+				chosen = NULL;
 			}
-			else if (ScanDirectionIsForward(dir) &&
-					 (strat == BTGreaterStrategyNumber ||
-					  strat == BTGreaterEqualStrategyNumber))
+
+			/* Can we use this key as a starting boundary for this attr? */
+			switch (cur->sk_strategy)
 			{
-				nKeyIs[keysCount++] = i;
-				strat_total = strat;
-				if (strat == BTGreaterStrategyNumber)
+				case BTLessStrategyNumber:
+				case BTLessEqualStrategyNumber:
+					if (chosen == NULL && ScanDirectionIsBackward(dir))
+						chosen = cur;
+					break;
+				case BTEqualStrategyNumber:
+					/* override any non-equality choice */
+					chosen = cur;
+					break;
+				case BTGreaterEqualStrategyNumber:
+				case BTGreaterStrategyNumber:
+					if (chosen == NULL && ScanDirectionIsForward(dir))
+						chosen = cur;
 					break;
 			}
 		}
-		if (keysCount == 0)
-			scanFromEnd = true;
 	}
-	else
-		scanFromEnd = true;
 
-	/* if we just need to walk down one edge of the tree, do that */
-	if (scanFromEnd)
+	/*
+	 * If we found no usable boundary keys, we have to start from one end
+	 * of the tree.  Walk down that edge to the first or last key, and
+	 * scan from there.
+	 */
+	if (keysCount == 0)
 	{
-		if (nKeyIs)
-			pfree(nKeyIs);
+		if (startKeys)
+			pfree(startKeys);
 		return _bt_endpoint(scan, dir);
 	}
 
 	/*
 	 * We want to start the scan somewhere within the index.  Set up a
-	 * scankey we can use to search for the correct starting point.
+	 * 3-way-comparison scankey we can use to search for the boundary
+	 * point we identified above.
 	 */
 	scankeys = (ScanKey) palloc(keysCount * sizeof(ScanKeyData));
 	for (i = 0; i < keysCount; i++)
 	{
-		FmgrInfo   *procinfo;
-
-		j = nKeyIs[i];
+		ScanKey		cur = startKeys[i];
 
 		/*
-		 * _bt_orderkeys disallows it, but it's place to add some code
+		 * _bt_preprocess_keys disallows it, but it's place to add some code
 		 * later
 		 */
-		if (so->keyData[j].sk_flags & SK_ISNULL)
+		if (cur->sk_flags & SK_ISNULL)
 		{
-			pfree(nKeyIs);
+			pfree(startKeys);
 			pfree(scankeys);
 			elog(ERROR, "btree doesn't support is(not)null, yet");
 			return false;
 		}
 		/*
-		 * XXX what if sk_argtype is not same as index?
+		 * If scankey operator is of default subtype, we can use the
+		 * cached comparison procedure; otherwise gotta look it up in
+		 * the catalogs.
 		 */
-		procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
-		ScanKeyEntryInitializeWithInfo(scankeys + i,
-									   so->keyData[j].sk_flags,
-									   i + 1,
-									   InvalidStrategy,
-									   procinfo,
-									   so->keyData[j].sk_argument,
-									   so->keyData[j].sk_argtype);
+		if (cur->sk_subtype == InvalidOid)
+		{
+			FmgrInfo   *procinfo;
+
+			procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
+			ScanKeyEntryInitializeWithInfo(scankeys + i,
+										   cur->sk_flags,
+										   i + 1,
+										   InvalidStrategy,
+										   InvalidOid,
+										   procinfo,
+										   cur->sk_argument);
+		}
+		else
+		{
+			RegProcedure cmp_proc;
+
+			cmp_proc = get_opclass_proc(rel->rd_index->indclass[i],
+										cur->sk_subtype,
+										BTORDER_PROC);
+			ScanKeyEntryInitialize(scankeys + i,
+								   cur->sk_flags,
+								   i + 1,
+								   InvalidStrategy,
+								   cur->sk_subtype,
+								   cmp_proc,
+								   cur->sk_argument);
+		}
 	}
-	if (nKeyIs)
-		pfree(nKeyIs);
+
+	pfree(startKeys);
 
 	current = &(scan->currentItemData);
 
@@ -607,7 +698,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
 	 *
 	 * We could step forward in the latter case, but that'd be a waste of
 	 * time if we want to scan backwards.  So, it's now time to examine
-	 * the scan strategy to find the exact place to start the scan.
+	 * the initial-positioning strategy to find the exact place to start
+	 * the scan.
 	 *
 	 * Note: if _bt_step fails (meaning we fell off the end of the index in
 	 * one direction or the other), we either return false (no matches) or
@@ -855,8 +947,8 @@ _bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
 		}
 	}
 	else
-/* backwards scan */
 	{
+		/* backwards scan */
 		if (offnum > P_FIRSTDATAKEY(opaque))
 			offnum = OffsetNumberPrev(offnum);
 		else
@@ -1115,7 +1207,8 @@ _bt_get_endpoint(Relation rel, uint32 level, bool rightmost)
 }
 
 /*
- *	_bt_endpoint() -- Find the first or last key in the index.
+ *	_bt_endpoint() -- Find the first or last key in the index, and scan
+ * from there to the first key satisfying all the quals.
  *
  * This is used by _bt_first() to set up a scan when we've determined
  * that the scan must start at the beginning or end of the index (for
@@ -1205,7 +1298,9 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
 	btitem = (BTItem) PageGetItem(page, PageGetItemId(page, start));
 	itup = &(btitem->bti_itup);
 
-	/* see if we picked a winner */
+	/*
+	 * Okay, we are on the first or last tuple.  Does it pass all the quals?
+	 */
 	if (_bt_checkkeys(scan, itup, dir, &continuescan))
 	{
 		/* yes, return it */
@@ -1214,7 +1309,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
 	}
 	else if (continuescan)
 	{
-		/* no, but there might be another one that is */
+		/* no, but there might be another one that does */
 		res = _bt_next(scan, dir);
 	}
 	else
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index a56665be5c4..ed920eafd33 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsort.c,v 1.77 2003/09/29 23:40:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsort.c,v 1.78 2003/11/12 21:15:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -594,33 +594,37 @@ _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
 		 * Another BTSpool for dead tuples exists. Now we have to merge
 		 * btspool and btspool2.
 		 */
-		ScanKey		entry;
-		Datum		attrDatum1,
-					attrDatum2;
-		bool		isFirstNull,
-					isSecondNull;
-		int32		compare;
 
 		/* the preparation of merge */
-		bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
-		bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
+		bti = (BTItem) tuplesort_getindextuple(btspool->sortstate,
+											   true, &should_free);
+		bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate,
+												true, &should_free2);
 		indexScanKey = _bt_mkscankey_nodata(index);
+
 		for (;;)
 		{
 			load1 = true;		/* load BTSpool next ? */
-			if (NULL == bti2)
+			if (bti2 == NULL)
 			{
-				if (NULL == bti)
+				if (bti == NULL)
 					break;
 			}
-			else if (NULL != bti)
+			else if (bti != NULL)
 			{
-
 				for (i = 1; i <= keysz; i++)
 				{
+					ScanKey		entry;
+					Datum		attrDatum1,
+								attrDatum2;
+					bool		isFirstNull,
+								isSecondNull;
+
 					entry = indexScanKey + i - 1;
-					attrDatum1 = index_getattr((IndexTuple) bti, i, tupdes, &isFirstNull);
-					attrDatum2 = index_getattr((IndexTuple) bti2, i, tupdes, &isSecondNull);
+					attrDatum1 = index_getattr((IndexTuple) bti, i, tupdes,
+											   &isFirstNull);
+					attrDatum2 = index_getattr((IndexTuple) bti2, i, tupdes,
+											   &isSecondNull);
 					if (isFirstNull)
 					{
 						if (!isSecondNull)
@@ -633,7 +637,11 @@ _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
 						break;
 					else
 					{
-						compare = DatumGetInt32(FunctionCall2(&entry->sk_func, attrDatum1, attrDatum2));
+						int32		compare;
+
+						compare = DatumGetInt32(FunctionCall2(&entry->sk_func,
+															  attrDatum1,
+															  attrDatum2));
 						if (compare > 0)
 						{
 							load1 = false;
@@ -656,14 +664,16 @@ _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
 				_bt_buildadd(index, state, bti);
 				if (should_free)
 					pfree((void *) bti);
-				bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free);
+				bti = (BTItem) tuplesort_getindextuple(btspool->sortstate,
+													   true, &should_free);
 			}
 			else
 			{
 				_bt_buildadd(index, state, bti2);
 				if (should_free2)
 					pfree((void *) bti2);
-				bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate, true, &should_free2);
+				bti2 = (BTItem) tuplesort_getindextuple(btspool2->sortstate,
+														true, &should_free2);
 			}
 		}
 		_bt_freeskey(indexScanKey);
@@ -671,7 +681,8 @@ _bt_load(Relation index, BTSpool *btspool, BTSpool *btspool2)
 	else
 	{
 		/* merge is unnecessary */
-		while (bti = (BTItem) tuplesort_getindextuple(btspool->sortstate, true, &should_free), bti != (BTItem) NULL)
+		while ((bti = (BTItem) tuplesort_getindextuple(btspool->sortstate,
+												true, &should_free)) != NULL)
 		{
 			/* When we see first tuple, create first index page */
 			if (state == NULL)
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 54cd7c8cd01..2c9fd741ff3 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.55 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.56 2003/11/12 21:15:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,6 @@
 #include "access/nbtree.h"
 #include "catalog/catalog.h"
 #include "executor/execdebug.h"
-#include "utils/lsyscache.h"
 
 
 /*
@@ -49,8 +48,8 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 		bool		null;
 
 		/*
-		 * We can use the cached support procs since no cross-type comparison
-		 * can be needed.
+		 * We can use the cached (default) support procs since no cross-type
+		 * comparison can be needed.
 		 */
 		procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
 		arg = index_getattr(itup, i + 1, itupdesc, &null);
@@ -58,9 +57,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 									   null ? SK_ISNULL : 0,
 									   (AttrNumber) (i + 1),
 									   InvalidStrategy,
+									   InvalidOid,
 									   procinfo,
-									   arg,
-									   itupdesc->attrs[i]->atttypid);
+									   arg);
 	}
 
 	return skey;
@@ -94,17 +93,17 @@ _bt_mkscankey_nodata(Relation rel)
 		FmgrInfo   *procinfo;
 
 		/*
-		 * We can use the cached support procs since no cross-type comparison
-		 * can be needed.
+		 * We can use the cached (default) support procs since no cross-type
+		 * comparison can be needed.
 		 */
 		procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
 		ScanKeyEntryInitializeWithInfo(&skey[i],
 									   SK_ISNULL,
 									   (AttrNumber) (i + 1),
 									   InvalidStrategy,
+									   InvalidOid,
 									   procinfo,
-									   (Datum) 0,
-									   itupdesc->attrs[i]->atttypid);
+									   (Datum) 0);
 	}
 
 	return skey;
@@ -161,105 +160,104 @@ _bt_formitem(IndexTuple itup)
 }
 
 /*----------
- *	_bt_orderkeys() -- Put keys in a sensible order for conjunctive quals.
+ *	_bt_preprocess_keys() -- Preprocess scan keys
  *
- * After this routine runs, the scan keys are ordered by index attribute
- * (all quals for attr 1, then all for attr 2, etc) and within each attr
- * the keys are ordered by constraint type: ">", ">=", "=", "<=", "<".
- * Furthermore, redundant keys are eliminated: we keep only the tightest
- * >/>= bound and the tightest </<= bound, and if there's an = key then
- * that's the only one returned.  (So, we return either a single = key,
- * or one or two boundary-condition keys for each attr.)
+ * The caller-supplied keys (in scan->keyData[]) are copied to
+ * so->keyData[] with possible transformation.  scan->numberOfKeys is
+ * the number of input keys, so->numberOfKeys gets the number of output
+ * keys (possibly less, never greater).
  *
- * As a byproduct of this work, we can detect contradictory quals such
- * as "x = 1 AND x > 2".  If we see that, we return so->quals_ok = FALSE,
- * indicating the scan need not be run at all since no tuples can match.
+ * The primary purpose of this routine is to discover how many scan keys
+ * must be satisfied to continue the scan.  It also attempts to eliminate
+ * redundant keys and detect contradictory keys.  At present, redundant and
+ * contradictory keys can only be detected for same-data-type comparisons,
+ * but that's the usual case so it seems worth doing.
+ *
+ * The output keys must be sorted by index attribute.  Presently we expect
+ * (but verify) that the input keys are already so sorted --- this is done
+ * by group_clauses_by_indexkey() in indxpath.c.  Some reordering of the keys
+ * within each attribute may be done as a byproduct of the processing here,
+ * but no other code depends on that.
  *
- * Another byproduct is to determine how many quals must be satisfied to
+ * Aside from preparing so->keyData[], this routine sets
+ * so->numberOfRequiredKeys to the number of quals that must be satisfied to
  * continue the scan.  _bt_checkkeys uses this.  For example, if the quals
  * are "x = 1 AND y < 4 AND z < 5", then _bt_checkkeys will reject a tuple
  * (1,2,7), but we must continue the scan in case there are tuples (1,3,z).
  * But once we reach tuples like (1,4,z) we can stop scanning because no
  * later tuples could match.  This is reflected by setting
- * so->numberOfRequiredKeys to the number of leading keys that must be
- * matched to continue the scan.  numberOfRequiredKeys is equal to the
- * number of leading "=" keys plus the key(s) for the first non "="
- * attribute, which can be seen to be correct by considering the above
- * example.
+ * so->numberOfRequiredKeys to 2, the number of leading keys that must be
+ * matched to continue the scan.  In general, numberOfRequiredKeys is equal
+ * to the number of keys for leading attributes with "=" keys, plus the
+ * key(s) for the first non "=" attribute, which can be seen to be correct
+ * by considering the above example.
+ *
+ * If possible, redundant keys are eliminated: we keep only the tightest
+ * >/>= bound and the tightest </<= bound, and if there's an = key then
+ * that's the only one returned.  (So, we return either a single = key,
+ * or one or two boundary-condition keys for each attr.)  However, we can
+ * only detect redundant keys when the right-hand datatypes are all equal
+ * to the index datatype, because we do not know suitable operators for
+ * comparing right-hand values of two different datatypes.  (In theory
+ * we could handle comparison of a RHS of the index datatype with a RHS of
+ * another type, but that seems too much pain for too little gain.)  So,
+ * keys whose operator has a nondefault subtype (ie, its RHS is not of the
+ * index datatype) are ignored here, except for noting whether they impose
+ * an "=" condition or not.
+ *
+ * As a byproduct of this work, we can detect contradictory quals such
+ * as "x = 1 AND x > 2".  If we see that, we return so->quals_ok = FALSE,
+ * indicating the scan need not be run at all since no tuples can match.
+ * Again though, only keys with RHS datatype equal to the index datatype
+ * can be checked for contradictions.
  *
  * Furthermore, we detect the case where the index is unique and we have
  * equality quals for all columns.	In this case there can be at most one
  * (visible) matching tuple.  index_getnext uses this to avoid uselessly
  * continuing the scan after finding one match.
- *
- * The initial ordering of the keys is expected to be by attribute already
- * (see group_clauses_by_indexkey() in indxpath.c).  The task here is to
- * standardize the appearance of multiple keys for the same attribute.
  *----------
  */
 void
-_bt_orderkeys(IndexScanDesc scan)
+_bt_preprocess_keys(IndexScanDesc scan)
 {
 	Relation	relation = scan->indexRelation;
 	BTScanOpaque so = (BTScanOpaque) scan->opaque;
-	ScanKeyData xform[BTMaxStrategyNumber];
-	bool		init[BTMaxStrategyNumber];
-	int			numberOfKeys = so->numberOfKeys;
-	ScanKey		key;
+	int			numberOfKeys = scan->numberOfKeys;
+	int			new_numberOfKeys;
+	ScanKey		inkeys;
+	ScanKey		outkeys;
 	ScanKey		cur;
+	ScanKey		xform[BTMaxStrategyNumber];
+	bool		allEqualSoFar;
+	bool		hasOtherTypeEqual;
 	Datum		test;
 	int			i,
 				j;
 	AttrNumber	attno;
-	int			new_numberOfKeys;
-	bool		allEqualSoFar;
 
+	/* initialize result variables */
 	so->qual_ok = true;
+	so->numberOfKeys = 0;
 	so->numberOfRequiredKeys = 0;
 	scan->keys_are_unique = false;
 
 	if (numberOfKeys < 1)
 		return;					/* done if qual-less scan */
 
-	key = so->keyData;
-	cur = &key[0];
-	/* check input keys are correctly ordered */
+	inkeys = scan->keyData;
+	outkeys = so->keyData;
+	cur = &inkeys[0];
+	/* we check that input keys are correctly ordered */
 	if (cur->sk_attno != 1)
 		elog(ERROR, "key(s) for attribute 1 missed");
 
-#if 0
-	/* XXX verify that operator strategy info is correct */
-	/* XXX this is temporary for debugging; it's pretty expensive */
-	/* XXX can't do it during bootstrap, else will recurse infinitely */
-	{
-		extern bool criticalRelcachesBuilt;
-		static bool inRecursion = false;
-
-		if (criticalRelcachesBuilt && !inRecursion)
-		{
-			inRecursion = true;
-			for (i = 0; i < numberOfKeys; i++)
-			{
-				AttrNumber	attno = key[i].sk_attno;
-				Oid			opclass;
-				Oid			chk_oper;
-
-				opclass = relation->rd_index->indclass[attno-1];
-				chk_oper = get_opclass_member(opclass, key[i].sk_strategy);
-				Assert(key[i].sk_func.fn_oid == get_opcode(chk_oper));
-			}
-			inRecursion = false;
-		}
-	}
-#endif
-
 	/* We can short-circuit most of the work if there's just one key */
 	if (numberOfKeys == 1)
 	{
 		/*
 		 * We don't use indices for 'A is null' and 'A is not null'
 		 * currently and 'A < = > <> NULL' will always fail - so qual is
-		 * not Ok if comparison value is NULL.		- vadim 03/21/97
+		 * not OK if comparison value is NULL.		- vadim 03/21/97
 		 */
 		if (cur->sk_flags & SK_ISNULL)
 			so->qual_ok = false;
@@ -270,6 +268,8 @@ _bt_orderkeys(IndexScanDesc scan)
 			if (cur->sk_strategy == BTEqualStrategyNumber)
 				scan->keys_are_unique = true;
 		}
+		memcpy(outkeys, inkeys, sizeof(ScanKeyData));
+		so->numberOfKeys = 1;
 		so->numberOfRequiredKeys = 1;
 		return;
 	}
@@ -283,12 +283,15 @@ _bt_orderkeys(IndexScanDesc scan)
 	/*
 	 * Initialize for processing of keys for attr 1.
 	 *
-	 * xform[i] holds a copy of the current scan key of strategy type i+1, if
-	 * any; init[i] is TRUE if we have found such a key for this attr.
+	 * xform[i] points to the currently best scan key of strategy type i+1,
+	 * if any is found with a default operator subtype; it is NULL if we
+	 * haven't yet found such a key for this attr.  Scan keys of nondefault
+	 * subtypes are transferred to the output with no processing except for
+	 * noting if they are of "=" type.
 	 */
 	attno = 1;
-	MemSet(xform, 0, sizeof(xform));	/* not really necessary */
-	MemSet(init, 0, sizeof(init));
+	memset(xform, 0, sizeof(xform));
+	hasOtherTypeEqual = false;
 
 	/*
 	 * Loop iterates from 0 to numberOfKeys inclusive; we use the last
@@ -329,80 +332,78 @@ _bt_orderkeys(IndexScanDesc scan)
 			 * of key > 2 && key == 1 and so on we have to set qual_ok to
 			 * false before discarding the other keys.
 			 */
-			if (init[BTEqualStrategyNumber - 1])
+			if (xform[BTEqualStrategyNumber - 1])
 			{
-				ScanKeyData *eq,
-						   *chk;
+				ScanKey		eq = xform[BTEqualStrategyNumber - 1];
 
-				eq = &xform[BTEqualStrategyNumber - 1];
 				for (j = BTMaxStrategyNumber; --j >= 0;)
 				{
-					if (!init[j] ||
-						j == (BTEqualStrategyNumber - 1))
+					ScanKey		chk = xform[j];
+
+					if (!chk || j == (BTEqualStrategyNumber - 1))
 						continue;
-					chk = &xform[j];
 					test = FunctionCall2(&chk->sk_func,
 										 eq->sk_argument,
 										 chk->sk_argument);
 					if (!DatumGetBool(test))
+					{
 						so->qual_ok = false;
+						break;
+					}
 				}
-				init[BTLessStrategyNumber - 1] = false;
-				init[BTLessEqualStrategyNumber - 1] = false;
-				init[BTGreaterEqualStrategyNumber - 1] = false;
-				init[BTGreaterStrategyNumber - 1] = false;
+				xform[BTLessStrategyNumber - 1] = NULL;
+				xform[BTLessEqualStrategyNumber - 1] = NULL;
+				xform[BTGreaterEqualStrategyNumber - 1] = NULL;
+				xform[BTGreaterStrategyNumber - 1] = NULL;
 			}
 			else
 			{
 				/*
-				 * No "=" for this key, so we're done with required keys
+				 * If no "=" for this key, we're done with required keys
 				 */
-				allEqualSoFar = false;
+				if (! hasOtherTypeEqual)
+					allEqualSoFar = false;
 			}
 
 			/* keep only one of <, <= */
-			if (init[BTLessStrategyNumber - 1]
-				&& init[BTLessEqualStrategyNumber - 1])
+			if (xform[BTLessStrategyNumber - 1]
+				&& xform[BTLessEqualStrategyNumber - 1])
 			{
-				ScanKeyData *lt = &xform[BTLessStrategyNumber - 1];
-				ScanKeyData *le = &xform[BTLessEqualStrategyNumber - 1];
+				ScanKey lt = xform[BTLessStrategyNumber - 1];
+				ScanKey le = xform[BTLessEqualStrategyNumber - 1];
 
 				test = FunctionCall2(&le->sk_func,
 									 lt->sk_argument,
 									 le->sk_argument);
 				if (DatumGetBool(test))
-					init[BTLessEqualStrategyNumber - 1] = false;
+					xform[BTLessEqualStrategyNumber - 1] = NULL;
 				else
-					init[BTLessStrategyNumber - 1] = false;
+					xform[BTLessStrategyNumber - 1] = NULL;
 			}
 
 			/* keep only one of >, >= */
-			if (init[BTGreaterStrategyNumber - 1]
-				&& init[BTGreaterEqualStrategyNumber - 1])
+			if (xform[BTGreaterStrategyNumber - 1]
+				&& xform[BTGreaterEqualStrategyNumber - 1])
 			{
-				ScanKeyData *gt = &xform[BTGreaterStrategyNumber - 1];
-				ScanKeyData *ge = &xform[BTGreaterEqualStrategyNumber - 1];
+				ScanKey gt = xform[BTGreaterStrategyNumber - 1];
+				ScanKey ge = xform[BTGreaterEqualStrategyNumber - 1];
 
 				test = FunctionCall2(&ge->sk_func,
 									 gt->sk_argument,
 									 ge->sk_argument);
 				if (DatumGetBool(test))
-					init[BTGreaterEqualStrategyNumber - 1] = false;
+					xform[BTGreaterEqualStrategyNumber - 1] = NULL;
 				else
-					init[BTGreaterStrategyNumber - 1] = false;
+					xform[BTGreaterStrategyNumber - 1] = NULL;
 			}
 
 			/*
-			 * Emit the cleaned-up keys back into the key[] array in the
-			 * correct order.  Note we are overwriting our input here!
-			 * It's OK because (a) xform[] is a physical copy of the keys
-			 * we want, (b) we cannot emit more keys than we input, so we
-			 * won't overwrite as-yet-unprocessed keys.
+			 * Emit the cleaned-up keys into the outkeys[] array.
 			 */
 			for (j = BTMaxStrategyNumber; --j >= 0;)
 			{
-				if (init[j])
-					memcpy(&key[new_numberOfKeys++], &xform[j],
+				if (xform[j])
+					memcpy(&outkeys[new_numberOfKeys++], xform[j],
 						   sizeof(ScanKeyData));
 			}
 
@@ -421,31 +422,43 @@ _bt_orderkeys(IndexScanDesc scan)
 
 			/* Re-initialize for new attno */
 			attno = cur->sk_attno;
-			MemSet(xform, 0, sizeof(xform));	/* not really necessary */
-			MemSet(init, 0, sizeof(init));
+			memset(xform, 0, sizeof(xform));
+			hasOtherTypeEqual = false;
 		}
 
-		/* figure out which strategy this key's operator corresponds to */
+		/* check strategy this key's operator corresponds to */
 		j = cur->sk_strategy - 1;
 
+		/* if wrong RHS data type, punt */
+		if (cur->sk_subtype != InvalidOid)
+		{
+			memcpy(&outkeys[new_numberOfKeys++], cur,
+				   sizeof(ScanKeyData));
+			if (j == (BTEqualStrategyNumber - 1))
+				hasOtherTypeEqual = true;
+			continue;
+		}
+
 		/* have we seen one of these before? */
-		if (init[j])
+		if (xform[j])
 		{
-			/* yup, keep the more restrictive value */
+			/* yup, keep the more restrictive key */
 			test = FunctionCall2(&cur->sk_func,
 								 cur->sk_argument,
-								 xform[j].sk_argument);
+								 xform[j]->sk_argument);
 			if (DatumGetBool(test))
-				xform[j].sk_argument = cur->sk_argument;
+				xform[j] = cur;
 			else if (j == (BTEqualStrategyNumber - 1))
+			{
+				/* key == a && key == b, but a != b */
 				so->qual_ok = false;
-			/* key == a && key == b, but a != b */
+				return;
+			}
 		}
 		else
 		{
 			/* nope, so remember this scankey */
-			memcpy(&xform[j], cur, sizeof(ScanKeyData));
-			init[j] = true;
+			xform[j] = cur;
 		}
 	}
 
@@ -465,8 +478,8 @@ _bt_orderkeys(IndexScanDesc scan)
  *
  * If the tuple fails to pass the qual, we also determine whether there's
  * any need to continue the scan beyond this tuple, and set *continuescan
- * accordingly.  See comments for _bt_orderkeys(), above, about how this is
- * done.
+ * accordingly.  See comments for _bt_preprocess_keys(), above, about how
+ * this is done.
  */
 bool
 _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
@@ -474,7 +487,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 {
 	BTScanOpaque so = (BTScanOpaque) scan->opaque;
 	int			keysz = so->numberOfKeys;
-	int			keysok;
+	int			ikey;
 	TupleDesc	tupdesc;
 	ScanKey		key;
 
@@ -484,13 +497,11 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 	if (keysz == 0)
 		return true;
 
-	tupdesc = RelationGetDescr(scan->indexRelation);
-	key = so->keyData;
-	keysok = 0;
-
 	IncrIndexProcessed();
 
-	while (keysz > 0)
+	tupdesc = RelationGetDescr(scan->indexRelation);
+
+	for (key = so->keyData, ikey = 0; ikey < keysz; key++, ikey++)
 	{
 		Datum		datum;
 		bool		isNull;
@@ -504,7 +515,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 		/* btree doesn't support 'A is null' clauses, yet */
 		if (key->sk_flags & SK_ISNULL)
 		{
-			/* we shouldn't get here, really; see _bt_orderkeys() */
+			/* we shouldn't get here, really; see _bt_preprocess_keys() */
 			*continuescan = false;
 			return false;
 		}
@@ -518,7 +529,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 			 * one of the "must match" subset.	On a backward scan,
 			 * however, we should keep going.
 			 */
-			if (keysok < so->numberOfRequiredKeys &&
+			if (ikey < so->numberOfRequiredKeys &&
 				ScanDirectionIsForward(dir))
 				*continuescan = false;
 
@@ -534,16 +545,50 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 		{
 			/*
 			 * Tuple fails this qual.  If it's a required qual, then we
-			 * can conclude no further tuples will pass, either.
+			 * may be able to conclude no further tuples will pass, either.
+			 * We have to look at the scan direction and the qual type.
+			 *
+			 * Note: the only case in which we would keep going after failing
+			 * a required qual is if there are partially-redundant quals that
+			 * _bt_preprocess_keys() was unable to eliminate.  For example,
+			 * given "x > 4 AND x > 10" where both are cross-type comparisons
+			 * and so not removable, we might start the scan at the x = 4
+			 * boundary point.  The "x > 10" condition will fail until we
+			 * pass x = 10, but we must not stop the scan on its account.
+			 *
+			 * Note: because we stop the scan as soon as any required equality
+			 * qual fails, it is critical that equality quals be used for the
+			 * initial positioning in _bt_first() when they are available.
+			 * See comments in _bt_first().
+			 */
+			if (ikey < so->numberOfRequiredKeys)
+			{
+				switch (key->sk_strategy)
+				{
+					case BTLessStrategyNumber:
+					case BTLessEqualStrategyNumber:
+						if (ScanDirectionIsForward(dir))
+							*continuescan = false;
+						break;
+					case BTEqualStrategyNumber:
+						*continuescan = false;
+						break;
+					case BTGreaterEqualStrategyNumber:
+					case BTGreaterStrategyNumber:
+						if (ScanDirectionIsBackward(dir))
+							*continuescan = false;
+						break;
+					default:
+						elog(ERROR, "unrecognized StrategyNumber: %d",
+							 key->sk_strategy);
+				}
+			}
+
+			/*
+			 * In any case, this indextuple doesn't match the qual.
 			 */
-			if (keysok < so->numberOfRequiredKeys)
-				*continuescan = false;
 			return false;
 		}
-
-		keysok++;
-		key++;
-		keysz--;
 	}
 
 	/* If we get here, the tuple passes all quals. */
diff --git a/src/backend/access/rtree/rtproc.c b/src/backend/access/rtree/rtproc.c
index 790f6bc7b69..985993fb687 100644
--- a/src/backend/access/rtree/rtproc.c
+++ b/src/backend/access/rtree/rtproc.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.37 2003/08/04 02:39:57 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.38 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,20 +82,6 @@ rt_box_size(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-/*
- *	rt_bigbox_size() -- Compute a size for big boxes.
- *
- *		In an earlier release of the system, this routine did something
- *		different from rt_box_size.  We now use floats, rather than ints,
- *		as the return type for the size routine, so we no longer need to
- *		have a special return type for big boxes.
- */
-Datum
-rt_bigbox_size(PG_FUNCTION_ARGS)
-{
-	return rt_box_size(fcinfo);
-}
-
 Datum
 rt_poly_union(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/access/rtree/rtscan.c b/src/backend/access/rtree/rtscan.c
index 263fff4bf26..5026bd78a70 100644
--- a/src/backend/access/rtree/rtscan.c
+++ b/src/backend/access/rtree/rtscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.48 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.49 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -123,15 +123,17 @@ rtrescan(PG_FUNCTION_ARGS)
 
 			opclass = s->indexRelation->rd_index->indclass[attno-1];
 			int_strategy = RTMapToInternalOperator(s->keyData[i].sk_strategy);
-			int_oper = get_opclass_member(opclass, int_strategy);
+			int_oper = get_opclass_member(opclass,
+										  s->keyData[i].sk_subtype,
+										  int_strategy);
 			int_proc = get_opcode(int_oper);
 			ScanKeyEntryInitialize(&(p->s_internalKey[i]),
 								   s->keyData[i].sk_flags,
 								   attno,
 								   int_strategy,
+								   s->keyData[i].sk_subtype,
 								   int_proc,
-								   s->keyData[i].sk_argument,
-								   s->keyData[i].sk_argtype);
+								   s->keyData[i].sk_argument);
 		}
 	}
 
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index a1b697bee85..7534750c99e 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.92 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.93 2003/11/12 21:15:48 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -366,10 +366,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
 		char		replaces[Natts_pg_database];
 
 		relation = heap_openr(DatabaseRelationName, RowExclusiveLock);
-		ScanKeyEntryInitialize(&entry[0], 0,
-							   Anum_pg_database_datname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(dbname), NAMEOID);
+		ScanKeyInit(&entry[0],
+					Anum_pg_database_datname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(dbname));
 		scan = heap_beginscan(relation, SnapshotNow, 1, entry);
 		tuple = heap_getnext(scan, ForwardScanDirection);
 		if (!HeapTupleIsValid(tuple))
@@ -1131,10 +1131,10 @@ pg_database_aclcheck(Oid db_oid, AclId userid, AclMode mode)
 	 * There's no syscache for pg_database, so must look the hard way
 	 */
 	pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(db_oid), OIDOID);
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(db_oid));
 	scan = heap_beginscan(pg_database, SnapshotNow, 1, entry);
 	tuple = heap_getnext(scan, ForwardScanDirection);
 	if (!HeapTupleIsValid(tuple))
@@ -1531,10 +1531,10 @@ pg_database_ownercheck(Oid db_oid, AclId userid)
 
 	/* There's no syscache for pg_database, so must look the hard way */
 	pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(db_oid), OIDOID);
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(db_oid));
 	scan = heap_beginscan(pg_database, SnapshotNow, 1, entry);
 
 	dbtuple = heap_getnext(scan, ForwardScanDirection);
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index b0e17652c74..d64f027ef64 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.32 2003/11/09 21:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.33 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,6 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_trigger.h"
-#include "catalog/pg_type.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
 #include "commands/proclang.h"
@@ -283,20 +282,20 @@ findAutoDeletableObjects(const ObjectAddress *object,
 	 * When dropping a whole object (subId = 0), find pg_depend records for
 	 * its sub-objects too.
 	 */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_refclassid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->classId), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_refobjid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->objectId), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
 	if (object->objectSubId != 0)
 	{
-		ScanKeyEntryInitialize(&key[2], 0,
-							   Anum_pg_depend_refobjsubid,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(object->objectSubId), INT4OID);
+		ScanKeyInit(&key[2],
+					Anum_pg_depend_refobjsubid,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(object->objectSubId));
 		nkeys = 3;
 	}
 	else
@@ -418,20 +417,20 @@ recursiveDeletion(const ObjectAddress *object,
 	 * When dropping a whole object (subId = 0), remove all pg_depend records
 	 * for its sub-objects too.
 	 */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_classid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->classId), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_objid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->objectId), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_classid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_objid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
 	if (object->objectSubId != 0)
 	{
-		ScanKeyEntryInitialize(&key[2], 0,
-							   Anum_pg_depend_objsubid,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(object->objectSubId), INT4OID);
+		ScanKeyInit(&key[2],
+					Anum_pg_depend_objsubid,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(object->objectSubId));
 		nkeys = 3;
 	}
 	else
@@ -651,20 +650,20 @@ deleteDependentObjects(const ObjectAddress *object,
 	HeapTuple	tup;
 	ObjectAddress otherObject;
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_refclassid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->classId), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_refobjid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->objectId), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
 	if (object->objectSubId != 0)
 	{
-		ScanKeyEntryInitialize(&key[2], 0,
-							   Anum_pg_depend_refobjsubid,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(object->objectSubId), INT4OID);
+		ScanKeyInit(&key[2],
+					Anum_pg_depend_refobjsubid,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(object->objectSubId));
 		nkeys = 3;
 	}
 	else
@@ -1473,11 +1472,10 @@ getObjectDescription(const ObjectAddress *object)
 
 				castDesc = heap_openr(CastRelationName, AccessShareLock);
 
-				ScanKeyEntryInitialize(&skey[0], 0,
-									   ObjectIdAttributeNumber,
-									   BTEqualStrategyNumber, F_OIDEQ,
-									   ObjectIdGetDatum(object->objectId),
-									   OIDOID);
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
 
 				rcscan = systable_beginscan(castDesc, CastOidIndex, true,
 											SnapshotNow, 1, skey);
@@ -1509,11 +1507,10 @@ getObjectDescription(const ObjectAddress *object)
 
 				conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
 
-				ScanKeyEntryInitialize(&skey[0], 0,
-									   ObjectIdAttributeNumber,
-									   BTEqualStrategyNumber, F_OIDEQ,
-									   ObjectIdGetDatum(object->objectId),
-									   OIDOID);
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
 
 				rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
 											SnapshotNow, 1, skey);
@@ -1570,11 +1567,10 @@ getObjectDescription(const ObjectAddress *object)
 
 				attrdefDesc = heap_openr(AttrDefaultRelationName, AccessShareLock);
 
-				ScanKeyEntryInitialize(&skey[0], 0,
-									   ObjectIdAttributeNumber,
-									   BTEqualStrategyNumber, F_OIDEQ,
-									   ObjectIdGetDatum(object->objectId),
-									   OIDOID);
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
 
 				adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndex,
 											true, SnapshotNow, 1, skey);
@@ -1672,11 +1668,10 @@ getObjectDescription(const ObjectAddress *object)
 
 				ruleDesc = heap_openr(RewriteRelationName, AccessShareLock);
 
-				ScanKeyEntryInitialize(&skey[0], 0,
-									   ObjectIdAttributeNumber,
-									   BTEqualStrategyNumber, F_OIDEQ,
-									   ObjectIdGetDatum(object->objectId),
-									   OIDOID);
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
 
 				rcscan = systable_beginscan(ruleDesc, RewriteOidIndex, true,
 											SnapshotNow, 1, skey);
@@ -1708,11 +1703,10 @@ getObjectDescription(const ObjectAddress *object)
 
 				trigDesc = heap_openr(TriggerRelationName, AccessShareLock);
 
-				ScanKeyEntryInitialize(&skey[0], 0,
-									   ObjectIdAttributeNumber,
-									   BTEqualStrategyNumber, F_OIDEQ,
-									   ObjectIdGetDatum(object->objectId),
-									   OIDOID);
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
 
 				tgscan = systable_beginscan(trigDesc, TriggerOidIndex, true,
 											SnapshotNow, 1, skey);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 75445334ec7..a0962f487dd 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.254 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.255 2003/11/12 21:15:48 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -853,11 +853,10 @@ RelationRemoveInheritance(Relation relation)
 
 	catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_inherits_inhrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&key,
+				Anum_pg_inherits_inhrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true,
 							  SnapshotNow, 1, &key);
@@ -920,10 +919,10 @@ DeleteAttributeTuples(Oid relid)
 	attrel = heap_openr(AttributeRelationName, RowExclusiveLock);
 
 	/* Use the index to scan only attributes of the target relation */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_attribute_attrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_attribute_attrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
 
 	scan = systable_beginscan(attrel, AttributeRelidNumIndex, true,
 							  SnapshotNow, 1, key);
@@ -1035,14 +1034,14 @@ RemoveAttrDefault(Oid relid, AttrNumber attnum,
 
 	attrdef_rel = heap_openr(AttrDefaultRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&scankeys[0], 0,
-						   Anum_pg_attrdef_adrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
-	ScanKeyEntryInitialize(&scankeys[1], 0,
-						   Anum_pg_attrdef_adnum,
-						   BTEqualStrategyNumber, F_INT2EQ,
-						   Int16GetDatum(attnum), INT2OID);
+	ScanKeyInit(&scankeys[0],
+				Anum_pg_attrdef_adrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+	ScanKeyInit(&scankeys[1],
+				Anum_pg_attrdef_adnum,
+				BTEqualStrategyNumber, F_INT2EQ,
+				Int16GetDatum(attnum));
 
 	scan = systable_beginscan(attrdef_rel, AttrDefaultIndex, true,
 							  SnapshotNow, 2, scankeys);
@@ -1092,10 +1091,10 @@ RemoveAttrDefaultById(Oid attrdefId)
 	attrdef_rel = heap_openr(AttrDefaultRelationName, RowExclusiveLock);
 
 	/* Find the pg_attrdef tuple */
-	ScanKeyEntryInitialize(&scankeys[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(attrdefId), OIDOID);
+	ScanKeyInit(&scankeys[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(attrdefId));
 
 	scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndex, true,
 							  SnapshotNow, 1, scankeys);
@@ -1829,10 +1828,10 @@ RemoveRelConstraints(Relation rel, const char *constrName,
 	conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
 
 	/* Use the index to scan only constraints of the target relation */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_constraint_conrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(rel)), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(rel)));
 
 	conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
 								 SnapshotNow, 1, key);
@@ -1883,19 +1882,19 @@ RemoveStatistics(Relation rel, AttrNumber attnum)
 
 	pgstatistic = heap_openr(StatisticRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_statistic_starelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(rel)), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_statistic_starelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(rel)));
 
 	if (attnum == 0)
 		nkeys = 1;
 	else
 	{
-		ScanKeyEntryInitialize(&key[1], 0,
-							   Anum_pg_statistic_staattnum,
-							   BTEqualStrategyNumber, F_INT2EQ,
-							   Int16GetDatum(attnum), INT2OID);
+		ScanKeyInit(&key[1],
+					Anum_pg_statistic_staattnum,
+					BTEqualStrategyNumber, F_INT2EQ,
+					Int16GetDatum(attnum));
 		nkeys = 2;
 	}
 
@@ -2050,10 +2049,10 @@ heap_truncate_check_FKs(Relation rel)
 	 */
 	fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_constraint_confrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
+	ScanKeyInit(&key,
+				Anum_pg_constraint_confrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
 
 	fkeyScan = systable_beginscan(fkeyRel, NULL, false,
 								  SnapshotNow, 1, &key);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 67f970c55d7..72f76b01b06 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.220 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.221 2003/11/12 21:15:48 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -992,10 +992,10 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
 	{
 		ScanKeyData key[1];
 
-		ScanKeyEntryInitialize(&key[0], 0,
-							   ObjectIdAttributeNumber,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(relid), OIDOID);
+		ScanKeyInit(&key[0],
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(relid));
 
 		pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
 		tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
@@ -1195,10 +1195,10 @@ UpdateStats(Oid relid, double reltuples)
 	{
 		ScanKeyData key[1];
 
-		ScanKeyEntryInitialize(&key[0], 0,
-							   ObjectIdAttributeNumber,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(relid), OIDOID);
+		ScanKeyInit(&key[0],
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(relid));
 
 		pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
 		tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 0fa665a568d..2cb71f6bfcc 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.17 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.18 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -281,15 +281,15 @@ ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, con
 
 	found = false;
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_constraint_conname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   CStringGetDatum(cname), NAMEOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_constraint_conname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(cname));
 
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_constraint_connamespace,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(objNamespace), OIDOID);
+	ScanKeyInit(&skey[1],
+				Anum_pg_constraint_connamespace,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(objNamespace));
 
 	conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
 								 SnapshotNow, 2, skey);
@@ -355,15 +355,15 @@ GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, i
 		 */
 		found = false;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_constraint_conname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(cname), NAMEOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_constraint_conname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(cname));
 
-		ScanKeyEntryInitialize(&skey[1], 0,
-							   Anum_pg_constraint_connamespace,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(objNamespace), OIDOID);
+		ScanKeyInit(&skey[1],
+					Anum_pg_constraint_connamespace,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(objNamespace));
 
 		conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
 									 SnapshotNow, 2, skey);
@@ -422,10 +422,10 @@ RemoveConstraintById(Oid conId)
 
 	conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(conId), OIDOID);
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(conId));
 
 	conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
 								 SnapshotNow, 1, skey);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 709f50f2a5d..7f7811c88df 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_conversion.c,v 1.16 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_conversion.c,v 1.17 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,6 @@
 #include "catalog/pg_class.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/namespace.h"
-#include "catalog/pg_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -177,10 +176,10 @@ RemoveConversionById(Oid conversionOid)
 	HeapScanDesc scan;
 	ScanKeyData scanKeyData;
 
-	ScanKeyEntryInitialize(&scanKeyData, 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(conversionOid), OIDOID);
+	ScanKeyInit(&scanKeyData,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(conversionOid));
 
 	/* open pg_conversion */
 	rel = heap_openr(ConversionRelationName, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 21eeb3e6543..2bdb5b12d8d 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.8 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.9 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 #include "catalog/indexing.h"
 #include "catalog/dependency.h"
 #include "catalog/pg_depend.h"
-#include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "utils/fmgroids.h"
 
@@ -139,14 +138,14 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
 
 	depRel = heap_openr(DependRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_classid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(classId), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_objid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(objectId), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_classid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(classId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_objid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(objectId));
 
 	scan = systable_beginscan(depRel, DependDependerIndex, true,
 							  SnapshotNow, 2, key);
@@ -181,15 +180,15 @@ isObjectPinned(const ObjectAddress *object, Relation rel)
 	HeapTuple	tup;
 	ScanKeyData key[2];
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_refclassid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->classId), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
 
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_refobjid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(object->objectId), OIDOID);
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
 
 	scan = systable_beginscan(rel, DependReferenceIndex, true,
 							  SnapshotNow, 2, key);
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
index 96f73056cf2..022ca67d10e 100644
--- a/src/backend/catalog/pg_largeobject.c
+++ b/src/backend/catalog/pg_largeobject.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_largeobject.c,v 1.18 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_largeobject.c,v 1.19 2003/11/12 21:15:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,6 @@
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_largeobject.h"
-#include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -82,10 +81,10 @@ LargeObjectDrop(Oid loid)
 	SysScanDesc sd;
 	HeapTuple	tuple;
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_largeobject_loid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(loid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_largeobject_loid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(loid));
 
 	pg_largeobject = heap_openr(LargeObjectRelationName, RowExclusiveLock);
 
@@ -120,10 +119,10 @@ LargeObjectExists(Oid loid)
 	/*
 	 * See if we can find any tuples belonging to the specified LO
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_largeobject_loid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(loid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_largeobject_loid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(loid));
 
 	pg_largeobject = heap_openr(LargeObjectRelationName, AccessShareLock);
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 2cda1f94a97..560a134aa86 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.103 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.104 2003/11/12 21:15:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,7 +80,6 @@
 #include "access/heapam.h"
 #include "catalog/catname.h"
 #include "catalog/pg_listener.h"
-#include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -353,10 +352,10 @@ Async_UnlistenAll(void)
 	tdesc = RelationGetDescr(lRel);
 
 	/* Find and delete all entries with my listenerPID */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_listener_pid,
-						   BTEqualStrategyNumber, F_INT4EQ,
-						   Int32GetDatum(MyProcPid), INT4OID);
+	ScanKeyInit(&key[0],
+				Anum_pg_listener_pid,
+				BTEqualStrategyNumber, F_INT4EQ,
+				Int32GetDatum(MyProcPid));
 	scan = heap_beginscan(lRel, SnapshotNow, 1, key);
 
 	while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
@@ -817,10 +816,10 @@ ProcessIncomingNotify(void)
 	tdesc = RelationGetDescr(lRel);
 
 	/* Scan only entries with my listenerPID */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_listener_pid,
-						   BTEqualStrategyNumber, F_INT4EQ,
-						   Int32GetDatum(MyProcPid), INT4OID);
+	ScanKeyInit(&key[0],
+				Anum_pg_listener_pid,
+				BTEqualStrategyNumber, F_INT4EQ,
+				Int32GetDatum(MyProcPid));
 	scan = heap_beginscan(lRel, SnapshotNow, 1, key);
 
 	/* Prepare data for rewriting 0 into notification field */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 780a60f7966..4d5bc491c71 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.117 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.118 2003/11/12 21:15:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,7 +26,6 @@
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
-#include "catalog/pg_type.h"
 #include "commands/cluster.h"
 #include "commands/tablecmds.h"
 #include "miscadmin.h"
@@ -879,10 +878,10 @@ get_tables_to_cluster(MemoryContext cluster_context)
 	 * when called with one of them as argument.
 	 */
 	indRelation = relation_openr(IndexRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry, 0,
-						   Anum_pg_index_indisclustered,
-						   BTEqualStrategyNumber, F_BOOLEQ,
-						   BoolGetDatum(true), BOOLOID);
+	ScanKeyInit(&entry,
+				Anum_pg_index_indisclustered,
+				BTEqualStrategyNumber, F_BOOLEQ,
+				BoolGetDatum(true));
 	scan = heap_beginscan(indRelation, SnapshotNow, 1, &entry);
 	while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index fbce38ca577..62765a96e0a 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1996-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.72 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.73 2003/11/12 21:15:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -156,18 +156,18 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
 
 	/* Use the index to search for a matching old tuple */
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_description_objoid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(oid), OIDOID);
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_description_classoid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(classoid), OIDOID);
-	ScanKeyEntryInitialize(&skey[2], 0,
-						   Anum_pg_description_objsubid,
-						   BTEqualStrategyNumber, F_INT4EQ,
-						   Int32GetDatum(subid), INT4OID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_description_objoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(oid));
+	ScanKeyInit(&skey[1],
+				Anum_pg_description_classoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(classoid));
+	ScanKeyInit(&skey[2],
+				Anum_pg_description_objsubid,
+				BTEqualStrategyNumber, F_INT4EQ,
+				Int32GetDatum(subid));
 
 	description = heap_openr(DescriptionRelationName, RowExclusiveLock);
 
@@ -231,21 +231,21 @@ DeleteComments(Oid oid, Oid classoid, int32 subid)
 
 	/* Use the index to search for all matching old tuples */
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_description_objoid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(oid), OIDOID);
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_description_classoid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(classoid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_description_objoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(oid));
+	ScanKeyInit(&skey[1],
+				Anum_pg_description_classoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(classoid));
 
 	if (subid != 0)
 	{
-		ScanKeyEntryInitialize(&skey[2], 0,
-							   Anum_pg_description_objsubid,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(subid), INT4OID);
+		ScanKeyInit(&skey[2],
+					Anum_pg_description_objsubid,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(subid));
 		nkeys = 3;
 	}
 	else
@@ -538,10 +538,10 @@ CommentRule(List *qualname, char *comment)
 		rulename = strVal(lfirst(qualname));
 
 		/* Search pg_rewrite for such a rule */
-		ScanKeyEntryInitialize(&scanKeyData, 0,
-							   Anum_pg_rewrite_rulename,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   PointerGetDatum(rulename), NAMEOID);
+		ScanKeyInit(&scanKeyData,
+					Anum_pg_rewrite_rulename,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					PointerGetDatum(rulename));
 
 		RewriteRelation = heap_openr(RewriteRelationName, AccessShareLock);
 		scanDesc = heap_beginscan(RewriteRelation, SnapshotNow,
@@ -791,15 +791,14 @@ CommentTrigger(List *qualname, char *comment)
 	 * because of the unique index.
 	 */
 	pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
-	ScanKeyEntryInitialize(&entry[1], 0,
-						   Anum_pg_trigger_tgname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   CStringGetDatum(trigname), NAMEOID);
+	ScanKeyInit(&entry[0],
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
+	ScanKeyInit(&entry[1],
+				Anum_pg_trigger_tgname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(trigname));
 	scan = systable_beginscan(pg_trigger, TriggerRelidNameIndex, true,
 							  SnapshotNow, 2, entry);
 	triggertuple = systable_getnext(scan);
@@ -872,11 +871,10 @@ CommentConstraint(List *qualname, char *comment)
 	 */
 	pg_constraint = heap_openr(ConstraintRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_constraint_conrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndex, true,
 							  SnapshotNow, 1, skey);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 5ac51bc84d3..320f1fc0ded 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.125 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.126 2003/11/12 21:15:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,6 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_shadow.h"
 #include "catalog/indexing.h"
-#include "catalog/pg_type.h"
 #include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
@@ -531,10 +530,10 @@ dropdb(const char *dbname)
 	/*
 	 * Find the database's tuple by OID (should be unique).
 	 */
-	ScanKeyEntryInitialize(&key, 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(db_id), OIDOID);
+	ScanKeyInit(&key,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(db_id));
 
 	pgdbscan = systable_beginscan(pgdbrel, DatabaseOidIndex, true,
 								  SnapshotNow, 1, &key);
@@ -616,10 +615,10 @@ RenameDatabase(const char *oldname, const char *newname)
 	 */
 	rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
 
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   NameGetDatum(oldname), NAMEOID);
+	ScanKeyInit(&key,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				NameGetDatum(oldname));
 	scan = systable_beginscan(rel, DatabaseNameIndex, true,
 							  SnapshotNow, 1, &key);
 
@@ -651,10 +650,10 @@ RenameDatabase(const char *oldname, const char *newname)
 					  oldname)));
 
 	/* make sure the new name doesn't exist */
-	ScanKeyEntryInitialize(&key2, 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   NameGetDatum(newname), NAMEOID);
+	ScanKeyInit(&key2,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				NameGetDatum(newname));
 	scan2 = systable_beginscan(rel, DatabaseNameIndex, true,
 							   SnapshotNow, 1, &key2);
 	if (HeapTupleIsValid(systable_getnext(scan2)))
@@ -712,10 +711,10 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
 	valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
 
 	rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
-	ScanKeyEntryInitialize(&scankey, 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   NameGetDatum(stmt->dbname), NAMEOID);
+	ScanKeyInit(&scankey,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				NameGetDatum(stmt->dbname));
 	scan = systable_beginscan(rel, DatabaseNameIndex, true,
 							  SnapshotNow, 1, &scankey);
 	tuple = systable_getnext(scan);
@@ -795,10 +794,10 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
 	/* Caller may wish to grab a better lock on pg_database beforehand... */
 	relation = heap_openr(DatabaseRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&scanKey, 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   NameGetDatum(name), NAMEOID);
+	ScanKeyInit(&scanKey,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				NameGetDatum(name));
 
 	scan = systable_beginscan(relation, DatabaseNameIndex, true,
 							  SnapshotNow, 1, &scanKey);
@@ -1001,10 +1000,10 @@ get_database_oid(const char *dbname)
 
 	/* There's no syscache for pg_database, so must look the hard way */
 	pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   CStringGetDatum(dbname), NAMEOID);
+	ScanKeyInit(&entry[0],
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(dbname));
 	scan = systable_beginscan(pg_database, DatabaseNameIndex, true,
 							  SnapshotNow, 1, entry);
 
@@ -1041,10 +1040,10 @@ get_database_name(Oid dbid)
 
 	/* There's no syscache for pg_database, so must look the hard way */
 	pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(dbid), OIDOID);
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(dbid));
 	scan = systable_beginscan(pg_database, DatabaseOidIndex, true,
 							  SnapshotNow, 1, entry);
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9e1e0746f55..7e3c7410a10 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.39 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.40 2003/11/12 21:15:50 tgl Exp $
  *
  * DESCRIPTION
  *	  These routines take the parse tree and pick out the
@@ -1097,10 +1097,10 @@ DropCastById(Oid castOid)
 
 	relation = heap_openr(CastRelationName, RowExclusiveLock);
 
-	ScanKeyEntryInitialize(&scankey, 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(castOid), OIDOID);
+	ScanKeyInit(&scankey,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(castOid));
 	scan = systable_beginscan(relation, CastOidIndex, true,
 							  SnapshotNow, 1, &scankey);
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index dbbbc376e00..2f83e22a276 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.114 2003/10/02 06:34:03 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.115 2003/11/12 21:15:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -416,6 +416,9 @@ GetIndexOpClass(List *opclass, Oid attrType,
 	 * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
 	 * too for awhile.	I'm starting to think we need a better approach.
 	 * tgl 2000/10/01
+	 *
+	 * Release 7.5 removes bigbox_ops (which was dead code for a long while
+	 * anyway).  tgl 2003/11/11
 	 */
 	if (length(opclass) == 1)
 	{
@@ -425,7 +428,8 @@ GetIndexOpClass(List *opclass, Oid attrType,
 			strcmp(claname, "timespan_ops") == 0 ||
 			strcmp(claname, "datetime_ops") == 0 ||
 			strcmp(claname, "lztext_ops") == 0 ||
-			strcmp(claname, "timestamp_ops") == 0)
+			strcmp(claname, "timestamp_ops") == 0 ||
+			strcmp(claname, "bigbox_ops") == 0)
 			opclass = NIL;
 	}
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 599d2eb8259..e251f8577da 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.22 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.23 2003/11/12 21:15:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,8 @@
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
@@ -38,9 +40,24 @@
 #include "utils/syscache.h"
 
 
-static void storeOperators(Oid opclassoid, int numOperators,
-			   Oid *operators, bool *recheck);
-static void storeProcedures(Oid opclassoid, int numProcs, Oid *procedures);
+/*
+ * We use lists of this struct type to keep track of both operators and
+ * procedures during DefineOpClass.
+ */
+typedef struct
+{
+	Oid			object;			/* operator or support proc's OID */
+	int			number;			/* strategy or support proc number */
+	Oid			subtype;		/* subtype */
+	bool		recheck;		/* oper recheck flag (unused for proc) */
+} OpClassMember;
+
+
+static Oid	assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid);
+static Oid	assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid);
+static void addClassMember(List **list, OpClassMember *member, bool isProc);
+static void storeOperators(Oid opclassoid, List *operators);
+static void storeProcedures(Oid opclassoid, List *procedures);
 
 
 /*
@@ -58,10 +75,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				opclassoid;		/* oid of opclass we create */
 	int			numOperators,	/* amstrategies value */
 				numProcs;		/* amsupport value */
-	Oid		   *operators,		/* oids of operators, by strategy num */
-			   *procedures;		/* oids of support procs */
-	bool	   *recheck;		/* do operators need recheck */
-	List	   *iteml;
+	List	   *operators;		/* OpClassMember list for operators */
+	List	   *procedures;		/* OpClassMember list for support procs */
+	List	   *l;
 	Relation	rel;
 	HeapTuple	tup;
 	Datum		values[Natts_pg_opclass];
@@ -123,26 +139,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					   format_type_be(typeoid));
 #endif
 
+	operators = NIL;
+	procedures = NIL;
+
 	/* Storage datatype is optional */
 	storageoid = InvalidOid;
 
-	/*
-	 * Create work arrays to hold info about operators and procedures. We
-	 * do this mainly so that we can detect duplicate strategy numbers and
-	 * support-proc numbers.
-	 */
-	operators = (Oid *) palloc0(sizeof(Oid) * numOperators);
-	procedures = (Oid *) palloc0(sizeof(Oid) * numProcs);
-	recheck = (bool *) palloc0(sizeof(bool) * numOperators);
-
 	/*
 	 * Scan the "items" list to obtain additional info.
 	 */
-	foreach(iteml, stmt->items)
+	foreach(l, stmt->items)
 	{
-		CreateOpClassItem *item = lfirst(iteml);
+		CreateOpClassItem *item = lfirst(l);
 		Oid			operOid;
 		Oid			funcOid;
+		OpClassMember *member;
 		AclResult	aclresult;
 
 		Assert(IsA(item, CreateOpClassItem));
@@ -155,11 +166,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 							 errmsg("invalid operator number %d,"
 									" must be between 1 and %d",
 									item->number, numOperators)));
-				if (operators[item->number - 1] != InvalidOid)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					  errmsg("operator number %d appears more than once",
-							 item->number)));
 				if (item->args != NIL)
 				{
 					TypeName   *typeName1 = (TypeName *) lfirst(item->args);
@@ -183,8 +189,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				if (aclresult != ACLCHECK_OK)
 					aclcheck_error(aclresult, ACL_KIND_PROC,
 								   get_func_name(funcOid));
-				operators[item->number - 1] = operOid;
-				recheck[item->number - 1] = item->recheck;
+				/* Save the info */
+				member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+				member->object = operOid;
+				member->number = item->number;
+				member->subtype = assignOperSubtype(amoid, typeoid, operOid);
+				member->recheck = item->recheck;
+				addClassMember(&operators, member, false);
 				break;
 			case OPCLASS_ITEM_FUNCTION:
 				if (item->number <= 0 || item->number > numProcs)
@@ -193,11 +204,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 							 errmsg("invalid procedure number %d,"
 									" must be between 1 and %d",
 									item->number, numProcs)));
-				if (procedures[item->number - 1] != InvalidOid)
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-							 errmsg("procedure number %d appears more than once",
-									item->number)));
 				funcOid = LookupFuncNameTypeNames(item->name, item->args,
 												  false);
 				/* Caller must have execute permission on functions */
@@ -206,7 +212,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				if (aclresult != ACLCHECK_OK)
 					aclcheck_error(aclresult, ACL_KIND_PROC,
 								   get_func_name(funcOid));
-				procedures[item->number - 1] = funcOid;
+				/* Save the info */
+				member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+				member->object = funcOid;
+				member->number = item->number;
+				member->subtype = assignProcSubtype(amoid, typeoid, funcOid);
+				addClassMember(&procedures, member, true);
 				break;
 			case OPCLASS_ITEM_STORAGETYPE:
 				if (OidIsValid(storageoid))
@@ -271,10 +282,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
 		ScanKeyData skey[1];
 		SysScanDesc scan;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_opclass_opcamid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(amoid), OIDOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_opclass_opcamid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(amoid));
 
 		scan = systable_beginscan(rel, OpclassAmNameNspIndex, true,
 								  SnapshotNow, 1, skey);
@@ -327,8 +338,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	 * Now add tuples to pg_amop and pg_amproc tying in the operators and
 	 * functions.
 	 */
-	storeOperators(opclassoid, numOperators, operators, recheck);
-	storeProcedures(opclassoid, numProcs, procedures);
+	storeOperators(opclassoid, operators);
+	storeProcedures(opclassoid, procedures);
 
 	/*
 	 * Create dependencies.  Note: we do not create a dependency link to
@@ -361,22 +372,22 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	/* dependencies on operators */
 	referenced.classId = get_system_catalog_relid(OperatorRelationName);
-	for (i = 0; i < numOperators; i++)
+	foreach(l, operators)
 	{
-		if (operators[i] == InvalidOid)
-			continue;
-		referenced.objectId = operators[i];
+		OpClassMember *op = (OpClassMember *) lfirst(l);
+
+		referenced.objectId = op->object;
 		referenced.objectSubId = 0;
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
 	/* dependencies on procedures */
-	for (i = 0; i < numProcs; i++)
+	foreach(l, procedures)
 	{
-		if (procedures[i] == InvalidOid)
-			continue;
+		OpClassMember *proc = (OpClassMember *) lfirst(l);
+
 		referenced.classId = RelOid_pg_proc;
-		referenced.objectId = procedures[i];
+		referenced.objectId = proc->object;
 		referenced.objectSubId = 0;
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
@@ -384,26 +395,159 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	heap_close(rel, RowExclusiveLock);
 }
 
+/*
+ * Determine the subtype to assign to an operator, and do any validity
+ * checking we can manage
+ *
+ * Currently this is done using hardwired rules; we don't let the user
+ * specify it directly.
+ */
+static Oid
+assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid)
+{
+	Oid			subtype;
+	Operator	optup;
+	Form_pg_operator opform;
+
+	/* Subtypes are currently only supported by btree, others use 0 */
+	if (amoid != BTREE_AM_OID)
+		return InvalidOid;
+
+	optup = SearchSysCache(OPEROID,
+						   ObjectIdGetDatum(operOid),
+						   0, 0, 0);
+	if (optup == NULL)
+		elog(ERROR, "cache lookup failed for operator %u", operOid);
+	opform = (Form_pg_operator) GETSTRUCT(optup);
+	/*
+	 * btree operators must be binary ops returning boolean, and the
+	 * left-side input type must match the operator class' input type.
+	 */
+	if (opform->oprkind != 'b')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree operators must be binary")));
+	if (opform->oprresult != BOOLOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree operators must return boolean")));
+	if (opform->oprleft != typeoid)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree operators must have index type as left input")));
+	/*
+	 * The subtype is "default" (0) if oprright matches the operator class,
+	 * otherwise it is oprright.
+	 */
+	if (opform->oprright == typeoid)
+		subtype = InvalidOid;
+	else
+		subtype = opform->oprright;
+	ReleaseSysCache(optup);
+	return subtype;
+}
+
+/*
+ * Determine the subtype to assign to a support procedure, and do any validity
+ * checking we can manage
+ *
+ * Currently this is done using hardwired rules; we don't let the user
+ * specify it directly.
+ */
+static Oid
+assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid)
+{
+	Oid			subtype;
+	HeapTuple	proctup;
+	Form_pg_proc procform;
+
+	/* Subtypes are currently only supported by btree, others use 0 */
+	if (amoid != BTREE_AM_OID)
+		return InvalidOid;
+
+	proctup = SearchSysCache(PROCOID,
+							 ObjectIdGetDatum(procOid),
+							 0, 0, 0);
+	if (proctup == NULL)
+		elog(ERROR, "cache lookup failed for function %u", procOid);
+	procform = (Form_pg_proc) GETSTRUCT(proctup);
+	/*
+	 * btree support procs must be 2-arg procs returning int4, and the
+	 * first input type must match the operator class' input type.
+	 */
+	if (procform->pronargs != 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree procedures must have two arguments")));
+	if (procform->prorettype != INT4OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree procedures must return integer")));
+	if (procform->proargtypes[0] != typeoid)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("btree procedures must have index type as first input")));
+	/*
+	 * The subtype is "default" (0) if second input type matches the operator
+	 * class, otherwise it is the second input type.
+	 */
+	if (procform->proargtypes[1] == typeoid)
+		subtype = InvalidOid;
+	else
+		subtype = procform->proargtypes[1];
+	ReleaseSysCache(proctup);
+	return subtype;
+}
+
+/*
+ * Add a new class member to the appropriate list, after checking for
+ * duplicated strategy or proc number.
+ */
+static void
+addClassMember(List **list, OpClassMember *member, bool isProc)
+{
+	List	   *l;
+
+	foreach(l, *list)
+	{
+		OpClassMember *old = (OpClassMember *) lfirst(l);
+
+		if (old->number == member->number &&
+			old->subtype == member->subtype)
+		{
+			if (isProc)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("procedure number %d appears more than once",
+								member->number)));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("operator number %d appears more than once",
+								member->number)));
+		}
+	}
+	*list = lappend(*list, member);
+}
+
 /*
  * Dump the operators to pg_amop
  */
 static void
-storeOperators(Oid opclassoid, int numOperators,
-			   Oid *operators, bool *recheck)
+storeOperators(Oid opclassoid, List *operators)
 {
 	Relation	rel;
 	Datum		values[Natts_pg_amop];
 	char		nulls[Natts_pg_amop];
 	HeapTuple	tup;
-	int			i,
-				j;
+	List	   *l;
+	int			i;
 
 	rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock);
 
-	for (j = 0; j < numOperators; j++)
+	foreach(l, operators)
 	{
-		if (operators[j] == InvalidOid)
-			continue;
+		OpClassMember *op = (OpClassMember *) lfirst(l);
 
 		for (i = 0; i < Natts_pg_amop; ++i)
 		{
@@ -413,9 +557,10 @@ storeOperators(Oid opclassoid, int numOperators,
 
 		i = 0;
 		values[i++] = ObjectIdGetDatum(opclassoid);		/* amopclaid */
-		values[i++] = Int16GetDatum(j + 1);		/* amopstrategy */
-		values[i++] = BoolGetDatum(recheck[j]); /* amopreqcheck */
-		values[i++] = ObjectIdGetDatum(operators[j]);	/* amopopr */
+		values[i++] = ObjectIdGetDatum(op->subtype);	/* amopsubtype */
+		values[i++] = Int16GetDatum(op->number);		/* amopstrategy */
+		values[i++] = BoolGetDatum(op->recheck);		/* amopreqcheck */
+		values[i++] = ObjectIdGetDatum(op->object);		/* amopopr */
 
 		tup = heap_formtuple(rel->rd_att, values, nulls);
 
@@ -433,21 +578,20 @@ storeOperators(Oid opclassoid, int numOperators,
  * Dump the procedures (support routines) to pg_amproc
  */
 static void
-storeProcedures(Oid opclassoid, int numProcs, Oid *procedures)
+storeProcedures(Oid opclassoid, List *procedures)
 {
 	Relation	rel;
 	Datum		values[Natts_pg_amproc];
 	char		nulls[Natts_pg_amproc];
 	HeapTuple	tup;
-	int			i,
-				j;
+	List	   *l;
+	int			i;
 
 	rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock);
 
-	for (j = 0; j < numProcs; j++)
+	foreach(l, procedures)
 	{
-		if (procedures[j] == InvalidOid)
-			continue;
+		OpClassMember *proc = (OpClassMember *) lfirst(l);
 
 		for (i = 0; i < Natts_pg_amproc; ++i)
 		{
@@ -457,8 +601,9 @@ storeProcedures(Oid opclassoid, int numProcs, Oid *procedures)
 
 		i = 0;
 		values[i++] = ObjectIdGetDatum(opclassoid);		/* amopclaid */
-		values[i++] = Int16GetDatum(j + 1);		/* amprocnum */
-		values[i++] = ObjectIdGetDatum(procedures[j]);	/* amproc */
+		values[i++] = ObjectIdGetDatum(proc->subtype);	/* amprocsubtype */
+		values[i++] = Int16GetDatum(proc->number);		/* amprocnum */
+		values[i++] = ObjectIdGetDatum(proc->object);	/* amproc */
 
 		tup = heap_formtuple(rel->rd_att, values, nulls);
 
@@ -590,10 +735,10 @@ RemoveOpClassById(Oid opclassOid)
 	/*
 	 * Remove associated entries in pg_amop.
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_amop_amopclaid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(opclassOid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_amop_amopclaid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(opclassOid));
 
 	rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock);
 
@@ -609,10 +754,10 @@ RemoveOpClassById(Oid opclassOid)
 	/*
 	 * Remove associated entries in pg_amproc.
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_amproc_amopclaid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(opclassOid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_amproc_amopclaid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(opclassOid));
 
 	rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index df441ca476c..feb9720a847 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.92 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.93 2003/11/12 21:15:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1393,20 +1393,20 @@ update_ri_trigger_args(Oid relid,
 	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
 	if (fk_scan)
 	{
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_trigger_tgconstrrelid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(relid), OIDOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_trigger_tgconstrrelid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(relid));
 		trigscan = systable_beginscan(tgrel, TriggerConstrRelidIndex,
 									  true, SnapshotNow,
 									  1, skey);
 	}
 	else
 	{
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_trigger_tgrelid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(relid), OIDOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_trigger_tgrelid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(relid));
 		trigscan = systable_beginscan(tgrel, TriggerRelidNameIndex,
 									  true, SnapshotNow,
 									  1, skey);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 80225f8f25f..4788d90e587 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.161 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.162 2003/11/12 21:15:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -253,10 +253,10 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
 	 * relation, so the trigger set won't be changing underneath us.
 	 */
 	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(rel)), OIDOID);
+	ScanKeyInit(&key,
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(rel)));
 	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
 								SnapshotNow, 1, &key);
 	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
@@ -465,15 +465,15 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior)
 	 */
 	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
 
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_trigger_tgname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   CStringGetDatum(trigname), NAMEOID);
+	ScanKeyInit(&skey[1],
+				Anum_pg_trigger_tgname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(trigname));
 
 	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
 								SnapshotNow, 2, skey);
@@ -524,10 +524,10 @@ RemoveTriggerById(Oid trigOid)
 	/*
 	 * Find the trigger to delete.
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(trigOid), OIDOID);
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(trigOid));
 
 	tgscan = systable_beginscan(tgrel, TriggerOidIndex, true,
 								SnapshotNow, 1, skey);
@@ -641,14 +641,14 @@ renametrig(Oid relid,
 	/*
 	 * First pass -- look for name conflict
 	 */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_trigger_tgname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   PointerGetDatum(newname), NAMEOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+	ScanKeyInit(&key[1],
+				Anum_pg_trigger_tgname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				PointerGetDatum(newname));
 	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
 								SnapshotNow, 2, key);
 	if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
@@ -661,14 +661,14 @@ renametrig(Oid relid,
 	/*
 	 * Second pass -- look for trigger existing with oldname and update
 	 */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(relid), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_trigger_tgname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   PointerGetDatum(oldname), NAMEOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relid));
+	ScanKeyInit(&key[1],
+				Anum_pg_trigger_tgname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				PointerGetDatum(oldname));
 	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
 								SnapshotNow, 2, key);
 	if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
@@ -744,11 +744,10 @@ RelationBuildTriggers(Relation relation)
 	 * emergency-recovery operations (ie, IsIgnoringSystemIndexes). This
 	 * in turn ensures that triggers will be fired in name order.
 	 */
-	ScanKeyEntryInitialize(&skey, 0,
-						   Anum_pg_trigger_tgrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&skey,
+				Anum_pg_trigger_tgrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
 	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
@@ -2262,10 +2261,10 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 			/*
 			 * Setup to scan pg_trigger by tgconstrname ...
 			 */
-			ScanKeyEntryInitialize(&skey, 0,
-								   Anum_pg_trigger_tgconstrname,
-								   BTEqualStrategyNumber, F_NAMEEQ,
-								   PointerGetDatum(cname), NAMEOID);
+			ScanKeyInit(&skey,
+						Anum_pg_trigger_tgconstrname,
+						BTEqualStrategyNumber, F_NAMEEQ,
+						PointerGetDatum(cname));
 
 			tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true,
 										SnapshotNow, 1, &skey);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 17b4df92179..896f1047070 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.49 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.50 2003/11/12 21:15:51 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -1362,10 +1362,10 @@ AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior beha
 	conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
 
 	/* Use the index to scan only constraints of the target relation */
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_constraint_contypid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(HeapTupleGetOid(tup)), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_constraint_contypid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(HeapTupleGetOid(tup)));
 
 	conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
 								 SnapshotNow, 1, key);
@@ -1615,14 +1615,14 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
 	 */
 	depRel = relation_openr(DependRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_depend_refclassid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelOid_pg_type), OIDOID);
-	ScanKeyEntryInitialize(&key[1], 0,
-						   Anum_pg_depend_refobjid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(domainOid), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelOid_pg_type));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(domainOid));
 
 	depScan = systable_beginscan(depRel, DependReferenceIndex, true,
 								 SnapshotNow, 2, key);
@@ -1901,10 +1901,10 @@ GetDomainConstraints(Oid typeOid)
 			notNull = true;
 
 		/* Look for CHECK Constraints on this domain */
-		ScanKeyEntryInitialize(&key[0], 0,
-							   Anum_pg_constraint_contypid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(typeOid), OIDOID);
+		ScanKeyInit(&key[0],
+					Anum_pg_constraint_contypid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(typeOid));
 
 		scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
 								  SnapshotNow, 1, key);
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 4fad67f43a3..869cd847927 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.129 2003/11/09 21:30:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.130 2003/11/12 21:15:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1074,10 +1074,10 @@ DropUser(DropUserStmt *stmt)
 		pg_rel = heap_openr(DatabaseRelationName, AccessShareLock);
 		pg_dsc = RelationGetDescr(pg_rel);
 
-		ScanKeyEntryInitialize(&scankey, 0,
-							   Anum_pg_database_datdba,
-							   BTEqualStrategyNumber, F_INT4EQ,
-							   Int32GetDatum(usesysid), INT4OID);
+		ScanKeyInit(&scankey,
+					Anum_pg_database_datdba,
+					BTEqualStrategyNumber, F_INT4EQ,
+					Int32GetDatum(usesysid));
 
 		scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey);
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4d6de2282c..c1c5d64ea2f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.264 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.265 2003/11/12 21:15:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,6 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_index.h"
-#include "catalog/pg_type.h"
 #include "commands/vacuum.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
@@ -400,10 +399,10 @@ getrels(const RangeVar *vacrel, const char *stmttype)
 		HeapTuple	tuple;
 		ScanKeyData key;
 
-		ScanKeyEntryInitialize(&key, 0,
-							   Anum_pg_class_relkind,
-							   BTEqualStrategyNumber, F_CHAREQ,
-							   CharGetDatum(RELKIND_RELATION), CHAROID);
+		ScanKeyInit(&key,
+					Anum_pg_class_relkind,
+					BTEqualStrategyNumber, F_CHAREQ,
+					CharGetDatum(RELKIND_RELATION));
 
 		pgclass = heap_openr(RelationRelationName, AccessShareLock);
 
@@ -583,10 +582,10 @@ vac_update_dbstats(Oid dbid,
 	relation = heap_openr(DatabaseRelationName, RowExclusiveLock);
 
 	/* Must use a heap scan, since there's no syscache for pg_database */
-	ScanKeyEntryInitialize(&entry[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(dbid), OIDOID);
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(dbid));
 
 	scan = heap_beginscan(relation, SnapshotNow, 1, entry);
 
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 54114ad9245..98158a00c9c 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.85 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.86 2003/11/12 21:15:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,6 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
-#include "parser/parse_expr.h"
 #include "parser/parsetree.h"
 
 
@@ -615,6 +614,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 	IndexScanState *indexstate;
 	List	   *indxqual;
 	List	   *indxstrategy;
+	List	   *indxsubtype;
 	List	   *indxid;
 	int			i;
 	int			numIndices;
@@ -711,10 +711,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 	 */
 	indxqual = node->indxqual;
 	indxstrategy = node->indxstrategy;
+	indxsubtype = node->indxsubtype;
 	for (i = 0; i < numIndices; i++)
 	{
 		List	   *quals;
 		List	   *strategies;
+		List	   *subtypes;
 		int			n_keys;
 		ScanKey		scan_keys;
 		ExprState **run_keys;
@@ -724,6 +726,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 		indxqual = lnext(indxqual);
 		strategies = lfirst(indxstrategy);
 		indxstrategy = lnext(indxstrategy);
+		subtypes = lfirst(indxsubtype);
+		indxsubtype = lnext(indxsubtype);
 		n_keys = length(quals);
 		scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
 			(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
@@ -742,9 +746,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 			int			flags = 0;
 			AttrNumber	varattno;		/* att number used in scan */
 			StrategyNumber strategy;	/* op's strategy number */
+			Oid			subtype;		/* op's strategy subtype */
 			RegProcedure opfuncid;		/* operator proc id used in scan */
 			Datum		scanvalue;		/* value used in scan (if const) */
-			Oid			rhstype;		/* datatype of comparison value */
 
 			/*
 			 * extract clause information from the qualification
@@ -753,6 +757,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 			quals = lnext(quals);
 			strategy = lfirsti(strategies);
 			strategies = lnext(strategies);
+			subtype = lfirsto(subtypes);
+			subtypes = lnext(subtypes);
 
 			if (!IsA(clause, OpExpr))
 				elog(ERROR, "indxqual is not an OpExpr");
@@ -795,8 +801,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 			 */
 			rightop = (Expr *) get_rightop((Expr *) clause);
 
-			rhstype = exprType((Node *) rightop);
-
 			if (rightop && IsA(rightop, RelabelType))
 				rightop = ((RelabelType *) rightop)->arg;
 
@@ -832,9 +836,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 								   varattno,	/* attribute number to
 												 * scan */
 								   strategy,	/* op's strategy */
+								   subtype,		/* strategy subtype */
 								   opfuncid,	/* reg proc to use */
-								   scanvalue,	/* constant */
-								   rhstype);	/* constant's type */
+								   scanvalue);	/* constant */
 		}
 
 		/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c02270fa2e7..6cf8450567a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.266 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.267 2003/11/12 21:15:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -273,6 +273,17 @@ _copyIndexScan(IndexScan *from)
 		}
 		newnode->indxstrategy = newstrat;
 	}
+	/* this can become COPY_NODE_FIELD when OID lists are normal objects: */
+	{
+		List	*newsubtype = NIL;
+		List    *tmp;
+
+		foreach(tmp, from->indxsubtype)
+		{
+			newsubtype = lappend(newsubtype, listCopy(lfirst(tmp)));
+		}
+		newnode->indxsubtype = newsubtype;
+	}
 	COPY_SCALAR_FIELD(indxorderdir);
 
 	return newnode;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b2fa96bd5d2..cb875bd6769 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.219 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.220 2003/11/12 21:15:52 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -346,6 +346,16 @@ _outIndexScan(StringInfo str, IndexScan *node)
 			_outIntList(str, lfirst(tmp));
 		}
 	}
+	/* this can become WRITE_NODE_FIELD when OID lists are normal objects: */
+	{
+		List    *tmp;
+
+		appendStringInfo(str, " :indxsubtype ");
+		foreach(tmp, node->indxsubtype)
+		{
+			_outOidList(str, lfirst(tmp));
+		}
+	}
 	WRITE_ENUM_FIELD(indxorderdir, ScanDirection);
 }
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 563bd17f41d..9c57f12ca62 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.147 2003/08/04 02:40:00 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.148 2003/11/12 21:15:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1072,7 +1072,7 @@ pred_test_recurse_pred(Expr *predicate, Node *clause)
  *
  *	 If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
  *	 want to determine whether "ATTR target_op CONST2" must also be true, then
- *	 you can use "CONST1 test_op CONST2" as a test.  If this test returns true,
+ *	 you can use "CONST2 test_op CONST1" as a test.  If this test returns true,
  *	 then the target expression must be true; if the test returns false, then
  *	 the target expression may be false.
  *
@@ -1082,11 +1082,11 @@ pred_test_recurse_pred(Expr *predicate, Node *clause)
 
 static const StrategyNumber
 			BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
-	{2, 2, 0, 0, 0},
-	{1, 2, 0, 0, 0},
-	{1, 2, 3, 4, 5},
-	{0, 0, 0, 4, 5},
-	{0, 0, 0, 4, 4}
+	{4, 4, 0, 0, 0},
+	{5, 4, 0, 0, 0},
+	{5, 4, 3, 2, 1},
+	{0, 0, 0, 2, 1},
+	{0, 0, 0, 2, 2}
 };
 
 
@@ -1118,12 +1118,13 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 			   *clause_const;
 	Oid			pred_op,
 				clause_op,
-				test_op;
-	Oid			opclass_id = InvalidOid;
+				test_op = InvalidOid;
+	Oid			opclass_id;
 	bool		found = false;
-	StrategyNumber pred_strategy = 0,
-				clause_strategy = 0,
+	StrategyNumber pred_strategy,
+				clause_strategy,
 				test_strategy;
+	Oid			clause_subtype;
 	Expr	   *test_expr;
 	ExprState  *test_exprstate;
 	Datum		test_result;
@@ -1140,7 +1141,9 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	/*
 	 * Can't do anything more unless they are both binary opclauses with a
 	 * Var on the left and a Const on the right.  (XXX someday try to
-	 * commute Const/Var cases?)
+	 * commute Const/Var cases?)  Note we don't have to think about binary
+	 * relabeling of the Const node, since that would have been folded right
+	 * into the Const.
 	 */
 	if (!is_opclause(predicate))
 		return false;
@@ -1173,14 +1176,20 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	clause_op = ((OpExpr *) clause)->opno;
 
 	/*
-	 * 1. Find "btree" strategy numbers for the pred_op and clause_op.
+	 * Try to find a btree opclass containing the needed operators.
 	 *
 	 * We must find a btree opclass that contains both operators, else the
-	 * implication can't be determined.  If there are multiple such
-	 * opclasses, assume we can use any one to determine the logical
-	 * relationship of the two operators and the correct corresponding
-	 * test operator.  This should work for any logically consistent
-	 * opclasses.
+	 * implication can't be determined.  Also, the pred_op has to be of
+	 * default subtype (implying left and right input datatypes are the same);
+	 * otherwise it's unsafe to put the pred_const on the left side of the
+	 * test.  Also, the opclass must contain a suitable test operator
+	 * matching the clause_const's type (which we take to mean that it has
+	 * the same subtype as the original clause_operator).
+	 *
+	 * If there are multiple matching opclasses, assume we can use any one to
+	 * determine the logical relationship of the two operators and the correct
+	 * corresponding test operator.  This should work for any logically
+	 * consistent opclasses.
 	 */
 	catlist = SearchSysCacheList(AMOPOPID, 1,
 								 ObjectIdGetDatum(pred_op),
@@ -1192,7 +1201,13 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 		Form_pg_amop pred_form = (Form_pg_amop) GETSTRUCT(pred_tuple);
 		HeapTuple	clause_tuple;
 
-		if (!opclass_is_btree(pred_form->amopclaid))
+		opclass_id = pred_form->amopclaid;
+
+		/* must be btree */
+		if (!opclass_is_btree(opclass_id))
+			continue;
+		/* predicate operator must be default within this opclass */
+		if (pred_form->amopsubtype != InvalidOid)
 			continue;
 
 		/* Get the predicate operator's btree strategy number */
@@ -1200,12 +1215,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 		Assert(pred_strategy >= 1 && pred_strategy <= 5);
 
 		/*
-		 * Remember which operator class this strategy number came from
-		 */
-		opclass_id = pred_form->amopclaid;
-
-		/*
-		 * From the same opclass, find a strategy num for the clause_op,
+		 * From the same opclass, find a strategy number for the clause_op,
 		 * if possible
 		 */
 		clause_tuple = SearchSysCache(AMOPOPID,
@@ -1216,13 +1226,35 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 		{
 			Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
 
-			/* Get the restriction clause operator's strategy number */
+			/* Get the restriction clause operator's strategy/subtype */
 			clause_strategy = (StrategyNumber) clause_form->amopstrategy;
 			Assert(clause_strategy >= 1 && clause_strategy <= 5);
+			clause_subtype = clause_form->amopsubtype;
 
+			/* done with clause_tuple */
 			ReleaseSysCache(clause_tuple);
-			found = true;
-			break;
+
+			/*
+			 * Look up the "test" strategy number in the implication table
+			 */
+			test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
+			if (test_strategy == 0)
+			{
+				/* Can't determine implication using this interpretation */
+				continue;
+			}
+
+			/*
+			 * See if opclass has an operator for the test strategy and the
+			 * clause datatype.
+			 */
+			test_op = get_opclass_member(opclass_id, clause_subtype,
+										 test_strategy);
+			if (OidIsValid(test_op))
+			{
+				found = true;
+				break;
+			}
 		}
 	}
 
@@ -1235,25 +1267,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	}
 
 	/*
-	 * 2. Look up the "test" strategy number in the implication table
-	 */
-	test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
-	if (test_strategy == 0)
-		return false;			/* the implication cannot be determined */
-
-	/*
-	 * 3. From the same opclass, find the operator for the test strategy
-	 */
-	test_op = get_opclass_member(opclass_id, test_strategy);
-	if (!OidIsValid(test_op))
-	{
-		/* This should not fail, else pg_amop entry is missing */
-		elog(ERROR, "missing pg_amop entry for opclass %u strategy %d",
-			 opclass_id, test_strategy);
-	}
-
-	/*
-	 * 4. Evaluate the test.  For this we need an EState.
+	 * Evaluate the test.  For this we need an EState.
 	 */
 	estate = CreateExecutorState();
 
@@ -1264,8 +1278,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	test_expr = make_opclause(test_op,
 							  BOOLOID,
 							  false,
-							  (Expr *) clause_const,
-							  (Expr *) pred_const);
+							  (Expr *) pred_const,
+							  (Expr *) clause_const);
 
 	/* Prepare it for execution */
 	test_exprstate = ExecPrepareExpr(test_expr, estate);
@@ -1907,7 +1921,7 @@ match_special_index_operator(Expr *clause, Oid opclass,
  * (The latter is not depended on by any part of the planner, so far as I can
  * tell; but some parts of the executor do assume that the indxqual list
  * ultimately delivered to the executor is so ordered.	One such place is
- * _bt_orderkeys() in the btree support.  Perhaps that ought to be fixed
+ * _bt_preprocess_keys() in the btree support.  Perhaps that ought to be fixed
  * someday --- tgl 7/00)
  */
 List *
@@ -2103,7 +2117,8 @@ prefix_quals(Node *leftop, Oid opclass,
 	 */
 	if (pstatus == Pattern_Prefix_Exact)
 	{
-		oproid = get_opclass_member(opclass, BTEqualStrategyNumber);
+		oproid = get_opclass_member(opclass, InvalidOid,
+									BTEqualStrategyNumber);
 		if (oproid == InvalidOid)
 			elog(ERROR, "no = operator for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
@@ -2117,7 +2132,8 @@ prefix_quals(Node *leftop, Oid opclass,
 	 *
 	 * We can always say "x >= prefix".
 	 */
-	oproid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
+	oproid = get_opclass_member(opclass, InvalidOid,
+								BTGreaterEqualStrategyNumber);
 	if (oproid == InvalidOid)
 		elog(ERROR, "no >= operator for opclass %u", opclass);
 	expr = make_opclause(oproid, BOOLOID, false,
@@ -2132,7 +2148,8 @@ prefix_quals(Node *leftop, Oid opclass,
 	greaterstr = make_greater_string(prefix_const);
 	if (greaterstr)
 	{
-		oproid = get_opclass_member(opclass, BTLessStrategyNumber);
+		oproid = get_opclass_member(opclass, InvalidOid,
+									BTLessStrategyNumber);
 		if (oproid == InvalidOid)
 			elog(ERROR, "no < operator for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
@@ -2189,13 +2206,15 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
 	 */
 	if (is_eq)
 	{
-		opr1oid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
+		opr1oid = get_opclass_member(opclass, InvalidOid,
+									 BTGreaterEqualStrategyNumber);
 		if (opr1oid == InvalidOid)
 			elog(ERROR, "no >= operator for opclass %u", opclass);
 	}
 	else
 	{
-		opr1oid = get_opclass_member(opclass, BTGreaterStrategyNumber);
+		opr1oid = get_opclass_member(opclass, InvalidOid,
+									 BTGreaterStrategyNumber);
 		if (opr1oid == InvalidOid)
 			elog(ERROR, "no > operator for opclass %u", opclass);
 	}
@@ -2210,7 +2229,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
 
 	/* create clause "key <= network_scan_last( rightop )" */
 
-	opr2oid = get_opclass_member(opclass, BTLessEqualStrategyNumber);
+	opr2oid = get_opclass_member(opclass, InvalidOid,
+								 BTLessEqualStrategyNumber);
 	if (opr2oid == InvalidOid)
 		elog(ERROR, "no <= operator for opclass %u", opclass);
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 37478d7de0a..a5efcb339e0 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.158 2003/11/09 21:30:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.159 2003/11/12 21:15:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,13 +63,15 @@ static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path,
 static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
 						List **fixed_indexquals,
 						List **recheck_indexquals,
-						List **indxstrategy);
+						List **indxstrategy,
+						List **indxsubtype);
 static void fix_indxqual_sublist(List *indexqual,
 					 Relids baserelids, int baserelid,
 					 IndexOptInfo *index,
 					 List **fixed_quals,
 					 List **recheck_quals,
-					 List **strategy);
+					 List **strategy,
+					 List **subtype);
 static Node *fix_indxqual_operand(Node *node, int baserelid,
 					 IndexOptInfo *index,
 					 Oid *opclass);
@@ -79,8 +81,8 @@ static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
-			   List *indxid, List *indxqual,
-			   List *indxqualorig, List *indxstrategy,
+			   List *indxid, List *indxqual, List *indxqualorig,
+			   List *indxstrategy, List *indxsubtype,
 			   ScanDirection indexscandir);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
 			 List *tideval);
@@ -704,6 +706,7 @@ create_indexscan_plan(Query *root,
 	List	   *fixed_indxqual;
 	List	   *recheck_indxqual;
 	List	   *indxstrategy;
+	List	   *indxsubtype;
 	FastList	indexids;
 	List	   *ixinfo;
 	IndexScan  *scan_plan;
@@ -771,7 +774,7 @@ create_indexscan_plan(Query *root,
 	 */
 	fix_indxqual_references(indxqual, best_path,
 							&fixed_indxqual, &recheck_indxqual,
-							&indxstrategy);
+							&indxstrategy, &indxsubtype);
 
 	/*
 	 * If there were any "lossy" operators, need to add back the
@@ -804,6 +807,7 @@ create_indexscan_plan(Query *root,
 							   fixed_indxqual,
 							   indxqual,
 							   indxstrategy,
+							   indxsubtype,
 							   best_path->indexscandir);
 
 	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
@@ -1151,8 +1155,8 @@ create_hashjoin_plan(Query *root,
  *	  must add (the original form of) the indexqual clause to the "qpquals"
  *	  of the indexscan node, where the operator will be re-evaluated to
  *	  ensure it passes.
- *	* We must construct a list of operator strategy numbers corresponding
- *	  to the top-level operators of each index clause.
+ *	* We must construct lists of operator strategy numbers and subtypes for
+ *	  the top-level operators of each index clause.
  *
  * Both the input list and the output lists have the form of lists of sublists
  * of qual clauses --- the top-level list has one entry for each indexscan
@@ -1167,11 +1171,12 @@ create_hashjoin_plan(Query *root,
  * need rechecking.
  *
  * indxstrategy receives a list of integer sublists of strategy numbers.
+ * indxsubtype receives a list of OID sublists of strategy subtypes.
  */
 static void
 fix_indxqual_references(List *indexquals, IndexPath *index_path,
 						List **fixed_indexquals, List **recheck_indexquals,
-						List **indxstrategy)
+						List **indxstrategy, List **indxsubtype)
 {
 	FastList	fixed_quals;
 	FastList	recheck_quals;
@@ -1183,6 +1188,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path,
 	FastListInit(&fixed_quals);
 	FastListInit(&recheck_quals);
 	*indxstrategy = NIL;
+	*indxsubtype = NIL;
 	foreach(i, indexquals)
 	{
 		List	   *indexqual = lfirst(i);
@@ -1190,13 +1196,16 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path,
 		List	   *fixed_qual;
 		List	   *recheck_qual;
 		List	   *strategy;
+		List	   *subtype;
 
 		fix_indxqual_sublist(indexqual, baserelids, baserelid, index,
-							 &fixed_qual, &recheck_qual, &strategy);
+							 &fixed_qual, &recheck_qual,
+							 &strategy, &subtype);
 		FastAppend(&fixed_quals, fixed_qual);
 		if (recheck_qual != NIL)
 			FastAppend(&recheck_quals, recheck_qual);
 		*indxstrategy = lappend(*indxstrategy, strategy);
+		*indxsubtype = lappend(*indxsubtype, subtype);
 
 		ixinfo = lnext(ixinfo);
 	}
@@ -1211,12 +1220,15 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path,
  * For each qual clause, commute if needed to put the indexkey operand on the
  * left, and then fix its varattno.  (We do not need to change the other side
  * of the clause.)	Also change the operator if necessary, check for
- * lossy index behavior, and determine the operator's strategy number.
+ * lossy index behavior, and determine the operator's strategy number and
+ * subtype number.
  *
- * Returns three lists: the list of fixed indexquals, the list (usually
- * empty) of original clauses that must be rechecked as qpquals because
- * the index is lossy for this operator type, and the integer list of
- * strategy numbers.
+ * Returns four lists:
+ *		the list of fixed indexquals
+ *		the list (usually empty) of original clauses that must be rechecked
+ *			as qpquals because the index is lossy for this operator type
+ *		the integer list of strategy numbers
+ *		the OID list of strategy subtypes
  */
 static void
 fix_indxqual_sublist(List *indexqual,
@@ -1224,7 +1236,8 @@ fix_indxqual_sublist(List *indexqual,
 					 IndexOptInfo *index,
 					 List **fixed_quals,
 					 List **recheck_quals,
-					 List **strategy)
+					 List **strategy,
+					 List **subtype)
 {
 	FastList	fixed_qual;
 	FastList	recheck_qual;
@@ -1233,6 +1246,7 @@ fix_indxqual_sublist(List *indexqual,
 	FastListInit(&fixed_qual);
 	FastListInit(&recheck_qual);
 	*strategy = NIL;
+	*subtype = NIL;
 	foreach(i, indexqual)
 	{
 		OpExpr	   *clause = (OpExpr *) lfirst(i);
@@ -1240,6 +1254,7 @@ fix_indxqual_sublist(List *indexqual,
 		Relids		leftvarnos;
 		Oid			opclass;
 		int			stratno;
+		Oid			stratsubtype;
 		bool		recheck;
 
 		if (!IsA(clause, OpExpr) ||
@@ -1278,13 +1293,14 @@ fix_indxqual_sublist(List *indexqual,
 
 		/*
 		 * Look up the operator in the operator class to get its strategy
-		 * number and the recheck indicator.  This also double-checks that
+		 * numbers and the recheck indicator.  This also double-checks that
 		 * we found an operator matching the index.
 		 */
 		get_op_opclass_properties(newclause->opno, opclass,
-								  &stratno, &recheck);
+								  &stratno, &stratsubtype, &recheck);
 
 		*strategy = lappendi(*strategy, stratno);
+		*subtype = lappendo(*subtype, stratsubtype);
 
 		/*
 		 * If index is lossy for this operator, add (a copy of) original form
@@ -1540,6 +1556,7 @@ make_indexscan(List *qptlist,
 			   List *indxqual,
 			   List *indxqualorig,
 			   List *indxstrategy,
+			   List *indxsubtype,
 			   ScanDirection indexscandir)
 {
 	IndexScan  *node = makeNode(IndexScan);
@@ -1555,6 +1572,7 @@ make_indexscan(List *qptlist,
 	node->indxqual = indxqual;
 	node->indxqualorig = indxqualorig;
 	node->indxstrategy = indxstrategy;
+	node->indxsubtype = indxsubtype;
 	node->indxorderdir = indexscandir;
 
 	return node;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8e621dd063a..8c42b431aa3 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.88 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.89 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,6 @@
 #include "catalog/pg_amop.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_index.h"
-#include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/plancat.h"
@@ -329,10 +328,10 @@ find_inheritance_children(Oid inhparent)
 	if (!has_subclass(inhparent))
 		return NIL;
 
-	ScanKeyEntryInitialize(&key[0], 0,
-						   Anum_pg_inherits_inhparent,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(inhparent), OIDOID);
+	ScanKeyInit(&key[0],
+				Anum_pg_inherits_inhparent,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(inhparent));
 	relation = heap_openr(InheritsRelationName, AccessShareLock);
 	scan = heap_beginscan(relation, SnapshotNow, 1, key);
 	while ((inheritsTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 95b5dc37cc6..1068f036d1b 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.162 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.163 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1039,10 +1039,10 @@ find_inheritors(Oid relid, Oid **supervec)
 	{
 		/* find all types this relid inherits from, and add them to queue */
 
-		ScanKeyEntryInitialize(&skey, 0,
-							   Anum_pg_inherits_inhrelid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(relid), OIDOID);
+		ScanKeyInit(&skey,
+					Anum_pg_inherits_inhrelid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(relid));
 
 		inhscan = heap_beginscan(inhrel, SnapshotNow, 1, &skey);
 
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index 53f94137e16..0c36b127a1e 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.57 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.58 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_rewrite.h"
-#include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "rewrite/rewriteRemove.h"
 #include "rewrite/rewriteSupport.h"
@@ -105,10 +104,10 @@ RemoveRewriteRuleById(Oid ruleOid)
 	/*
 	 * Find the tuple for the target rule.
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(ruleOid), OIDOID);
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(ruleOid));
 
 	rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true,
 								SnapshotNow, 1, skey);
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 33e40109d3e..f777fb33b95 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/large_object/inv_api.c,v 1.99 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/large_object/inv_api.c,v 1.100 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -202,10 +202,10 @@ inv_getsize(LargeObjectDesc *obj_desc)
 
 	Assert(PointerIsValid(obj_desc));
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_largeobject_loid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(obj_desc->id), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_largeobject_loid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(obj_desc->id));
 
 	sd = index_beginscan(obj_desc->heap_r, obj_desc->index_r,
 						 SnapshotNow, 1, skey);
@@ -306,15 +306,15 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 	if (nbytes <= 0)
 		return 0;
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_largeobject_loid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(obj_desc->id), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_largeobject_loid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(obj_desc->id));
 
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_largeobject_pageno,
-						   BTGreaterEqualStrategyNumber, F_INT4GE,
-						   Int32GetDatum(pageno), INT4OID);
+	ScanKeyInit(&skey[1],
+				Anum_pg_largeobject_pageno,
+				BTGreaterEqualStrategyNumber, F_INT4GE,
+				Int32GetDatum(pageno));
 
 	sd = index_beginscan(obj_desc->heap_r, obj_desc->index_r,
 						 SnapshotNow, 2, skey);
@@ -413,15 +413,15 @@ inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 
 	indstate = CatalogOpenIndexes(obj_desc->heap_r);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_largeobject_loid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(obj_desc->id), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_largeobject_loid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(obj_desc->id));
 
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_largeobject_pageno,
-						   BTGreaterEqualStrategyNumber, F_INT4GE,
-						   Int32GetDatum(pageno), INT4OID);
+	ScanKeyInit(&skey[1],
+				Anum_pg_largeobject_pageno,
+				BTGreaterEqualStrategyNumber, F_INT4GE,
+				Int32GetDatum(pageno));
 
 	sd = index_beginscan(obj_desc->heap_r, obj_desc->index_r,
 						 SnapshotNow, 2, skey);
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 9f0fa0de32d..1493a58de74 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.94 2003/09/25 06:58:03 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.95 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -839,6 +839,26 @@ btfloat8cmp(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
 }
 
+Datum
+btfloat48cmp(PG_FUNCTION_ARGS)
+{
+	float4		arg1 = PG_GETARG_FLOAT4(0);
+	float8		arg2 = PG_GETARG_FLOAT8(1);
+
+	/* widen float4 to float8 and then compare */
+	PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
+}
+
+Datum
+btfloat84cmp(PG_FUNCTION_ARGS)
+{
+	float8		arg1 = PG_GETARG_FLOAT8(0);
+	float4		arg2 = PG_GETARG_FLOAT4(1);
+
+	/* widen float4 to float8 and then compare */
+	PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
+}
+
 
 /*
  *		===================
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 211549afeaf..ee8a00912a2 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.83 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.84 2003/11/12 21:15:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,10 +92,10 @@ regprocin(PG_FUNCTION_ARGS)
 		SysScanDesc sysscan;
 		HeapTuple	tuple;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_proc_proname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(pro_name_or_oid), NAMEOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_proc_proname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(pro_name_or_oid));
 
 		hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
 		sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true,
@@ -442,10 +442,10 @@ regoperin(PG_FUNCTION_ARGS)
 		SysScanDesc sysscan;
 		HeapTuple	tuple;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_operator_oprname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(opr_name_or_oid), NAMEOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_operator_oprname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(opr_name_or_oid));
 
 		hdesc = heap_openr(OperatorRelationName, AccessShareLock);
 		sysscan = systable_beginscan(hdesc, OperatorNameNspIndex, true,
@@ -820,10 +820,10 @@ regclassin(PG_FUNCTION_ARGS)
 		SysScanDesc sysscan;
 		HeapTuple	tuple;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_class_relname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(class_name_or_oid), NAMEOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_class_relname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(class_name_or_oid));
 
 		hdesc = heap_openr(RelationRelationName, AccessShareLock);
 		sysscan = systable_beginscan(hdesc, ClassNameNspIndex, true,
@@ -986,10 +986,10 @@ regtypein(PG_FUNCTION_ARGS)
 		SysScanDesc sysscan;
 		HeapTuple	tuple;
 
-		ScanKeyEntryInitialize(&skey[0], 0,
-							   Anum_pg_type_typname,
-							   BTEqualStrategyNumber, F_NAMEEQ,
-							   CStringGetDatum(typ_name_or_oid), NAMEOID);
+		ScanKeyInit(&skey[0],
+					Anum_pg_type_typname,
+					BTEqualStrategyNumber, F_NAMEEQ,
+					CStringGetDatum(typ_name_or_oid));
 
 		hdesc = heap_openr(TypeRelationName, AccessShareLock);
 		sysscan = systable_beginscan(hdesc, TypeNameNspIndex, true,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index fe749cc9b12..70d5626948a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.158 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.159 2003/11/12 21:15:55 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -488,10 +488,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
 	 */
 	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(trigid), OIDOID);
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(trigid));
 
 	tgscan = systable_beginscan(tgrel, TriggerOidIndex, true,
 								SnapshotNow, 1, skey);
@@ -886,10 +886,10 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags)
 	 */
 	conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   ObjectIdAttributeNumber,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(constraintId), OIDOID);
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(constraintId));
 
 	conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
 								 SnapshotNow, 1, skey);
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 978c0201466..3c4b780c56c 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.147 2003/10/16 21:37:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.148 2003/11/12 21:15:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -952,7 +952,8 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 		/*
 		 * Pattern specifies an exact match, so pretend operator is '='
 		 */
-		Oid			eqopr = get_opclass_member(opclass, BTEqualStrategyNumber);
+		Oid			eqopr = get_opclass_member(opclass, InvalidOid,
+											   BTEqualStrategyNumber);
 		List	   *eqargs;
 
 		if (eqopr == InvalidOid)
@@ -3382,7 +3383,8 @@ prefix_selectivity(Query *root, Var *var, Oid opclass, Const *prefixcon)
 	List	   *cmpargs;
 	Const	   *greaterstrcon;
 
-	cmpopr = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
+	cmpopr = get_opclass_member(opclass, InvalidOid,
+								BTGreaterEqualStrategyNumber);
 	if (cmpopr == InvalidOid)
 		elog(ERROR, "no >= operator for opclass %u", opclass);
 	cmpargs = makeList2(var, prefixcon);
@@ -3403,7 +3405,8 @@ prefix_selectivity(Query *root, Var *var, Oid opclass, Const *prefixcon)
 	{
 		Selectivity topsel;
 
-		cmpopr = get_opclass_member(opclass, BTLessStrategyNumber);
+		cmpopr = get_opclass_member(opclass, InvalidOid,
+									BTLessStrategyNumber);
 		if (cmpopr == InvalidOid)
 			elog(ERROR, "no < operator for opclass %u", opclass);
 		cmpargs = makeList2(var, greaterstrcon);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 640629c3a04..36125c2374a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.109 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.110 2003/11/12 21:15:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -967,9 +967,9 @@ CatalogCacheInitializeCache(CatCache *cache)
 		/* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
 		cache->cc_skey[i].sk_attno = cache->cc_key[i];
 
-		/* Fill in sk_strategy and sk_argtype correctly as well */
+		/* Fill in sk_strategy as well --- always standard equality */
 		cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
-		cache->cc_skey[i].sk_argtype = keytype;
+		cache->cc_skey[i].sk_subtype = InvalidOid;
 
 		CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p",
 					cache->cc_relname,
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bed4fcdbce6..20c10802bc4 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.109 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.110 2003/11/12 21:15:55 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -55,7 +55,7 @@ op_in_opclass(Oid opno, Oid opclass)
 /*
  * get_op_opclass_properties
  *
- *		Get the operator's strategy number and recheck (lossy) flag
+ *		Get the operator's strategy number, subtype, and recheck (lossy) flag
  *		within the specified opclass.
  *
  * Caller should already have verified that opno is a member of opclass,
@@ -63,7 +63,7 @@ op_in_opclass(Oid opno, Oid opclass)
  */
 void
 get_op_opclass_properties(Oid opno, Oid opclass,
-						  int *strategy, bool *recheck)
+						  int *strategy, Oid *subtype, bool *recheck)
 {
 	HeapTuple	tp;
 	Form_pg_amop amop_tup;
@@ -77,6 +77,7 @@ get_op_opclass_properties(Oid opno, Oid opclass,
 			 opno, opclass);
 	amop_tup = (Form_pg_amop) GETSTRUCT(tp);
 	*strategy = amop_tup->amopstrategy;
+	*subtype = amop_tup->amopsubtype;
 	*recheck = amop_tup->amopreqcheck;
 	ReleaseSysCache(tp);
 }
@@ -84,12 +85,12 @@ get_op_opclass_properties(Oid opno, Oid opclass,
 /*
  * get_opclass_member
  *		Get the OID of the operator that implements the specified strategy
- *		for the specified opclass.
+ *		with the specified subtype for the specified opclass.
  *
  * Returns InvalidOid if there is no pg_amop entry for the given keys.
  */
 Oid
-get_opclass_member(Oid opclass, int16 strategy)
+get_opclass_member(Oid opclass, Oid subtype, int16 strategy)
 {
 	HeapTuple	tp;
 	Form_pg_amop amop_tup;
@@ -97,8 +98,9 @@ get_opclass_member(Oid opclass, int16 strategy)
 
 	tp = SearchSysCache(AMOPSTRATEGY,
 						ObjectIdGetDatum(opclass),
+						ObjectIdGetDatum(subtype),
 						Int16GetDatum(strategy),
-						0, 0);
+						0);
 	if (!HeapTupleIsValid(tp))
 		return InvalidOid;
 	amop_tup = (Form_pg_amop) GETSTRUCT(tp);
@@ -149,8 +151,8 @@ get_op_hash_function(Oid opno)
 
 	if (OidIsValid(opclass))
 	{
-		/* Found a suitable opclass, get its hash support function */
-		return get_opclass_proc(opclass, HASHPROC);
+		/* Found a suitable opclass, get its default hash support function */
+		return get_opclass_proc(opclass, InvalidOid, HASHPROC);
 	}
 
 	/* Didn't find a match... */
@@ -163,12 +165,12 @@ get_op_hash_function(Oid opno)
 /*
  * get_opclass_proc
  *		Get the OID of the specified support function
- *		for the specified opclass.
+ *		for the specified opclass and subtype.
  *
  * Returns InvalidOid if there is no pg_amproc entry for the given keys.
  */
 Oid
-get_opclass_proc(Oid opclass, int16 procnum)
+get_opclass_proc(Oid opclass, Oid subtype, int16 procnum)
 {
 	HeapTuple	tp;
 	Form_pg_amproc amproc_tup;
@@ -176,8 +178,9 @@ get_opclass_proc(Oid opclass, int16 procnum)
 
 	tp = SearchSysCache(AMPROCNUM,
 						ObjectIdGetDatum(opclass),
+						ObjectIdGetDatum(subtype),
 						Int16GetDatum(procnum),
-						0, 0);
+						0);
 	if (!HeapTupleIsValid(tp))
 		return InvalidOid;
 	amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 88c8cbb1418..ac5e53a91ae 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.191 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.192 2003/11/12 21:15:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -262,7 +262,7 @@ do { \
 /*
  * Special cache for opclass-related information
  *
- * Note: only non-cross-type operators and support procs get cached
+ * Note: only default-subtype operators and support procs get cached
  */
 typedef struct opclasscacheent
 {
@@ -336,26 +336,23 @@ ScanPgRelation(RelationBuildDescInfo buildinfo, bool indexOK)
 	switch (buildinfo.infotype)
 	{
 		case INFO_RELID:
-			ScanKeyEntryInitialize(&key[0], 0,
-								   ObjectIdAttributeNumber,
-								   BTEqualStrategyNumber, F_OIDEQ,
-								   ObjectIdGetDatum(buildinfo.i.info_id),
-								   OIDOID);
+			ScanKeyInit(&key[0],
+						ObjectIdAttributeNumber,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(buildinfo.i.info_id));
 			nkeys = 1;
 			indexRelname = ClassOidIndex;
 			break;
 
 		case INFO_RELNAME:
-			ScanKeyEntryInitialize(&key[0], 0,
-								   Anum_pg_class_relname,
-								   BTEqualStrategyNumber, F_NAMEEQ,
-								   NameGetDatum(buildinfo.i.info_name),
-								   NAMEOID);
-			ScanKeyEntryInitialize(&key[1], 0,
-								   Anum_pg_class_relnamespace,
-								   BTEqualStrategyNumber, F_OIDEQ,
-								   ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
-								   OIDOID);
+			ScanKeyInit(&key[0],
+						Anum_pg_class_relname,
+						BTEqualStrategyNumber, F_NAMEEQ,
+						NameGetDatum(buildinfo.i.info_name));
+			ScanKeyInit(&key[1],
+						Anum_pg_class_relnamespace,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
 			nkeys = 2;
 			indexRelname = ClassNameNspIndex;
 			break;
@@ -483,15 +480,14 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 	 * (Eliminating system attribute rows at the index level is lots
 	 * faster than fetching them.)
 	 */
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_attribute_attrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
-	ScanKeyEntryInitialize(&skey[1], 0,
-						   Anum_pg_attribute_attnum,
-						   BTGreaterStrategyNumber, F_INT2GT,
-						   Int16GetDatum(0), INT2OID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_attribute_attrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
+	ScanKeyInit(&skey[1],
+				Anum_pg_attribute_attnum,
+				BTGreaterStrategyNumber, F_INT2GT,
+				Int16GetDatum(0));
 
 	/*
 	 * Open pg_attribute and begin a scan.	Force heap scan if we haven't
@@ -673,11 +669,10 @@ RelationBuildRuleLock(Relation relation)
 	/*
 	 * form a scan key
 	 */
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_rewrite_ev_class,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&key,
+				Anum_pg_rewrite_ev_class,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	/*
 	 * open pg_rewrite and begin a scan
@@ -1058,7 +1053,7 @@ RelationInitIndexAccessInfo(Relation relation)
 
 /*
  * IndexSupportInitialize
- *		Initializes an index's cached lists of operators and support procs,
+ *		Initializes an index's cached opclass information,
  *		given the index's pg_index tuple.
  *
  * Data is returned into *indexOperator and *indexSupport, which are arrays
@@ -1131,11 +1126,9 @@ LookupOpclassInfo(Oid operatorClassOid,
 {
 	OpClassCacheEnt *opcentry;
 	bool		found;
-	Relation	pg_amop_desc;
-	Relation	pg_amproc_desc;
-	SysScanDesc pg_amop_scan;
-	SysScanDesc pg_amproc_scan;
-	ScanKeyData key;
+	Relation	rel;
+	SysScanDesc scan;
+	ScanKeyData skey[2];
 	HeapTuple	htup;
 	bool		indexOK;
 
@@ -1191,7 +1184,7 @@ LookupOpclassInfo(Oid operatorClassOid,
 		opcentry->supportProcs = NULL;
 
 	/*
-	 * To avoid infinite recursion during startup, force a heap scan if
+	 * To avoid infinite recursion during startup, force heap scans if
 	 * we're looking up info for the opclasses used by the indexes we
 	 * would like to reference here.
 	 */
@@ -1200,24 +1193,25 @@ LookupOpclassInfo(Oid operatorClassOid,
 		 operatorClassOid != INT2_BTREE_OPS_OID);
 
 	/*
-	 * Scan pg_amop to obtain operators for the opclass
+	 * Scan pg_amop to obtain operators for the opclass.  We only fetch
+	 * the default ones (those with subtype zero).
 	 */
 	if (numStrats > 0)
 	{
-		ScanKeyEntryInitialize(&key, 0,
-							   Anum_pg_amop_amopclaid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(operatorClassOid),
-							   OIDOID);
-		pg_amop_desc = heap_openr(AccessMethodOperatorRelationName,
-								  AccessShareLock);
-		pg_amop_scan = systable_beginscan(pg_amop_desc,
-										  AccessMethodStrategyIndex,
-										  indexOK,
-										  SnapshotNow,
-										  1, &key);
-
-		while (HeapTupleIsValid(htup = systable_getnext(pg_amop_scan)))
+		ScanKeyInit(&skey[0],
+					Anum_pg_amop_amopclaid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(operatorClassOid));
+		ScanKeyInit(&skey[1],
+					Anum_pg_amop_amopsubtype,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(InvalidOid));
+		rel = heap_openr(AccessMethodOperatorRelationName,
+						 AccessShareLock);
+		scan = systable_beginscan(rel, AccessMethodStrategyIndex, indexOK,
+								  SnapshotNow, 2, skey);
+
+		while (HeapTupleIsValid(htup = systable_getnext(scan)))
 		{
 			Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup);
 
@@ -1229,29 +1223,30 @@ LookupOpclassInfo(Oid operatorClassOid,
 				amopform->amopopr;
 		}
 
-		systable_endscan(pg_amop_scan);
-		heap_close(pg_amop_desc, AccessShareLock);
+		systable_endscan(scan);
+		heap_close(rel, AccessShareLock);
 	}
 
 	/*
-	 * Scan pg_amproc to obtain support procs for the opclass
+	 * Scan pg_amproc to obtain support procs for the opclass.  We only fetch
+	 * the default ones (those with subtype zero).
 	 */
 	if (numSupport > 0)
 	{
-		ScanKeyEntryInitialize(&key, 0,
-							   Anum_pg_amproc_amopclaid,
-							   BTEqualStrategyNumber, F_OIDEQ,
-							   ObjectIdGetDatum(operatorClassOid),
-							   OIDOID);
-		pg_amproc_desc = heap_openr(AccessMethodProcedureRelationName,
-									AccessShareLock);
-		pg_amproc_scan = systable_beginscan(pg_amproc_desc,
-											AccessMethodProcedureIndex,
-											indexOK,
-											SnapshotNow,
-											1, &key);
-
-		while (HeapTupleIsValid(htup = systable_getnext(pg_amproc_scan)))
+		ScanKeyInit(&skey[0],
+					Anum_pg_amproc_amopclaid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(operatorClassOid));
+		ScanKeyInit(&skey[1],
+					Anum_pg_amproc_amprocsubtype,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(InvalidOid));
+		rel = heap_openr(AccessMethodProcedureRelationName,
+						 AccessShareLock);
+		scan = systable_beginscan(rel, AccessMethodProcedureIndex, indexOK,
+								  SnapshotNow, 2, skey);
+
+		while (HeapTupleIsValid(htup = systable_getnext(scan)))
 		{
 			Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
 
@@ -1264,8 +1259,8 @@ LookupOpclassInfo(Oid operatorClassOid,
 				amprocform->amproc;
 		}
 
-		systable_endscan(pg_amproc_scan);
-		heap_close(pg_amproc_desc, AccessShareLock);
+		systable_endscan(scan);
+		heap_close(rel, AccessShareLock);
 	}
 
 	opcentry->valid = true;
@@ -2483,16 +2478,14 @@ AttrDefaultFetch(Relation relation)
 	int			found;
 	int			i;
 
-	ScanKeyEntryInitialize(&skey, 0,
-						   Anum_pg_attrdef_adrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&skey,
+				Anum_pg_attrdef_adrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	adrel = heap_openr(AttrDefaultRelationName, AccessShareLock);
 	adscan = systable_beginscan(adrel, AttrDefaultIndex, true,
-								SnapshotNow,
-								1, &skey);
+								SnapshotNow, 1, &skey);
 	found = 0;
 
 	while (HeapTupleIsValid(htup = systable_getnext(adscan)))
@@ -2550,11 +2543,10 @@ CheckConstraintFetch(Relation relation)
 	bool		isnull;
 	int			found = 0;
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_constraint_conrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	conrel = heap_openr(ConstraintRelationName, AccessShareLock);
 	conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
@@ -2642,16 +2634,14 @@ RelationGetIndexList(Relation relation)
 	result = NIL;
 
 	/* Prepare to scan pg_index for entries having indrelid = this rel. */
-	ScanKeyEntryInitialize(&skey, 0,
-						   Anum_pg_index_indrelid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)),
-						   OIDOID);
+	ScanKeyInit(&skey,
+				Anum_pg_index_indrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	indrel = heap_openr(IndexRelationName, AccessShareLock);
 	indscan = systable_beginscan(indrel, IndexIndrelidIndex, true,
-								 SnapshotNow,
-								 1, &skey);
+								 SnapshotNow, 1, &skey);
 
 	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
 	{
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 9b250eef62e..eb2f6eb1af7 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.91 2003/09/24 18:54:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.92 2003/11/12 21:15:56 tgl Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -136,21 +136,21 @@ static const struct cachedesc cacheinfo[] = {
 	{AccessMethodOperatorRelationName,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndex,
 		0,
-		2,
+		3,
 		{
 			Anum_pg_amop_amopclaid,
+			Anum_pg_amop_amopsubtype,
 			Anum_pg_amop_amopstrategy,
-			0,
 			0
 	}},
 	{AccessMethodProcedureRelationName, /* AMPROCNUM */
 		AccessMethodProcedureIndex,
 		0,
-		2,
+		3,
 		{
 			Anum_pg_amproc_amopclaid,
+			Anum_pg_amproc_amprocsubtype,
 			Anum_pg_amproc_amprocnum,
-			0,
 			0
 	}},
 	{AttributeRelationName,		/* ATTNAME */
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 9b7620b0f0a..12d295c5a59 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -33,7 +33,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/typcache.c,v 1.2 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/typcache.c,v 1.3 2003/11/12 21:15:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,22 +158,26 @@ lookup_type_cache(Oid type_id, int flags)
 	{
 		if (typentry->btree_opc != InvalidOid)
 			typentry->eq_opr = get_opclass_member(typentry->btree_opc,
+												  InvalidOid,
 												  BTEqualStrategyNumber);
 		if (typentry->eq_opr == InvalidOid &&
 			typentry->hash_opc != InvalidOid)
 			typentry->eq_opr = get_opclass_member(typentry->hash_opc,
+												  InvalidOid,
 												  HTEqualStrategyNumber);
 	}
 	if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
 	{
 		if (typentry->btree_opc != InvalidOid)
 			typentry->lt_opr = get_opclass_member(typentry->btree_opc,
+												  InvalidOid,
 												  BTLessStrategyNumber);
 	}
 	if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
 	{
 		if (typentry->btree_opc != InvalidOid)
 			typentry->gt_opr = get_opclass_member(typentry->btree_opc,
+												  InvalidOid,
 												  BTGreaterStrategyNumber);
 	}
 	if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
@@ -181,6 +185,7 @@ lookup_type_cache(Oid type_id, int flags)
 	{
 		if (typentry->btree_opc != InvalidOid)
 			typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
+												  InvalidOid,
 												  BTORDER_PROC);
 	}
 
@@ -248,10 +253,10 @@ lookup_default_opclass(Oid type_id, Oid am_id)
 	 */
 	rel = heap_openr(OperatorClassRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&skey[0], 0,
-						   Anum_pg_opclass_opcamid,
-						   BTEqualStrategyNumber, F_OIDEQ,
-						   ObjectIdGetDatum(am_id), OIDOID);
+	ScanKeyInit(&skey[0],
+				Anum_pg_opclass_opcamid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(am_id));
 
 	scan = systable_beginscan(rel, OpclassAmNameNspIndex, true,
 							  SnapshotNow, 1, skey);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 21e2efdda75..54175189df5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.128 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.129 2003/11/12 21:15:56 tgl Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -26,7 +26,6 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_shadow.h"
-#include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -92,10 +91,10 @@ ReverifyMyDatabase(const char *name)
 	 */
 	pgdbrel = heap_openr(DatabaseRelationName, AccessShareLock);
 
-	ScanKeyEntryInitialize(&key, 0,
-						   Anum_pg_database_datname,
-						   BTEqualStrategyNumber, F_NAMEEQ,
-						   NameGetDatum(name), NAMEOID);
+	ScanKeyInit(&key,
+				Anum_pg_database_datname,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				NameGetDatum(name));
 
 	pgdbscan = heap_beginscan(pgdbrel, SnapshotNow, 1, &key);
 
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 9b3c0bb4075..a6accb5db4d 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -78,7 +78,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.38 2003/11/09 21:30:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.39 2003/11/12 21:15:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -472,15 +472,14 @@ tuplesort_begin_heap(TupleDesc tupDesc,
 						   &state->sortFnKinds[i]);
 
 		/*
-		 * We needn't fill in sk_strategy or sk_argtype since these scankeys
+		 * We needn't fill in sk_strategy or sk_subtype since these scankeys
 		 * will never be passed to an index.
 		 */
-		ScanKeyEntryInitialize(&state->scanKeys[i], 0,
-							   attNums[i],
-							   InvalidStrategy,
-							   sortFunction,
-							   (Datum) 0,
-							   InvalidOid);
+		ScanKeyInit(&state->scanKeys[i],
+					attNums[i],
+					InvalidStrategy,
+					sortFunction,
+					(Datum) 0);
 	}
 
 	return state;
@@ -1739,6 +1738,10 @@ SelectSortFunction(Oid sortOperator,
 
 		if (!opclass_is_btree(aform->amopclaid))
 			continue;
+		/* must be of default subtype, too */
+		if (aform->amopsubtype != InvalidOid)
+			continue;
+
 		if (aform->amopstrategy == BTLessStrategyNumber)
 		{
 			opclass = aform->amopclaid;
@@ -1757,8 +1760,8 @@ SelectSortFunction(Oid sortOperator,
 
 	if (OidIsValid(opclass))
 	{
-		/* Found a suitable opclass, get its comparator support function */
-		*sortFunction = get_opclass_proc(opclass, BTORDER_PROC);
+		/* Found a suitable opclass, get its default comparator function */
+		*sortFunction = get_opclass_proc(opclass, InvalidOid, BTORDER_PROC);
 		Assert(RegProcedureIsValid(*sortFunction));
 		return;
 	}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 80d412a52e5..2d75fa5b23f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nbtree.h,v 1.72 2003/11/09 21:30:37 tgl Exp $
+ * $Id: nbtree.h,v 1.73 2003/11/12 21:15:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -341,7 +341,7 @@ typedef struct xl_btree_newpage
 
 /*
  *	Operator strategy numbers for B-tree have been moved to access/skey.h,
- *	because many places need to use them in ScanKeyEntryInitialize() calls.
+ *	because many places need to use them in ScanKeyInit() calls.
  */
 
 /*
@@ -404,12 +404,12 @@ typedef struct BTScanOpaqueData
 	Buffer		btso_mrkbuf;
 	ItemPointerData curHeapIptr;
 	ItemPointerData mrkHeapIptr;
-	/* these fields are set by _bt_orderkeys(), which see for more info: */
+	/* these fields are set by _bt_preprocess_keys(): */
 	bool		qual_ok;		/* false if qual can never be satisfied */
-	int			numberOfKeys;	/* number of scan keys */
+	int			numberOfKeys;	/* number of preprocessed scan keys */
 	int			numberOfRequiredKeys;	/* number of keys that must be
 										 * matched to continue the scan */
-	ScanKey		keyData;		/* array of scan keys */
+	ScanKey		keyData;		/* array of preprocessed scan keys */
 } BTScanOpaqueData;
 
 typedef BTScanOpaqueData *BTScanOpaque;
@@ -424,7 +424,6 @@ extern Datum btinsert(PG_FUNCTION_ARGS);
 extern Datum btgettuple(PG_FUNCTION_ARGS);
 extern Datum btbeginscan(PG_FUNCTION_ARGS);
 extern Datum btrescan(PG_FUNCTION_ARGS);
-extern void btmovescan(IndexScanDesc scan, Datum v);
 extern Datum btendscan(PG_FUNCTION_ARGS);
 extern Datum btmarkpos(PG_FUNCTION_ARGS);
 extern Datum btrestrpos(PG_FUNCTION_ARGS);
@@ -480,7 +479,7 @@ extern ScanKey _bt_mkscankey(Relation rel, IndexTuple itup);
 extern ScanKey _bt_mkscankey_nodata(Relation rel);
 extern void _bt_freeskey(ScanKey skey);
 extern void _bt_freestack(BTStack stack);
-extern void _bt_orderkeys(IndexScanDesc scan);
+extern void _bt_preprocess_keys(IndexScanDesc scan);
 extern bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 			  ScanDirection dir, bool *continuescan);
 extern BTItem _bt_formitem(IndexTuple itup);
diff --git a/src/include/access/skey.h b/src/include/access/skey.h
index b7373dd6410..5cce3249fb5 100644
--- a/src/include/access/skey.h
+++ b/src/include/access/skey.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: skey.h,v 1.23 2003/11/09 21:30:37 tgl Exp $
+ * $Id: skey.h,v 1.24 2003/11/12 21:15:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,8 @@
 
 /*
  * Strategy numbers identify the semantics that particular operators have
- * with respect to particular operator classes.
+ * with respect to particular operator classes.  In some cases a strategy
+ * subtype (an OID) is used as further information.
  */
 typedef uint16 StrategyNumber;
 
@@ -47,23 +48,23 @@ typedef uint16 StrategyNumber;
  * (The data structure can support unary indexable operators too; in that
  * case sk_argument would go unused.  This is not currently implemented.)
  *
- * For an index scan, sk_strategy must be set correctly for the operator.
- * When using a ScanKey in a heap scan, sk_strategy is not used and may be
- * set to InvalidStrategy.
+ * For an index scan, sk_strategy and sk_subtype must be set correctly for
+ * the operator.  When using a ScanKey in a heap scan, these fields are not
+ * used and may be set to InvalidStrategy/InvalidOid.
  *
  * Note: in some places, ScanKeys are used as a convenient representation
  * for the invocation of an access method support procedure.  In this case
- * sk_strategy is not meaningful, and sk_func may refer to a function that
- * returns something other than boolean.
+ * sk_strategy/sk_subtype are not meaningful, and sk_func may refer to a
+ * function that returns something other than boolean.
  */
 typedef struct ScanKeyData
 {
 	int			sk_flags;		/* flags, see below */
 	AttrNumber	sk_attno;		/* table or index column number */
 	StrategyNumber sk_strategy;	/* operator strategy number */
+	Oid			sk_subtype;		/* strategy subtype */
 	FmgrInfo	sk_func;		/* lookup info for function to call */
 	Datum		sk_argument;	/* data to compare */
-	Oid			sk_argtype;		/* datatype of sk_argument */
 } ScanKeyData;
 
 typedef ScanKeyData *ScanKey;
@@ -76,19 +77,24 @@ typedef ScanKeyData *ScanKey;
 /*
  * prototypes for functions in access/common/scankey.c
  */
+extern void ScanKeyInit(ScanKey entry,
+						AttrNumber attributeNumber,
+						StrategyNumber strategy,
+						RegProcedure procedure,
+						Datum argument);
 extern void ScanKeyEntryInitialize(ScanKey entry,
 								   int flags,
 								   AttrNumber attributeNumber,
 								   StrategyNumber strategy,
+								   Oid subtype,
 								   RegProcedure procedure,
-								   Datum argument,
-								   Oid argtype);
+								   Datum argument);
 extern void ScanKeyEntryInitializeWithInfo(ScanKey entry,
 										   int flags,
 										   AttrNumber attributeNumber,
 										   StrategyNumber strategy,
+										   Oid subtype,
 										   FmgrInfo *finfo,
-										   Datum argument,
-										   Oid argtype);
+										   Datum argument);
 
 #endif   /* SKEY_H */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 9efcfe447d5..8209b34880d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.210 2003/10/21 16:23:16 tgl Exp $
+ * $Id: catversion.h,v 1.211 2003/11/12 21:15:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200310211
+#define CATALOG_VERSION_NO	200311101
 
 #endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 1150f1e47c5..720c8eec0a9 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: indexing.h,v 1.79 2003/08/04 02:40:10 momjian Exp $
+ * $Id: indexing.h,v 1.80 2003/11/12 21:15:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,8 +24,8 @@
  * macros rather than hardwiring the actual index name.
  */
 #define AccessMethodOperatorIndex	"pg_amop_opr_opc_index"
-#define AccessMethodStrategyIndex	"pg_amop_opc_strategy_index"
-#define AccessMethodProcedureIndex	"pg_amproc_opc_procnum_index"
+#define AccessMethodStrategyIndex	"pg_amop_opc_strat_index"
+#define AccessMethodProcedureIndex	"pg_amproc_opc_proc_index"
 #define AggregateFnoidIndex			"pg_aggregate_fnoid_index"
 #define AmNameIndex					"pg_am_name_index"
 #define AmOidIndex					"pg_am_oid_index"
@@ -115,9 +115,9 @@ extern void CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple);
 DECLARE_UNIQUE_INDEX(pg_aggregate_fnoid_index on pg_aggregate using btree(aggfnoid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_am_name_index on pg_am using btree(amname name_ops));
 DECLARE_UNIQUE_INDEX(pg_am_oid_index on pg_am using btree(oid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_amop_opc_strat_index on pg_amop using btree(amopclaid oid_ops, amopsubtype oid_ops, amopstrategy int2_ops));
 DECLARE_UNIQUE_INDEX(pg_amop_opr_opc_index on pg_amop using btree(amopopr oid_ops, amopclaid oid_ops));
-DECLARE_UNIQUE_INDEX(pg_amop_opc_strategy_index on pg_amop using btree(amopclaid oid_ops, amopstrategy int2_ops));
-DECLARE_UNIQUE_INDEX(pg_amproc_opc_procnum_index on pg_amproc using btree(amopclaid oid_ops, amprocnum int2_ops));
+DECLARE_UNIQUE_INDEX(pg_amproc_opc_proc_index on pg_amproc using btree(amopclaid oid_ops, amprocsubtype oid_ops, amprocnum int2_ops));
 DECLARE_UNIQUE_INDEX(pg_attrdef_adrelid_adnum_index on pg_attrdef using btree(adrelid oid_ops, adnum int2_ops));
 DECLARE_UNIQUE_INDEX(pg_attrdef_oid_index on pg_attrdef using btree(oid oid_ops));
 DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnam_index on pg_attribute using btree(attrelid oid_ops, attname name_ops));
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7e70d726d14..746b9bfb6d1 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: namespace.h,v 1.27 2003/08/04 02:40:10 momjian Exp $
+ * $Id: namespace.h,v 1.28 2003/11/12 21:15:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,9 +42,9 @@ typedef struct _OpclassCandidateList
 	char	   *opcname_tmp;	/* for internal use of namespace lookup */
 	int			pathpos;		/* for internal use of namespace lookup */
 	Oid			oid;			/* the opclass's OID */
-	Oid			opcintype;		/* type of input data for opclass */
+	Oid			opcintype;		/* type of data indexed by opclass */
 	bool		opcdefault;		/* T if opclass is default for opcintype */
-	Oid			opckeytype;		/* type of index data, or InvalidOid */
+	Oid			opckeytype;		/* type of data in index, or InvalidOid */
 }	*OpclassCandidateList;
 
 
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index bff4b217003..1bab599fdb4 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -6,17 +6,24 @@
  *
  * The amop table identifies the operators associated with each index opclass.
  *
- * Note: the primary key for this table is <amopclaid, amopstrategy>.
+ * The primary key for this table is <amopclaid, amopsubtype, amopstrategy>.
+ * amopsubtype is equal to zero for an opclass's "default" operators
+ * (which normally are those that accept the opclass's opcintype on both
+ * left and right sides).  Some index AMs allow nondefault operators to
+ * exist for a single strategy --- for example, in the btree AM nondefault
+ * operators can have right-hand input data types different from opcintype,
+ * and their amopsubtype is equal to the right-hand input data type.
+ *
  * We also keep a unique index on <amopclaid, amopopr>, so that we can
  * use a syscache to quickly answer questions of the form "is this operator
  * in this opclass?".  This implies that the same operator cannot be listed
- * for multiple strategy numbers of a single opclass.
+ * for multiple subtypes or strategy numbers of a single opclass.
  *
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.55 2003/08/17 19:58:06 tgl Exp $
+ * $Id: pg_amop.h,v 1.56 2003/11/12 21:15:57 tgl Exp $
  *
  * NOTES
  *	 the genbki.sh script reads this file and generates .bki
@@ -42,6 +49,7 @@
 CATALOG(pg_amop) BKI_WITHOUT_OIDS
 {
 	Oid			amopclaid;		/* the index opclass this entry is for */
+	Oid			amopsubtype;	/* operator subtype, or zero if default */
 	int2		amopstrategy;	/* operator strategy number */
 	bool		amopreqcheck;	/* index hit must be rechecked */
 	Oid			amopopr;		/* the operator's pg_operator OID */
@@ -58,11 +66,12 @@ typedef FormData_pg_amop *Form_pg_amop;
  *		compiler constants for pg_amop
  * ----------------
  */
-#define Natts_pg_amop					4
+#define Natts_pg_amop					5
 #define Anum_pg_amop_amopclaid			1
-#define Anum_pg_amop_amopstrategy		2
-#define Anum_pg_amop_amopreqcheck		3
-#define Anum_pg_amop_amopopr			4
+#define Anum_pg_amop_amopsubtype		2
+#define Anum_pg_amop_amopstrategy		3
+#define Anum_pg_amop_amopreqcheck		4
+#define Anum_pg_amop_amopopr			5
 
 /* ----------------
  *		initial contents of pg_amop
@@ -73,460 +82,495 @@ typedef FormData_pg_amop *Form_pg_amop;
  *	rtree box_ops
  */
 
-DATA(insert (	 425 1 f  493 ));
-DATA(insert (	 425 2 f  494 ));
-DATA(insert (	 425 3 f  500 ));
-DATA(insert (	 425 4 f  495 ));
-DATA(insert (	 425 5 f  496 ));
-DATA(insert (	 425 6 f  499 ));
-DATA(insert (	 425 7 f  498 ));
-DATA(insert (	 425 8 f  497 ));
-
-/*
- *	rtree bigbox_ops
- */
-
-DATA(insert (	 422 1 f  493 ));
-DATA(insert (	 422 2 f  494 ));
-DATA(insert (	 422 3 f  500 ));
-DATA(insert (	 422 4 f  495 ));
-DATA(insert (	 422 5 f  496 ));
-DATA(insert (	 422 6 f  499 ));
-DATA(insert (	 422 7 f  498 ));
-DATA(insert (	 422 8 f  497 ));
+DATA(insert (	 425    0 1 f  493 ));
+DATA(insert (	 425    0 2 f  494 ));
+DATA(insert (	 425    0 3 f  500 ));
+DATA(insert (	 425    0 4 f  495 ));
+DATA(insert (	 425    0 5 f  496 ));
+DATA(insert (	 425    0 6 f  499 ));
+DATA(insert (	 425    0 7 f  498 ));
+DATA(insert (	 425    0 8 f  497 ));
 
 /*
  *	rtree poly_ops (supports polygons)
  */
 
-DATA(insert (	1993 1 f  485 ));
-DATA(insert (	1993 2 f  486 ));
-DATA(insert (	1993 3 f  492 ));
-DATA(insert (	1993 4 f  487 ));
-DATA(insert (	1993 5 f  488 ));
-DATA(insert (	1993 6 f  491 ));
-DATA(insert (	1993 7 f  490 ));
-DATA(insert (	1993 8 f  489 ));
+DATA(insert (	1993    0 1 f  485 ));
+DATA(insert (	1993    0 2 f  486 ));
+DATA(insert (	1993    0 3 f  492 ));
+DATA(insert (	1993    0 4 f  487 ));
+DATA(insert (	1993    0 5 f  488 ));
+DATA(insert (	1993    0 6 f  491 ));
+DATA(insert (	1993    0 7 f  490 ));
+DATA(insert (	1993    0 8 f  489 ));
 
 /*
  *	btree int2_ops
  */
 
-DATA(insert (	1976 1 f   95 ));
-DATA(insert (	1976 2 f  522 ));
-DATA(insert (	1976 3 f   94 ));
-DATA(insert (	1976 4 f  524 ));
-DATA(insert (	1976 5 f  520 ));
+DATA(insert (	1976    0 1 f   95 ));
+DATA(insert (	1976    0 2 f  522 ));
+DATA(insert (	1976    0 3 f   94 ));
+DATA(insert (	1976    0 4 f  524 ));
+DATA(insert (	1976    0 5 f  520 ));
+/* crosstype operators int24 */
+DATA(insert (	1976   23 1 f  534 ));
+DATA(insert (	1976   23 2 f  540 ));
+DATA(insert (	1976   23 3 f  532 ));
+DATA(insert (	1976   23 4 f  542 ));
+DATA(insert (	1976   23 5 f  536 ));
+/* crosstype operators int28 */
+DATA(insert (	1976   20 1 f  1864 ));
+DATA(insert (	1976   20 2 f  1866 ));
+DATA(insert (	1976   20 3 f  1862 ));
+DATA(insert (	1976   20 4 f  1867 ));
+DATA(insert (	1976   20 5 f  1865 ));
 
 /*
  *	btree int4_ops
  */
 
-DATA(insert (	1978 1 f   97 ));
-DATA(insert (	1978 2 f  523 ));
-DATA(insert (	1978 3 f   96 ));
-DATA(insert (	1978 4 f  525 ));
-DATA(insert (	1978 5 f  521 ));
+DATA(insert (	1978    0 1 f   97 ));
+DATA(insert (	1978    0 2 f  523 ));
+DATA(insert (	1978    0 3 f   96 ));
+DATA(insert (	1978    0 4 f  525 ));
+DATA(insert (	1978    0 5 f  521 ));
+/* crosstype operators int42 */
+DATA(insert (	1978   21 1 f  535 ));
+DATA(insert (	1978   21 2 f  541 ));
+DATA(insert (	1978   21 3 f  533 ));
+DATA(insert (	1978   21 4 f  543 ));
+DATA(insert (	1978   21 5 f  537 ));
+/* crosstype operators int48 */
+DATA(insert (	1978   20 1 f   37 ));
+DATA(insert (	1978   20 2 f   80 ));
+DATA(insert (	1978   20 3 f   15 ));
+DATA(insert (	1978   20 4 f   82 ));
+DATA(insert (	1978   20 5 f   76 ));
 
 /*
  *	btree int8_ops
  */
 
-DATA(insert (	1980 1 f  412 ));
-DATA(insert (	1980 2 f  414 ));
-DATA(insert (	1980 3 f  410 ));
-DATA(insert (	1980 4 f  415 ));
-DATA(insert (	1980 5 f  413 ));
+DATA(insert (	1980    0 1 f  412 ));
+DATA(insert (	1980    0 2 f  414 ));
+DATA(insert (	1980    0 3 f  410 ));
+DATA(insert (	1980    0 4 f  415 ));
+DATA(insert (	1980    0 5 f  413 ));
+/* crosstype operators int82 */
+DATA(insert (	1980   21 1 f  1870 ));
+DATA(insert (	1980   21 2 f  1872 ));
+DATA(insert (	1980   21 3 f  1868 ));
+DATA(insert (	1980   21 4 f  1873 ));
+DATA(insert (	1980   21 5 f  1871 ));
+/* crosstype operators int84 */
+DATA(insert (	1980   23 1 f  418 ));
+DATA(insert (	1980   23 2 f  420 ));
+DATA(insert (	1980   23 3 f  416 ));
+DATA(insert (	1980   23 4 f  430 ));
+DATA(insert (	1980   23 5 f  419 ));
 
 /*
  *	btree oid_ops
  */
 
-DATA(insert (	1989 1 f  609 ));
-DATA(insert (	1989 2 f  611 ));
-DATA(insert (	1989 3 f  607 ));
-DATA(insert (	1989 4 f  612 ));
-DATA(insert (	1989 5 f  610 ));
+DATA(insert (	1989    0 1 f  609 ));
+DATA(insert (	1989    0 2 f  611 ));
+DATA(insert (	1989    0 3 f  607 ));
+DATA(insert (	1989    0 4 f  612 ));
+DATA(insert (	1989    0 5 f  610 ));
 
 /*
  *	btree oidvector_ops
  */
 
-DATA(insert (	1991 1 f  645 ));
-DATA(insert (	1991 2 f  647 ));
-DATA(insert (	1991 3 f  649 ));
-DATA(insert (	1991 4 f  648 ));
-DATA(insert (	1991 5 f  646 ));
+DATA(insert (	1991    0 1 f  645 ));
+DATA(insert (	1991    0 2 f  647 ));
+DATA(insert (	1991    0 3 f  649 ));
+DATA(insert (	1991    0 4 f  648 ));
+DATA(insert (	1991    0 5 f  646 ));
 
 /*
  *	btree float4_ops
  */
 
-DATA(insert (	1970 1 f  622 ));
-DATA(insert (	1970 2 f  624 ));
-DATA(insert (	1970 3 f  620 ));
-DATA(insert (	1970 4 f  625 ));
-DATA(insert (	1970 5 f  623 ));
+DATA(insert (	1970    0 1 f  622 ));
+DATA(insert (	1970    0 2 f  624 ));
+DATA(insert (	1970    0 3 f  620 ));
+DATA(insert (	1970    0 4 f  625 ));
+DATA(insert (	1970    0 5 f  623 ));
+/* crosstype operators float48 */
+DATA(insert (	1970  701 1 f  1122 ));
+DATA(insert (	1970  701 2 f  1124 ));
+DATA(insert (	1970  701 3 f  1120 ));
+DATA(insert (	1970  701 4 f  1125 ));
+DATA(insert (	1970  701 5 f  1123 ));
 
 /*
  *	btree float8_ops
  */
 
-DATA(insert (	1972 1 f  672 ));
-DATA(insert (	1972 2 f  673 ));
-DATA(insert (	1972 3 f  670 ));
-DATA(insert (	1972 4 f  675 ));
-DATA(insert (	1972 5 f  674 ));
+DATA(insert (	1972    0 1 f  672 ));
+DATA(insert (	1972    0 2 f  673 ));
+DATA(insert (	1972    0 3 f  670 ));
+DATA(insert (	1972    0 4 f  675 ));
+DATA(insert (	1972    0 5 f  674 ));
+/* crosstype operators float84 */
+DATA(insert (	1972  700 1 f  1132 ));
+DATA(insert (	1972  700 2 f  1134 ));
+DATA(insert (	1972  700 3 f  1130 ));
+DATA(insert (	1972  700 4 f  1135 ));
+DATA(insert (	1972  700 5 f  1133 ));
 
 /*
  *	btree char_ops
  */
 
-DATA(insert (	 429 1 f  631 ));
-DATA(insert (	 429 2 f  632 ));
-DATA(insert (	 429 3 f   92 ));
-DATA(insert (	 429 4 f  634 ));
-DATA(insert (	 429 5 f  633 ));
+DATA(insert (	 429    0 1 f  631 ));
+DATA(insert (	 429    0 2 f  632 ));
+DATA(insert (	 429    0 3 f   92 ));
+DATA(insert (	 429    0 4 f  634 ));
+DATA(insert (	 429    0 5 f  633 ));
 
 /*
  *	btree name_ops
  */
 
-DATA(insert (	1986 1 f  660 ));
-DATA(insert (	1986 2 f  661 ));
-DATA(insert (	1986 3 f   93 ));
-DATA(insert (	1986 4 f  663 ));
-DATA(insert (	1986 5 f  662 ));
+DATA(insert (	1986    0 1 f  660 ));
+DATA(insert (	1986    0 2 f  661 ));
+DATA(insert (	1986    0 3 f   93 ));
+DATA(insert (	1986    0 4 f  663 ));
+DATA(insert (	1986    0 5 f  662 ));
 
 /*
  *	btree text_ops
  */
 
-DATA(insert (	1994 1 f  664 ));
-DATA(insert (	1994 2 f  665 ));
-DATA(insert (	1994 3 f   98 ));
-DATA(insert (	1994 4 f  667 ));
-DATA(insert (	1994 5 f  666 ));
+DATA(insert (	1994    0 1 f  664 ));
+DATA(insert (	1994    0 2 f  665 ));
+DATA(insert (	1994    0 3 f   98 ));
+DATA(insert (	1994    0 4 f  667 ));
+DATA(insert (	1994    0 5 f  666 ));
 
 /*
  *	btree bpchar_ops
  */
 
-DATA(insert (	 426 1 f 1058 ));
-DATA(insert (	 426 2 f 1059 ));
-DATA(insert (	 426 3 f 1054 ));
-DATA(insert (	 426 4 f 1061 ));
-DATA(insert (	 426 5 f 1060 ));
+DATA(insert (	 426    0 1 f 1058 ));
+DATA(insert (	 426    0 2 f 1059 ));
+DATA(insert (	 426    0 3 f 1054 ));
+DATA(insert (	 426    0 4 f 1061 ));
+DATA(insert (	 426    0 5 f 1060 ));
 
 /*
  *	btree varchar_ops (same operators as text_ops)
  */
 
-DATA(insert (	2003 1 f 664 ));
-DATA(insert (	2003 2 f 665 ));
-DATA(insert (	2003 3 f  98 ));
-DATA(insert (	2003 4 f 667 ));
-DATA(insert (	2003 5 f 666 ));
+DATA(insert (	2003    0 1 f 664 ));
+DATA(insert (	2003    0 2 f 665 ));
+DATA(insert (	2003    0 3 f  98 ));
+DATA(insert (	2003    0 4 f 667 ));
+DATA(insert (	2003    0 5 f 666 ));
 
 /*
  *	btree bytea_ops
  */
 
-DATA(insert (	 428 1 f 1957 ));
-DATA(insert (	 428 2 f 1958 ));
-DATA(insert (	 428 3 f 1955 ));
-DATA(insert (	 428 4 f 1960 ));
-DATA(insert (	 428 5 f 1959 ));
+DATA(insert (	 428    0 1 f 1957 ));
+DATA(insert (	 428    0 2 f 1958 ));
+DATA(insert (	 428    0 3 f 1955 ));
+DATA(insert (	 428    0 4 f 1960 ));
+DATA(insert (	 428    0 5 f 1959 ));
 
 /*
  *	btree abstime_ops
  */
 
-DATA(insert (	 421 1 f  562 ));
-DATA(insert (	 421 2 f  564 ));
-DATA(insert (	 421 3 f  560 ));
-DATA(insert (	 421 4 f  565 ));
-DATA(insert (	 421 5 f  563 ));
+DATA(insert (	 421    0 1 f  562 ));
+DATA(insert (	 421    0 2 f  564 ));
+DATA(insert (	 421    0 3 f  560 ));
+DATA(insert (	 421    0 4 f  565 ));
+DATA(insert (	 421    0 5 f  563 ));
 
 /*
  *	btree date_ops
  */
 
-DATA(insert (	 434 1 f 1095 ));
-DATA(insert (	 434 2 f 1096 ));
-DATA(insert (	 434 3 f 1093 ));
-DATA(insert (	 434 4 f 1098 ));
-DATA(insert (	 434 5 f 1097 ));
+DATA(insert (	 434    0 1 f 1095 ));
+DATA(insert (	 434    0 2 f 1096 ));
+DATA(insert (	 434    0 3 f 1093 ));
+DATA(insert (	 434    0 4 f 1098 ));
+DATA(insert (	 434    0 5 f 1097 ));
 
 /*
  *	btree time_ops
  */
 
-DATA(insert (	1996 1 f 1110 ));
-DATA(insert (	1996 2 f 1111 ));
-DATA(insert (	1996 3 f 1108 ));
-DATA(insert (	1996 4 f 1113 ));
-DATA(insert (	1996 5 f 1112 ));
+DATA(insert (	1996    0 1 f 1110 ));
+DATA(insert (	1996    0 2 f 1111 ));
+DATA(insert (	1996    0 3 f 1108 ));
+DATA(insert (	1996    0 4 f 1113 ));
+DATA(insert (	1996    0 5 f 1112 ));
 
 /*
  *	btree timetz_ops
  */
 
-DATA(insert (	2000 1 f 1552 ));
-DATA(insert (	2000 2 f 1553 ));
-DATA(insert (	2000 3 f 1550 ));
-DATA(insert (	2000 4 f 1555 ));
-DATA(insert (	2000 5 f 1554 ));
+DATA(insert (	2000    0 1 f 1552 ));
+DATA(insert (	2000    0 2 f 1553 ));
+DATA(insert (	2000    0 3 f 1550 ));
+DATA(insert (	2000    0 4 f 1555 ));
+DATA(insert (	2000    0 5 f 1554 ));
 
 /*
  *	btree timestamp_ops
  */
 
-DATA(insert (	2039 1 f 2062 ));
-DATA(insert (	2039 2 f 2063 ));
-DATA(insert (	2039 3 f 2060 ));
-DATA(insert (	2039 4 f 2065 ));
-DATA(insert (	2039 5 f 2064 ));
+DATA(insert (	2039    0 1 f 2062 ));
+DATA(insert (	2039    0 2 f 2063 ));
+DATA(insert (	2039    0 3 f 2060 ));
+DATA(insert (	2039    0 4 f 2065 ));
+DATA(insert (	2039    0 5 f 2064 ));
 
 /*
  *	btree timestamptz_ops
  */
 
-DATA(insert (	1998 1 f 1322 ));
-DATA(insert (	1998 2 f 1323 ));
-DATA(insert (	1998 3 f 1320 ));
-DATA(insert (	1998 4 f 1325 ));
-DATA(insert (	1998 5 f 1324 ));
+DATA(insert (	1998    0 1 f 1322 ));
+DATA(insert (	1998    0 2 f 1323 ));
+DATA(insert (	1998    0 3 f 1320 ));
+DATA(insert (	1998    0 4 f 1325 ));
+DATA(insert (	1998    0 5 f 1324 ));
 
 /*
  *	btree interval_ops
  */
 
-DATA(insert (	1982 1 f 1332 ));
-DATA(insert (	1982 2 f 1333 ));
-DATA(insert (	1982 3 f 1330 ));
-DATA(insert (	1982 4 f 1335 ));
-DATA(insert (	1982 5 f 1334 ));
+DATA(insert (	1982    0 1 f 1332 ));
+DATA(insert (	1982    0 2 f 1333 ));
+DATA(insert (	1982    0 3 f 1330 ));
+DATA(insert (	1982    0 4 f 1335 ));
+DATA(insert (	1982    0 5 f 1334 ));
 
 /*
  *	btree macaddr
  */
 
-DATA(insert (	1984 1 f 1222 ));
-DATA(insert (	1984 2 f 1223 ));
-DATA(insert (	1984 3 f 1220 ));
-DATA(insert (	1984 4 f 1225 ));
-DATA(insert (	1984 5 f 1224 ));
+DATA(insert (	1984    0 1 f 1222 ));
+DATA(insert (	1984    0 2 f 1223 ));
+DATA(insert (	1984    0 3 f 1220 ));
+DATA(insert (	1984    0 4 f 1225 ));
+DATA(insert (	1984    0 5 f 1224 ));
 
 /*
  *	btree inet
  */
 
-DATA(insert (	1974 1 f 1203 ));
-DATA(insert (	1974 2 f 1204 ));
-DATA(insert (	1974 3 f 1201 ));
-DATA(insert (	1974 4 f 1206 ));
-DATA(insert (	1974 5 f 1205 ));
+DATA(insert (	1974    0 1 f 1203 ));
+DATA(insert (	1974    0 2 f 1204 ));
+DATA(insert (	1974    0 3 f 1201 ));
+DATA(insert (	1974    0 4 f 1206 ));
+DATA(insert (	1974    0 5 f 1205 ));
 
 /*
  *	btree cidr
  */
 
-DATA(insert (	 432 1 f  822 ));
-DATA(insert (	 432 2 f  823 ));
-DATA(insert (	 432 3 f  820 ));
-DATA(insert (	 432 4 f  825 ));
-DATA(insert (	 432 5 f  824 ));
+DATA(insert (	 432    0 1 f  822 ));
+DATA(insert (	 432    0 2 f  823 ));
+DATA(insert (	 432    0 3 f  820 ));
+DATA(insert (	 432    0 4 f  825 ));
+DATA(insert (	 432    0 5 f  824 ));
 
 /*
  *	btree numeric
  */
 
-DATA(insert (	1988 1 f 1754 ));
-DATA(insert (	1988 2 f 1755 ));
-DATA(insert (	1988 3 f 1752 ));
-DATA(insert (	1988 4 f 1757 ));
-DATA(insert (	1988 5 f 1756 ));
+DATA(insert (	1988    0 1 f 1754 ));
+DATA(insert (	1988    0 2 f 1755 ));
+DATA(insert (	1988    0 3 f 1752 ));
+DATA(insert (	1988    0 4 f 1757 ));
+DATA(insert (	1988    0 5 f 1756 ));
 
 /*
  *	btree bool
  */
 
-DATA(insert (	 424 1 f   58 ));
-DATA(insert (	 424 2 f 1694 ));
-DATA(insert (	 424 3 f   91 ));
-DATA(insert (	 424 4 f 1695 ));
-DATA(insert (	 424 5 f   59 ));
+DATA(insert (	 424    0 1 f   58 ));
+DATA(insert (	 424    0 2 f 1694 ));
+DATA(insert (	 424    0 3 f   91 ));
+DATA(insert (	 424    0 4 f 1695 ));
+DATA(insert (	 424    0 5 f   59 ));
 
 /*
  *	btree bit
  */
 
-DATA(insert (	 423 1 f 1786 ));
-DATA(insert (	 423 2 f 1788 ));
-DATA(insert (	 423 3 f 1784 ));
-DATA(insert (	 423 4 f 1789 ));
-DATA(insert (	 423 5 f 1787 ));
+DATA(insert (	 423    0 1 f 1786 ));
+DATA(insert (	 423    0 2 f 1788 ));
+DATA(insert (	 423    0 3 f 1784 ));
+DATA(insert (	 423    0 4 f 1789 ));
+DATA(insert (	 423    0 5 f 1787 ));
 
 /*
  *	btree varbit
  */
 
-DATA(insert (	2002 1 f 1806 ));
-DATA(insert (	2002 2 f 1808 ));
-DATA(insert (	2002 3 f 1804 ));
-DATA(insert (	2002 4 f 1809 ));
-DATA(insert (	2002 5 f 1807 ));
+DATA(insert (	2002    0 1 f 1806 ));
+DATA(insert (	2002    0 2 f 1808 ));
+DATA(insert (	2002    0 3 f 1804 ));
+DATA(insert (	2002    0 4 f 1809 ));
+DATA(insert (	2002    0 5 f 1807 ));
 
 /*
  *	btree text pattern
  */
 
-DATA(insert (	2095 1 f 2314 ));
-DATA(insert (	2095 2 f 2315 ));
-DATA(insert (	2095 3 f 2316 ));
-DATA(insert (	2095 4 f 2317 ));
-DATA(insert (	2095 5 f 2318 ));
+DATA(insert (	2095    0 1 f 2314 ));
+DATA(insert (	2095    0 2 f 2315 ));
+DATA(insert (	2095    0 3 f 2316 ));
+DATA(insert (	2095    0 4 f 2317 ));
+DATA(insert (	2095    0 5 f 2318 ));
 
 /*
  *	btree varchar pattern (same operators as text)
  */
 
-DATA(insert (	2096 1 f 2314 ));
-DATA(insert (	2096 2 f 2315 ));
-DATA(insert (	2096 3 f 2316 ));
-DATA(insert (	2096 4 f 2317 ));
-DATA(insert (	2096 5 f 2318 ));
+DATA(insert (	2096    0 1 f 2314 ));
+DATA(insert (	2096    0 2 f 2315 ));
+DATA(insert (	2096    0 3 f 2316 ));
+DATA(insert (	2096    0 4 f 2317 ));
+DATA(insert (	2096    0 5 f 2318 ));
 
 /*
  *	btree bpchar pattern
  */
 
-DATA(insert (	2097 1 f 2326 ));
-DATA(insert (	2097 2 f 2327 ));
-DATA(insert (	2097 3 f 2328 ));
-DATA(insert (	2097 4 f 2329 ));
-DATA(insert (	2097 5 f 2330 ));
+DATA(insert (	2097    0 1 f 2326 ));
+DATA(insert (	2097    0 2 f 2327 ));
+DATA(insert (	2097    0 3 f 2328 ));
+DATA(insert (	2097    0 4 f 2329 ));
+DATA(insert (	2097    0 5 f 2330 ));
 
 /*
  *	btree name pattern
  */
 
-DATA(insert (	2098 1 f 2332 ));
-DATA(insert (	2098 2 f 2333 ));
-DATA(insert (	2098 3 f 2334 ));
-DATA(insert (	2098 4 f 2335 ));
-DATA(insert (	2098 5 f 2336 ));
+DATA(insert (	2098    0 1 f 2332 ));
+DATA(insert (	2098    0 2 f 2333 ));
+DATA(insert (	2098    0 3 f 2334 ));
+DATA(insert (	2098    0 4 f 2335 ));
+DATA(insert (	2098    0 5 f 2336 ));
 
 /*
  *	btree money_ops
  */
 
-DATA(insert (	2099 1 f  902 ));
-DATA(insert (	2099 2 f  904 ));
-DATA(insert (	2099 3 f  900 ));
-DATA(insert (	2099 4 f  905 ));
-DATA(insert (	2099 5 f  903 ));
+DATA(insert (	2099    0 1 f  902 ));
+DATA(insert (	2099    0 2 f  904 ));
+DATA(insert (	2099    0 3 f  900 ));
+DATA(insert (	2099    0 4 f  905 ));
+DATA(insert (	2099    0 5 f  903 ));
 
 /*
  *	btree reltime_ops
  */
 
-DATA(insert (	2233 1 f  568 ));
-DATA(insert (	2233 2 f  570 ));
-DATA(insert (	2233 3 f  566 ));
-DATA(insert (	2233 4 f  569 ));
-DATA(insert (	2233 5 f  571 ));
+DATA(insert (	2233    0 1 f  568 ));
+DATA(insert (	2233    0 2 f  570 ));
+DATA(insert (	2233    0 3 f  566 ));
+DATA(insert (	2233    0 4 f  571 ));
+DATA(insert (	2233    0 5 f  569 ));
 
 /*
  *	btree tinterval_ops
  */
 
-DATA(insert (	2234 1 f  813 ));
-DATA(insert (	2234 2 f  815 ));
-DATA(insert (	2234 3 f  811 ));
-DATA(insert (	2234 4 f  814 ));
-DATA(insert (	2234 5 f  816 ));
+DATA(insert (	2234    0 1 f  813 ));
+DATA(insert (	2234    0 2 f  815 ));
+DATA(insert (	2234    0 3 f  811 ));
+DATA(insert (	2234    0 4 f  816 ));
+DATA(insert (	2234    0 5 f  814 ));
 
 /*
  *	btree array_ops
  */
 
-DATA(insert (	 397 1 f 1072 ));
-DATA(insert (	 397 2 f 1074 ));
-DATA(insert (	 397 3 f 1070 ));
-DATA(insert (	 397 4 f 1075 ));
-DATA(insert (	 397 5 f 1073 ));
+DATA(insert (	 397    0 1 f 1072 ));
+DATA(insert (	 397    0 2 f 1074 ));
+DATA(insert (	 397    0 3 f 1070 ));
+DATA(insert (	 397    0 4 f 1075 ));
+DATA(insert (	 397    0 5 f 1073 ));
 
 /*
  *	hash index _ops
  */
 
 /* bpchar_ops */
-DATA(insert (	 427 1 f 1054 ));
+DATA(insert (	 427    0 1 f 1054 ));
 /* char_ops */
-DATA(insert (	 431 1 f   92 ));
+DATA(insert (	 431    0 1 f   92 ));
 /* cidr_ops */
-DATA(insert (	 433 1 f  820 ));
+DATA(insert (	 433    0 1 f  820 ));
 /* date_ops */
-DATA(insert (	 435 1 f 1093 ));
+DATA(insert (	 435    0 1 f 1093 ));
 /* float4_ops */
-DATA(insert (	1971 1 f  620 ));
+DATA(insert (	1971    0 1 f  620 ));
 /* float8_ops */
-DATA(insert (	1973 1 f  670 ));
+DATA(insert (	1973    0 1 f  670 ));
 /* inet_ops */
-DATA(insert (	1975 1 f 1201 ));
+DATA(insert (	1975    0 1 f 1201 ));
 /* int2_ops */
-DATA(insert (	1977 1 f   94 ));
+DATA(insert (	1977    0 1 f   94 ));
 /* int4_ops */
-DATA(insert (	1979 1 f   96 ));
+DATA(insert (	1979    0 1 f   96 ));
 /* int8_ops */
-DATA(insert (	1981 1 f  410 ));
+DATA(insert (	1981    0 1 f  410 ));
 /* interval_ops */
-DATA(insert (	1983 1 f 1330 ));
+DATA(insert (	1983    0 1 f 1330 ));
 /* macaddr_ops */
-DATA(insert (	1985 1 f 1220 ));
+DATA(insert (	1985    0 1 f 1220 ));
 /* name_ops */
-DATA(insert (	1987 1 f   93 ));
+DATA(insert (	1987    0 1 f   93 ));
 /* oid_ops */
-DATA(insert (	1990 1 f  607 ));
+DATA(insert (	1990    0 1 f  607 ));
 /* oidvector_ops */
-DATA(insert (	1992 1 f  649 ));
+DATA(insert (	1992    0 1 f  649 ));
 /* text_ops */
-DATA(insert (	1995 1 f   98 ));
+DATA(insert (	1995    0 1 f   98 ));
 /* time_ops */
-DATA(insert (	1997 1 f 1108 ));
+DATA(insert (	1997    0 1 f 1108 ));
 /* timestamptz_ops */
-DATA(insert (	1999 1 f 1320 ));
+DATA(insert (	1999    0 1 f 1320 ));
 /* timetz_ops */
-DATA(insert (	2001 1 f 1550 ));
+DATA(insert (	2001    0 1 f 1550 ));
 /* varchar_ops */
-DATA(insert (	2004 1 f   98 ));
+DATA(insert (	2004    0 1 f   98 ));
 /* timestamp_ops */
-DATA(insert (	2040 1 f 2060 ));
+DATA(insert (	2040    0 1 f 2060 ));
 /* bool_ops */
-DATA(insert (	2222 1 f   91 ));
+DATA(insert (	2222    0 1 f   91 ));
 /* bytea_ops */
-DATA(insert (	2223 1 f 1955 ));
+DATA(insert (	2223    0 1 f 1955 ));
 /* int2vector_ops */
-DATA(insert (	2224 1 f  386 ));
+DATA(insert (	2224    0 1 f  386 ));
 /* xid_ops */
-DATA(insert (	2225 1 f  352 ));
+DATA(insert (	2225    0 1 f  352 ));
 /* cid_ops */
-DATA(insert (	2226 1 f  385 ));
+DATA(insert (	2226    0 1 f  385 ));
 /* abstime_ops */
-DATA(insert (	2227 1 f  560 ));
+DATA(insert (	2227    0 1 f  560 ));
 /* reltime_ops */
-DATA(insert (	2228 1 f  566 ));
+DATA(insert (	2228    0 1 f  566 ));
 /* text_pattern_ops */
-DATA(insert (	2229 1 f 2316 ));
+DATA(insert (	2229    0 1 f 2316 ));
 /* varchar_pattern_ops */
-DATA(insert (	2230 1 f 2316 ));
+DATA(insert (	2230    0 1 f 2316 ));
 /* bpchar_pattern_ops */
-DATA(insert (	2231 1 f 2328 ));
+DATA(insert (	2231    0 1 f 2328 ));
 /* name_pattern_ops */
-DATA(insert (	2232 1 f 2334 ));
+DATA(insert (	2232    0 1 f 2334 ));
 /* aclitem_ops */
-DATA(insert (	2235 1 f  974 ));
+DATA(insert (	2235    0 1 f  974 ));
 
 #endif   /* PG_AMOP_H */
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index a2607db5eb8..e284dedbf0d 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -6,15 +6,20 @@
  *
  * The amproc table identifies support procedures associated with index
  * opclasses.  These procedures can't be listed in pg_amop since they are
- * not associated with indexable operators for the opclass.
+ * not the implementation of any indexable operator for the opclass.
  *
- * Note: the primary key for this table is <amopclaid, amprocnum>.
+ * The primary key for this table is <amopclaid, amprocsubtype, amprocnum>.
+ * amprocsubtype is equal to zero for an opclass's "default" procedures.
+ * Usually a nondefault amprocsubtype indicates a support procedure to be
+ * used with operators having the same nondefault amopsubtype.  The exact
+ * behavior depends on the index AM, however, and some don't pay attention
+ * to subtype at all.
  *
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.44 2003/08/17 19:58:06 tgl Exp $
+ * $Id: pg_amproc.h,v 1.45 2003/11/12 21:15:57 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -40,6 +45,7 @@
 CATALOG(pg_amproc) BKI_WITHOUT_OIDS
 {
 	Oid			amopclaid;		/* the index opclass this entry is for */
+	Oid			amprocsubtype;	/* procedure subtype, or zero if default */
 	int2		amprocnum;		/* support procedure index */
 	regproc		amproc;			/* OID of the proc */
 } FormData_pg_amproc;
@@ -55,10 +61,11 @@ typedef FormData_pg_amproc *Form_pg_amproc;
  *		compiler constants for pg_amproc
  * ----------------
  */
-#define Natts_pg_amproc					3
+#define Natts_pg_amproc					4
 #define Anum_pg_amproc_amopclaid		1
-#define Anum_pg_amproc_amprocnum		2
-#define Anum_pg_amproc_amproc			3
+#define Anum_pg_amproc_amprocsubtype	2
+#define Anum_pg_amproc_amprocnum		3
+#define Anum_pg_amproc_amproc			4
 
 /* ----------------
  *		initial contents of pg_amproc
@@ -66,88 +73,93 @@ typedef FormData_pg_amproc *Form_pg_amproc;
  */
 
 /* rtree */
-DATA(insert (	 422 1	193 ));
-DATA(insert (	 422 2	194 ));
-DATA(insert (	 422 3	196 ));
-DATA(insert (	 425 1	193 ));
-DATA(insert (	 425 2	194 ));
-DATA(insert (	 425 3	195 ));
-DATA(insert (	1993 1	197 ));
-DATA(insert (	1993 2	198 ));
-DATA(insert (	1993 3	199 ));
+DATA(insert (	 425    0 1	193 ));
+DATA(insert (	 425    0 2	194 ));
+DATA(insert (	 425    0 3	195 ));
+DATA(insert (	1993    0 1	197 ));
+DATA(insert (	1993    0 2	198 ));
+DATA(insert (	1993    0 3	199 ));
 
 
 /* btree */
-DATA(insert (	 397 1	382 ));
-DATA(insert (	 421 1	357 ));
-DATA(insert (	 423 1 1596 ));
-DATA(insert (	 424 1 1693 ));
-DATA(insert (	 426 1 1078 ));
-DATA(insert (	 428 1 1954 ));
-DATA(insert (	 429 1	358 ));
-DATA(insert (	 432 1	926 ));
-DATA(insert (	 434 1 1092 ));
-DATA(insert (	1970 1	354 ));
-DATA(insert (	1972 1	355 ));
-DATA(insert (	1974 1	926 ));
-DATA(insert (	1976 1	350 ));
-DATA(insert (	1978 1	351 ));
-DATA(insert (	1980 1	842 ));
-DATA(insert (	1982 1 1315 ));
-DATA(insert (	1984 1	836 ));
-DATA(insert (	1986 1	359 ));
-DATA(insert (	1988 1 1769 ));
-DATA(insert (	1989 1	356 ));
-DATA(insert (	1991 1	404 ));
-DATA(insert (	1994 1	360 ));
-DATA(insert (	1996 1 1107 ));
-DATA(insert (	1998 1 1314 ));
-DATA(insert (	2000 1 1358 ));
-DATA(insert (	2002 1 1672 ));
-DATA(insert (	2003 1	360 ));
-DATA(insert (	2039 1 2045 ));
-DATA(insert (	2095 1 2166 ));
-DATA(insert (	2096 1 2166 ));
-DATA(insert (	2097 1 2180 ));
-DATA(insert (	2098 1 2187 ));
-DATA(insert (	2099 1  377 ));
-DATA(insert (	2233 1  380 ));
-DATA(insert (	2234 1  381 ));
+DATA(insert (	 397    0 1	382 ));
+DATA(insert (	 421    0 1	357 ));
+DATA(insert (	 423    0 1 1596 ));
+DATA(insert (	 424    0 1 1693 ));
+DATA(insert (	 426    0 1 1078 ));
+DATA(insert (	 428    0 1 1954 ));
+DATA(insert (	 429    0 1	358 ));
+DATA(insert (	 432    0 1	926 ));
+DATA(insert (	 434    0 1 1092 ));
+DATA(insert (	1970    0 1	354 ));
+DATA(insert (	1970  701 1	2194 ));
+DATA(insert (	1972    0 1	355 ));
+DATA(insert (	1972  700 1	2195 ));
+DATA(insert (	1974    0 1	926 ));
+DATA(insert (	1976    0 1	350 ));
+DATA(insert (	1976   23 1	2190 ));
+DATA(insert (	1976   20 1	2192 ));
+DATA(insert (	1978    0 1	351 ));
+DATA(insert (	1978   20 1	2188 ));
+DATA(insert (	1978   21 1	2191 ));
+DATA(insert (	1980    0 1	842 ));
+DATA(insert (	1980   23 1	2189 ));
+DATA(insert (	1980   21 1	2193 ));
+DATA(insert (	1982    0 1 1315 ));
+DATA(insert (	1984    0 1	836 ));
+DATA(insert (	1986    0 1	359 ));
+DATA(insert (	1988    0 1 1769 ));
+DATA(insert (	1989    0 1	356 ));
+DATA(insert (	1991    0 1	404 ));
+DATA(insert (	1994    0 1	360 ));
+DATA(insert (	1996    0 1 1107 ));
+DATA(insert (	1998    0 1 1314 ));
+DATA(insert (	2000    0 1 1358 ));
+DATA(insert (	2002    0 1 1672 ));
+DATA(insert (	2003    0 1	360 ));
+DATA(insert (	2039    0 1 2045 ));
+DATA(insert (	2095    0 1 2166 ));
+DATA(insert (	2096    0 1 2166 ));
+DATA(insert (	2097    0 1 2180 ));
+DATA(insert (	2098    0 1 2187 ));
+DATA(insert (	2099    0 1  377 ));
+DATA(insert (	2233    0 1  380 ));
+DATA(insert (	2234    0 1  381 ));
 
 
 /* hash */
-DATA(insert (	 427 1 1080 ));
-DATA(insert (	 431 1	454 ));
-DATA(insert (	 433 1	456 ));
-DATA(insert (	 435 1	450 ));
-DATA(insert (	1971 1	451 ));
-DATA(insert (	1973 1	452 ));
-DATA(insert (	1975 1	456 ));
-DATA(insert (	1977 1	449 ));
-DATA(insert (	1979 1	450 ));
-DATA(insert (	1981 1	949 ));
-DATA(insert (	1983 1 1697 ));
-DATA(insert (	1985 1	399 ));
-DATA(insert (	1987 1	455 ));
-DATA(insert (	1990 1	453 ));
-DATA(insert (	1992 1	457 ));
-DATA(insert (	1995 1	400 ));
-DATA(insert (	1997 1	452 ));
-DATA(insert (	1999 1	452 ));
-DATA(insert (	2001 1 1696 ));
-DATA(insert (	2004 1	400 ));
-DATA(insert (	2040 1	452 ));
-DATA(insert (	2222 1	454 ));
-DATA(insert (	2223 1	456 ));
-DATA(insert (	2224 1	398 ));
-DATA(insert (	2225 1	450 ));
-DATA(insert (	2226 1	450 ));
-DATA(insert (	2227 1	450 ));
-DATA(insert (	2228 1	450 ));
-DATA(insert (	2229 1	456 ));
-DATA(insert (	2230 1	456 ));
-DATA(insert (	2231 1	456 ));
-DATA(insert (	2232 1	455 ));
-DATA(insert (	2235 1	329 ));
+DATA(insert (	 427    0 1 1080 ));
+DATA(insert (	 431    0 1	454 ));
+DATA(insert (	 433    0 1	456 ));
+DATA(insert (	 435    0 1	450 ));
+DATA(insert (	1971    0 1	451 ));
+DATA(insert (	1973    0 1	452 ));
+DATA(insert (	1975    0 1	456 ));
+DATA(insert (	1977    0 1	449 ));
+DATA(insert (	1979    0 1	450 ));
+DATA(insert (	1981    0 1	949 ));
+DATA(insert (	1983    0 1 1697 ));
+DATA(insert (	1985    0 1	399 ));
+DATA(insert (	1987    0 1	455 ));
+DATA(insert (	1990    0 1	453 ));
+DATA(insert (	1992    0 1	457 ));
+DATA(insert (	1995    0 1	400 ));
+DATA(insert (	1997    0 1	452 ));
+DATA(insert (	1999    0 1	452 ));
+DATA(insert (	2001    0 1 1696 ));
+DATA(insert (	2004    0 1	400 ));
+DATA(insert (	2040    0 1	452 ));
+DATA(insert (	2222    0 1	454 ));
+DATA(insert (	2223    0 1	456 ));
+DATA(insert (	2224    0 1	398 ));
+DATA(insert (	2225    0 1	450 ));
+DATA(insert (	2226    0 1	450 ));
+DATA(insert (	2227    0 1	450 ));
+DATA(insert (	2228    0 1	450 ));
+DATA(insert (	2229    0 1	456 ));
+DATA(insert (	2230    0 1	456 ));
+DATA(insert (	2231    0 1	456 ));
+DATA(insert (	2232    0 1	455 ));
+DATA(insert (	2235    0 1	329 ));
 
 #endif   /* PG_AMPROC_H */
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index af277ad687a..0f196de19fc 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -16,17 +16,18 @@
  * such an index.
  *
  * Normally opckeytype = InvalidOid (zero), indicating that the data stored
- * in the index is the same as the input data.	If opckeytype is nonzero
- * then it indicates that a conversion step is needed to produce the stored
- * index data, which will be of type opckeytype (which might be the same or
- * different from the input data).	Performing such a conversion is the
- * responsibility of the index access method --- not all AMs support this.
+ * in the index is the same as the data in the indexed column.  If opckeytype
+ * is nonzero then it indicates that a conversion step is needed to produce
+ * the stored index data, which will be of type opckeytype (which might be
+ * the same or different from the input datatype).  Performing such a
+ * conversion is the responsibility of the index access method --- not all
+ * AMs support this.
  *
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.57 2003/08/17 19:58:06 tgl Exp $
+ * $Id: pg_opclass.h,v 1.58 2003/11/12 21:15:57 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -56,9 +57,9 @@ CATALOG(pg_opclass)
 	NameData	opcname;		/* name of this opclass */
 	Oid			opcnamespace;	/* namespace of this opclass */
 	int4		opcowner;		/* opclass owner */
-	Oid			opcintype;		/* type of input data for opclass */
+	Oid			opcintype;		/* type of data indexed by opclass */
 	bool		opcdefault;		/* T if opclass is default for opcintype */
-	Oid			opckeytype;		/* type of index data, or InvalidOid */
+	Oid			opckeytype;		/* type of data in index, or InvalidOid */
 } FormData_pg_opclass;
 
 /* ----------------
@@ -89,7 +90,6 @@ typedef FormData_pg_opclass *Form_pg_opclass;
 DATA(insert OID =  421 (	403		abstime_ops		PGNSP PGUID  702 t 0 ));
 DATA(insert OID =  397 (	403		array_ops		PGNSP PGUID 2277 t 0 ));
 #define ARRAY_BTREE_OPS_OID 397
-DATA(insert OID =  422 (	402		bigbox_ops		PGNSP PGUID  603 f 0 ));
 DATA(insert OID =  423 (	403		bit_ops			PGNSP PGUID 1560 t 0 ));
 DATA(insert OID =  424 (	403		bool_ops		PGNSP PGUID   16 t 0 ));
 DATA(insert OID =  425 (	402		box_ops			PGNSP PGUID  603 t 0 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4128d5f1643..50b363eda34 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.314 2003/10/21 16:23:16 tgl Exp $
+ * $Id: pg_proc.h,v 1.315 2003/11/12 21:15:57 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -400,8 +400,6 @@ DATA(insert OID = 194 (  rt_box_inter	   PGNSP PGUID 12 f f t f i 2 2278 "603 60
 DESCR("r-tree");
 DATA(insert OID = 195 (  rt_box_size	   PGNSP PGUID 12 f f t f i 2 2278 "603 2281"  rt_box_size - _null_ ));
 DESCR("r-tree");
-DATA(insert OID = 196 (  rt_bigbox_size    PGNSP PGUID 12 f f t f i 2 2278 "603 2281"  rt_bigbox_size - _null_ ));
-DESCR("r-tree");
 DATA(insert OID = 197 (  rt_poly_union	   PGNSP PGUID 12 f f t f i 2 604 "604 604"  rt_poly_union - _null_ ));
 DESCR("r-tree");
 DATA(insert OID = 198 (  rt_poly_inter	   PGNSP PGUID 12 f f t f i 2 2278 "604 604"  rt_poly_inter - _null_ ));
@@ -3075,6 +3073,15 @@ DATA(insert OID = 2185 ( name_pattern_gt	PGNSP PGUID 12 f f t f i 2 16 "19 19" n
 DATA(insert OID = 2186 ( name_pattern_ne	PGNSP PGUID 12 f f t f i 2 16 "19 19" name_pattern_ne - _null_ ));
 DATA(insert OID = 2187 ( btname_pattern_cmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btname_pattern_cmp - _null_ ));
 
+DATA(insert OID = 2188 ( btint48cmp         PGNSP PGUID 12 f f t f i 2 23 "23 20" btint48cmp - _null_ ));
+DATA(insert OID = 2189 ( btint84cmp         PGNSP PGUID 12 f f t f i 2 23 "20 23" btint84cmp - _null_ ));
+DATA(insert OID = 2190 ( btint24cmp         PGNSP PGUID 12 f f t f i 2 23 "21 23" btint24cmp - _null_ ));
+DATA(insert OID = 2191 ( btint42cmp         PGNSP PGUID 12 f f t f i 2 23 "23 21" btint42cmp - _null_ ));
+DATA(insert OID = 2192 ( btint28cmp         PGNSP PGUID 12 f f t f i 2 23 "21 20" btint28cmp - _null_ ));
+DATA(insert OID = 2193 ( btint82cmp         PGNSP PGUID 12 f f t f i 2 23 "20 21" btint82cmp - _null_ ));
+DATA(insert OID = 2194 ( btfloat48cmp       PGNSP PGUID 12 f f t f i 2 23 "700 701" btfloat48cmp - _null_ ));
+DATA(insert OID = 2195 ( btfloat84cmp       PGNSP PGUID 12 f f t f i 2 23 "701 700" btfloat84cmp - _null_ ));
+
 
 DATA(insert OID = 2212 (  regprocedurein	PGNSP PGUID 12 f f t f s 1 2202 "2275"	regprocedurein - _null_ ));
 DESCR("I/O");
@@ -3157,7 +3164,6 @@ DATA(insert OID = 2273 (  has_schema_privilege		   PGNSP PGUID 12 f f t f s 2 16
 DESCR("current user privilege on schema by schema oid");
 
 
-
 DATA(insert OID = 2290 (  record_in			PGNSP PGUID 12 f f t f i 1 2249 "2275"	record_in - _null_ ));
 DESCR("I/O");
 DATA(insert OID = 2291 (  record_out		PGNSP PGUID 12 f f t f i 1 2275 "2249"	record_out - _null_ ));
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 2312c0562e3..669ce93e70a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.69 2003/11/09 21:30:37 tgl Exp $
+ * $Id: plannodes.h,v 1.70 2003/11/12 21:15:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -165,6 +165,7 @@ typedef struct IndexScan
 	List	   *indxqual;		/* list of sublists of index quals */
 	List	   *indxqualorig;	/* the same in original form */
 	List	   *indxstrategy;	/* list of sublists of strategy numbers */
+	List	   *indxsubtype;	/* list of sublists of strategy subtypes */
 	ScanDirection indxorderdir;	/* forward or backward or don't care */
 } IndexScan;
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 71615b6610f..60255311477 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.228 2003/08/17 19:58:06 tgl Exp $
+ * $Id: builtins.h,v 1.229 2003/11/12 21:15:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -219,6 +219,14 @@ extern Datum btint4cmp(PG_FUNCTION_ARGS);
 extern Datum btint8cmp(PG_FUNCTION_ARGS);
 extern Datum btfloat4cmp(PG_FUNCTION_ARGS);
 extern Datum btfloat8cmp(PG_FUNCTION_ARGS);
+extern Datum btint48cmp(PG_FUNCTION_ARGS);
+extern Datum btint84cmp(PG_FUNCTION_ARGS);
+extern Datum btint24cmp(PG_FUNCTION_ARGS);
+extern Datum btint42cmp(PG_FUNCTION_ARGS);
+extern Datum btint28cmp(PG_FUNCTION_ARGS);
+extern Datum btint82cmp(PG_FUNCTION_ARGS);
+extern Datum btfloat48cmp(PG_FUNCTION_ARGS);
+extern Datum btfloat84cmp(PG_FUNCTION_ARGS);
 extern Datum btoidcmp(PG_FUNCTION_ARGS);
 extern Datum btoidvectorcmp(PG_FUNCTION_ARGS);
 extern Datum btabstimecmp(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index e029c136b85..8b945885095 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: geo_decls.h,v 1.41 2003/08/04 02:40:15 momjian Exp $
+ * $Id: geo_decls.h,v 1.42 2003/11/12 21:15:59 tgl Exp $
  *
  * NOTE
  *	  These routines do *not* use the float types from adt/.
@@ -399,7 +399,6 @@ extern Datum circle_area(PG_FUNCTION_ARGS);
 extern Datum rt_box_union(PG_FUNCTION_ARGS);
 extern Datum rt_box_inter(PG_FUNCTION_ARGS);
 extern Datum rt_box_size(PG_FUNCTION_ARGS);
-extern Datum rt_bigbox_size(PG_FUNCTION_ARGS);
 extern Datum rt_poly_size(PG_FUNCTION_ARGS);
 extern Datum rt_poly_union(PG_FUNCTION_ARGS);
 extern Datum rt_poly_inter(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 36146f23d11..2f017db844a 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.83 2003/11/09 21:30:38 tgl Exp $
+ * $Id: lsyscache.h,v 1.84 2003/11/12 21:15:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,10 +26,11 @@ typedef enum IOFuncSelector
 
 extern bool op_in_opclass(Oid opno, Oid opclass);
 extern void get_op_opclass_properties(Oid opno, Oid opclass,
-									  int *strategy, bool *recheck);
-extern Oid	get_opclass_member(Oid opclass, int16 strategy);
+									  int *strategy, Oid *subtype,
+									  bool *recheck);
+extern Oid	get_opclass_member(Oid opclass, Oid subtype, int16 strategy);
 extern Oid	get_op_hash_function(Oid opno);
-extern Oid	get_opclass_proc(Oid opclass, int16 procnum);
+extern Oid	get_opclass_proc(Oid opclass, Oid subtype, int16 procnum);
 extern char *get_attname(Oid relid, AttrNumber attnum);
 extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f08d6c0f1f8..eb24cf839ff 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.69 2003/11/09 21:30:38 tgl Exp $
+ * $Id: rel.h,v 1.70 2003/11/12 21:15:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -139,8 +139,8 @@ typedef struct RelationData
 	/*
 	 * index access support info (used only for an index relation)
 	 *
-	 * Note: only operators and support procs for the index's own datatype
-	 * are cached, not any cross-type operators.  The arrays are indexed by
+	 * Note: only default operators and support procs for each opclass are
+	 * cached, namely those with subtype zero.  The arrays are indexed by
 	 * strategy or support number, which is a sufficient identifier given
 	 * that restriction.
 	 */
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 464ab8d7e8c..46d2657d015 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -47,7 +47,7 @@ CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
 -- in the regression test (we check them using the sequoia 2000
 -- benchmark).
 --
-CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base bigbox_ops);
+CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base);
 -- there's no easy way to check that this command actually is using
 -- the index, unfortunately.  (EXPLAIN would work, but its output
 -- changes too often for me to want to put an EXPLAIN in the test...)
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 0bdecf5ed1b..21628b6e69b 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -129,6 +129,14 @@ WHERE	amopclaid != 0 AND
 ------+-----------
 (0 rows)
 
+SELECT	ctid, amopsubtype 
+FROM	pg_catalog.pg_amop fk 
+WHERE	amopsubtype != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amopsubtype);
+ ctid | amopsubtype 
+------+-------------
+(0 rows)
+
 SELECT	ctid, amopopr 
 FROM	pg_catalog.pg_amop fk 
 WHERE	amopopr != 0 AND 
@@ -145,6 +153,14 @@ WHERE	amopclaid != 0 AND
 ------+-----------
 (0 rows)
 
+SELECT	ctid, amprocsubtype 
+FROM	pg_catalog.pg_amproc fk 
+WHERE	amprocsubtype != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amprocsubtype);
+ ctid | amprocsubtype 
+------+---------------
+(0 rows)
+
 SELECT	ctid, amproc 
 FROM	pg_catalog.pg_amproc fk 
 WHERE	amproc != 0 AND 
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 985e06595e7..4e2c76de5c2 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -685,30 +685,61 @@ WHERE p1.amopclaid = p3.oid AND p3.opcamid = p2.oid AND
 -----------+---------+-----+--------
 (0 rows)
 
--- Detect missing pg_amop entries: should have as many strategy functions
--- as AM expects for each opclass for the AM
-SELECT p1.oid, p1.amname, p2.oid, p2.opcname
-FROM pg_am AS p1, pg_opclass AS p2
-WHERE p2.opcamid = p1.oid AND
-    p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
-                        WHERE p3.amopclaid = p2.oid);
- oid | amname | oid | opcname 
------+--------+-----+---------
+-- Detect missing pg_amop entries: should have as many strategy operators
+-- as AM expects for each opclass for the AM.  When nondefault subtypes are
+-- present, enforce condition separately for each subtype.
+SELECT p1.oid, p1.amname, p2.oid, p2.opcname, p3.amopsubtype
+FROM pg_am AS p1, pg_opclass AS p2, pg_amop AS p3
+WHERE p2.opcamid = p1.oid AND p3.amopclaid = p2.oid AND
+    p1.amstrategies != (SELECT count(*) FROM pg_amop AS p4
+                        WHERE p4.amopclaid = p2.oid AND
+                              p4.amopsubtype = p3.amopsubtype);
+ oid | amname | oid | opcname | amopsubtype 
+-----+--------+-----+---------+-------------
 (0 rows)
 
 -- Check that amopopr points at a reasonable-looking operator, ie a binary
 -- operator yielding boolean.
--- NOTE: for 7.1, add restriction that operator inputs are of same type.
--- We used to have opclasses like "int24_ops" but these were broken.
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname
 FROM pg_amop AS p1, pg_operator AS p2
 WHERE p1.amopopr = p2.oid AND
-    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR
-     p2.oprleft != p2.oprright);
+    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
  amopclaid | amopopr | oid | oprname 
 -----------+---------+-----+---------
 (0 rows)
 
+-- Make a list of all the distinct operator names being used in particular
+-- strategy slots.  This is a bit hokey, since the list might need to change
+-- in future releases, but it's an effective way of spotting mistakes such as
+-- swapping two operators within a class.
+SELECT DISTINCT opcamid, amopstrategy, oprname
+FROM pg_amop p1 LEFT JOIN pg_opclass p2 ON amopclaid = p2.oid
+                LEFT JOIN pg_operator p3 ON amopopr = p3.oid
+ORDER BY 1, 2, 3;
+ opcamid | amopstrategy | oprname 
+---------+--------------+---------
+     402 |            1 | <<
+     402 |            2 | &<
+     402 |            3 | &&
+     402 |            4 | &>
+     402 |            5 | >>
+     402 |            6 | ~=
+     402 |            7 | ~
+     402 |            8 | @
+     403 |            1 | <
+     403 |            1 | ~<~
+     403 |            2 | <=
+     403 |            2 | ~<=~
+     403 |            3 | =
+     403 |            3 | ~=~
+     403 |            4 | >=
+     403 |            4 | ~>=~
+     403 |            5 | >
+     403 |            5 | ~>~
+     405 |            1 | =
+     405 |            1 | ~=~
+(20 rows)
+
 -- Check that all operators linked to by opclass entries have selectivity
 -- estimators.  This is not absolutely required, but it seems a reasonable
 -- thing to insist on for all standard datatypes.
@@ -721,11 +752,31 @@ WHERE p1.amopopr = p2.oid AND
 (0 rows)
 
 -- Check that operator input types match the opclass
+-- For 7.5, we require that oprleft match opcintype (possibly by coercion).
+-- When amopsubtype is zero (default), oprright must equal oprleft;
+-- when amopsubtype is not zero, oprright must equal amopsubtype.
+SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
+FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
+WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
+    NOT binary_coercible(p3.opcintype, p2.oprleft);
+ amopclaid | amopopr | oid | oprname | opcname 
+-----------+---------+-----+---------+---------
+(0 rows)
+
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
-     p2.oprleft != p2.oprright);
+    p1.amopsubtype = 0 AND
+    p2.oprleft != p2.oprright;
+ amopclaid | amopopr | oid | oprname | opcname 
+-----------+---------+-----+---------+---------
+(0 rows)
+
+SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
+FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
+WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
+    p1.amopsubtype != 0 AND
+    p1.amopsubtype != p2.oprright;
  amopclaid | amopopr | oid | oprname | opcname 
 -----------+---------+-----+---------+---------
 (0 rows)
@@ -749,14 +800,16 @@ WHERE p1.amopclaid = p3.oid AND p3.opcamid = p2.oid AND
 (0 rows)
 
 -- Detect missing pg_amproc entries: should have as many support functions
--- as AM expects for each opclass for the AM
-SELECT p1.oid, p1.amname, p2.oid, p2.opcname
-FROM pg_am AS p1, pg_opclass AS p2
-WHERE p2.opcamid = p1.oid AND
-    p1.amsupport != (SELECT count(*) FROM pg_amproc AS p3
-                     WHERE p3.amopclaid = p2.oid);
- oid | amname | oid | opcname 
------+--------+-----+---------
+-- as AM expects for each opclass for the AM.  When nondefault subtypes are
+-- present, enforce condition separately for each subtype.
+SELECT p1.oid, p1.amname, p2.oid, p2.opcname, p3.amprocsubtype
+FROM pg_am AS p1, pg_opclass AS p2, pg_amproc AS p3
+WHERE p2.opcamid = p1.oid AND p3.amopclaid = p2.oid AND
+    p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
+                     WHERE p4.amopclaid = p2.oid AND
+                           p4.amprocsubtype = p3.amprocsubtype);
+ oid | amname | oid | opcname | amprocsubtype 
+-----+--------+-----+---------+---------------
 (0 rows)
 
 -- Unfortunately, we can't check the amproc link very well because the
@@ -782,13 +835,15 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
 (0 rows)
 
 -- For btree, though, we can do better since we know the support routines
--- must be of the form cmp(input, input) returns int4.
+-- must be of the form cmp(input, input) returns int4 in the default case
+-- (subtype = 0), and cmp(input, subtype) returns int4 when subtype != 0.
 SELECT p1.amopclaid, p1.amprocnum,
 	p2.oid, p2.proname,
 	p3.opcname
 FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
 WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
     AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    amprocsubtype = 0 AND
     (opckeytype != 0
      OR amprocnum != 1
      OR proretset
@@ -800,6 +855,24 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
 -----------+-----------+-----+---------+---------
 (0 rows)
 
+SELECT p1.amopclaid, p1.amprocnum,
+	p2.oid, p2.proname,
+	p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    amprocsubtype != 0 AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[1] != amprocsubtype);
+ amopclaid | amprocnum | oid | proname | opcname 
+-----------+-----------+-----+---------+---------
+(0 rows)
+
 -- For hash we can also do a little better: the support routines must be
 -- of the form hash(something) returns int4.  Ideally we'd check that the
 -- opcintype is binary-coercible to the function's input, but there are
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 8400a3bb333..d904bacd66b 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -68,7 +68,7 @@ CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
 -- in the regression test (we check them using the sequoia 2000
 -- benchmark).
 --
-CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base bigbox_ops);
+CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base);
 
 -- there's no easy way to check that this command actually is using
 -- the index, unfortunately.  (EXPLAIN would work, but its output
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index 5e8323bc11b..d822aaf4e55 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -65,6 +65,10 @@ SELECT	ctid, amopclaid
 FROM	pg_catalog.pg_amop fk 
 WHERE	amopclaid != 0 AND 
 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opclass pk WHERE pk.oid = fk.amopclaid);
+SELECT	ctid, amopsubtype 
+FROM	pg_catalog.pg_amop fk 
+WHERE	amopsubtype != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amopsubtype);
 SELECT	ctid, amopopr 
 FROM	pg_catalog.pg_amop fk 
 WHERE	amopopr != 0 AND 
@@ -73,6 +77,10 @@ SELECT	ctid, amopclaid
 FROM	pg_catalog.pg_amproc fk 
 WHERE	amopclaid != 0 AND 
 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opclass pk WHERE pk.oid = fk.amopclaid);
+SELECT	ctid, amprocsubtype 
+FROM	pg_catalog.pg_amproc fk 
+WHERE	amprocsubtype != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amprocsubtype);
 SELECT	ctid, amproc 
 FROM	pg_catalog.pg_amproc fk 
 WHERE	amproc != 0 AND 
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9cba48b4296..448f3ff0c5b 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -574,25 +574,34 @@ FROM pg_amop AS p1, pg_am AS p2, pg_opclass AS p3
 WHERE p1.amopclaid = p3.oid AND p3.opcamid = p2.oid AND
     p1.amopstrategy > p2.amstrategies;
 
--- Detect missing pg_amop entries: should have as many strategy functions
--- as AM expects for each opclass for the AM
+-- Detect missing pg_amop entries: should have as many strategy operators
+-- as AM expects for each opclass for the AM.  When nondefault subtypes are
+-- present, enforce condition separately for each subtype.
 
-SELECT p1.oid, p1.amname, p2.oid, p2.opcname
-FROM pg_am AS p1, pg_opclass AS p2
-WHERE p2.opcamid = p1.oid AND
-    p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
-                        WHERE p3.amopclaid = p2.oid);
+SELECT p1.oid, p1.amname, p2.oid, p2.opcname, p3.amopsubtype
+FROM pg_am AS p1, pg_opclass AS p2, pg_amop AS p3
+WHERE p2.opcamid = p1.oid AND p3.amopclaid = p2.oid AND
+    p1.amstrategies != (SELECT count(*) FROM pg_amop AS p4
+                        WHERE p4.amopclaid = p2.oid AND
+                              p4.amopsubtype = p3.amopsubtype);
 
 -- Check that amopopr points at a reasonable-looking operator, ie a binary
 -- operator yielding boolean.
--- NOTE: for 7.1, add restriction that operator inputs are of same type.
--- We used to have opclasses like "int24_ops" but these were broken.
 
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname
 FROM pg_amop AS p1, pg_operator AS p2
 WHERE p1.amopopr = p2.oid AND
-    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR
-     p2.oprleft != p2.oprright);
+    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype);
+
+-- Make a list of all the distinct operator names being used in particular
+-- strategy slots.  This is a bit hokey, since the list might need to change
+-- in future releases, but it's an effective way of spotting mistakes such as
+-- swapping two operators within a class.
+
+SELECT DISTINCT opcamid, amopstrategy, oprname
+FROM pg_amop p1 LEFT JOIN pg_opclass p2 ON amopclaid = p2.oid
+                LEFT JOIN pg_operator p3 ON amopopr = p3.oid
+ORDER BY 1, 2, 3;
 
 -- Check that all operators linked to by opclass entries have selectivity
 -- estimators.  This is not absolutely required, but it seems a reasonable
@@ -604,12 +613,26 @@ WHERE p1.amopopr = p2.oid AND
     (p2.oprrest = 0 OR p2.oprjoin = 0);
 
 -- Check that operator input types match the opclass
+-- For 7.5, we require that oprleft match opcintype (possibly by coercion).
+-- When amopsubtype is zero (default), oprright must equal oprleft;
+-- when amopsubtype is not zero, oprright must equal amopsubtype.
+
+SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
+FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
+WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
+    NOT binary_coercible(p3.opcintype, p2.oprleft);
 
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
-     p2.oprleft != p2.oprright);
+    p1.amopsubtype = 0 AND
+    p2.oprleft != p2.oprright;
+
+SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
+FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
+WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
+    p1.amopsubtype != 0 AND
+    p1.amopsubtype != p2.oprright;
 
 -- **************** pg_amproc ****************
 
@@ -627,13 +650,15 @@ WHERE p1.amopclaid = p3.oid AND p3.opcamid = p2.oid AND
     p1.amprocnum > p2.amsupport;
 
 -- Detect missing pg_amproc entries: should have as many support functions
--- as AM expects for each opclass for the AM
+-- as AM expects for each opclass for the AM.  When nondefault subtypes are
+-- present, enforce condition separately for each subtype.
 
-SELECT p1.oid, p1.amname, p2.oid, p2.opcname
-FROM pg_am AS p1, pg_opclass AS p2
-WHERE p2.opcamid = p1.oid AND
-    p1.amsupport != (SELECT count(*) FROM pg_amproc AS p3
-                     WHERE p3.amopclaid = p2.oid);
+SELECT p1.oid, p1.amname, p2.oid, p2.opcname, p3.amprocsubtype
+FROM pg_am AS p1, pg_opclass AS p2, pg_amproc AS p3
+WHERE p2.opcamid = p1.oid AND p3.amopclaid = p2.oid AND
+    p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
+                     WHERE p4.amopclaid = p2.oid AND
+                           p4.amprocsubtype = p3.amprocsubtype);
 
 -- Unfortunately, we can't check the amproc link very well because the
 -- signature of the function may be different for different support routines
@@ -656,7 +681,8 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
     (p2.proretset OR p5.proretset OR p2.pronargs != p5.pronargs);
 
 -- For btree, though, we can do better since we know the support routines
--- must be of the form cmp(input, input) returns int4.
+-- must be of the form cmp(input, input) returns int4 in the default case
+-- (subtype = 0), and cmp(input, subtype) returns int4 when subtype != 0.
 
 SELECT p1.amopclaid, p1.amprocnum,
 	p2.oid, p2.proname,
@@ -664,6 +690,7 @@ SELECT p1.amopclaid, p1.amprocnum,
 FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
 WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
     AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    amprocsubtype = 0 AND
     (opckeytype != 0
      OR amprocnum != 1
      OR proretset
@@ -672,6 +699,21 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
      OR NOT binary_coercible(opcintype, proargtypes[0])
      OR proargtypes[0] != proargtypes[1]);
 
+SELECT p1.amopclaid, p1.amprocnum,
+	p2.oid, p2.proname,
+	p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    amprocsubtype != 0 AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[1] != amprocsubtype);
+
 -- For hash we can also do a little better: the support routines must be
 -- of the form hash(something) returns int4.  Ideally we'd check that the
 -- opcintype is binary-coercible to the function's input, but there are
-- 
GitLab