]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/cache/lsyscache.c
Fix ALTER COLUMN TYPE to preserve the tablespace and reloptions of indexes
[postgresql] / src / backend / utils / cache / lsyscache.c
index 5436acb24fc07cbfa8936ec673b05b9cffd59a4c..d82e7debf5302bf527f4cb7f0e333015d3d18ecf 100644 (file)
@@ -3,34 +3,34 @@
  * lsyscache.c
  *       Convenience routines for common queries in the system catalog cache.
  *
- * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, 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.115 2004/08/29 04:12:53 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.153 2007/10/13 15:55:40 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
-#include "miscadmin.h"
 
 #include "access/hash.h"
-#include "access/tupmacs.h"
+#include "access/nbtree.h"
+#include "bootstrap/bootstrap.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.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_shadow.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
-#include "utils/catcache.h"
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 /*                             ---------- AMOP CACHES ----------                                                */
 
 /*
- * op_in_opclass
+ * op_in_opfamily
  *
- *             Return t iff operator 'opno' is in operator class 'opclass'.
+ *             Return t iff operator 'opno' is in operator family 'opfamily'.
  */
 bool
-op_in_opclass(Oid opno, Oid opclass)
+op_in_opfamily(Oid opno, Oid opfamily)
 {
        return SearchSysCacheExists(AMOPOPID,
                                                                ObjectIdGetDatum(opno),
-                                                               ObjectIdGetDatum(opclass),
+                                                               ObjectIdGetDatum(opfamily),
                                                                0, 0);
 }
 
 /*
- * get_op_opclass_properties
+ * get_op_opfamily_strategy
  *
- *             Get the operator's strategy number, subtype, and recheck (lossy) flag
- *             within the specified opclass.
+ *             Get the operator's strategy number within the specified opfamily,
+ *             or 0 if it's not a member of the opfamily.
+ */
+int
+get_op_opfamily_strategy(Oid opno, Oid opfamily)
+{
+       HeapTuple       tp;
+       Form_pg_amop amop_tup;
+       int                     result;
+
+       tp = SearchSysCache(AMOPOPID,
+                                               ObjectIdGetDatum(opno),
+                                               ObjectIdGetDatum(opfamily),
+                                               0, 0);
+       if (!HeapTupleIsValid(tp))
+               return 0;
+       amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+       result = amop_tup->amopstrategy;
+       ReleaseSysCache(tp);
+       return result;
+}
+
+/*
+ * get_op_opfamily_properties
  *
- * Caller should already have verified that opno is a member of opclass,
+ *             Get the operator's strategy number, input types, and recheck (lossy)
+ *             flag 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_opclass_properties(Oid opno, Oid opclass,
-                                                 int *strategy, Oid *subtype, bool *recheck)
+get_op_opfamily_properties(Oid opno, Oid opfamily,
+                                                  int *strategy,
+                                                  Oid *lefttype,
+                                                  Oid *righttype,
+                                                  bool *recheck)
 {
        HeapTuple       tp;
        Form_pg_amop amop_tup;
 
        tp = SearchSysCache(AMOPOPID,
                                                ObjectIdGetDatum(opno),
-                                               ObjectIdGetDatum(opclass),
+                                               ObjectIdGetDatum(opfamily),
                                                0, 0);
        if (!HeapTupleIsValid(tp))
-               elog(ERROR, "operator %u is not a member of opclass %u",
-                        opno, opclass);
+               elog(ERROR, "operator %u is not a member of opfamily %u",
+                        opno, opfamily);
        amop_tup = (Form_pg_amop) GETSTRUCT(tp);
        *strategy = amop_tup->amopstrategy;
-       *subtype = amop_tup->amopsubtype;
+       *lefttype = amop_tup->amoplefttype;
+       *righttype = amop_tup->amoprighttype;
        *recheck = amop_tup->amopreqcheck;
        ReleaseSysCache(tp);
 }
 
 /*
- * get_opclass_member
+ * get_opfamily_member
  *             Get the OID of the operator that implements the specified strategy
- *             with the specified subtype for the specified opclass.
+ *             with the specified datatypes for the specified opfamily.
  *
  * Returns InvalidOid if there is no pg_amop entry for the given keys.
  */
 Oid
-get_opclass_member(Oid opclass, Oid subtype, int16 strategy)
+get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
+                                       int16 strategy)
 {
        HeapTuple       tp;
        Form_pg_amop amop_tup;
        Oid                     result;
 
        tp = SearchSysCache(AMOPSTRATEGY,
-                                               ObjectIdGetDatum(opclass),
-                                               ObjectIdGetDatum(subtype),
-                                               Int16GetDatum(strategy),
-                                               0);
+                                               ObjectIdGetDatum(opfamily),
+                                               ObjectIdGetDatum(lefttype),
+                                               ObjectIdGetDatum(righttype),
+                                               Int16GetDatum(strategy));
        if (!HeapTupleIsValid(tp))
                return InvalidOid;
        amop_tup = (Form_pg_amop) GETSTRUCT(tp);
@@ -110,25 +140,171 @@ get_opclass_member(Oid opclass, Oid subtype, int16 strategy)
 }
 
 /*
- * get_op_hash_function
- *             Get the OID of the datatype-specific hash function associated with
- *             a hashable equality operator.
+ * 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.)
+ *
+ * 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.
+ */
+bool
+get_ordering_op_properties(Oid opno,
+                                                  Oid *opfamily, Oid *opcintype, int16 *strategy)
+{
+       bool            result = false;
+       CatCList   *catlist;
+       int                     i;
+
+       /* ensure outputs are initialized on failure */
+       *opfamily = InvalidOid;
+       *opcintype = InvalidOid;
+       *strategy = 0;
+
+       /*
+        * Search pg_amop to see if the target operator is registered as the "<"
+        * or ">" operator of any btree opfamily.
+        */
+       catlist = SearchSysCacheList(AMOPOPID, 1,
+                                                                ObjectIdGetDatum(opno),
+                                                                0, 0, 0);
+
+       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 */
+               if (aform->amopmethod != BTREE_AM_OID)
+                       continue;
+
+               if (aform->amopstrategy == BTLessStrategyNumber ||
+                       aform->amopstrategy == BTGreaterStrategyNumber)
+               {
+                       /* Found it ... should have consistent input types */
+                       if (aform->amoplefttype == aform->amoprighttype)
+                       {
+                               /* Found a suitable opfamily, return info */
+                               *opfamily = aform->amopfamily;
+                               *opcintype = aform->amoplefttype;
+                               *strategy = aform->amopstrategy;
+                               result = true;
+                               break;
+                       }
+               }
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       return result;
+}
+
+/*
+ * get_compare_function_for_ordering_op
+ *             Get the OID of the datatype-specific btree comparison function
+ *             associated with an ordering operator (a "<" or ">" operator).
+ *
+ * *cmpfunc receives the comparison function OID.
+ * *reverse is set FALSE if the operator is "<", TRUE if it's ">"
+ * (indicating the comparison result must be negated before use).
  *
- * Returns InvalidOid if no hash function can be found.  (This indicates
- * that the operator should not have been marked oprcanhash.)
+ * 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)
+{
+       Oid                     opfamily;
+       Oid                     opcintype;
+       int16           strategy;
+
+       /* Find the operator in pg_amop */
+       if (get_ordering_op_properties(opno,
+                                                                  &opfamily, &opcintype, &strategy))
+       {
+               /* Found a suitable opfamily, get matching support function */
+               *cmpfunc = get_opfamily_proc(opfamily,
+                                                                        opcintype,
+                                                                        opcintype,
+                                                                        BTORDER_PROC);
+               if (!OidIsValid(*cmpfunc))                              /* should not happen */
+                       elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+                                BTORDER_PROC, opcintype, opcintype, opfamily);
+               *reverse = (strategy == BTGreaterStrategyNumber);
+               return true;
+       }
+
+       /* ensure outputs are set on failure */
+       *cmpfunc = InvalidOid;
+       *reverse = false;
+       return false;
+}
+
+/*
+ * get_equality_op_for_ordering_op
+ *             Get the OID of the datatype-specific btree equality operator
+ *             associated with an ordering operator (a "<" or ">" operator).
+ *
+ * Returns InvalidOid if no matching equality operator can be found.
+ * (This indicates that the operator is not a valid ordering operator.)
  */
 Oid
-get_op_hash_function(Oid opno)
+get_equality_op_for_ordering_op(Oid opno)
 {
+       Oid                     result = InvalidOid;
+       Oid                     opfamily;
+       Oid                     opcintype;
+       int16           strategy;
+
+       /* Find the operator in pg_amop */
+       if (get_ordering_op_properties(opno,
+                                                                  &opfamily, &opcintype, &strategy))
+       {
+               /* Found a suitable opfamily, get matching equality operator */
+               result = get_opfamily_member(opfamily,
+                                                                        opcintype,
+                                                                        opcintype,
+                                                                        BTEqualStrategyNumber);
+       }
+
+       return result;
+}
+
+/*
+ * get_ordering_op_for_equality_op
+ *             Get the OID of a datatype-specific btree ordering operator
+ *             associated with an equality operator.  (If there are multiple
+ *             possibilities, assume any one will do.)
+ *
+ * This function is used when we have to sort data before unique-ifying,
+ * and don't much care which sorting op is used as long as it's compatible
+ * with the intended equality operator.  Since we need a sorting operator,
+ * it should be single-data-type even if the given operator is cross-type.
+ * The caller specifies whether to find an op for the LHS or RHS data type.
+ *
+ * Returns InvalidOid if no matching ordering operator can be found.
+ */
+Oid
+get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
+{
+       Oid                     result = InvalidOid;
        CatCList   *catlist;
        int                     i;
-       Oid                     opclass = InvalidOid;
 
        /*
-        * Search pg_amop to see if the target operator is registered as the
-        * "=" operator of any hash opclass.  If the operator is registered in
-        * multiple opclasses, assume we can use the associated hash function
-        * from any one.
+        * Search pg_amop to see if the target operator is registered as the "="
+        * operator of any btree opfamily.
         */
        catlist = SearchSysCacheList(AMOPOPID, 1,
                                                                 ObjectIdGetDatum(opno),
@@ -139,48 +315,418 @@ get_op_hash_function(Oid opno)
                HeapTuple       tuple = &catlist->members[i]->tuple;
                Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
 
-               if (aform->amopstrategy == HTEqualStrategyNumber &&
-                       opclass_is_hash(aform->amopclaid))
+               /* must be btree */
+               if (aform->amopmethod != BTREE_AM_OID)
+                       continue;
+
+               if (aform->amopstrategy == BTEqualStrategyNumber)
                {
-                       opclass = aform->amopclaid;
-                       break;
+                       /* Found a suitable opfamily, get matching ordering operator */
+                       Oid             typid;
+
+                       typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
+                       result = get_opfamily_member(aform->amopfamily,
+                                                                                typid, typid,
+                                                                                BTLessStrategyNumber);
+                       if (OidIsValid(result))
+                               break;
+                       /* failure probably shouldn't happen, but keep looking if so */
+               }
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       return result;
+}
+
+/*
+ * get_mergejoin_opfamilies
+ *             Given a putatively mergejoinable operator, return a list of the OIDs
+ *             of the btree opfamilies in which it represents equality.
+ *
+ * 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.
+ *
+ * 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.
+ */
+List *
+get_mergejoin_opfamilies(Oid opno)
+{
+       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 = SearchSysCacheList(AMOPOPID, 1,
+                                                                ObjectIdGetDatum(opno),
+                                                                0, 0, 0);
+
+       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);
+
+       for (i = 0; i < catlist->n_members; i++)
+       {
+               HeapTuple       tuple = &catlist->members[i]->tuple;
+               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+               if (aform->amopmethod == HASH_AM_OID &&
+                       aform->amopstrategy == HTEqualStrategyNumber)
+               {
+                       /* No extra lookup needed if given operator is single-type */
+                       if (aform->amoplefttype == aform->amoprighttype)
+                       {
+                               if (lhs_opno)
+                                       *lhs_opno = opno;
+                               if (rhs_opno)
+                                       *rhs_opno = opno;
+                               result = true;
+                               break;
+                       }
+                       /*
+                        * 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;
+                       }
+               }
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       return result;
+}
+
+/*
+ * 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.
+ *
+ * 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 true if able to find the requested function(s), false if not.
+ * (This indicates that the operator should not have been marked oprcanhash.)
+ */
+bool
+get_op_hash_functions(Oid opno,
+                                         RegProcedure *lhs_procno, RegProcedure *rhs_procno)
+{
+       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 any one.
+        */
+       catlist = SearchSysCacheList(AMOPOPID, 1,
+                                                                ObjectIdGetDatum(opno),
+                                                                0, 0, 0);
+
+       for (i = 0; i < catlist->n_members; i++)
+       {
+               HeapTuple       tuple = &catlist->members[i]->tuple;
+               Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+               if (aform->amopmethod == HASH_AM_OID &&
+                       aform->amopstrategy == HTEqualStrategyNumber)
+               {
+                       /*
+                        * 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;
+                       }
                }
        }
 
        ReleaseSysCacheList(catlist);
 
-       if (OidIsValid(opclass))
+       return result;
+}
+
+/*
+ * 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.
+ *
+ * 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)
+{
+       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);
+               }
+       }
+
+       /* Now search the opfamilies */
+       for (i = 0; i < catlist->n_members; i++)
        {
-               /* Found a suitable opclass, get its default hash support function */
-               return get_opclass_proc(opclass, InvalidOid, HASHPROC);
+               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 */
+               if (op_form->amopmethod != BTREE_AM_OID)
+                       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)
+               {
+                       /* Only consider negators that are = */
+                       if (op_strategy != BTEqualStrategyNumber)
+                               continue;
+                       op_strategy = ROWCOMPARE_NE;
+               }
+
+               *opfamilies = lappend_oid(*opfamilies, opfamily_id);
+               *opstrats = lappend_int(*opstrats, op_strategy);
        }
 
-       /* Didn't find a match... */
-       return InvalidOid;
+       ReleaseSysCacheList(catlist);
+}
+
+/*
+ * 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.)
+ */
+bool
+ops_in_same_btree_opfamily(Oid opno1, Oid opno2)
+{
+       bool            result = false;
+       CatCList   *catlist;
+       int                     i;
+
+       /*
+        * We search through all the pg_amop entries for opno1.
+        */
+       catlist = SearchSysCacheList(AMOPOPID, 1,
+                                                                ObjectIdGetDatum(opno1),
+                                                                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);
+
+               /* must be btree */
+               if (op_form->amopmethod != BTREE_AM_OID)
+                       continue;
+
+               if (op_in_opfamily(opno2, op_form->amopfamily))
+               {
+                       result = true;
+                       break;
+               }
+       }
+
+       ReleaseSysCacheList(catlist);
+
+       return result;
 }
 
 
 /*                             ---------- AMPROC CACHES ----------                                              */
 
 /*
- * get_opclass_proc
+ * get_opfamily_proc
  *             Get the OID of the specified support function
- *             for the specified opclass and subtype.
+ *             for the specified opfamily and datatypes.
  *
  * Returns InvalidOid if there is no pg_amproc entry for the given keys.
  */
 Oid
-get_opclass_proc(Oid opclass, Oid subtype, int16 procnum)
+get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
 {
        HeapTuple       tp;
        Form_pg_amproc amproc_tup;
        RegProcedure result;
 
        tp = SearchSysCache(AMPROCNUM,
-                                               ObjectIdGetDatum(opclass),
-                                               ObjectIdGetDatum(subtype),
-                                               Int16GetDatum(procnum),
-                                               0);
+                                               ObjectIdGetDatum(opfamily),
+                                               ObjectIdGetDatum(lefttype),
+                                               ObjectIdGetDatum(righttype),
+                                               Int16GetDatum(procnum));
        if (!HeapTupleIsValid(tp))
                return InvalidOid;
        amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
@@ -352,25 +898,51 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
        ReleaseSysCache(tp);
 }
 
-/*                             ---------- INDEX CACHE ----------                                                */
+/*                             ---------- 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 = SearchSysCache(CONSTROID,
+                                               ObjectIdGetDatum(conoid),
+                                               0, 0, 0);
+       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 ----------                                              */
 
 /*
- * opclass_is_btree
+ * get_opclass_family
  *
- *             Returns TRUE iff the specified opclass is associated with the
- *             btree index access method.
+ *             Returns the OID of the operator family the opclass belongs to.
  */
-bool
-opclass_is_btree(Oid opclass)
+Oid
+get_opclass_family(Oid opclass)
 {
        HeapTuple       tp;
        Form_pg_opclass cla_tup;
-       bool            result;
+       Oid                     result;
 
        tp = SearchSysCache(CLAOID,
                                                ObjectIdGetDatum(opclass),
@@ -379,23 +951,22 @@ opclass_is_btree(Oid opclass)
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
 
-       result = (cla_tup->opcamid == BTREE_AM_OID);
+       result = cla_tup->opcfamily;
        ReleaseSysCache(tp);
        return result;
 }
 
 /*
- * opclass_is_hash
+ * get_opclass_input_type
  *
- *             Returns TRUE iff the specified opclass is associated with the
- *             hash index access method.
+ *             Returns the OID of the datatype the opclass indexes.
  */
-bool
-opclass_is_hash(Oid opclass)
+Oid
+get_opclass_input_type(Oid opclass)
 {
        HeapTuple       tp;
        Form_pg_opclass cla_tup;
-       bool            result;
+       Oid                     result;
 
        tp = SearchSysCache(CLAOID,
                                                ObjectIdGetDatum(opclass),
@@ -404,7 +975,7 @@ opclass_is_hash(Oid opclass)
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
 
-       result = (cla_tup->opcamid == HASH_AM_OID);
+       result = cla_tup->opcintype;
        ReleaseSysCache(tp);
        return result;
 }
@@ -489,83 +1060,37 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
 }
 
 /*
- * op_mergejoinable
- *
- *             Returns the left and right sort operators corresponding to a
- *             mergejoinable operator, or false if the operator is not mergejoinable.
- */
-bool
-op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
-{
-       HeapTuple       tp;
-       bool            result = false;
-
-       tp = SearchSysCache(OPEROID,
-                                               ObjectIdGetDatum(opno),
-                                               0, 0, 0);
-       if (HeapTupleIsValid(tp))
-       {
-               Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
-
-               if (optup->oprlsortop &&
-                       optup->oprrsortop)
-               {
-                       *leftOp = optup->oprlsortop;
-                       *rightOp = optup->oprrsortop;
-                       result = true;
-               }
-               ReleaseSysCache(tp);
-       }
-       return result;
-}
-
-/*
- * op_mergejoin_crossops
+ * op_mergejoinable
  *
- *             Returns the cross-type comparison operators (ltype "<" rtype and
- *             ltype ">" rtype) for an operator previously determined to be
- *             mergejoinable.  Optionally, fetches the regproc ids of these
- *             operators, as well as their operator OIDs.
+ * Returns true if the operator is potentially mergejoinable.  (The planner
+ * 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.)
  */
-void
-op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
-                                         RegProcedure *ltproc, RegProcedure *gtproc)
+bool
+op_mergejoinable(Oid opno)
 {
        HeapTuple       tp;
-       Form_pg_operator optup;
+       bool            result = false;
 
-       /*
-        * Get the declared comparison operators of the operator.
-        */
        tp = SearchSysCache(OPEROID,
                                                ObjectIdGetDatum(opno),
                                                0, 0, 0);
-       if (!HeapTupleIsValid(tp))      /* shouldn't happen */
-               elog(ERROR, "cache lookup failed for operator %u", opno);
-       optup = (Form_pg_operator) GETSTRUCT(tp);
-       *ltop = optup->oprltcmpop;
-       *gtop = optup->oprgtcmpop;
-       ReleaseSysCache(tp);
-
-       /* Check < op provided */
-       if (!OidIsValid(*ltop))
-               elog(ERROR, "mergejoin operator %u has no matching < operator",
-                        opno);
-       if (ltproc)
-               *ltproc = get_opcode(*ltop);
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
 
-       /* Check > op provided */
-       if (!OidIsValid(*gtop))
-               elog(ERROR, "mergejoin operator %u has no matching > operator",
-                        opno);
-       if (gtproc)
-               *gtproc = get_opcode(*gtop);
+               result = optup->oprcanmerge;
+               ReleaseSysCache(tp);
+       }
+       return result;
 }
 
 /*
  * op_hashjoinable
  *
- * Returns true if the operator is hashjoinable.
+ * Returns true if the operator is hashjoinable.  (There must be a suitable
+ * hash opfamily entry for this operator if it is so marked.)
  */
 bool
 op_hashjoinable(Oid opno)
@@ -772,15 +1297,36 @@ get_func_rettype(Oid funcid)
        return result;
 }
 
+/*
+ * get_func_nargs
+ *             Given procedure id, return the number of arguments.
+ */
+int
+get_func_nargs(Oid funcid)
+{
+       HeapTuple       tp;
+       int                     result;
+
+       tp = SearchSysCache(PROCOID,
+                                               ObjectIdGetDatum(funcid),
+                                               0, 0, 0);
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for function %u", funcid);
+
+       result = ((Form_pg_proc) GETSTRUCT(tp))->pronargs;
+       ReleaseSysCache(tp);
+       return result;
+}
+
 /*
  * get_func_signature
  *             Given procedure id, return the function's argument and result types.
  *             (The return value is the result type.)
  *
- * argtypes must point to a vector of size FUNC_MAX_ARGS.
+ * The arguments are returned as a palloc'd array.
  */
 Oid
-get_func_signature(Oid funcid, Oid *argtypes, int *nargs)
+get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
 {
        HeapTuple       tp;
        Form_pg_proc procstruct;
@@ -795,8 +1341,10 @@ get_func_signature(Oid funcid, Oid *argtypes, int *nargs)
        procstruct = (Form_pg_proc) GETSTRUCT(tp);
 
        result = procstruct->prorettype;
-       memcpy(argtypes, procstruct->proargtypes, FUNC_MAX_ARGS * sizeof(Oid));
        *nargs = (int) procstruct->pronargs;
+       Assert(*nargs == procstruct->proargtypes.dim1);
+       *argtypes = (Oid *) palloc(*nargs * sizeof(Oid));
+       memcpy(*argtypes, procstruct->proargtypes.values, *nargs * sizeof(Oid));
 
        ReleaseSysCache(tp);
        return result;
@@ -865,6 +1413,48 @@ 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 = SearchSysCache(PROCOID,
+                                               ObjectIdGetDatum(funcid),
+                                               0, 0, 0);
+       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 = SearchSysCache(PROCOID,
+                                               ObjectIdGetDatum(funcid),
+                                               0, 0, 0);
+       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 ----------                                     */
 
 /*
@@ -882,25 +1472,6 @@ get_relname_relid(const char *relname, Oid relnamespace)
                                                  0, 0);
 }
 
-/*
- * get_system_catalog_relid
- *             Get the OID of a system catalog identified by name.
- */
-Oid
-get_system_catalog_relid(const char *catname)
-{
-       Oid                     relid;
-
-       relid = GetSysCacheOid(RELNAMENSP,
-                                                  PointerGetDatum(catname),
-                                                  ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
-                                                  0, 0);
-       if (!OidIsValid(relid))
-               elog(ERROR, "cache lookup failed for system relation %s", catname);
-
-       return relid;
-}
-
 #ifdef NOT_USED
 /*
  * get_relnatts
@@ -986,14 +1557,15 @@ get_rel_namespace(Oid relid)
 }
 
 /*
- * get_rel_tablespace
- *             Returns the pg_tablespace OID associated with a given relation.
+ * get_rel_type_id
+ *
+ *             Returns the pg_type OID associated with a given relation.
  *
- * Note: failure return is InvalidOid, which cannot be distinguished from
- * "default tablespace for this database", but that seems OK.
+ * Note: not all pg_class entries have associated pg_type OIDs; so be
+ * careful to check for InvalidOid result.
  */
 Oid
-get_rel_tablespace(Oid relid)
+get_rel_type_id(Oid relid)
 {
        HeapTuple       tp;
 
@@ -1005,7 +1577,7 @@ get_rel_tablespace(Oid relid)
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
                Oid                     result;
 
-               result = reltup->reltablespace;
+               result = reltup->reltype;
                ReleaseSysCache(tp);
                return result;
        }
@@ -1014,15 +1586,12 @@ get_rel_tablespace(Oid relid)
 }
 
 /*
- * get_rel_type_id
- *
- *             Returns the pg_type OID associated with a given relation.
+ * get_rel_relkind
  *
- * Note: not all pg_class entries have associated pg_type OIDs; so be
- * careful to check for InvalidOid result.
+ *             Returns the relkind associated with a given relation.
  */
-Oid
-get_rel_type_id(Oid relid)
+char
+get_rel_relkind(Oid relid)
 {
        HeapTuple       tp;
 
@@ -1032,23 +1601,26 @@ get_rel_type_id(Oid relid)
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
-               Oid                     result;
+               char            result;
 
-               result = reltup->reltype;
+               result = reltup->relkind;
                ReleaseSysCache(tp);
                return result;
        }
        else
-               return InvalidOid;
+               return '\0';
 }
 
 /*
- * get_rel_relkind
+ * get_rel_tablespace
  *
- *             Returns the relkind associated with a given relation.
+ *             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.
  */
-char
-get_rel_relkind(Oid relid)
+Oid
+get_rel_tablespace(Oid relid)
 {
        HeapTuple       tp;
 
@@ -1058,14 +1630,14 @@ get_rel_relkind(Oid relid)
        if (HeapTupleIsValid(tp))
        {
                Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
-               char            result;
+               Oid                     result;
 
-               result = reltup->relkind;
+               result = reltup->reltablespace;
                ReleaseSysCache(tp);
                return result;
        }
        else
-               return '\0';
+               return InvalidOid;
 }
 
 
@@ -1211,7 +1783,11 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
  * This knowledge is intended to be centralized here --- direct references
  * to typelem elsewhere in the code are wrong, if they are associated with
  * I/O calls and not with actual subscripting operations!  (But see
- * bootstrap.c, which can't conveniently use this routine.)
+ * bootstrap.c's boot_get_type_io_data() if you need to change this.)
+ *
+ * As of PostgreSQL 8.1, output functions receive only the value itself
+ * and not any auxiliary parameters, so the name of this routine is now
+ * a bit of a misnomer ... it should be getTypeInputParam.
  */
 Oid
 getTypeIOParam(HeapTuple typeTuple)
@@ -1219,13 +1795,14 @@ getTypeIOParam(HeapTuple typeTuple)
        Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
 
        /*
-        * Composite types get their own OID as parameter; array types get
-        * their typelem as parameter; everybody else gets zero.
+        * Array types get their typelem as parameter; everybody else gets their
+        * own type OID as parameter.  (As of 8.2, domains must get their own OID
+        * even if their base type is an array.)
         */
-       if (typeStruct->typtype == 'c')
-               return HeapTupleGetOid(typeTuple);
-       else
+       if (typeStruct->typtype == TYPTYPE_BASE && OidIsValid(typeStruct->typelem))
                return typeStruct->typelem;
+       else
+               return HeapTupleGetOid(typeTuple);
 }
 
 /*
@@ -1248,6 +1825,38 @@ get_type_io_data(Oid typid,
        HeapTuple       typeTuple;
        Form_pg_type typeStruct;
 
+       /*
+        * In bootstrap mode, pass it off to bootstrap.c.  This hack allows us to
+        * use array_in and array_out during bootstrap.
+        */
+       if (IsBootstrapProcessingMode())
+       {
+               Oid                     typinput;
+               Oid                     typoutput;
+
+               boot_get_type_io_data(typid,
+                                                         typlen,
+                                                         typbyval,
+                                                         typalign,
+                                                         typdelim,
+                                                         typioparam,
+                                                         &typinput,
+                                                         &typoutput);
+               switch (which_func)
+               {
+                       case IOFunc_input:
+                               *func = typinput;
+                               break;
+                       case IOFunc_output:
+                               *func = typoutput;
+                               break;
+                       default:
+                               elog(ERROR, "binary I/O not supported during bootstrap");
+                               break;
+               }
+               return;
+       }
+
        typeTuple = SearchSysCache(TYPEOID,
                                                           ObjectIdGetDatum(typid),
                                                           0, 0, 0);
@@ -1322,33 +1931,6 @@ get_typstorage(Oid typid)
                return 'p';
 }
 
-/*
- * get_typtypmod
- *
- *             Given the type OID, return the typtypmod field (domain's typmod
- *             for base type)
- */
-int32
-get_typtypmod(Oid typid)
-{
-       HeapTuple       tp;
-
-       tp = SearchSysCache(TYPEOID,
-                                               ObjectIdGetDatum(typid),
-                                               0, 0, 0);
-       if (HeapTupleIsValid(tp))
-       {
-               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
-               int32           result;
-
-               result = typtup->typtypmod;
-               ReleaseSysCache(tp);
-               return result;
-       }
-       else
-               return -1;
-}
-
 /*
  * get_typdefault
  *       Given a type OID, return the type's default value, if any.
@@ -1407,12 +1989,11 @@ get_typdefault(Oid typid)
                        strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
                                                                                                                                datum));
                        /* Convert C string to a value of the given type */
-                       datum = OidFunctionCall3(type->typinput,
-                                                                        CStringGetDatum(strDefaultVal),
-                                                                        ObjectIdGetDatum(getTypeIOParam(typeTuple)),
-                                                                        Int32GetDatum(-1));
+                       datum = OidInputFunctionCall(type->typinput, strDefaultVal,
+                                                                                getTypeIOParam(typeTuple), -1);
                        /* Build a Const node containing the value */
                        expr = (Node *) makeConst(typid,
+                                                                         -1,
                                                                          type->typlen,
                                                                          datum,
                                                                          false,
@@ -1438,6 +2019,23 @@ get_typdefault(Oid typid)
  */
 Oid
 getBaseType(Oid typid)
+{
+       int32           typmod = -1;
+
+       return getBaseTypeAndTypmod(typid, &typmod);
+}
+
+/*
+ * getBaseTypeAndTypmod
+ *             If the given type is a domain, return its base type and typmod;
+ *             otherwise return the type's own OID, and leave *typmod unchanged.
+ *
+ * Note that the "applied typmod" should be -1 for every domain level
+ * above the bottommost; therefore, if the passed-in typid is indeed
+ * a domain, *typmod should be -1.
+ */
+Oid
+getBaseTypeAndTypmod(Oid typid, int32 *typmod)
 {
        /*
         * We loop to find the bottom base type in a stack of domains.
@@ -1453,14 +2051,17 @@ getBaseType(Oid 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);
                        break;
                }
 
+               Assert(*typmod == -1);
                typid = typTup->typbasetype;
+               *typmod = typTup->typtypmod;
+
                ReleaseSysCache(tup);
        }
 
@@ -1496,8 +2097,8 @@ get_typavgwidth(Oid typid, int32 typmod)
        {
                /*
                 * For BPCHAR, the max width is also the only width.  Otherwise we
-                * need to guess about the typical data width given the max. A
-                * sliding scale for percentage of max width seems reasonable.
+                * need to guess about the typical data width given the max. A sliding
+                * scale for percentage of max width seems reasonable.
                 */
                if (typid == BPCHAROID)
                        return maxwidth;
@@ -1508,8 +2109,8 @@ get_typavgwidth(Oid typid, int32 typmod)
 
                /*
                 * Beyond 1000, assume we're looking at something like
-                * "varchar(10000)" where the limit isn't actually reached often,
-                * and use a fixed estimate.
+                * "varchar(10000)" where the limit isn't actually reached often, and
+                * use a fixed estimate.
                 */
                return 32 + (1000 - 32) / 2;
        }
@@ -1547,6 +2148,28 @@ get_typtype(Oid typid)
                return '\0';
 }
 
+/*
+ * type_is_rowtype
+ *
+ *             Convenience function to determine whether a type OID represents
+ *             a "rowtype" type --- either RECORD or a named composite type.
+ */
+bool
+type_is_rowtype(Oid typid)
+{
+       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);
+}
+
 /*
  * get_typ_typrelid
  *
@@ -1609,50 +2232,24 @@ 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);
        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);
-
-               tp = SearchSysCache(TYPENAMENSP,
-                                                       PointerGetDatum(array_typename),
-                                                       ObjectIdGetDatum(namespaceId),
-                                                       0, 0);
-
-               pfree(array_typename);
-
-               if (HeapTupleIsValid(tp))
-               {
-                       Oid                     result;
-
-                       typtup = (Form_pg_type) GETSTRUCT(tp);
-                       if (typtup->typlen == -1 && typtup->typelem == typid)
-                               result = HeapTupleGetOid(tp);
-                       else
-                               result = InvalidOid;
-                       ReleaseSysCache(tp);
-                       return result;
-               }
        }
-       return InvalidOid;
+       return result;
 }
 
 /*
@@ -1696,8 +2293,7 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
  *             Get info needed for printing values of a type
  */
 void
-getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typIOParam,
-                                 bool *typIsVarlena)
+getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
 {
        HeapTuple       typeTuple;
        Form_pg_type pt;
@@ -1721,7 +2317,6 @@ getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typIOParam,
                                                format_type_be(type))));
 
        *typOutput = pt->typoutput;
-       *typIOParam = getTypeIOParam(typeTuple);
        *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
 
        ReleaseSysCache(typeTuple);
@@ -1768,8 +2363,7 @@ getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
  *             Get info needed for binary output of values of a type
  */
 void
-getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typIOParam,
-                                               bool *typIsVarlena)
+getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
 {
        HeapTuple       typeTuple;
        Form_pg_type pt;
@@ -1793,12 +2387,65 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typIOParam,
                                                format_type_be(type))));
 
        *typSend = pt->typsend;
-       *typIOParam = getTypeIOParam(typeTuple);
        *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
 
        ReleaseSysCache(typeTuple);
 }
 
+/*
+ * get_typmodin
+ *
+ *             Given the type OID, return the type's typmodin procedure, if any.
+ */
+Oid
+get_typmodin(Oid typid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache(TYPEOID,
+                                               ObjectIdGetDatum(typid),
+                                               0, 0, 0);
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = typtup->typmodin;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
+#ifdef NOT_USED
+/*
+ * get_typmodout
+ *
+ *             Given the type OID, return the type's typmodout procedure, if any.
+ */
+Oid
+get_typmodout(Oid typid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache(TYPEOID,
+                                               ObjectIdGetDatum(typid),
+                                               0, 0, 0);
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = typtup->typmodout;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+#endif /* NOT_USED */
+
 
 /*                             ---------- STATISTICS CACHE ----------                                   */
 
@@ -1840,8 +2487,8 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
  * entry, and we don't want to repeat the cache lookup unnecessarily.
  *
  * statstuple: pg_statistics tuple to be examined.
- * atttype: type OID of attribute.
- * atttypmod: typmod of attribute.
+ * 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.
  * values, nvalues: if not NULL, the slot's stavalues are extracted.
@@ -1895,18 +2542,18 @@ get_attstatsslot(HeapTuple statstuple,
                        elog(ERROR, "cache lookup failed for type %u", atttype);
                typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
 
-               /* Deconstruct array into Datum elements */
+               /* Deconstruct array into Datum elements; NULLs not expected */
                deconstruct_array(statarray,
                                                  atttype,
                                                  typeForm->typlen,
                                                  typeForm->typbyval,
                                                  typeForm->typalign,
-                                                 values, nvalues);
+                                                 values, NULL, nvalues);
 
                /*
-                * If the element type is pass-by-reference, we now have a bunch
-                * of Datums that are pointers into the syscache value.  Copy them
-                * to avoid problems if syscache decides to drop the entry.
+                * If the element type is pass-by-reference, we now have a bunch of
+                * Datums that are pointers into the syscache value.  Copy them to
+                * avoid problems if syscache decides to drop the entry.
                 */
                if (!typeForm->typbyval)
                {
@@ -1937,12 +2584,13 @@ get_attstatsslot(HeapTuple statstuple,
                statarray = DatumGetArrayTypeP(val);
 
                /*
-                * We expect the array to be a 1-D float4 array; verify that. We
-                * don't need to use deconstruct_array() since the array data is
-                * just going to look like a C array of float4 values.
+                * We expect the array to be a 1-D float4 array; verify that. We don't
+                * need to use deconstruct_array() since the array data is just going
+                * to look like a C array of float4 values.
                 */
                narrayelem = ARR_DIMS(statarray)[0];
                if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 ||
+                       ARR_HASNULL(statarray) ||
                        ARR_ELEMTYPE(statarray) != FLOAT4OID)
                        elog(ERROR, "stanumbers is not a 1-D float4 array");
                *numbers = (float4 *) palloc(narrayelem * sizeof(float4));
@@ -1959,6 +2607,12 @@ get_attstatsslot(HeapTuple statstuple,
        return true;
 }
 
+/*
+ * free_attstatsslot
+ *             Free data allocated by get_attstatsslot
+ *
+ * atttype need be valid only if values != NULL.
+ */
 void
 free_attstatsslot(Oid atttype,
                                  Datum *values, int nvalues,
@@ -2008,64 +2662,35 @@ get_namespace_name(Oid nspid)
                return NULL;
 }
 
+/*                             ---------- PG_AUTHID CACHE ----------                                    */
+
 /*
- * get_namespace_tablespace
- *             Returns the default tablespace of a given namespace
- *
- * Note: failure return is InvalidOid, which cannot be distinguished from
- * "default tablespace for this database", but that seems OK.
+ * get_roleid
+ *       Given a role name, look up the role's OID.
+ *       Returns InvalidOid if no such role.
  */
 Oid
-get_namespace_tablespace(Oid nspid)
+get_roleid(const char *rolname)
 {
-       HeapTuple       tp;
-
-       tp = SearchSysCache(NAMESPACEOID,
-                                               ObjectIdGetDatum(nspid),
-                                               0, 0, 0);
-       if (HeapTupleIsValid(tp))
-       {
-               Form_pg_namespace nsptup = (Form_pg_namespace) GETSTRUCT(tp);
-               Oid                     result;
-
-               result = nsptup->nsptablespace;
-               ReleaseSysCache(tp);
-               return result;
-       }
-       else
-               return InvalidOid;
+       return GetSysCacheOid(AUTHNAME,
+                                                 PointerGetDatum(rolname),
+                                                 0, 0, 0);
 }
 
-/*                             ---------- PG_SHADOW CACHE ----------                                    */
-
 /*
- * get_usesysid
- *
- *       Given a user name, look up the user's sysid.
- *       Raises an error if no such user (rather than returning zero,
- *       which might possibly be a valid usesysid).
- *
- * Note: the type of usesysid is currently int4, but may change to Oid
- * someday.  It'd be reasonable to return zero on failure if we were
- * using Oid ...
+ * get_roleid_checked
+ *       Given a role name, look up the role's OID.
+ *       ereports if no such role.
  */
-AclId
-get_usesysid(const char *username)
+Oid
+get_roleid_checked(const char *rolname)
 {
-       int32           result;
-       HeapTuple       userTup;
+       Oid                     roleid;
 
-       userTup = SearchSysCache(SHADOWNAME,
-                                                        PointerGetDatum(username),
-                                                        0, 0, 0);
-       if (!HeapTupleIsValid(userTup))
+       roleid = get_roleid(rolname);
+       if (!OidIsValid(roleid))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("user \"%s\" does not exist", username)));
-
-       result = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
-
-       ReleaseSysCache(userTup);
-
-       return result;
+                                errmsg("role \"%s\" does not exist", rolname)));
+       return roleid;
 }