]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/cache/lsyscache.c
Create a "sort support" interface API for faster sorting.
[postgresql] / src / backend / utils / cache / lsyscache.c
index 40a74594facac48c8e3ea94a6dfce702e3a85302..7a4306e93f5545764cbd02e6aa042120b385790c 100644 (file)
@@ -3,11 +3,11 @@
  * lsyscache.c
  *       Convenience routines for common queries in the system catalog cache.
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.143 2007/01/10 18:06:04 tgl Exp $
+ *       src/backend/utils/cache/lsyscache.c
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
+
+/* Hook for plugins to get control in get_attavgwidth() */
+get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
 
 
 /*                             ---------- AMOP CACHES ----------                                                */
  * op_in_opfamily
  *
  *             Return t iff operator 'opno' is in operator family 'opfamily'.
+ *
+ * This function only considers search operators, not ordering operators.
  */
 bool
 op_in_opfamily(Oid opno, Oid opfamily)
 {
-       return SearchSysCacheExists(AMOPOPID,
-                                                               ObjectIdGetDatum(opno),
-                                                               ObjectIdGetDatum(opfamily),
-                                                               0, 0);
+       return SearchSysCacheExists3(AMOPOPID,
+                                                                ObjectIdGetDatum(opno),
+                                                                CharGetDatum(AMOP_SEARCH),
+                                                                ObjectIdGetDatum(opfamily));
 }
 
 /*
@@ -56,6 +67,8 @@ op_in_opfamily(Oid opno, Oid opfamily)
  *
  *             Get the operator's strategy number within the specified opfamily,
  *             or 0 if it's not a member of the opfamily.
+ *
+ * This function only considers search operators, not ordering operators.
  */
 int
 get_op_opfamily_strategy(Oid opno, Oid opfamily)
@@ -64,10 +77,10 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily)
        Form_pg_amop amop_tup;
        int                     result;
 
-       tp = SearchSysCache(AMOPOPID,
-                                               ObjectIdGetDatum(opno),
-                                               ObjectIdGetDatum(opfamily),
-                                               0, 0);
+       tp = SearchSysCache3(AMOPOPID,
+                                                ObjectIdGetDatum(opno),
+                                                CharGetDatum(AMOP_SEARCH),
+                                                ObjectIdGetDatum(opfamily));
        if (!HeapTupleIsValid(tp))
                return 0;
        amop_tup = (Form_pg_amop) GETSTRUCT(tp);
@@ -76,29 +89,53 @@ get_op_opfamily_strategy(Oid opno, Oid opfamily)
        return result;
 }
 
+/*
+ * get_op_opfamily_sortfamily
+ *
+ *             If the operator is an ordering operator within the specified opfamily,
+ *             return its amopsortfamily OID; else return InvalidOid.
+ */
+Oid
+get_op_opfamily_sortfamily(Oid opno, Oid opfamily)
+{
+       HeapTuple       tp;
+       Form_pg_amop amop_tup;
+       Oid                     result;
+
+       tp = SearchSysCache3(AMOPOPID,
+                                                ObjectIdGetDatum(opno),
+                                                CharGetDatum(AMOP_ORDER),
+                                                ObjectIdGetDatum(opfamily));
+       if (!HeapTupleIsValid(tp))
+               return InvalidOid;
+       amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+       result = amop_tup->amopsortfamily;
+       ReleaseSysCache(tp);
+       return result;
+}
+
 /*
  * get_op_opfamily_properties
  *
- *             Get the operator's strategy number, input types, and recheck (lossy)
- *             flag within the specified opfamily.
+ *             Get the operator's strategy number and declared input data types
+ *             within the specified opfamily.
  *
  * Caller should already have verified that opno is a member of opfamily,
  * therefore we raise an error if the tuple is not found.
  */
 void
-get_op_opfamily_properties(Oid opno, Oid opfamily,
+get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op,
                                                   int *strategy,
                                                   Oid *lefttype,
-                                                  Oid *righttype,
-                                                  bool *recheck)
+                                                  Oid *righttype)
 {
        HeapTuple       tp;
        Form_pg_amop amop_tup;
 
-       tp = SearchSysCache(AMOPOPID,
-                                               ObjectIdGetDatum(opno),
-                                               ObjectIdGetDatum(opfamily),
-                                               0, 0);
+       tp = SearchSysCache3(AMOPOPID,
+                                                ObjectIdGetDatum(opno),
+                                                CharGetDatum(ordering_op ? AMOP_ORDER : AMOP_SEARCH),
+                                                ObjectIdGetDatum(opfamily));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "operator %u is not a member of opfamily %u",
                         opno, opfamily);
@@ -106,7 +143,6 @@ get_op_opfamily_properties(Oid opno, Oid opfamily,
        *strategy = amop_tup->amopstrategy;
        *lefttype = amop_tup->amoplefttype;
        *righttype = amop_tup->amoprighttype;
-       *recheck = amop_tup->amopreqcheck;
        ReleaseSysCache(tp);
 }
 
@@ -125,11 +161,11 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
        Form_pg_amop amop_tup;
        Oid                     result;
 
-       tp = SearchSysCache(AMOPSTRATEGY,
-                                               ObjectIdGetDatum(opfamily),
-                                               ObjectIdGetDatum(lefttype),
-                                               ObjectIdGetDatum(righttype),
-                                               Int16GetDatum(strategy));
+       tp = SearchSysCache4(AMOPSTRATEGY,
+                                                ObjectIdGetDatum(opfamily),
+                                                ObjectIdGetDatum(lefttype),
+                                                ObjectIdGetDatum(righttype),
+                                                Int16GetDatum(strategy));
        if (!HeapTupleIsValid(tp))
                return InvalidOid;
        amop_tup = (Form_pg_amop) GETSTRUCT(tp);
@@ -139,76 +175,63 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
 }
 
 /*
- * get_op_mergejoin_info
- *             Given the OIDs of a (putatively) mergejoinable equality operator
- *             and a sortop defining the sort ordering of the lefthand input of
- *             the merge clause, determine whether this sort ordering is actually
- *             usable for merging.  If so, return the required sort ordering op
- *             for the righthand input, as well as the btree opfamily OID containing
- *             these operators and the operator strategy number of the two sortops
- *             (either BTLessStrategyNumber or BTGreaterStrategyNumber).
+ * get_ordering_op_properties
+ *             Given the OID of an ordering operator (a btree "<" or ">" operator),
+ *             determine its opfamily, its declared input datatype, and its
+ *             strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber).
+ *
+ * Returns TRUE if successful, FALSE if no matching pg_amop entry exists.
+ * (This indicates that the operator is not a valid ordering operator.)
  *
- * We can mergejoin if we find the two operators in the same opfamily as
- * equality and either less-than or greater-than respectively.  If there
- * are multiple such opfamilies, assume we can use any one.
+ * Note: the operator could be registered in multiple families, for example
+ * if someone were to build a "reverse sort" opfamily. This would result in
+ * uncertainty as to whether "ORDER BY USING op" would default to NULLS FIRST
+ * or NULLS LAST, as well as inefficient planning due to failure to match up
+ * pathkeys that should be the same.  So we want a determinate result here.
+ * Because of the way the syscache search works, we'll use the interpretation
+ * associated with the opfamily with smallest OID, which is probably
+ * determinate enough. Since there is no longer any particularly good reason
+ * to build reverse-sort opfamilies, it doesn't seem worth expending any
+ * additional effort on ensuring consistency.
  */
-#ifdef NOT_YET
-/* eventually should look like this */
 bool
-get_op_mergejoin_info(Oid eq_op, Oid left_sortop,
-                                         Oid *right_sortop, Oid *opfamily, int *opstrategy)
+get_ordering_op_properties(Oid opno,
+                                                  Oid *opfamily, Oid *opcintype, int16 *strategy)
 {
        bool            result = false;
-       Oid                     lefttype;
-       Oid                     righttype;
        CatCList   *catlist;
        int                     i;
 
-       /* Make sure output args are initialized even on failure */
-       *right_sortop = InvalidOid;
+       /* ensure outputs are initialized on failure */
        *opfamily = InvalidOid;
-       *opstrategy = 0;
-
-       /* Need the righthand input datatype */
-       op_input_types(eq_op, &lefttype, &righttype);
+       *opcintype = InvalidOid;
+       *strategy = 0;
 
        /*
-        * Search through all the pg_amop entries containing the equality operator
+        * Search pg_amop to see if the target operator is registered as the "<"
+        * or ">" operator of any btree opfamily.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(eq_op),
-                                                                0, 0, 0);
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
 
        for (i = 0; i < catlist->n_members; i++)
        {
-               HeapTuple       op_tuple = &catlist->members[i]->tuple;
-               Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
-               Oid                     opfamily_id;
-               StrategyNumber op_strategy;
+               HeapTuple       tuple = &catlist->members[i]->tuple;
+               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
 
                /* must be btree */
-               if (op_form->amopmethod != BTREE_AM_OID)
-                       continue;
-               /* must use the operator as equality */
-               if (op_form->amopstrategy != BTEqualStrategyNumber)
+               if (aform->amopmethod != BTREE_AM_OID)
                        continue;
 
-               /* See if sort operator is also in this opfamily with OK semantics */
-               opfamily_id = op_form->amopfamily;
-               op_strategy = get_op_opfamily_strategy(left_sortop, opfamily_id);
-               if (op_strategy == BTLessStrategyNumber ||
-                       op_strategy == BTGreaterStrategyNumber)
+               if (aform->amopstrategy == BTLessStrategyNumber ||
+                       aform->amopstrategy == BTGreaterStrategyNumber)
                {
-                       /* Yes, so find the corresponding righthand sortop */
-                       *right_sortop = get_opfamily_member(opfamily_id,
-                                                                                               righttype,
-                                                                                               righttype,
-                                                                                               op_strategy);
-                       if (OidIsValid(*right_sortop))
+                       /* Found it ... should have consistent input types */
+                       if (aform->amoplefttype == aform->amoprighttype)
                        {
-                               /* Found a workable mergejoin semantics */
-                               *opfamily = opfamily_id;
-                               *opstrategy = op_strategy;
+                               /* Found a suitable opfamily, return info */
+                               *opfamily = aform->amopfamily;
+                               *opcintype = aform->amoplefttype;
+                               *strategy = aform->amopstrategy;
                                result = true;
                                break;
                        }
@@ -219,142 +242,61 @@ get_op_mergejoin_info(Oid eq_op, Oid left_sortop,
 
        return result;
 }
-#else
-/* temp implementation until planner gets smarter: left_sortop is output */
-bool
-get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
-                                         Oid *right_sortop, Oid *opfamily)
-{
-       bool            result = false;
-       Oid                     lefttype;
-       Oid                     righttype;
-       CatCList   *catlist;
-       int                     i;
-
-       /* Make sure output args are initialized even on failure */
-       *left_sortop = InvalidOid;
-       *right_sortop = InvalidOid;
-       *opfamily = InvalidOid;
-
-       /* Need the input datatypes */
-       op_input_types(eq_op, &lefttype, &righttype);
-
-       /*
-        * Search through all the pg_amop entries containing the equality operator
-        */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(eq_op),
-                                                                0, 0, 0);
-
-       for (i = 0; i < catlist->n_members; i++)
-       {
-               HeapTuple       op_tuple = &catlist->members[i]->tuple;
-               Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
-               Oid                     opfamily_id;
-
-               /* must be btree */
-               if (op_form->amopmethod != BTREE_AM_OID)
-                       continue;
-               /* must use the operator as equality */
-               if (op_form->amopstrategy != BTEqualStrategyNumber)
-                       continue;
-
-               opfamily_id = op_form->amopfamily;
-
-               /* Find the matching sortops */
-               *left_sortop = get_opfamily_member(opfamily_id,
-                                                                                  lefttype,
-                                                                                  lefttype,
-                                                                                  BTLessStrategyNumber);
-               *right_sortop = get_opfamily_member(opfamily_id,
-                                                                                       righttype,
-                                                                                       righttype,
-                                                                                       BTLessStrategyNumber);
-               if (OidIsValid(*left_sortop) && OidIsValid(*right_sortop))
-               {
-                       /* Found a workable mergejoin semantics */
-                       *opfamily = opfamily_id;
-                       result = true;
-                       break;
-               }
-       }
-
-       ReleaseSysCacheList(catlist);
-
-       return result;
-}
-#endif
 
 /*
- * get_compare_function_for_ordering_op
- *             Get the OID of the datatype-specific btree comparison function
+ * get_sort_function_for_ordering_op
+ *             Get the OID of the datatype-specific btree sort support function,
+ *             or if there is none, the btree comparison function,
  *             associated with an ordering operator (a "<" or ">" operator).
  *
- * *cmpfunc receives the comparison function OID.
+ * *sortfunc receives the support or comparison function OID.
+ * *issupport is set TRUE if it's a support func, FALSE if a comparison func.
  * *reverse is set FALSE if the operator is "<", TRUE if it's ">"
- * (indicating the comparison result must be negated before use).
+ * (indicating that comparison results must be negated before use).
  *
  * Returns TRUE if successful, FALSE if no btree function can be found.
  * (This indicates that the operator is not a valid ordering operator.)
  */
 bool
-get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
+get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc,
+                                                                 bool *issupport, bool *reverse)
 {
-       bool            result = false;
-       CatCList   *catlist;
-       int                     i;
-
-       /* ensure outputs are set on failure */
-       *cmpfunc = InvalidOid;
-       *reverse = false;
-
-       /*
-        * Search pg_amop to see if the target operator is registered as the "<"
-        * or ">" operator of any btree opfamily.  It's possible that it might be
-        * registered both ways (if someone were to build a "reverse sort"
-        * opfamily); assume we can use either interpretation.  (Note: the
-        * existence of a reverse-sort opfamily would result in uncertainty as
-        * to whether "ORDER BY USING op" would default to NULLS FIRST or NULLS
-        * LAST.  Since there is no longer any particularly good reason to build
-        * reverse-sort opfamilies, we don't bother expending any extra work to
-        * make this more determinate.  In practice, because of the way the
-        * syscache search works, we'll use the interpretation associated with
-        * the opfamily with smallest OID, which is probably determinate enough.)
-        */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
+       Oid                     opfamily;
+       Oid                     opcintype;
+       int16           strategy;
 
-       for (i = 0; i < catlist->n_members; i++)
+       /* Find the operator in pg_amop */
+       if (get_ordering_op_properties(opno,
+                                                                  &opfamily, &opcintype, &strategy))
        {
-               HeapTuple       tuple = &catlist->members[i]->tuple;
-               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
-
-               /* must be btree */
-               if (aform->amopmethod != BTREE_AM_OID)
-                       continue;
-
-               if (aform->amopstrategy == BTLessStrategyNumber ||
-                       aform->amopstrategy == BTGreaterStrategyNumber)
+               /* Found a suitable opfamily, get matching support function */
+               *sortfunc = get_opfamily_proc(opfamily,
+                                                                         opcintype,
+                                                                         opcintype,
+                                                                         BTSORTSUPPORT_PROC);
+               if (OidIsValid(*sortfunc))
+                       *issupport = true;
+               else
                {
-                       /* Found a suitable opfamily, get matching support function */
-                       *reverse = (aform->amopstrategy == BTGreaterStrategyNumber);
-                       *cmpfunc = get_opfamily_proc(aform->amopfamily,
-                                                                                aform->amoplefttype,
-                                                                                aform->amoprighttype,
-                                                                                BTORDER_PROC);
-                       if (!OidIsValid(*cmpfunc))                              /* should not happen */
+                       /* opfamily doesn't provide sort support, get comparison func */
+                       *sortfunc = get_opfamily_proc(opfamily,
+                                                                                 opcintype,
+                                                                                 opcintype,
+                                                                                 BTORDER_PROC);
+                       if (!OidIsValid(*sortfunc))             /* should not happen */
                                elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
-                                        BTORDER_PROC, aform->amoplefttype, aform->amoprighttype,
-                                        aform->amopfamily);
-                       result = true;
-                       break;
+                                        BTORDER_PROC, opcintype, opcintype, opfamily);
+                       *issupport = false;
                }
+               *reverse = (strategy == BTGreaterStrategyNumber);
+               return true;
        }
 
-       ReleaseSysCacheList(catlist);
-
-       return result;
+       /* ensure outputs are set on failure */
+       *sortfunc = InvalidOid;
+       *issupport = false;
+       *reverse = false;
+       return false;
 }
 
 /*
@@ -362,52 +304,33 @@ get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
  *             Get the OID of the datatype-specific btree equality operator
  *             associated with an ordering operator (a "<" or ">" operator).
  *
+ * If "reverse" isn't NULL, also set *reverse to FALSE if the operator is "<",
+ * TRUE if it's ">"
+ *
  * Returns InvalidOid if no matching equality operator can be found.
  * (This indicates that the operator is not a valid ordering operator.)
  */
 Oid
-get_equality_op_for_ordering_op(Oid opno)
+get_equality_op_for_ordering_op(Oid opno, bool *reverse)
 {
        Oid                     result = InvalidOid;
-       CatCList   *catlist;
-       int                     i;
-
-       /*
-        * Search pg_amop to see if the target operator is registered as the "<"
-        * or ">" operator of any btree opfamily.  This is exactly like
-        * get_compare_function_for_ordering_op except we don't care whether the
-        * ordering op is "<" or ">" ... the equality operator will be the same
-        * either way.
-        */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
+       Oid                     opfamily;
+       Oid                     opcintype;
+       int16           strategy;
 
-       for (i = 0; i < catlist->n_members; i++)
+       /* Find the operator in pg_amop */
+       if (get_ordering_op_properties(opno,
+                                                                  &opfamily, &opcintype, &strategy))
        {
-               HeapTuple       tuple = &catlist->members[i]->tuple;
-               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
-
-               /* must be btree */
-               if (aform->amopmethod != BTREE_AM_OID)
-                       continue;
-
-               if (aform->amopstrategy == BTLessStrategyNumber ||
-                       aform->amopstrategy == BTGreaterStrategyNumber)
-               {
-                       /* Found a suitable opfamily, get matching equality operator */
-                       result = get_opfamily_member(aform->amopfamily,
-                                                                                aform->amoplefttype,
-                                                                                aform->amoprighttype,
-                                                                                BTEqualStrategyNumber);
-                       if (OidIsValid(result))
-                               break;
-                       /* failure probably shouldn't happen, but keep looking if so */
-               }
+               /* Found a suitable opfamily, get matching equality operator */
+               result = get_opfamily_member(opfamily,
+                                                                        opcintype,
+                                                                        opcintype,
+                                                                        BTEqualStrategyNumber);
+               if (reverse)
+                       *reverse = (strategy == BTGreaterStrategyNumber);
        }
 
-       ReleaseSysCacheList(catlist);
-
        return result;
 }
 
@@ -436,9 +359,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
         * Search pg_amop to see if the target operator is registered as the "="
         * operator of any btree opfamily.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
 
        for (i = 0; i < catlist->n_members; i++)
        {
@@ -452,7 +373,7 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
                if (aform->amopstrategy == BTEqualStrategyNumber)
                {
                        /* Found a suitable opfamily, get matching ordering operator */
-                       Oid             typid;
+                       Oid                     typid;
 
                        typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
                        result = get_opfamily_member(aform->amopfamily,
@@ -470,31 +391,88 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
 }
 
 /*
- * get_compatible_hash_operator
- *             Get the OID of a hash equality operator compatible with the given
- *             operator, but operating on its LHS or RHS datatype as specified.
+ * get_mergejoin_opfamilies
+ *             Given a putatively mergejoinable operator, return a list of the OIDs
+ *             of the btree opfamilies in which it represents equality.
  *
- * If the given operator is not cross-type, the result should be the same
- * operator, but in cross-type situations it is different.
+ * It is possible (though at present unusual) for an operator to be equality
+ * in more than one opfamily, hence the result is a list.  This also lets us
+ * return NIL if the operator is not found in any opfamilies.
  *
- * Returns InvalidOid if no compatible operator can be found.  (This indicates
- * that the operator should not have been marked oprcanhash.)
+ * The planner currently uses simple equal() tests to compare the lists
+ * returned by this function, which makes the list order relevant, though
+ * strictly speaking it should not be. Because of the way syscache list
+ * searches are handled, in normal operation the result will be sorted by OID
+ * so everything works fine.  If running with system index usage disabled,
+ * the result ordering is unspecified and hence the planner might fail to
+ * recognize optimization opportunities ... but that's hardly a scenario in
+ * which performance is good anyway, so there's no point in expending code
+ * or cycles here to guarantee the ordering in that case.
  */
-Oid
-get_compatible_hash_operator(Oid opno, bool use_lhs_type)
+List *
+get_mergejoin_opfamilies(Oid opno)
 {
-       Oid                     result = InvalidOid;
+       List       *result = NIL;
+       CatCList   *catlist;
+       int                     i;
+
+       /*
+        * Search pg_amop to see if the target operator is registered as the "="
+        * operator of any btree opfamily.
+        */
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+       for (i = 0; i < catlist->n_members; i++)
+       {
+               HeapTuple       tuple = &catlist->members[i]->tuple;
+               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+               /* must be btree equality */
+               if (aform->amopmethod == BTREE_AM_OID &&
+                       aform->amopstrategy == BTEqualStrategyNumber)
+                       result = lappend_oid(result, aform->amopfamily);
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       return result;
+}
+
+/*
+ * get_compatible_hash_operators
+ *             Get the OID(s) of hash equality operator(s) compatible with the given
+ *             operator, but operating on its LHS and/or RHS datatype.
+ *
+ * An operator for the LHS type is sought and returned into *lhs_opno if
+ * lhs_opno isn't NULL.  Similarly, an operator for the RHS type is sought
+ * and returned into *rhs_opno if rhs_opno isn't NULL.
+ *
+ * If the given operator is not cross-type, the results should be the same
+ * operator, but in cross-type situations they will be different.
+ *
+ * Returns true if able to find the requested operator(s), false if not.
+ * (This indicates that the operator should not have been marked oprcanhash.)
+ */
+bool
+get_compatible_hash_operators(Oid opno,
+                                                         Oid *lhs_opno, Oid *rhs_opno)
+{
+       bool            result = false;
        CatCList   *catlist;
        int                     i;
 
+       /* Ensure output args are initialized on failure */
+       if (lhs_opno)
+               *lhs_opno = InvalidOid;
+       if (rhs_opno)
+               *rhs_opno = InvalidOid;
+
        /*
         * Search pg_amop to see if the target operator is registered as the "="
         * operator of any hash opfamily.  If the operator is registered in
         * multiple opfamilies, assume we can use any one.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
 
        for (i = 0; i < catlist->n_members; i++)
        {
@@ -504,22 +482,54 @@ get_compatible_hash_operator(Oid opno, bool use_lhs_type)
                if (aform->amopmethod == HASH_AM_OID &&
                        aform->amopstrategy == HTEqualStrategyNumber)
                {
-                       /* Found a suitable opfamily, get matching single-type operator */
-                       Oid             typid;
-
                        /* No extra lookup needed if given operator is single-type */
                        if (aform->amoplefttype == aform->amoprighttype)
                        {
-                               result = opno;
+                               if (lhs_opno)
+                                       *lhs_opno = opno;
+                               if (rhs_opno)
+                                       *rhs_opno = opno;
+                               result = true;
                                break;
                        }
-                       typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
-                       result = get_opfamily_member(aform->amopfamily,
-                                                                                typid, typid,
-                                                                                HTEqualStrategyNumber);
-                       if (OidIsValid(result))
+
+                       /*
+                        * Get the matching single-type operator(s).  Failure probably
+                        * shouldn't happen --- it implies a bogus opfamily --- but
+                        * continue looking if so.
+                        */
+                       if (lhs_opno)
+                       {
+                               *lhs_opno = get_opfamily_member(aform->amopfamily,
+                                                                                               aform->amoplefttype,
+                                                                                               aform->amoplefttype,
+                                                                                               HTEqualStrategyNumber);
+                               if (!OidIsValid(*lhs_opno))
+                                       continue;
+                               /* Matching LHS found, done if caller doesn't want RHS */
+                               if (!rhs_opno)
+                               {
+                                       result = true;
+                                       break;
+                               }
+                       }
+                       if (rhs_opno)
+                       {
+                               *rhs_opno = get_opfamily_member(aform->amopfamily,
+                                                                                               aform->amoprighttype,
+                                                                                               aform->amoprighttype,
+                                                                                               HTEqualStrategyNumber);
+                               if (!OidIsValid(*rhs_opno))
+                               {
+                                       /* Forget any LHS operator from this opfamily */
+                                       if (lhs_opno)
+                                               *lhs_opno = InvalidOid;
+                                       continue;
+                               }
+                               /* Matching RHS found, so done */
+                               result = true;
                                break;
-                       /* failure probably shouldn't happen, but keep looking if so */
+                       }
                }
        }
 
@@ -529,32 +539,40 @@ get_compatible_hash_operator(Oid opno, bool use_lhs_type)
 }
 
 /*
- * get_op_hash_function
- *             Get the OID of the datatype-specific hash function associated with
- *             a hashable equality operator.
+ * get_op_hash_functions
+ *             Get the OID(s) of hash support function(s) compatible with the given
+ *             operator, operating on its LHS and/or RHS datatype as required.
+ *
+ * A function for the LHS type is sought and returned into *lhs_procno if
+ * lhs_procno isn't NULL.  Similarly, a function for the RHS type is sought
+ * and returned into *rhs_procno if rhs_procno isn't NULL.
  *
- * XXX API needs to be generalized for the case of different left and right
- * datatypes.
+ * If the given operator is not cross-type, the results should be the same
+ * function, but in cross-type situations they will be different.
  *
- * Returns InvalidOid if no hash function can be found.  (This indicates
- * that the operator should not have been marked oprcanhash.)
+ * Returns true if able to find the requested function(s), false if not.
+ * (This indicates that the operator should not have been marked oprcanhash.)
  */
-Oid
-get_op_hash_function(Oid opno)
+bool
+get_op_hash_functions(Oid opno,
+                                         RegProcedure *lhs_procno, RegProcedure *rhs_procno)
 {
-       Oid                     result = InvalidOid;
+       bool            result = false;
        CatCList   *catlist;
        int                     i;
 
+       /* Ensure output args are initialized on failure */
+       if (lhs_procno)
+               *lhs_procno = InvalidOid;
+       if (rhs_procno)
+               *rhs_procno = InvalidOid;
+
        /*
         * Search pg_amop to see if the target operator is registered as the "="
         * operator of any hash opfamily.  If the operator is registered in
-        * multiple opfamilies, assume we can use the associated hash function from
-        * any one.
+        * multiple opfamilies, assume we can use any one.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
 
        for (i = 0; i < catlist->n_members; i++)
        {
@@ -564,12 +582,50 @@ get_op_hash_function(Oid opno)
                if (aform->amopmethod == HASH_AM_OID &&
                        aform->amopstrategy == HTEqualStrategyNumber)
                {
-                       /* Found a suitable opfamily, get matching hash support function */
-                       result = get_opfamily_proc(aform->amopfamily,
-                                                                          aform->amoplefttype,
-                                                                          aform->amoprighttype,
-                                                                          HASHPROC);
-                       break;
+                       /*
+                        * Get the matching support function(s).  Failure probably
+                        * shouldn't happen --- it implies a bogus opfamily --- but
+                        * continue looking if so.
+                        */
+                       if (lhs_procno)
+                       {
+                               *lhs_procno = get_opfamily_proc(aform->amopfamily,
+                                                                                               aform->amoplefttype,
+                                                                                               aform->amoplefttype,
+                                                                                               HASHPROC);
+                               if (!OidIsValid(*lhs_procno))
+                                       continue;
+                               /* Matching LHS found, done if caller doesn't want RHS */
+                               if (!rhs_procno)
+                               {
+                                       result = true;
+                                       break;
+                               }
+                               /* Only one lookup needed if given operator is single-type */
+                               if (aform->amoplefttype == aform->amoprighttype)
+                               {
+                                       *rhs_procno = *lhs_procno;
+                                       result = true;
+                                       break;
+                               }
+                       }
+                       if (rhs_procno)
+                       {
+                               *rhs_procno = get_opfamily_proc(aform->amopfamily,
+                                                                                               aform->amoprighttype,
+                                                                                               aform->amoprighttype,
+                                                                                               HASHPROC);
+                               if (!OidIsValid(*rhs_procno))
+                               {
+                                       /* Forget any LHS function from this opfamily */
+                                       if (lhs_procno)
+                                               *lhs_procno = InvalidOid;
+                                       continue;
+                               }
+                               /* Matching RHS found, so done */
+                               result = true;
+                               break;
+                       }
                }
        }
 
@@ -581,55 +637,30 @@ get_op_hash_function(Oid opno)
 /*
  * get_op_btree_interpretation
  *             Given an operator's OID, find out which btree opfamilies it belongs to,
- *             and what strategy number it has within each one.  The results are
- *             returned as an OID list and a parallel integer list.
+ *             and what properties it has within each one.  The results are returned
+ *             as a palloc'd list of OpBtreeInterpretation structs.
  *
  * In addition to the normal btree operators, we consider a <> operator to be
  * a "member" of an opfamily if its negator is an equality operator of the
  * opfamily.  ROWCOMPARE_NE is returned as the strategy number for this case.
  */
-void
-get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
+List *
+get_op_btree_interpretation(Oid opno)
 {
+       List       *result = NIL;
+       OpBtreeInterpretation *thisresult;
        CatCList   *catlist;
-       bool            op_negated;
        int                     i;
 
-       *opfamilies = NIL;
-       *opstrats = NIL;
-
        /*
         * Find all the pg_amop entries containing the operator.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno),
-                                                                0, 0, 0);
-
-       /*
-        * If we can't find any opfamily containing the op, perhaps it is a <>
-        * operator.  See if it has a negator that is in an opfamily.
-        */
-       op_negated = false;
-       if (catlist->n_members == 0)
-       {
-               Oid                     op_negator = get_negator(opno);
-
-               if (OidIsValid(op_negator))
-               {
-                       op_negated = true;
-                       ReleaseSysCacheList(catlist);
-                       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                                ObjectIdGetDatum(op_negator),
-                                                                                0, 0, 0);
-               }
-       }
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
 
-       /* Now search the opfamilies */
        for (i = 0; i < catlist->n_members; i++)
        {
                HeapTuple       op_tuple = &catlist->members[i]->tuple;
                Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
-               Oid                     opfamily_id;
                StrategyNumber op_strategy;
 
                /* must be btree */
@@ -637,56 +668,110 @@ get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
                        continue;
 
                /* Get the operator's btree strategy number */
-               opfamily_id = op_form->amopfamily;
                op_strategy = (StrategyNumber) op_form->amopstrategy;
                Assert(op_strategy >= 1 && op_strategy <= 5);
 
-               if (op_negated)
+               thisresult = (OpBtreeInterpretation *)
+                       palloc(sizeof(OpBtreeInterpretation));
+               thisresult->opfamily_id = op_form->amopfamily;
+               thisresult->strategy = op_strategy;
+               thisresult->oplefttype = op_form->amoplefttype;
+               thisresult->oprighttype = op_form->amoprighttype;
+               result = lappend(result, thisresult);
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       /*
+        * If we didn't find any btree opfamily containing the operator, perhaps
+        * it is a <> operator.  See if it has a negator that is in an opfamily.
+        */
+       if (result == NIL)
+       {
+               Oid                     op_negator = get_negator(opno);
+
+               if (OidIsValid(op_negator))
                {
-                       /* Only consider negators that are = */
-                       if (op_strategy != BTEqualStrategyNumber)
-                               continue;
-                       op_strategy = ROWCOMPARE_NE;
-               }
+                       catlist = SearchSysCacheList1(AMOPOPID,
+                                                                                 ObjectIdGetDatum(op_negator));
 
-               *opfamilies = lappend_oid(*opfamilies, opfamily_id);
-               *opstrats = lappend_int(*opstrats, op_strategy);
+                       for (i = 0; i < catlist->n_members; i++)
+                       {
+                               HeapTuple       op_tuple = &catlist->members[i]->tuple;
+                               Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+                               StrategyNumber op_strategy;
+
+                               /* must be btree */
+                               if (op_form->amopmethod != BTREE_AM_OID)
+                                       continue;
+
+                               /* Get the operator's btree strategy number */
+                               op_strategy = (StrategyNumber) op_form->amopstrategy;
+                               Assert(op_strategy >= 1 && op_strategy <= 5);
+
+                               /* Only consider negators that are = */
+                               if (op_strategy != BTEqualStrategyNumber)
+                                       continue;
+
+                               /* OK, report it with "strategy" ROWCOMPARE_NE */
+                               thisresult = (OpBtreeInterpretation *)
+                                       palloc(sizeof(OpBtreeInterpretation));
+                               thisresult->opfamily_id = op_form->amopfamily;
+                               thisresult->strategy = ROWCOMPARE_NE;
+                               thisresult->oplefttype = op_form->amoplefttype;
+                               thisresult->oprighttype = op_form->amoprighttype;
+                               result = lappend(result, thisresult);
+                       }
+
+                       ReleaseSysCacheList(catlist);
+               }
        }
 
-       ReleaseSysCacheList(catlist);
+       return result;
 }
 
 /*
- * ops_in_same_btree_opfamily
- *             Return TRUE if there exists a btree opfamily containing both operators.
- *             (This implies that they have compatible notions of equality.)
+ * equality_ops_are_compatible
+ *             Return TRUE if the two given equality operators have compatible
+ *             semantics.
+ *
+ * This is trivially true if they are the same operator.  Otherwise,
+ * we look to see if they can be found in the same btree or hash opfamily.
+ * Either finding allows us to assume that they have compatible notions
+ * of equality.  (The reason we need to do these pushups is that one might
+ * be a cross-type operator; for instance int24eq vs int4eq.)
  */
 bool
-ops_in_same_btree_opfamily(Oid opno1, Oid opno2)
+equality_ops_are_compatible(Oid opno1, Oid opno2)
 {
-       bool            result = false;
+       bool            result;
        CatCList   *catlist;
        int                     i;
 
+       /* Easy if they're the same operator */
+       if (opno1 == opno2)
+               return true;
+
        /*
         * We search through all the pg_amop entries for opno1.
         */
-       catlist = SearchSysCacheList(AMOPOPID, 1,
-                                                                ObjectIdGetDatum(opno1),
-                                                                0, 0, 0);
+       catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno1));
+
+       result = false;
        for (i = 0; i < catlist->n_members; i++)
        {
                HeapTuple       op_tuple = &catlist->members[i]->tuple;
                Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
 
-               /* must be btree */
-               if (op_form->amopmethod != BTREE_AM_OID)
-                       continue;
-
-               if (op_in_opfamily(opno2, op_form->amopfamily))
+               /* must be btree or hash */
+               if (op_form->amopmethod == BTREE_AM_OID ||
+                       op_form->amopmethod == HASH_AM_OID)
                {
-                       result = true;
-                       break;
+                       if (op_in_opfamily(opno2, op_form->amopfamily))
+                       {
+                               result = true;
+                               break;
+                       }
                }
        }
 
@@ -712,11 +797,11 @@ get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
        Form_pg_amproc amproc_tup;
        RegProcedure result;
 
-       tp = SearchSysCache(AMPROCNUM,
-                                               ObjectIdGetDatum(opfamily),
-                                               ObjectIdGetDatum(lefttype),
-                                               ObjectIdGetDatum(righttype),
-                                               Int16GetDatum(procnum));
+       tp = SearchSysCache4(AMPROCNUM,
+                                                ObjectIdGetDatum(opfamily),
+                                                ObjectIdGetDatum(lefttype),
+                                                ObjectIdGetDatum(righttype),
+                                                Int16GetDatum(procnum));
        if (!HeapTupleIsValid(tp))
                return InvalidOid;
        amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
@@ -740,10 +825,9 @@ get_attname(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(ATTNUM,
-                                               ObjectIdGetDatum(relid),
-                                               Int16GetDatum(attnum),
-                                               0, 0);
+       tp = SearchSysCache2(ATTNUM,
+                                                ObjectIdGetDatum(relid),
+                                                Int16GetDatum(attnum));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
@@ -813,10 +897,9 @@ get_atttype(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(ATTNUM,
-                                               ObjectIdGetDatum(relid),
-                                               Int16GetDatum(attnum),
-                                               0, 0);
+       tp = SearchSysCache2(ATTNUM,
+                                                ObjectIdGetDatum(relid),
+                                                Int16GetDatum(attnum));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
@@ -841,10 +924,9 @@ get_atttypmod(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(ATTNUM,
-                                               ObjectIdGetDatum(relid),
-                                               Int16GetDatum(attnum),
-                                               0, 0);
+       tp = SearchSysCache2(ATTNUM,
+                                                ObjectIdGetDatum(relid),
+                                                Int16GetDatum(attnum));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
@@ -859,25 +941,24 @@ get_atttypmod(Oid relid, AttrNumber attnum)
 }
 
 /*
- * get_atttypetypmod
+ * get_atttypetypmodcoll
  *
- *             A two-fer: given the relation id and the attribute number,
- *             fetch both type OID and atttypmod in a single cache lookup.
+ *             A three-fer: given the relation id and the attribute number,
+ *             fetch atttypid, atttypmod, and attcollation in a single cache lookup.
  *
  * Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
  * raises an error if it can't obtain the information.
  */
 void
-get_atttypetypmod(Oid relid, AttrNumber attnum,
-                                 Oid *typid, int32 *typmod)
+get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
+                                         Oid *typid, int32 *typmod, Oid *collid)
 {
        HeapTuple       tp;
        Form_pg_attribute att_tup;
 
-       tp = SearchSysCache(ATTNUM,
-                                               ObjectIdGetDatum(relid),
-                                               Int16GetDatum(attnum),
-                                               0, 0);
+       tp = SearchSysCache2(ATTNUM,
+                                                ObjectIdGetDatum(relid),
+                                                Int16GetDatum(attnum));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for attribute %d of relation %u",
                         attnum, relid);
@@ -885,13 +966,69 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
 
        *typid = att_tup->atttypid;
        *typmod = att_tup->atttypmod;
+       *collid = att_tup->attcollation;
        ReleaseSysCache(tp);
 }
 
-/*                             ---------- INDEX CACHE ----------                                                */
+/*                             ---------- COLLATION CACHE ----------                                    */
+
+/*
+ * get_collation_name
+ *             Returns the name of a given pg_collation entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since collation name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
+ */
+char *
+get_collation_name(Oid colloid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp);
+               char       *result;
+
+               result = pstrdup(NameStr(colltup->collname));
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return NULL;
+}
+
+/*                             ---------- CONSTRAINT CACHE ----------                                   */
 
-/*             watch this space...
+/*
+ * get_constraint_name
+ *             Returns the name of a given pg_constraint entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since constraint name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
  */
+char *
+get_constraint_name(Oid conoid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
+               char       *result;
+
+               result = pstrdup(NameStr(contup->conname));
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return NULL;
+}
 
 /*                             ---------- OPCLASS CACHE ----------                                              */
 
@@ -907,9 +1044,7 @@ get_opclass_family(Oid opclass)
        Form_pg_opclass cla_tup;
        Oid                     result;
 
-       tp = SearchSysCache(CLAOID,
-                                               ObjectIdGetDatum(opclass),
-                                               0, 0, 0);
+       tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
@@ -931,9 +1066,7 @@ get_opclass_input_type(Oid opclass)
        Form_pg_opclass cla_tup;
        Oid                     result;
 
-       tp = SearchSysCache(CLAOID,
-                                               ObjectIdGetDatum(opclass),
-                                               0, 0, 0);
+       tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
@@ -956,9 +1089,7 @@ get_opcode(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -983,9 +1114,7 @@ get_opname(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1011,9 +1140,7 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
        HeapTuple       tp;
        Form_pg_operator optup;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (!HeapTupleIsValid(tp))      /* shouldn't happen */
                elog(ERROR, "cache lookup failed for operator %u", opno);
        optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1029,22 +1156,48 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
  * will fail to find any mergejoin plans unless there are suitable btree
  * opfamily entries for this operator and associated sortops.  The pg_operator
  * flag is just a hint to tell the planner whether to bother looking.)
+ *
+ * In some cases (currently only array_eq and record_eq), mergejoinability
+ * depends on the specific input data type the operator is invoked for, so
+ * that must be passed as well. We currently assume that only one input's type
+ * is needed to check this --- by convention, pass the left input's data type.
  */
 bool
-op_mergejoinable(Oid opno)
+op_mergejoinable(Oid opno, Oid inputtype)
 {
-       HeapTuple       tp;
        bool            result = false;
+       HeapTuple       tp;
+       TypeCacheEntry *typentry;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
-       if (HeapTupleIsValid(tp))
+       /*
+        * For array_eq or record_eq, we can sort if the element or field types
+        * are all sortable.  We could implement all the checks for that here, but
+        * the typcache already does that and caches the results too, so let's
+        * rely on the typcache.
+        */
+       if (opno == ARRAY_EQ_OP)
        {
-               Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+               typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+               if (typentry->cmp_proc == F_BTARRAYCMP)
+                       result = true;
+       }
+       else if (opno == RECORD_EQ_OP)
+       {
+               typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+               if (typentry->cmp_proc == F_BTRECORDCMP)
+                       result = true;
+       }
+       else
+       {
+               /* For all other operators, rely on pg_operator.oprcanmerge */
+               tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+               if (HeapTupleIsValid(tp))
+               {
+                       Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
 
-               result = optup->oprcanmerge;
-               ReleaseSysCache(tp);
+                       result = optup->oprcanmerge;
+                       ReleaseSysCache(tp);
+               }
        }
        return result;
 }
@@ -1054,22 +1207,38 @@ op_mergejoinable(Oid opno)
  *
  * Returns true if the operator is hashjoinable.  (There must be a suitable
  * hash opfamily entry for this operator if it is so marked.)
+ *
+ * In some cases (currently only array_eq), hashjoinability depends on the
+ * specific input data type the operator is invoked for, so that must be
+ * passed as well.     We currently assume that only one input's type is needed
+ * to check this --- by convention, pass the left input's data type.
  */
 bool
-op_hashjoinable(Oid opno)
+op_hashjoinable(Oid opno, Oid inputtype)
 {
-       HeapTuple       tp;
        bool            result = false;
+       HeapTuple       tp;
+       TypeCacheEntry *typentry;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
-       if (HeapTupleIsValid(tp))
+       /* As in op_mergejoinable, let the typcache handle the hard cases */
+       /* Eventually we'll need a similar case for record_eq ... */
+       if (opno == ARRAY_EQ_OP)
        {
-               Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
+               typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+               if (typentry->hash_proc == F_HASH_ARRAY)
+                       result = true;
+       }
+       else
+       {
+               /* For all other operators, rely on pg_operator.oprcanhash */
+               tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
+               if (HeapTupleIsValid(tp))
+               {
+                       Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
 
-               result = optup->oprcanhash;
-               ReleaseSysCache(tp);
+                       result = optup->oprcanhash;
+                       ReleaseSysCache(tp);
+               }
        }
        return result;
 }
@@ -1116,9 +1285,7 @@ get_commutator(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1142,9 +1309,7 @@ get_negator(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1168,9 +1333,7 @@ get_oprrest(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1194,9 +1357,7 @@ get_oprjoin(Oid opno)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
+       tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -1223,9 +1384,7 @@ get_func_name(Oid funcid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
@@ -1239,6 +1398,30 @@ get_func_name(Oid funcid)
                return NULL;
 }
 
+/*
+ * get_func_namespace
+ *
+ *             Returns the pg_namespace OID associated with a given function.
+ */
+Oid
+get_func_namespace(Oid funcid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = functup->pronamespace;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
 /*
  * get_func_rettype
  *             Given procedure id, return the function's result type.
@@ -1249,9 +1432,7 @@ get_func_rettype(Oid funcid)
        HeapTuple       tp;
        Oid                     result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1270,9 +1451,7 @@ get_func_nargs(Oid funcid)
        HeapTuple       tp;
        int                     result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1295,9 +1474,7 @@ get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
        Form_pg_proc procstruct;
        Oid                     result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1323,9 +1500,7 @@ get_func_retset(Oid funcid)
        HeapTuple       tp;
        bool            result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1344,9 +1519,7 @@ func_strict(Oid funcid)
        HeapTuple       tp;
        bool            result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1365,9 +1538,7 @@ func_volatile(Oid funcid)
        HeapTuple       tp;
        char            result;
 
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1376,6 +1547,44 @@ func_volatile(Oid funcid)
        return result;
 }
 
+/*
+ * get_func_cost
+ *             Given procedure id, return the function's procost field.
+ */
+float4
+get_func_cost(Oid funcid)
+{
+       HeapTuple       tp;
+       float4          result;
+
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for function %u", funcid);
+
+       result = ((Form_pg_proc) GETSTRUCT(tp))->procost;
+       ReleaseSysCache(tp);
+       return result;
+}
+
+/*
+ * get_func_rows
+ *             Given procedure id, return the function's prorows field.
+ */
+float4
+get_func_rows(Oid funcid)
+{
+       HeapTuple       tp;
+       float4          result;
+
+       tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for function %u", funcid);
+
+       result = ((Form_pg_proc) GETSTRUCT(tp))->prorows;
+       ReleaseSysCache(tp);
+       return result;
+}
+
 /*                             ---------- RELATION CACHE ----------                                     */
 
 /*
@@ -1387,10 +1596,9 @@ func_volatile(Oid funcid)
 Oid
 get_relname_relid(const char *relname, Oid relnamespace)
 {
-       return GetSysCacheOid(RELNAMENSP,
-                                                 PointerGetDatum(relname),
-                                                 ObjectIdGetDatum(relnamespace),
-                                                 0, 0);
+       return GetSysCacheOid2(RELNAMENSP,
+                                                  PointerGetDatum(relname),
+                                                  ObjectIdGetDatum(relnamespace));
 }
 
 #ifdef NOT_USED
@@ -1404,9 +1612,7 @@ get_relnatts(Oid relid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
@@ -1435,9 +1641,7 @@ get_rel_name(Oid relid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
@@ -1461,9 +1665,7 @@ get_rel_namespace(Oid relid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
@@ -1490,9 +1692,7 @@ get_rel_type_id(Oid relid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
@@ -1516,9 +1716,7 @@ get_rel_relkind(Oid relid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
@@ -1532,6 +1730,33 @@ get_rel_relkind(Oid relid)
                return '\0';
 }
 
+/*
+ * get_rel_tablespace
+ *
+ *             Returns the pg_tablespace OID associated with a given relation.
+ *
+ * Note: InvalidOid might mean either that we couldn't find the relation,
+ * or that it is in the database's default tablespace.
+ */
+Oid
+get_rel_tablespace(Oid relid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = reltup->reltablespace;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
 
 /*                             ---------- TYPE CACHE ----------                                                 */
 
@@ -1546,9 +1771,7 @@ get_typisdefined(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1572,9 +1795,7 @@ get_typlen(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1599,9 +1820,7 @@ get_typbyval(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1631,9 +1850,7 @@ get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
        HeapTuple       tp;
        Form_pg_type typtup;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for type %u", typid);
        typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1654,9 +1871,7 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
        HeapTuple       tp;
        Form_pg_type typtup;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for type %u", typid);
        typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1688,8 +1903,7 @@ getTypeIOParam(HeapTuple typeTuple)
 
        /*
         * Array types get their typelem as parameter; everybody else gets their
-        * own type OID as parameter.  (This is a change from 8.0, in which only
-        * composite types got their own OID as parameter.)
+        * own type OID as parameter.
         */
        if (OidIsValid(typeStruct->typelem))
                return typeStruct->typelem;
@@ -1749,9 +1963,7 @@ get_type_io_data(Oid typid,
                return;
        }
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(typid),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", typid);
        typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -1785,9 +1997,7 @@ get_typalign(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1807,9 +2017,7 @@ get_typstorage(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -1842,9 +2050,7 @@ get_typdefault(Oid typid)
        bool            isNull;
        Node       *expr;
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(typid),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", typid);
        type = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -1862,8 +2068,7 @@ get_typdefault(Oid typid)
        if (!isNull)
        {
                /* We have an expression default */
-               expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                               datum)));
+               expr = stringToNode(TextDatumGetCString(datum));
        }
        else
        {
@@ -1878,13 +2083,14 @@ get_typdefault(Oid typid)
                        char       *strDefaultVal;
 
                        /* Convert text datum to C string */
-                       strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                               datum));
+                       strDefaultVal = TextDatumGetCString(datum);
                        /* Convert C string to a value of the given type */
                        datum = OidInputFunctionCall(type->typinput, strDefaultVal,
                                                                                 getTypeIOParam(typeTuple), -1);
                        /* Build a Const node containing the value */
                        expr = (Node *) makeConst(typid,
+                                                                         -1,
+                                                                         type->typcollation,
                                                                          type->typlen,
                                                                          datum,
                                                                          false,
@@ -1936,13 +2142,11 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
                HeapTuple       tup;
                Form_pg_type typTup;
 
-               tup = SearchSysCache(TYPEOID,
-                                                        ObjectIdGetDatum(typid),
-                                                        0, 0, 0);
+               tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
                if (!HeapTupleIsValid(tup))
                        elog(ERROR, "cache lookup failed for type %u", typid);
                typTup = (Form_pg_type) GETSTRUCT(tup);
-               if (typTup->typtype != 'd')
+               if (typTup->typtype != TYPTYPE_DOMAIN)
                {
                        /* Not a domain, so done */
                        ReleaseSysCache(tup);
@@ -2023,9 +2227,7 @@ get_typtype(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -2048,7 +2250,48 @@ get_typtype(Oid typid)
 bool
 type_is_rowtype(Oid typid)
 {
-       return (typid == RECORDOID || get_typtype(typid) == 'c');
+       return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE);
+}
+
+/*
+ * type_is_enum
+ *       Returns true if the given type is an enum type.
+ */
+bool
+type_is_enum(Oid typid)
+{
+       return (get_typtype(typid) == TYPTYPE_ENUM);
+}
+
+/*
+ * type_is_range
+ *       Returns true if the given type is a range type.
+ */
+bool
+type_is_range(Oid typid)
+{
+       return (get_typtype(typid) == TYPTYPE_RANGE);
+}
+
+/*
+ * get_type_category_preferred
+ *
+ *             Given the type OID, fetch its category and preferred-type status.
+ *             Throws error on failure.
+ */
+void
+get_type_category_preferred(Oid typid, char *typcategory, bool *typispreferred)
+{
+       HeapTuple       tp;
+       Form_pg_type typtup;
+
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for type %u", typid);
+       typtup = (Form_pg_type) GETSTRUCT(tp);
+       *typcategory = typtup->typcategory;
+       *typispreferred = typtup->typispreferred;
+       ReleaseSysCache(tp);
 }
 
 /*
@@ -2062,9 +2305,7 @@ get_typ_typrelid(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -2091,9 +2332,7 @@ get_element_type(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -2113,49 +2352,67 @@ get_element_type(Oid typid)
 /*
  * get_array_type
  *
- *             Given the type OID, get the corresponding array type.
+ *             Given the type OID, get the corresponding "true" array type.
  *             Returns InvalidOid if no array type can be found.
- *
- * NB: this only considers varlena arrays to be true arrays.
  */
 Oid
 get_array_type(Oid typid)
 {
        HeapTuple       tp;
+       Oid                     result = InvalidOid;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
-               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
-               char       *array_typename;
-               Oid                     namespaceId;
-
-               array_typename = makeArrayTypeName(NameStr(typtup->typname));
-               namespaceId = typtup->typnamespace;
+               result = ((Form_pg_type) GETSTRUCT(tp))->typarray;
                ReleaseSysCache(tp);
+       }
+       return result;
+}
 
-               tp = SearchSysCache(TYPENAMENSP,
-                                                       PointerGetDatum(array_typename),
-                                                       ObjectIdGetDatum(namespaceId),
-                                                       0, 0);
-
-               pfree(array_typename);
+/*
+ * get_base_element_type
+ *             Given the type OID, get the typelem, looking "through" any domain
+ *             to its underlying array type.
+ *
+ * This is equivalent to get_element_type(getBaseType(typid)), but avoids
+ * an extra cache lookup.  Note that it fails to provide any information
+ * about the typmod of the array.
+ */
+Oid
+get_base_element_type(Oid typid)
+{
+       /*
+        * We loop to find the bottom base type in a stack of domains.
+        */
+       for (;;)
+       {
+               HeapTuple       tup;
+               Form_pg_type typTup;
 
-               if (HeapTupleIsValid(tp))
+               tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+               if (!HeapTupleIsValid(tup))
+                       break;
+               typTup = (Form_pg_type) GETSTRUCT(tup);
+               if (typTup->typtype != TYPTYPE_DOMAIN)
                {
+                       /* Not a domain, so stop descending */
                        Oid                     result;
 
-                       typtup = (Form_pg_type) GETSTRUCT(tp);
-                       if (typtup->typlen == -1 && typtup->typelem == typid)
-                               result = HeapTupleGetOid(tp);
+                       /* This test must match get_element_type */
+                       if (typTup->typlen == -1)
+                               result = typTup->typelem;
                        else
                                result = InvalidOid;
-                       ReleaseSysCache(tp);
+                       ReleaseSysCache(tup);
                        return result;
                }
+
+               typid = typTup->typbasetype;
+               ReleaseSysCache(tup);
        }
+
+       /* Like get_element_type, silently return InvalidOid for bogus input */
        return InvalidOid;
 }
 
@@ -2170,9 +2427,7 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
        HeapTuple       typeTuple;
        Form_pg_type pt;
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(type),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -2205,9 +2460,7 @@ getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
        HeapTuple       typeTuple;
        Form_pg_type pt;
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(type),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -2240,9 +2493,7 @@ getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
        HeapTuple       typeTuple;
        Form_pg_type pt;
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(type),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -2275,9 +2526,7 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
        HeapTuple       typeTuple;
        Form_pg_type pt;
 
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(type),
-                                                          0, 0, 0);
+       typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
        if (!HeapTupleIsValid(typeTuple))
                elog(ERROR, "cache lookup failed for type %u", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -2309,9 +2558,7 @@ get_typmodin(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -2336,9 +2583,7 @@ get_typmodout(Oid typid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
@@ -2351,7 +2596,43 @@ get_typmodout(Oid typid)
        else
                return InvalidOid;
 }
-#endif /* NOT_USED */
+#endif   /* NOT_USED */
+
+/*
+ * get_typcollation
+ *
+ *             Given the type OID, return the type's typcollation attribute.
+ */
+Oid
+get_typcollation(Oid typid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = typtup->typcollation;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
+
+/*
+ * type_is_collatable
+ *
+ *             Return whether the type cares about collations
+ */
+bool
+type_is_collatable(Oid typid)
+{
+       return OidIsValid(get_typcollation(typid));
+}
 
 
 /*                             ---------- STATISTICS CACHE ----------                                   */
@@ -2361,20 +2642,33 @@ get_typmodout(Oid typid)
  *
  *       Given the table and attribute number of a column, get the average
  *       width of entries in the column.  Return zero if no data available.
+ *
+ * Currently this is only consulted for individual tables, not for inheritance
+ * trees, so we don't need an "inh" parameter.
+ *
+ * Calling a hook at this point looks somewhat strange, but is required
+ * because the optimizer calls this function without any other way for
+ * plug-ins to control the result.
  */
 int32
 get_attavgwidth(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
+       int32           stawidth;
 
-       tp = SearchSysCache(STATRELATT,
-                                               ObjectIdGetDatum(relid),
-                                               Int16GetDatum(attnum),
-                                               0, 0);
+       if (get_attavgwidth_hook)
+       {
+               stawidth = (*get_attavgwidth_hook) (relid, attnum);
+               if (stawidth > 0)
+                       return stawidth;
+       }
+       tp = SearchSysCache3(STATRELATTINH,
+                                                ObjectIdGetDatum(relid),
+                                                Int16GetDatum(attnum),
+                                                BoolGetDatum(false));
        if (HeapTupleIsValid(tp))
        {
-               int32           stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
-
+               stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
                ReleaseSysCache(tp);
                if (stawidth > 0)
                        return stawidth;
@@ -2392,12 +2686,16 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
  * already-looked-up tuple in the pg_statistic cache.  We do this since
  * most callers will want to extract more than one value from the cache
  * entry, and we don't want to repeat the cache lookup unnecessarily.
+ * Also, this API allows this routine to be used with statistics tuples
+ * that have been provided by a stats hook and didn't really come from
+ * pg_statistic.
  *
  * statstuple: pg_statistics tuple to be examined.
  * atttype: type OID of attribute (can be InvalidOid if values == NULL).
  * atttypmod: typmod of attribute (can be 0 if values == NULL).
  * reqkind: STAKIND code for desired statistics slot kind.
  * reqop: STAOP value wanted, or InvalidOid if don't care.
+ * actualop: if not NULL, *actualop receives the actual STAOP value.
  * values, nvalues: if not NULL, the slot's stavalues are extracted.
  * numbers, nnumbers: if not NULL, the slot's stanumbers are extracted.
  *
@@ -2405,11 +2703,16 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
  * If the attribute type is pass-by-reference, the values referenced by
  * the values array are themselves palloc'd.  The palloc'd stuff can be
  * freed by calling free_attstatsslot.
+ *
+ * Note: at present, atttype/atttypmod aren't actually used here at all.
+ * But the caller must have the correct (or at least binary-compatible)
+ * type ID to pass to free_attstatsslot later.
  */
 bool
 get_attstatsslot(HeapTuple statstuple,
                                 Oid atttype, int32 atttypmod,
                                 int reqkind, Oid reqop,
+                                Oid *actualop,
                                 Datum **values, int *nvalues,
                                 float4 **numbers, int *nnumbers)
 {
@@ -2419,6 +2722,7 @@ get_attstatsslot(HeapTuple statstuple,
        Datum           val;
        bool            isnull;
        ArrayType  *statarray;
+       Oid                     arrayelemtype;
        int                     narrayelem;
        HeapTuple       typeTuple;
        Form_pg_type typeForm;
@@ -2432,26 +2736,34 @@ get_attstatsslot(HeapTuple statstuple,
        if (i >= STATISTIC_NUM_SLOTS)
                return false;                   /* not there */
 
+       if (actualop)
+               *actualop = (&stats->staop1)[i];
+
        if (values)
        {
-               val = SysCacheGetAttr(STATRELATT, statstuple,
+               val = SysCacheGetAttr(STATRELATTINH, statstuple,
                                                          Anum_pg_statistic_stavalues1 + i,
                                                          &isnull);
                if (isnull)
                        elog(ERROR, "stavalues is null");
                statarray = DatumGetArrayTypeP(val);
 
-               /* Need to get info about the array element type */
-               typeTuple = SearchSysCache(TYPEOID,
-                                                                  ObjectIdGetDatum(atttype),
-                                                                  0, 0, 0);
+               /*
+                * Need to get info about the array element type.  We look at the
+                * actual element type embedded in the array, which might be only
+                * binary-compatible with the passed-in atttype.  The info we extract
+                * here should be the same either way, but deconstruct_array is picky
+                * about having an exact type OID match.
+                */
+               arrayelemtype = ARR_ELEMTYPE(statarray);
+               typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype));
                if (!HeapTupleIsValid(typeTuple))
-                       elog(ERROR, "cache lookup failed for type %u", atttype);
+                       elog(ERROR, "cache lookup failed for type %u", arrayelemtype);
                typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
 
                /* Deconstruct array into Datum elements; NULLs not expected */
                deconstruct_array(statarray,
-                                                 atttype,
+                                                 arrayelemtype,
                                                  typeForm->typlen,
                                                  typeForm->typbyval,
                                                  typeForm->typalign,
@@ -2483,7 +2795,7 @@ get_attstatsslot(HeapTuple statstuple,
 
        if (numbers)
        {
-               val = SysCacheGetAttr(STATRELATT, statstuple,
+               val = SysCacheGetAttr(STATRELATTINH, statstuple,
                                                          Anum_pg_statistic_stanumbers1 + i,
                                                          &isnull);
                if (isnull)
@@ -2553,9 +2865,7 @@ get_namespace_name(Oid nspid)
 {
        HeapTuple       tp;
 
-       tp = SearchSysCache(NAMESPACEOID,
-                                               ObjectIdGetDatum(nspid),
-                                               0, 0, 0);
+       tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
        if (HeapTupleIsValid(tp))
        {
                Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp);
@@ -2569,35 +2879,29 @@ get_namespace_name(Oid nspid)
                return NULL;
 }
 
-/*                             ---------- PG_AUTHID CACHE ----------                                    */
+/*                             ---------- PG_RANGE CACHE ----------                             */
 
 /*
- * get_roleid
- *       Given a role name, look up the role's OID.
- *       Returns InvalidOid if no such role.
+ * get_range_subtype
+ *             Returns the subtype of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
  */
 Oid
-get_roleid(const char *rolname)
+get_range_subtype(Oid rangeOid)
 {
-       return GetSysCacheOid(AUTHNAME,
-                                                 PointerGetDatum(rolname),
-                                                 0, 0, 0);
-}
+       HeapTuple       tp;
 
-/*
- * get_roleid_checked
- *       Given a role name, look up the role's OID.
- *       ereports if no such role.
- */
-Oid
-get_roleid_checked(const char *rolname)
-{
-       Oid                     roleid;
+       tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_range   rngtup = (Form_pg_range) GETSTRUCT(tp);
+               Oid                             result;
 
-       roleid = get_roleid(rolname);
-       if (!OidIsValid(roleid))
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("role \"%s\" does not exist", rolname)));
-       return roleid;
+               result = rngtup->rngsubtype;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
 }