OSDN Git Service

Allow reloption names to have qualifiers, initially supporting a TOAST
[pg-rex/syncrep.git] / src / backend / commands / typecmds.c
index f3f99c6..ca87b15 100644 (file)
@@ -3,12 +3,12 @@
  * typecmds.c
  *       Routines for SQL commands that manipulate types (and domains).
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.32 2003/02/19 23:41:15 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.131 2009/02/02 19:31:39 alvherre Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
@@ -20,7 +20,7 @@
  * NOTES
  *       These things must be defined and committed in the following order:
  *             "create function":
- *                             input/output functions
+ *                             input/output, recv/send functions
  *             "create type":
  *                             type
  *             "create operator":
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/genam.h"
-#include "catalog/catname.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/indexing.h"
-#include "catalog/namespace.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_enum.h"
+#include "catalog/pg_namespace.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_type_fn.h"
 #include "commands/defrem.h"
 #include "commands/tablecmds.h"
 #include "commands/typecmds.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
-#include "nodes/execnodes.h"
-#include "nodes/nodes.h"
-#include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/planner.h"
 #include "optimizer/var.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_func.h"
-#include "parser/parse_relation.h"
 #include "parser/parse_type.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
+#include "utils/tqual.h"
 
 
 /* result structure for get_rels_with_domain() */
 typedef struct
 {
-       Relation rel;                           /* opened and locked relation */
-       int             natts;                          /* number of attributes of interest */
-       int             *atts;                          /* attribute numbers */
+       Relation        rel;                    /* opened and locked relation */
+       int                     natts;                  /* number of attributes of interest */
+       int                *atts;                       /* attribute numbers */
        /* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
 } RelToCheck;
 
 
-static Oid     findTypeIOFunction(List *procname, Oid typeOid, bool isOutput);
+static Oid     findTypeInputFunction(List *procname, Oid typeOid);
+static Oid     findTypeOutputFunction(List *procname, Oid typeOid);
+static Oid     findTypeReceiveFunction(List *procname, Oid typeOid);
+static Oid     findTypeSendFunction(List *procname, Oid typeOid);
+static Oid     findTypeTypmodinFunction(List *procname);
+static Oid     findTypeTypmodoutFunction(List *procname);
+static Oid     findTypeAnalyzeFunction(List *procname, Oid typeOid);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
-static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
+static void checkDomainOwner(HeapTuple tup, TypeName *typename);
 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
-                                                                Oid baseTypeOid,
-                                                                int typMod, Constraint *constr,
-                                                                int *counter, char *domainName);
+                                       Oid baseTypeOid,
+                                       int typMod, Constraint *constr,
+                                       char *domainName);
 
 
 /*
  * DefineType
- *             Registers a new type.
+ *             Registers a new base type.
  */
 void
 DefineType(List *names, List *parameters)
 {
        char       *typeName;
        Oid                     typeNamespace;
-       AclResult       aclresult;
-       int16           internalLength = -1;    /* int2 */
-       Oid                     elemType = InvalidOid;
+       int16           internalLength = -1;    /* default: variable-length */
        List       *inputName = NIL;
        List       *outputName = NIL;
+       List       *receiveName = NIL;
+       List       *sendName = NIL;
+       List       *typmodinName = NIL;
+       List       *typmodoutName = NIL;
+       List       *analyzeName = NIL;
+       char            category = TYPCATEGORY_USER;
+       bool            preferred = false;
+       char            delimiter = DEFAULT_TYPDELIM;
+       Oid                     elemType = InvalidOid;
        char       *defaultValue = NULL;
        bool            byValue = false;
-       char            delimiter = DEFAULT_TYPDELIM;
        char            alignment = 'i';        /* default alignment */
        char            storage = 'p';  /* default TOAST storage method */
+       DefElem    *likeTypeEl = NULL;
+       DefElem    *internalLengthEl = NULL;
+       DefElem    *inputNameEl = NULL;
+       DefElem    *outputNameEl = NULL;
+       DefElem    *receiveNameEl = NULL;
+       DefElem    *sendNameEl = NULL;
+       DefElem    *typmodinNameEl = NULL;
+       DefElem    *typmodoutNameEl = NULL;
+       DefElem    *analyzeNameEl = NULL;
+       DefElem    *categoryEl = NULL;
+       DefElem    *preferredEl = NULL;
+       DefElem    *delimiterEl = NULL;
+       DefElem    *elemTypeEl = NULL;
+       DefElem    *defaultValueEl = NULL;
+       DefElem    *byValueEl = NULL;
+       DefElem    *alignmentEl = NULL;
+       DefElem    *storageEl = NULL;
        Oid                     inputOid;
        Oid                     outputOid;
-       char       *shadow_type;
-       List       *pl;
+       Oid                     receiveOid = InvalidOid;
+       Oid                     sendOid = InvalidOid;
+       Oid                     typmodinOid = InvalidOid;
+       Oid                     typmodoutOid = InvalidOid;
+       Oid                     analyzeOid = InvalidOid;
+       char       *array_type;
+       Oid                     array_oid;
        Oid                     typoid;
        Oid                     resulttype;
+       Relation        pg_type;
+       ListCell   *pl;
+
+       /*
+        * As of Postgres 8.4, we require superuser privilege to create a base
+        * type.  This is simple paranoia: there are too many ways to mess up the
+        * system with an incorrect type definition (for instance, representation
+        * parameters that don't match what the C code expects).  In practice
+        * it takes superuser privilege to create the I/O functions, and so the
+        * former requirement that you own the I/O functions pretty much forced
+        * superuserness anyway.  We're just making doubly sure here.
+        *
+        * XXX re-enable NOT_USED code sections below if you remove this test.
+        */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to create a base type")));
 
        /* Convert list of names to a name and namespace */
        typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
 
+#ifdef NOT_USED
+       /* XXX this is unnecessary given the superuser check above */
        /* Check we have creation rights in target namespace */
        aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
        if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, get_namespace_name(typeNamespace));
+               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                          get_namespace_name(typeNamespace));
+#endif
 
        /*
-        * Type names must be one character shorter than other names, allowing
-        * room to create the corresponding array type name with prepended
-        * "_".
+        * Look to see if type already exists (presumably as a shell; if not,
+        * TypeCreate will complain).
         */
-       if (strlen(typeName) > (NAMEDATALEN - 2))
-               elog(ERROR, "DefineType: type names must be %d characters or less",
-                        NAMEDATALEN - 2);
+       typoid = GetSysCacheOid(TYPENAMENSP,
+                                                       CStringGetDatum(typeName),
+                                                       ObjectIdGetDatum(typeNamespace),
+                                                       0, 0);
 
-       foreach(pl, parameters)
+       /*
+        * If it's not a shell, see if it's an autogenerated array type, and if so
+        * rename it out of the way.
+        */
+       if (OidIsValid(typoid) && get_typisdefined(typoid))
        {
-               DefElem    *defel = (DefElem *) lfirst(pl);
+               if (moveArrayTypeName(typoid, typeName, typeNamespace))
+                       typoid = InvalidOid;
+       }
 
-               if (strcasecmp(defel->defname, "internallength") == 0)
-                       internalLength = defGetTypeLength(defel);
-               else if (strcasecmp(defel->defname, "externallength") == 0)
-                       ;                                       /* ignored -- remove after 7.3 */
-               else if (strcasecmp(defel->defname, "input") == 0)
-                       inputName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "output") == 0)
-                       outputName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "send") == 0)
-                       ;                                       /* ignored -- remove after 7.3 */
-               else if (strcasecmp(defel->defname, "receive") == 0)
-                       ;                                       /* ignored -- remove after 7.3 */
-               else if (strcasecmp(defel->defname, "delimiter") == 0)
-               {
-                       char       *p = defGetString(defel);
+       /*
+        * If it doesn't exist, create it as a shell, so that the OID is known for
+        * use in the I/O function definitions.
+        */
+       if (!OidIsValid(typoid))
+       {
+               typoid = TypeShellMake(typeName, typeNamespace);
+               /* Make new shell type visible for modification below */
+               CommandCounterIncrement();
 
-                       delimiter = p[0];
-               }
-               else if (strcasecmp(defel->defname, "element") == 0)
-               {
-                       elemType = typenameTypeId(defGetTypeName(defel));
-                       /* disallow arrays of pseudotypes */
-                       if (get_typtype(elemType) == 'p')
-                               elog(ERROR, "Array element type cannot be %s",
-                                        format_type_be(elemType));
-               }
-               else if (strcasecmp(defel->defname, "default") == 0)
-                       defaultValue = defGetString(defel);
-               else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
-                       byValue = true;
-               else if (strcasecmp(defel->defname, "alignment") == 0)
-               {
-                       char       *a = defGetString(defel);
+               /*
+                * If the command was a parameterless CREATE TYPE, we're done ---
+                * creating the shell type was all we're supposed to do.
+                */
+               if (parameters == NIL)
+                       return;
+       }
+       else
+       {
+               /* Complain if dummy CREATE TYPE and entry already exists */
+               if (parameters == NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                        errmsg("type \"%s\" already exists", typeName)));
+       }
 
-                       /*
-                        * Note: if argument was an unquoted identifier, parser will
-                        * have applied translations to it, so be prepared to
-                        * recognize translated type names as well as the nominal
-                        * form.
-                        */
-                       if (strcasecmp(a, "double") == 0 ||
-                               strcasecmp(a, "float8") == 0 ||
-                               strcasecmp(a, "pg_catalog.float8") == 0)
-                               alignment = 'd';
-                       else if (strcasecmp(a, "int4") == 0 ||
-                                        strcasecmp(a, "pg_catalog.int4") == 0)
-                               alignment = 'i';
-                       else if (strcasecmp(a, "int2") == 0 ||
-                                        strcasecmp(a, "pg_catalog.int2") == 0)
-                               alignment = 's';
-                       else if (strcasecmp(a, "char") == 0 ||
-                                        strcasecmp(a, "pg_catalog.bpchar") == 0)
-                               alignment = 'c';
-                       else
-                               elog(ERROR, "DefineType: \"%s\" alignment not recognized",
-                                        a);
-               }
-               else if (strcasecmp(defel->defname, "storage") == 0)
-               {
-                       char       *a = defGetString(defel);
-
-                       if (strcasecmp(a, "plain") == 0)
-                               storage = 'p';
-                       else if (strcasecmp(a, "external") == 0)
-                               storage = 'e';
-                       else if (strcasecmp(a, "extended") == 0)
-                               storage = 'x';
-                       else if (strcasecmp(a, "main") == 0)
-                               storage = 'm';
-                       else
-                               elog(ERROR, "DefineType: \"%s\" storage not recognized",
-                                        a);
-               }
+       /* Extract the parameters from the parameter list */
+       foreach(pl, parameters)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+               DefElem   **defelp;
+
+               if (pg_strcasecmp(defel->defname, "like") == 0)
+                       defelp = &likeTypeEl;
+               else if (pg_strcasecmp(defel->defname, "internallength") == 0)
+                       defelp = &internalLengthEl;
+               else if (pg_strcasecmp(defel->defname, "input") == 0)
+                       defelp = &inputNameEl;
+               else if (pg_strcasecmp(defel->defname, "output") == 0)
+                       defelp = &outputNameEl;
+               else if (pg_strcasecmp(defel->defname, "receive") == 0)
+                       defelp = &receiveNameEl;
+               else if (pg_strcasecmp(defel->defname, "send") == 0)
+                       defelp = &sendNameEl;
+               else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
+                       defelp = &typmodinNameEl;
+               else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
+                       defelp = &typmodoutNameEl;
+               else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
+                                pg_strcasecmp(defel->defname, "analyse") == 0)
+                       defelp = &analyzeNameEl;
+               else if (pg_strcasecmp(defel->defname, "category") == 0)
+                       defelp = &categoryEl;
+               else if (pg_strcasecmp(defel->defname, "preferred") == 0)
+                       defelp = &preferredEl;
+               else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
+                       defelp = &delimiterEl;
+               else if (pg_strcasecmp(defel->defname, "element") == 0)
+                       defelp = &elemTypeEl;
+               else if (pg_strcasecmp(defel->defname, "default") == 0)
+                       defelp = &defaultValueEl;
+               else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
+                       defelp = &byValueEl;
+               else if (pg_strcasecmp(defel->defname, "alignment") == 0)
+                       defelp = &alignmentEl;
+               else if (pg_strcasecmp(defel->defname, "storage") == 0)
+                       defelp = &storageEl;
                else
                {
-                       elog(WARNING, "DefineType: attribute \"%s\" not recognized",
-                                defel->defname);
+                       /* WARNING, not ERROR, for historical backwards-compatibility */
+                       ereport(WARNING,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("type attribute \"%s\" not recognized",
+                                                       defel->defname)));
+                       continue;
                }
+               if (*defelp != NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("conflicting or redundant options")));
+               *defelp = defel;
+       }
+
+       /*
+        * Now interpret the options; we do this separately so that LIKE can
+        * be overridden by other options regardless of the ordering in the
+        * parameter list.
+        */
+       if (likeTypeEl)
+       {
+               Type     likeType;
+               Form_pg_type likeForm;
+
+               likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+               likeForm = (Form_pg_type) GETSTRUCT(likeType);
+               internalLength = likeForm->typlen;
+               byValue = likeForm->typbyval;
+               alignment = likeForm->typalign;
+               storage = likeForm->typstorage;
+               ReleaseSysCache(likeType);
+       }
+       if (internalLengthEl)
+               internalLength = defGetTypeLength(internalLengthEl);
+       if (inputNameEl)
+               inputName = defGetQualifiedName(inputNameEl);
+       if (outputNameEl)
+               outputName = defGetQualifiedName(outputNameEl);
+       if (receiveNameEl)
+               receiveName = defGetQualifiedName(receiveNameEl);
+       if (sendNameEl)
+               sendName = defGetQualifiedName(sendNameEl);
+       if (typmodinNameEl)
+               typmodinName = defGetQualifiedName(typmodinNameEl);
+       if (typmodoutNameEl)
+               typmodoutName = defGetQualifiedName(typmodoutNameEl);
+       if (analyzeNameEl)
+               analyzeName = defGetQualifiedName(analyzeNameEl);
+       if (categoryEl)
+       {
+               char       *p = defGetString(categoryEl);
+
+               category = p[0];
+               /* restrict to non-control ASCII */
+               if (category < 32 || category > 126)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("invalid type category \"%s\": must be simple ASCII",
+                                                       p)));
+       }
+       if (preferredEl)
+               preferred = defGetBoolean(preferredEl);
+       if (delimiterEl)
+       {
+               char       *p = defGetString(delimiterEl);
+
+               delimiter = p[0];
+               /* XXX shouldn't we restrict the delimiter? */
+       }
+       if (elemTypeEl)
+       {
+               elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl), NULL);
+               /* disallow arrays of pseudotypes */
+               if (get_typtype(elemType) == TYPTYPE_PSEUDO)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("array element type cannot be %s",
+                                                       format_type_be(elemType))));
+       }
+       if (defaultValueEl)
+               defaultValue = defGetString(defaultValueEl);
+       if (byValueEl)
+               byValue = defGetBoolean(byValueEl);
+       if (alignmentEl)
+       {
+               char       *a = defGetString(alignmentEl);
+
+               /*
+                * Note: if argument was an unquoted identifier, parser will have
+                * applied translations to it, so be prepared to recognize
+                * translated type names as well as the nominal form.
+                */
+               if (pg_strcasecmp(a, "double") == 0 ||
+                       pg_strcasecmp(a, "float8") == 0 ||
+                       pg_strcasecmp(a, "pg_catalog.float8") == 0)
+                       alignment = 'd';
+               else if (pg_strcasecmp(a, "int4") == 0 ||
+                                pg_strcasecmp(a, "pg_catalog.int4") == 0)
+                       alignment = 'i';
+               else if (pg_strcasecmp(a, "int2") == 0 ||
+                                pg_strcasecmp(a, "pg_catalog.int2") == 0)
+                       alignment = 's';
+               else if (pg_strcasecmp(a, "char") == 0 ||
+                                pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
+                       alignment = 'c';
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("alignment \"%s\" not recognized", a)));
+       }
+       if (storageEl)
+       {
+               char       *a = defGetString(storageEl);
+
+               if (pg_strcasecmp(a, "plain") == 0)
+                       storage = 'p';
+               else if (pg_strcasecmp(a, "external") == 0)
+                       storage = 'e';
+               else if (pg_strcasecmp(a, "extended") == 0)
+                       storage = 'x';
+               else if (pg_strcasecmp(a, "main") == 0)
+                       storage = 'm';
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("storage \"%s\" not recognized", a)));
        }
 
        /*
         * make sure we have our required definitions
         */
        if (inputName == NIL)
-               elog(ERROR, "Define: \"input\" unspecified");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type input function must be specified")));
        if (outputName == NIL)
-               elog(ERROR, "Define: \"output\" unspecified");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type output function must be specified")));
 
-       /*
-        * Look to see if type already exists (presumably as a shell; if not,
-        * TypeCreate will complain).  If it doesn't, create it as a shell,
-        * so that the OID is known for use in the I/O function definitions.
-        */
-       typoid = GetSysCacheOid(TYPENAMENSP,
-                                                       CStringGetDatum(typeName),
-                                                       ObjectIdGetDatum(typeNamespace),
-                                                       0, 0);
-       if (!OidIsValid(typoid))
-       {
-               typoid = TypeShellMake(typeName, typeNamespace);
-               /* Make new shell type visible for modification below */
-               CommandCounterIncrement();
-       }
+       if (typmodinName == NIL && typmodoutName != NIL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type modifier output function is useless without a type modifier input function")));
 
        /*
         * Convert I/O proc names to OIDs
         */
-       inputOid = findTypeIOFunction(inputName, typoid, false);
-       outputOid = findTypeIOFunction(outputName, typoid, true);
+       inputOid = findTypeInputFunction(inputName, typoid);
+       outputOid = findTypeOutputFunction(outputName, typoid);
+       if (receiveName)
+               receiveOid = findTypeReceiveFunction(receiveName, typoid);
+       if (sendName)
+               sendOid = findTypeSendFunction(sendName, typoid);
 
        /*
         * Verify that I/O procs return the expected thing.  If we see OPAQUE,
@@ -248,43 +426,131 @@ DefineType(List *names, List *parameters)
        {
                if (resulttype == OPAQUEOID)
                {
-                       elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to %s",
-                                NameListToString(inputName), typeName);
+                       /* backwards-compatibility hack */
+                       ereport(WARNING,
+                                       (errmsg("changing return type of function %s from \"opaque\" to %s",
+                                                       NameListToString(inputName), typeName)));
                        SetFunctionReturnType(inputOid, typoid);
                }
                else
-                       elog(ERROR, "Type input function %s must return %s",
-                                NameListToString(inputName), typeName);
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("type input function %s must return type %s",
+                                                       NameListToString(inputName), typeName)));
        }
        resulttype = get_func_rettype(outputOid);
        if (resulttype != CSTRINGOID)
        {
                if (resulttype == OPAQUEOID)
                {
-                       elog(NOTICE, "TypeCreate: changing return type of function %s from OPAQUE to CSTRING",
-                                NameListToString(outputName));
+                       /* backwards-compatibility hack */
+                       ereport(WARNING,
+                                       (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
+                                                       NameListToString(outputName))));
                        SetFunctionReturnType(outputOid, CSTRINGOID);
                }
                else
-                       elog(ERROR, "Type output function %s must return cstring",
-                                NameListToString(outputName));
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                          errmsg("type output function %s must return type \"cstring\"",
+                                         NameListToString(outputName))));
+       }
+       if (receiveOid)
+       {
+               resulttype = get_func_rettype(receiveOid);
+               if (resulttype != typoid)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("type receive function %s must return type %s",
+                                                       NameListToString(receiveName), typeName)));
        }
+       if (sendOid)
+       {
+               resulttype = get_func_rettype(sendOid);
+               if (resulttype != BYTEAOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                  errmsg("type send function %s must return type \"bytea\"",
+                                                 NameListToString(sendName))));
+       }
+
+       /*
+        * Convert typmodin/out function proc names to OIDs.
+        */
+       if (typmodinName)
+               typmodinOid = findTypeTypmodinFunction(typmodinName);
+       if (typmodoutName)
+               typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
+
+       /*
+        * Convert analysis function proc name to an OID. If no analysis function
+        * is specified, we'll use zero to select the built-in default algorithm.
+        */
+       if (analyzeName)
+               analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+
+       /*
+        * Check permissions on functions.      We choose to require the creator/owner
+        * of a type to also own the underlying functions.      Since creating a type
+        * is tantamount to granting public execute access on the functions, the
+        * minimum sane check would be for execute-with-grant-option.  But we
+        * don't have a way to make the type go away if the grant option is
+        * revoked, so ownership seems better.
+        */
+#ifdef NOT_USED
+       /* XXX this is unnecessary given the superuser check above */
+       if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(inputName));
+       if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(outputName));
+       if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(receiveName));
+       if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(sendName));
+       if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(typmodinName));
+       if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(typmodoutName));
+       if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+                                          NameListToString(analyzeName));
+#endif
+
+       /* Preassign array type OID so we can insert it in pg_type.typarray */
+       pg_type = heap_open(TypeRelationId, AccessShareLock);
+       array_oid = GetNewOid(pg_type);
+       heap_close(pg_type, AccessShareLock);
 
        /*
         * now have TypeCreate do all the real work.
         */
        typoid =
-               TypeCreate(typeName,    /* type name */
+               TypeCreate(InvalidOid,  /* no predetermined type OID */
+                                  typeName,    /* type name */
                                   typeNamespace,               /* namespace */
-                                  InvalidOid,  /* preassigned type oid (not done here) */
                                   InvalidOid,  /* relation oid (n/a here) */
                                   0,                   /* relation kind (ditto) */
                                   internalLength,              /* internal size */
-                                  'b',                 /* type-type (base type) */
+                                  TYPTYPE_BASE,        /* type-type (base type) */
+                                  category,    /* type-category */
+                                  preferred,   /* is it a preferred type? */
                                   delimiter,   /* array element delimiter */
                                   inputOid,    /* input procedure */
                                   outputOid,   /* output procedure */
+                                  receiveOid,  /* receive procedure */
+                                  sendOid,             /* send procedure */
+                                  typmodinOid, /* typmodin procedure */
+                                  typmodoutOid,        /* typmodout procedure */
+                                  analyzeOid,  /* analyze procedure */
                                   elemType,    /* element type ID */
+                                  false,               /* this is not an array type */
+                                  array_oid,   /* array type we are about to create */
                                   InvalidOid,  /* base type ID (only for domains) */
                                   defaultValue,        /* default type value */
                                   NULL,                /* no binary form available */
@@ -296,25 +562,33 @@ DefineType(List *names, List *parameters)
                                   false);              /* Type NOT NULL */
 
        /*
-        * When we create a base type (as opposed to a complex type) we need
-        * to have an array entry for it in pg_type as well.
+        * Create the array type that goes with it.
         */
-       shadow_type = makeArrayTypeName(typeName);
+       array_type = makeArrayTypeName(typeName, typeNamespace);
 
        /* alignment must be 'i' or 'd' for arrays */
        alignment = (alignment == 'd') ? 'd' : 'i';
 
-       TypeCreate(shadow_type,         /* type name */
+       TypeCreate(array_oid,           /* force assignment of this type OID */
+                          array_type,          /* type name */
                           typeNamespace,       /* namespace */
-                          InvalidOid,          /* preassigned type oid (not done here) */
                           InvalidOid,          /* relation oid (n/a here) */
                           0,                           /* relation kind (ditto) */
-                          -1,                          /* internal size */
-                          'b',                         /* type-type (base type) */
-                          DEFAULT_TYPDELIM,    /* array element delimiter */
+                          -1,                          /* internal size (always varlena) */
+                          TYPTYPE_BASE,        /* type-type (base type) */
+                          TYPCATEGORY_ARRAY, /* type-category (array) */
+                          false,                       /* array types are never preferred */
+                          delimiter,           /* array element delimiter */
                           F_ARRAY_IN,          /* input procedure */
                           F_ARRAY_OUT,         /* output procedure */
+                          F_ARRAY_RECV,        /* receive procedure */
+                          F_ARRAY_SEND,        /* send procedure */
+                          typmodinOid,         /* typmodin procedure */
+                          typmodoutOid,        /* typmodout procedure */
+                          InvalidOid,          /* analyze procedure - default */
                           typoid,                      /* element type ID */
+                          true,                        /* yes this is an array type */
+                          InvalidOid,          /* no further array type */
                           InvalidOid,          /* base type ID */
                           NULL,                        /* never a default type value */
                           NULL,                        /* binary default isn't sent either */
@@ -325,57 +599,98 @@ DefineType(List *names, List *parameters)
                           0,                           /* Array dimensions of typbasetype */
                           false);                      /* Type NOT NULL */
 
-       pfree(shadow_type);
+       pfree(array_type);
 }
 
 
 /*
- *     RemoveType
- *             Removes a datatype.
+ *     RemoveTypes
+ *             Implements DROP TYPE and DROP DOMAIN
+ *
+ * Note: if DOMAIN is specified, we enforce that each type is a domain, but
+ * we don't enforce the converse for DROP TYPE
  */
 void
-RemoveType(List *names, DropBehavior behavior)
+RemoveTypes(DropStmt *drop)
 {
-       TypeName   *typename;
-       Oid                     typeoid;
-       HeapTuple       tup;
-       ObjectAddress object;
+       ObjectAddresses *objects;
+       ListCell                *cell;
 
-       /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
+       /*
+        * First we identify all the types, then we delete them in a single
+        * performMultipleDeletions() call.  This is to avoid unwanted
+        * DROP RESTRICT errors if one of the types depends on another.
+        */
+       objects = new_object_addresses();
 
-       /* Use LookupTypeName here so that shell types can be removed. */
-       typeoid = LookupTypeName(typename);
-       if (!OidIsValid(typeoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       foreach(cell, drop->objects)
+       {
+               List       *names = (List *) lfirst(cell);
+               TypeName   *typename;
+               Oid                     typeoid;
+               HeapTuple       tup;
+               ObjectAddress object;
+               Form_pg_type typ;
 
-       tup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(typeoid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               /* Make a TypeName so we can use standard type lookup machinery */
+               typename = makeTypeNameFromNameList(names);
 
-       /* Permission check: must own type or its namespace */
-       if (!pg_type_ownercheck(typeoid, GetUserId()) &&
-               !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
-                                                                GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
+               /* Use LookupTypeName here so that shell types can be removed. */
+               tup = LookupTypeName(NULL, typename, NULL);
+               if (tup == NULL)
+               {
+                       if (!drop->missing_ok)
+                       {
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("type \"%s\" does not exist",
+                                                               TypeNameToString(typename))));
+                       }
+                       else
+                       {
+                               ereport(NOTICE,
+                                               (errmsg("type \"%s\" does not exist, skipping",
+                                                               TypeNameToString(typename))));
+                       }
+                       continue;
+               }
 
-       ReleaseSysCache(tup);
+               typeoid = typeTypeId(tup);
+               typ = (Form_pg_type) GETSTRUCT(tup);
 
-       /*
-        * Do the deletion
-        */
-       object.classId = RelOid_pg_type;
-       object.objectId = typeoid;
-       object.objectSubId = 0;
+               /* Permission check: must own type or its namespace */
+               if (!pg_type_ownercheck(typeoid, GetUserId()) &&
+                       !pg_namespace_ownercheck(typ->typnamespace, GetUserId()))
+                       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                                                  format_type_be(typeoid));
 
-       performDeletion(&object, behavior);
+               if (drop->removeType == OBJECT_DOMAIN)
+               {
+                       /* Check that this is actually a domain */
+                       if (typ->typtype != TYPTYPE_DOMAIN)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("\"%s\" is not a domain",
+                                                               TypeNameToString(typename))));
+               }
+
+               /*
+                * Note: we need no special check for array types here, as the normal
+                * treatment of internal dependencies handles it just fine
+                */
+
+               object.classId = TypeRelationId;
+               object.objectId = typeoid;
+               object.objectSubId = 0;
+
+               add_exact_object_address(&object, objects);
+
+               ReleaseSysCache(tup);
+       }
+
+       performMultipleDeletions(objects, drop->behavior);
+
+       free_object_addresses(objects);
 }
 
 
@@ -388,17 +703,24 @@ RemoveTypeById(Oid typeOid)
        Relation        relation;
        HeapTuple       tup;
 
-       relation = heap_openr(TypeRelationName, RowExclusiveLock);
+       relation = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCache(TYPEOID,
                                                 ObjectIdGetDatum(typeOid),
                                                 0, 0, 0);
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "RemoveTypeById: type %u not found",
-                        typeOid);
+               elog(ERROR, "cache lookup failed for type %u", typeOid);
 
        simple_heap_delete(relation, &tup->t_self);
 
+       /*
+        * If it is an enum, delete the pg_enum entries too; we don't bother with
+        * making dependency entries for those, so it has to be done "by hand"
+        * here.
+        */
+       if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
+               EnumValuesDelete(typeOid);
+
        ReleaseSysCache(tup);
 
        heap_close(relation, RowExclusiveLock);
@@ -418,27 +740,32 @@ DefineDomain(CreateDomainStmt *stmt)
        int16           internalLength;
        Oid                     inputProcedure;
        Oid                     outputProcedure;
+       Oid                     receiveProcedure;
+       Oid                     sendProcedure;
+       Oid                     analyzeProcedure;
        bool            byValue;
+       Oid                     typelem;
+       char            category;
        char            delimiter;
        char            alignment;
        char            storage;
        char            typtype;
        Datum           datum;
        bool            isnull;
-       Node       *defaultExpr = NULL;
        char       *defaultValue = NULL;
        char       *defaultValueBin = NULL;
+       bool            saw_default = false;
        bool            typNotNull = false;
        bool            nullDefined = false;
-       Oid                     basetypelem;
-       int32           typNDims = length(stmt->typename->arrayBounds);
+       int32           typNDims = list_length(stmt->typename->arrayBounds);
        HeapTuple       typeTup;
        List       *schema = stmt->constraints;
-       List       *listptr;
+       ListCell   *listptr;
        Oid                     basetypeoid;
        Oid                     domainoid;
-       Form_pg_type    baseType;
-       int                     counter = 0;
+       Oid                     old_type_oid;
+       Form_pg_type baseType;
+       int32           basetypeMod;
 
        /* Convert list of names to a name and namespace */
        domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -448,34 +775,45 @@ DefineDomain(CreateDomainStmt *stmt)
        aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
                                                                          ACL_CREATE);
        if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, get_namespace_name(domainNamespace));
+               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                          get_namespace_name(domainNamespace));
 
        /*
-        * Domainnames, unlike typenames don't need to account for the '_'
-        * prefix.      So they can be one character longer.
+        * Check for collision with an existing type name.      If there is one and
+        * it's an autogenerated array, we can rename it out of the way.
         */
-       if (strlen(domainName) > (NAMEDATALEN - 1))
-               elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
-                        NAMEDATALEN - 1);
+       old_type_oid = GetSysCacheOid(TYPENAMENSP,
+                                                                 CStringGetDatum(domainName),
+                                                                 ObjectIdGetDatum(domainNamespace),
+                                                                 0, 0);
+       if (OidIsValid(old_type_oid))
+       {
+               if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                        errmsg("type \"%s\" already exists", domainName)));
+       }
 
        /*
         * Look up the base type.
         */
-       typeTup = typenameType(stmt->typename);
-
+       typeTup = typenameType(NULL, stmt->typename, &basetypeMod);
        baseType = (Form_pg_type) GETSTRUCT(typeTup);
        basetypeoid = HeapTupleGetOid(typeTup);
 
        /*
-        * Base type must be a plain base type.  Domains over pseudo types
-        * would create a security hole.  Domains of domains might be made to
-        * work in the future, but not today.  Ditto for domains over complex
-        * types.
+        * Base type must be a plain base type, another domain or an enum. Domains
+        * over pseudotypes would create a security hole.  Domains over composite
+        * types might be made to work in the future, but not today.
         */
        typtype = baseType->typtype;
-       if (typtype != 'b')
-               elog(ERROR, "DefineDomain: %s is not a basetype",
-                        TypeNameToString(stmt->typename));
+       if (typtype != TYPTYPE_BASE &&
+               typtype != TYPTYPE_DOMAIN &&
+               typtype != TYPTYPE_ENUM)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("\"%s\" is not a valid base type for a domain",
+                                               TypeNameToString(stmt->typename))));
 
        /* passed by value */
        byValue = baseType->typbyval;
@@ -489,127 +827,184 @@ DefineDomain(CreateDomainStmt *stmt)
        /* Storage Length */
        internalLength = baseType->typlen;
 
+       /* Type Category */
+       category = baseType->typcategory;
+
+       /* Array element type (in case base type is an array) */
+       typelem = baseType->typelem;
+
        /* Array element Delimiter */
        delimiter = baseType->typdelim;
 
        /* I/O Functions */
-       inputProcedure = baseType->typinput;
+       inputProcedure = F_DOMAIN_IN;
        outputProcedure = baseType->typoutput;
+       receiveProcedure = F_DOMAIN_RECV;
+       sendProcedure = baseType->typsend;
+
+       /* Domains never accept typmods, so no typmodin/typmodout needed */
+
+       /* Analysis function */
+       analyzeProcedure = baseType->typanalyze;
 
        /* Inherited default value */
        datum = SysCacheGetAttr(TYPEOID, typeTup,
                                                        Anum_pg_type_typdefault, &isnull);
        if (!isnull)
-               defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
+               defaultValue = TextDatumGetCString(datum);
 
        /* Inherited default binary value */
        datum = SysCacheGetAttr(TYPEOID, typeTup,
                                                        Anum_pg_type_typdefaultbin, &isnull);
        if (!isnull)
-               defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
-
-       /*
-        * Pull out the typelem name of the parent OID.
-        *
-        * This is what enables us to make a domain of an array
-        */
-       basetypelem = baseType->typelem;
+               defaultValueBin = TextDatumGetCString(datum);
 
        /*
-        * Run through constraints manually to avoid the additional
-        * processing conducted by DefineRelation() and friends.
+        * Run through constraints manually to avoid the additional processing
+        * conducted by DefineRelation() and friends.
         */
        foreach(listptr, schema)
        {
                Node       *newConstraint = lfirst(listptr);
-               Constraint *colDef;
-               ParseState *pstate;
+               Constraint *constr;
 
                /* Check for unsupported constraint types */
                if (IsA(newConstraint, FkConstraint))
-                       elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                               errmsg("foreign key constraints not possible for domains")));
 
-               /* this case should not happen */
+               /* otherwise it should be a plain Constraint */
                if (!IsA(newConstraint, Constraint))
-                       elog(ERROR, "DefineDomain: unexpected constraint node type");
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(newConstraint));
 
-               colDef = (Constraint *) newConstraint;
+               constr = (Constraint *) newConstraint;
 
-               switch (colDef->contype)
+               switch (constr->contype)
                {
                        case CONSTR_DEFAULT:
-                               /*
-                                * The inherited default value may be overridden by the
-                                * user with the DEFAULT <expr> statement.
-                                */
-                               if (defaultExpr)
-                                       elog(ERROR, "CREATE DOMAIN has multiple DEFAULT expressions");
-                               /* Create a dummy ParseState for transformExpr */
-                               pstate = make_parsestate(NULL);
 
                                /*
-                                * Cook the colDef->raw_expr into an expression. Note:
-                                * Name is strictly for error message
+                                * The inherited default value may be overridden by the user
+                                * with the DEFAULT <expr> clause ... but only once.
                                 */
-                               defaultExpr = cookDefault(pstate, colDef->raw_expr,
-                                                                                 basetypeoid,
-                                                                                 stmt->typename->typmod,
-                                                                                 domainName);
+                               if (saw_default)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("multiple default expressions")));
+                               saw_default = true;
 
-                               /*
-                                * Expression must be stored as a nodeToString result, but
-                                * we also require a valid textual representation (mainly
-                                * to make life easier for pg_dump).
-                                */
-                               defaultValue = deparse_expression(defaultExpr,
-                                                                                 deparse_context_for(domainName,
-                                                                                                                         InvalidOid),
-                                                                                                 false, false);
-                               defaultValueBin = nodeToString(defaultExpr);
+                               if (constr->raw_expr)
+                               {
+                                       ParseState *pstate;
+                                       Node       *defaultExpr;
+
+                                       /* Create a dummy ParseState for transformExpr */
+                                       pstate = make_parsestate(NULL);
+
+                                       /*
+                                        * Cook the constr->raw_expr into an expression. Note:
+                                        * name is strictly for error message
+                                        */
+                                       defaultExpr = cookDefault(pstate, constr->raw_expr,
+                                                                                         basetypeoid,
+                                                                                         basetypeMod,
+                                                                                         domainName);
+
+                                       /*
+                                        * If the expression is just a NULL constant, we treat it
+                                        * like not having a default.
+                                        *
+                                        * Note that if the basetype is another domain, we'll see
+                                        * a CoerceToDomain expr here and not discard the default.
+                                        * This is critical because the domain default needs to be
+                                        * retained to override any default that the base domain
+                                        * might have.
+                                        */
+                                       if (defaultExpr == NULL ||
+                                               (IsA(defaultExpr, Const) &&
+                                                ((Const *) defaultExpr)->constisnull))
+                                       {
+                                               defaultValue = NULL;
+                                               defaultValueBin = NULL;
+                                       }
+                                       else
+                                       {
+                                               /*
+                                                * Expression must be stored as a nodeToString result,
+                                                * but we also require a valid textual representation
+                                                * (mainly to make life easier for pg_dump).
+                                                */
+                                               defaultValue =
+                                                       deparse_expression(defaultExpr,
+                                                                                          deparse_context_for(domainName,
+                                                                                                                                InvalidOid),
+                                                                                          false, false);
+                                               defaultValueBin = nodeToString(defaultExpr);
+                                       }
+                               }
+                               else
+                               {
+                                       /* No default (can this still happen?) */
+                                       defaultValue = NULL;
+                                       defaultValueBin = NULL;
+                               }
                                break;
 
                        case CONSTR_NOTNULL:
                                if (nullDefined && !typNotNull)
-                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                  errmsg("conflicting NULL/NOT NULL constraints")));
                                typNotNull = true;
                                nullDefined = true;
                                break;
 
                        case CONSTR_NULL:
                                if (nullDefined && typNotNull)
-                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                  errmsg("conflicting NULL/NOT NULL constraints")));
                                typNotNull = false;
                                nullDefined = true;
-                               break;
+                               break;
+
+                       case CONSTR_CHECK:
 
-                       case CONSTR_CHECK:
                                /*
-                                * Check constraints are handled after domain creation, as they
-                                * require the Oid of the domain
+                                * Check constraints are handled after domain creation, as
+                                * they require the Oid of the domain
                                 */
-                               break;
+                               break;
 
                                /*
                                 * All else are error cases
                                 */
-                       case CONSTR_UNIQUE:
-                               elog(ERROR, "CREATE DOMAIN / UNIQUE not supported");
-                               break;
-
-                       case CONSTR_PRIMARY:
-                               elog(ERROR, "CREATE DOMAIN / PRIMARY KEY not supported");
-                               break;
-
-                       case CONSTR_ATTR_DEFERRABLE:
-                       case CONSTR_ATTR_NOT_DEFERRABLE:
-                       case CONSTR_ATTR_DEFERRED:
-                       case CONSTR_ATTR_IMMEDIATE:
-                               elog(ERROR, "CREATE DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
-                                                       " and IMMEDIATE not supported");
-                               break;
+                       case CONSTR_UNIQUE:
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("unique constraints not possible for domains")));
+                               break;
+
+                       case CONSTR_PRIMARY:
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                               errmsg("primary key constraints not possible for domains")));
+                               break;
+
+                       case CONSTR_ATTR_DEFERRABLE:
+                       case CONSTR_ATTR_NOT_DEFERRABLE:
+                       case CONSTR_ATTR_DEFERRED:
+                       case CONSTR_ATTR_IMMEDIATE:
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("specifying constraint deferrability not supported for domains")));
+                               break;
 
                        default:
-                               elog(ERROR, "DefineDomain: unrecognized constraint subtype");
+                               elog(ERROR, "unrecognized constraint subtype: %d",
+                                        (int) constr->contype);
                                break;
                }
        }
@@ -618,26 +1013,35 @@ DefineDomain(CreateDomainStmt *stmt)
         * Have TypeCreate do all the real work.
         */
        domainoid =
-               TypeCreate(domainName,  /* type name */
+               TypeCreate(InvalidOid,  /* no predetermined type OID */
+                                  domainName,  /* type name */
                                   domainNamespace,             /* namespace */
-                                  InvalidOid,  /* preassigned type oid (none here) */
                                   InvalidOid,  /* relation oid (n/a here) */
                                   0,                   /* relation kind (ditto) */
                                   internalLength,              /* internal size */
-                                  'd',                 /* type-type (domain type) */
+                                  TYPTYPE_DOMAIN,              /* type-type (domain type) */
+                                  category,    /* type-category */
+                                  false,               /* domain types are never preferred */
                                   delimiter,   /* array element delimiter */
                                   inputProcedure,              /* input procedure */
                                   outputProcedure,             /* output procedure */
-                                  basetypelem, /* element type ID */
+                                  receiveProcedure,    /* receive procedure */
+                                  sendProcedure,               /* send procedure */
+                                  InvalidOid,  /* typmodin procedure - none */
+                                  InvalidOid,  /* typmodout procedure - none */
+                                  analyzeProcedure,    /* analyze procedure */
+                                  typelem,             /* element type ID */
+                                  false,               /* this isn't an array */
+                                  InvalidOid,  /* no arrays for domains (yet) */
                                   basetypeoid, /* base type ID */
                                   defaultValue,        /* default type value (text) */
                                   defaultValueBin,             /* default type value (binary) */
-                                  byValue,                             /* passed by value */
-                                  alignment,                   /* required alignment */
-                                  storage,                             /* TOAST strategy */
-                                  stmt->typename->typmod, /* typeMod value */
-                                  typNDims,                    /* Array dimensions for base type */
-                                  typNotNull);                 /* Type NOT NULL */
+                                  byValue,             /* passed by value */
+                                  alignment,   /* required alignment */
+                                  storage,             /* TOAST strategy */
+                                  basetypeMod, /* typeMod value */
+                                  typNDims,    /* Array dimensions for base type */
+                                  typNotNull); /* Type NOT NULL */
 
        /*
         * Process constraints which refer to the domain ID returned by TypeCreate
@@ -650,17 +1054,20 @@ DefineDomain(CreateDomainStmt *stmt)
 
                switch (constr->contype)
                {
-                       case CONSTR_CHECK:
+                       case CONSTR_CHECK:
                                domainAddConstraint(domainoid, domainNamespace,
-                                                                       basetypeoid, stmt->typename->typmod,
-                                                                       constr, &counter, domainName);
-                               break;
+                                                                       basetypeoid, basetypeMod,
+                                                                       constr, domainName);
+                               break;
 
-                       /* Other constraint types were fully processed above */
+                               /* Other constraint types were fully processed above */
 
                        default:
-                               break;
+                               break;
                }
+
+               /* CCI so we can detect duplicate constraint names */
+               CommandCounterIncrement();
        }
 
        /*
@@ -671,199 +1078,386 @@ DefineDomain(CreateDomainStmt *stmt)
 
 
 /*
- *     RemoveDomain
- *             Removes a domain.
- *
- * This is identical to RemoveType except we insist it be a domain.
+ * DefineEnum
+ *             Registers a new enum.
  */
 void
-RemoveDomain(List *names, DropBehavior behavior)
+DefineEnum(CreateEnumStmt *stmt)
 {
-       TypeName   *typename;
-       Oid                     typeoid;
-       HeapTuple       tup;
-       char            typtype;
-       ObjectAddress object;
-
-       /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
+       char       *enumName;
+       char       *enumArrayName;
+       Oid                     enumNamespace;
+       Oid                     enumTypeOid;
+       AclResult       aclresult;
+       Oid                     old_type_oid;
+       Oid                     enumArrayOid;
+       Relation        pg_type;
 
-       /* Use LookupTypeName here so that shell types can be removed. */
-       typeoid = LookupTypeName(typename);
-       if (!OidIsValid(typeoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Convert list of names to a name and namespace */
+       enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename,
+                                                                                                         &enumName);
 
-       tup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(typeoid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))
-               elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Check we have creation rights in target namespace */
+       aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                          get_namespace_name(enumNamespace));
 
-       /* Permission check: must own type or its namespace */
-       if (!pg_type_ownercheck(typeoid, GetUserId()) &&
-               !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
-                                                                GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
+       /*
+        * Check for collision with an existing type name.      If there is one and
+        * it's an autogenerated array, we can rename it out of the way.
+        */
+       old_type_oid = GetSysCacheOid(TYPENAMENSP,
+                                                                 CStringGetDatum(enumName),
+                                                                 ObjectIdGetDatum(enumNamespace),
+                                                                 0, 0);
+       if (OidIsValid(old_type_oid))
+       {
+               if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                        errmsg("type \"%s\" already exists", enumName)));
+       }
 
-       /* Check that this is actually a domain */
-       typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
+       /* Preassign array type OID so we can insert it in pg_type.typarray */
+       pg_type = heap_open(TypeRelationId, AccessShareLock);
+       enumArrayOid = GetNewOid(pg_type);
+       heap_close(pg_type, AccessShareLock);
 
-       if (typtype != 'd')
-               elog(ERROR, "%s is not a domain",
-                        TypeNameToString(typename));
+       /* Create the pg_type entry */
+       enumTypeOid =
+               TypeCreate(InvalidOid,  /* no predetermined type OID */
+                                  enumName,    /* type name */
+                                  enumNamespace,               /* namespace */
+                                  InvalidOid,  /* relation oid (n/a here) */
+                                  0,                   /* relation kind (ditto) */
+                                  sizeof(Oid), /* internal size */
+                                  TYPTYPE_ENUM,        /* type-type (enum type) */
+                                  TYPCATEGORY_ENUM,    /* type-category (enum type) */
+                                  false,               /* enum types are never preferred */
+                                  DEFAULT_TYPDELIM,    /* array element delimiter */
+                                  F_ENUM_IN,   /* input procedure */
+                                  F_ENUM_OUT,  /* output procedure */
+                                  F_ENUM_RECV, /* receive procedure */
+                                  F_ENUM_SEND, /* send procedure */
+                                  InvalidOid,  /* typmodin procedure - none */
+                                  InvalidOid,  /* typmodout procedure - none */
+                                  InvalidOid,  /* analyze procedure - default */
+                                  InvalidOid,  /* element type ID */
+                                  false,               /* this is not an array type */
+                                  enumArrayOid,        /* array type we are about to create */
+                                  InvalidOid,  /* base type ID (only for domains) */
+                                  NULL,                /* never a default type value */
+                                  NULL,                /* binary default isn't sent either */
+                                  true,                /* always passed by value */
+                                  'i',                 /* int alignment */
+                                  'p',                 /* TOAST strategy always plain */
+                                  -1,                  /* typMod (Domains only) */
+                                  0,                   /* Array dimensions of typbasetype */
+                                  false);              /* Type NOT NULL */
 
-       ReleaseSysCache(tup);
+       /* Enter the enum's values into pg_enum */
+       EnumValuesCreate(enumTypeOid, stmt->vals);
 
        /*
-        * Do the deletion
+        * Create the array type that goes with it.
         */
-       object.classId = RelOid_pg_type;
-       object.objectId = typeoid;
-       object.objectSubId = 0;
+       enumArrayName = makeArrayTypeName(enumName, enumNamespace);
 
-       performDeletion(&object, behavior);
+       TypeCreate(enumArrayOid,        /* force assignment of this type OID */
+                          enumArrayName,       /* type name */
+                          enumNamespace,       /* namespace */
+                          InvalidOid,          /* relation oid (n/a here) */
+                          0,                           /* relation kind (ditto) */
+                          -1,                          /* internal size (always varlena) */
+                          TYPTYPE_BASE,        /* type-type (base type) */
+                          TYPCATEGORY_ARRAY, /* type-category (array) */
+                          false,                       /* array types are never preferred */
+                          DEFAULT_TYPDELIM,    /* array element delimiter */
+                          F_ARRAY_IN,          /* input procedure */
+                          F_ARRAY_OUT,         /* output procedure */
+                          F_ARRAY_RECV,        /* receive procedure */
+                          F_ARRAY_SEND,        /* send procedure */
+                          InvalidOid,          /* typmodin procedure - none */
+                          InvalidOid,          /* typmodout procedure - none */
+                          InvalidOid,          /* analyze procedure - default */
+                          enumTypeOid,         /* element type ID */
+                          true,                        /* yes this is an array type */
+                          InvalidOid,          /* no further array type */
+                          InvalidOid,          /* base type ID */
+                          NULL,                        /* never a default type value */
+                          NULL,                        /* binary default isn't sent either */
+                          false,                       /* never passed by value */
+                          'i',                         /* enums have align i, so do their arrays */
+                          'x',                         /* ARRAY is always toastable */
+                          -1,                          /* typMod (Domains only) */
+                          0,                           /* Array dimensions of typbasetype */
+                          false);                      /* Type NOT NULL */
+
+       pfree(enumArrayName);
 }
 
 
 /*
- * Find a suitable I/O function for a type.
+ * Find suitable I/O functions for a type.
  *
  * typeOid is the type's OID (which will already exist, if only as a shell
  * type).
  */
+
 static Oid
-findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
+findTypeInputFunction(List *procname, Oid typeOid)
 {
-       Oid                     argList[FUNC_MAX_ARGS];
+       Oid                     argList[3];
        Oid                     procOid;
 
-       if (isOutput)
-       {
-               /*
-                * Output functions can take a single argument of the type, or two
-                * arguments (data value, element OID).
-                *
-                * For backwards compatibility we allow OPAQUE in place of the actual
-                * type name; if we see this, we issue a NOTICE and fix up the
-                * pg_proc entry.
-                */
-               MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+       /*
+        * Input functions can take a single argument of type CSTRING, or three
+        * arguments (string, typioparam OID, typmod).
+        *
+        * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
+        * see this, we issue a warning and fix up the pg_proc entry.
+        */
+       argList[0] = CSTRINGOID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
-               argList[0] = typeOid;
+       argList[1] = OIDOID;
+       argList[2] = INT4OID;
 
-               procOid = LookupFuncName(procname, 1, argList);
-               if (OidIsValid(procOid))
-                       return procOid;
+       procOid = LookupFuncName(procname, 3, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
+       /* No luck, try it with OPAQUE */
+       argList[0] = OPAQUEOID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+
+       if (!OidIsValid(procOid))
+       {
                argList[1] = OIDOID;
+               argList[2] = INT4OID;
 
-               procOid = LookupFuncName(procname, 2, argList);
-               if (OidIsValid(procOid))
-                       return procOid;
+               procOid = LookupFuncName(procname, 3, argList, true);
+       }
 
-               /* No luck, try it with OPAQUE */
-               MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+       if (OidIsValid(procOid))
+       {
+               /* Found, but must complain and fix the pg_proc entry */
+               ereport(WARNING,
+                               (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
+                                               NameListToString(procname))));
+               SetFunctionArgType(procOid, 0, CSTRINGOID);
 
-               argList[0] = OPAQUEOID;
+               /*
+                * Need CommandCounterIncrement since DefineType will likely try to
+                * alter the pg_proc tuple again.
+                */
+               CommandCounterIncrement();
 
-               procOid = LookupFuncName(procname, 1, argList);
+               return procOid;
+       }
 
-               if (!OidIsValid(procOid))
-               {
-                       argList[1] = OIDOID;
+       /* Use CSTRING (preferred) in the error message */
+       argList[0] = CSTRINGOID;
 
-                       procOid = LookupFuncName(procname, 2, argList);
-               }
+       ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                        errmsg("function %s does not exist",
+                                       func_signature_string(procname, 1, argList))));
 
-               if (OidIsValid(procOid))
-               {
-                       /* Found, but must complain and fix the pg_proc entry */
-                       elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to %s",
-                                NameListToString(procname), format_type_be(typeOid));
-                       SetFunctionArgType(procOid, 0, typeOid);
-                       /*
-                        * Need CommandCounterIncrement since DefineType will likely
-                        * try to alter the pg_proc tuple again.
-                        */
-                       CommandCounterIncrement();
+       return InvalidOid;                      /* keep compiler quiet */
+}
 
-                       return procOid;
-               }
+static Oid
+findTypeOutputFunction(List *procname, Oid typeOid)
+{
+       Oid                     argList[1];
+       Oid                     procOid;
+
+       /*
+        * Output functions can take a single argument of the type.
+        *
+        * For backwards compatibility we allow OPAQUE in place of the actual type
+        * name; if we see this, we issue a warning and fix up the pg_proc entry.
+        */
+       argList[0] = typeOid;
 
-               /* Use type name, not OPAQUE, in the failure message. */
-               argList[0] = typeOid;
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
-               func_error("TypeCreate", procname, 1, argList, NULL);
-       }
-       else
+       /* No luck, try it with OPAQUE */
+       argList[0] = OPAQUEOID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+
+       if (OidIsValid(procOid))
        {
+               /* Found, but must complain and fix the pg_proc entry */
+               ereport(WARNING,
+               (errmsg("changing argument type of function %s from \"opaque\" to %s",
+                               NameListToString(procname), format_type_be(typeOid))));
+               SetFunctionArgType(procOid, 0, typeOid);
+
                /*
-                * Input functions can take a single argument of type CSTRING, or
-                * three arguments (string, element OID, typmod).
-                *
-                * For backwards compatibility we allow OPAQUE in place of CSTRING;
-                * if we see this, we issue a NOTICE and fix up the pg_proc entry.
+                * Need CommandCounterIncrement since DefineType will likely try to
+                * alter the pg_proc tuple again.
                 */
-               MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+               CommandCounterIncrement();
 
-               argList[0] = CSTRINGOID;
+               return procOid;
+       }
 
-               procOid = LookupFuncName(procname, 1, argList);
-               if (OidIsValid(procOid))
-                       return procOid;
+       /* Use type name, not OPAQUE, in the failure message. */
+       argList[0] = typeOid;
 
-               argList[1] = OIDOID;
-               argList[2] = INT4OID;
+       ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                        errmsg("function %s does not exist",
+                                       func_signature_string(procname, 1, argList))));
 
-               procOid = LookupFuncName(procname, 3, argList);
-               if (OidIsValid(procOid))
-                       return procOid;
+       return InvalidOid;                      /* keep compiler quiet */
+}
 
-               /* No luck, try it with OPAQUE */
-               MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+static Oid
+findTypeReceiveFunction(List *procname, Oid typeOid)
+{
+       Oid                     argList[3];
+       Oid                     procOid;
 
-               argList[0] = OPAQUEOID;
+       /*
+        * Receive functions can take a single argument of type INTERNAL, or three
+        * arguments (internal, typioparam OID, typmod).
+        */
+       argList[0] = INTERNALOID;
 
-               procOid = LookupFuncName(procname, 1, argList);
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
-               if (!OidIsValid(procOid))
-               {
-                       argList[1] = OIDOID;
-                       argList[2] = INT4OID;
+       argList[1] = OIDOID;
+       argList[2] = INT4OID;
 
-                       procOid = LookupFuncName(procname, 3, argList);
-               }
+       procOid = LookupFuncName(procname, 3, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
-               if (OidIsValid(procOid))
-               {
-                       /* Found, but must complain and fix the pg_proc entry */
-                       elog(NOTICE, "TypeCreate: changing argument type of function %s "
-                                "from OPAQUE to CSTRING",
-                                NameListToString(procname));
-                       SetFunctionArgType(procOid, 0, CSTRINGOID);
-                       /*
-                        * Need CommandCounterIncrement since DefineType will likely
-                        * try to alter the pg_proc tuple again.
-                        */
-                       CommandCounterIncrement();
+       ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                        errmsg("function %s does not exist",
+                                       func_signature_string(procname, 1, argList))));
 
-                       return procOid;
-               }
+       return InvalidOid;                      /* keep compiler quiet */
+}
+
+static Oid
+findTypeSendFunction(List *procname, Oid typeOid)
+{
+       Oid                     argList[1];
+       Oid                     procOid;
+
+       /*
+        * Send functions can take a single argument of the type.
+        */
+       argList[0] = typeOid;
 
-               /* Use CSTRING (preferred) in the error message */
-               argList[0] = CSTRINGOID;
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (OidIsValid(procOid))
+               return procOid;
 
-               func_error("TypeCreate", procname, 1, argList, NULL);
-       }
+       ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                        errmsg("function %s does not exist",
+                                       func_signature_string(procname, 1, argList))));
 
        return InvalidOid;                      /* keep compiler quiet */
 }
 
+static Oid
+findTypeTypmodinFunction(List *procname)
+{
+       Oid                     argList[1];
+       Oid                     procOid;
+
+       /*
+        * typmodin functions always take one cstring[] argument and return int4.
+        */
+       argList[0] = CSTRINGARRAYOID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procname, 1, argList))));
+
+       if (get_func_rettype(procOid) != INT4OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("typmod_in function %s must return type \"integer\"",
+                                               NameListToString(procname))));
+
+       return procOid;
+}
+
+static Oid
+findTypeTypmodoutFunction(List *procname)
+{
+       Oid                     argList[1];
+       Oid                     procOid;
+
+       /*
+        * typmodout functions always take one int4 argument and return cstring.
+        */
+       argList[0] = INT4OID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procname, 1, argList))));
+
+       if (get_func_rettype(procOid) != CSTRINGOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("typmod_out function %s must return type \"cstring\"",
+                                               NameListToString(procname))));
+
+       return procOid;
+}
+
+static Oid
+findTypeAnalyzeFunction(List *procname, Oid typeOid)
+{
+       Oid                     argList[1];
+       Oid                     procOid;
+
+       /*
+        * Analyze functions always take one INTERNAL argument and return bool.
+        */
+       argList[0] = INTERNALOID;
+
+       procOid = LookupFuncName(procname, 1, argList, true);
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procname, 1, argList))));
+
+       if (get_func_rettype(procOid) != BOOLOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                         errmsg("type analyze function %s must return type \"boolean\"",
+                                        NameListToString(procname))));
+
+       return procOid;
+}
+
 
 /*-------------------------------------------------------------------
  * DefineCompositeType
@@ -885,19 +1479,21 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
        CreateStmt *createStmt = makeNode(CreateStmt);
 
        if (coldeflist == NIL)
-               elog(ERROR, "attempted to define composite type relation with"
-                        " no attrs");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("composite type must have at least one attribute")));
 
        /*
-        * now create the parameters for keys/inheritance etc. All of them are
-        * nil...
+        * now set the parameters for keys/inheritance etc. All of these are
+        * uninteresting for composite types...
         */
        createStmt->relation = (RangeVar *) typevar;
        createStmt->tableElts = coldeflist;
        createStmt->inhRelations = NIL;
        createStmt->constraints = NIL;
-       createStmt->hasoids = false;
+       createStmt->options = list_make1(reloptWithOids(false));
        createStmt->oncommit = ONCOMMIT_NOOP;
+       createStmt->tablespacename = NULL;
 
        /*
         * finally create the relation...
@@ -908,7 +1504,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
 /*
  * AlterDomainDefault
  *
- * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements. 
+ * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
  */
 void
 AlterDomainDefault(List *names, Node *defaultRaw)
@@ -919,55 +1515,44 @@ AlterDomainDefault(List *names, Node *defaultRaw)
        ParseState *pstate;
        Relation        rel;
        char       *defaultValue;
-       Node       *defaultExpr = NULL; /* NULL if no default specified */
+       Node       *defaultExpr = NULL;         /* NULL if no default specified */
        Datum           new_record[Natts_pg_type];
-       char            new_record_nulls[Natts_pg_type];
-       char            new_record_repl[Natts_pg_type];
+       bool            new_record_nulls[Natts_pg_type];
+       bool            new_record_repl[Natts_pg_type];
        HeapTuple       newtuple;
-       Form_pg_type    typTup;
+       Form_pg_type typTup;
 
        /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
-
-       /* Lock the domain in the type table */
-       rel = heap_openr(TypeRelationName, RowExclusiveLock);
+       typename = makeTypeNameFromNameList(names);
+       domainoid = typenameTypeId(NULL, typename, NULL);
 
-       /* Use LookupTypeName here so that shell types can be removed. */
-       domainoid = LookupTypeName(typename);
-       if (!OidIsValid(domainoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Look up the domain in the type table */
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy(TYPEOID,
                                                         ObjectIdGetDatum(domainoid),
                                                         0, 0, 0);
-
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "AlterDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               elog(ERROR, "cache lookup failed for type %u", domainoid);
+       typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       /* Doesn't return if user isn't allowed to alter the domain */ 
-       domainOwnerCheck(tup, typename);
+       /* Check it's a domain and check user has permission for ALTER DOMAIN */
+       checkDomainOwner(tup, typename);
 
        /* Setup new tuple */
        MemSet(new_record, (Datum) 0, sizeof(new_record));
-       MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
-       MemSet(new_record_repl, ' ', sizeof(new_record_repl));
-
-       /* Useful later */
-       typTup = (Form_pg_type) GETSTRUCT(tup);
+       MemSet(new_record_nulls, false, sizeof(new_record_nulls));
+       MemSet(new_record_repl, false, sizeof(new_record_repl));
 
-       /* Store the new default, if null then skip this step */
+       /* Store the new default into the tuple */
        if (defaultRaw)
        {
                /* Create a dummy ParseState for transformExpr */
                pstate = make_parsestate(NULL);
+
                /*
-                * Cook the colDef->raw_expr into an expression. Note:
-                * Name is strictly for error message
+                * Cook the colDef->raw_expr into an expression. Note: Name is
+                * strictly for error message
                 */
                defaultExpr = cookDefault(pstate, defaultRaw,
                                                                  typTup->typbasetype,
@@ -975,36 +1560,53 @@ AlterDomainDefault(List *names, Node *defaultRaw)
                                                                  NameStr(typTup->typname));
 
                /*
-                * Expression must be stored as a nodeToString result, but
-                * we also require a valid textual representation (mainly
-                * to make life easier for pg_dump).
-                */
-               defaultValue = deparse_expression(defaultExpr,
-                                                                 deparse_context_for(NameStr(typTup->typname),
-                                                                                                         InvalidOid),
-                                                                                 false, false);
-               /*
-                * Form an updated tuple with the new default and write it back.
+                * If the expression is just a NULL constant, we treat the command
+                * like ALTER ... DROP DEFAULT.  (But see note for same test in
+                * DefineDomain.)
                 */
-               new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
-                                                                                                               CStringGetDatum(
-                                                                                                                       nodeToString(defaultExpr)));
-
-               new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
-               new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
-                                                                                                       CStringGetDatum(defaultValue));
-               new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
+               if (defaultExpr == NULL ||
+                       (IsA(defaultExpr, Const) &&((Const *) defaultExpr)->constisnull))
+               {
+                       /* Default is NULL, drop it */
+                       new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
+                       new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
+                       new_record_nulls[Anum_pg_type_typdefault - 1] = true;
+                       new_record_repl[Anum_pg_type_typdefault - 1] = true;
+               }
+               else
+               {
+                       /*
+                        * Expression must be stored as a nodeToString result, but we also
+                        * require a valid textual representation (mainly to make life
+                        * easier for pg_dump).
+                        */
+                       defaultValue = deparse_expression(defaultExpr,
+                                                               deparse_context_for(NameStr(typTup->typname),
+                                                                                                       InvalidOid),
+                                                                                         false, false);
+
+                       /*
+                        * Form an updated tuple with the new default and write it back.
+                        */
+                       new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
+
+                       new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
+                       new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
+                       new_record_repl[Anum_pg_type_typdefault - 1] = true;
+               }
        }
-       else /* Default is NULL, drop it */
+       else
        {
-               new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
-               new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
-               new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
-               new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
+               /* ALTER ... DROP DEFAULT */
+               new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
+               new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
+               new_record_nulls[Anum_pg_type_typdefault - 1] = true;
+               new_record_repl[Anum_pg_type_typdefault - 1] = true;
        }
 
-       newtuple = heap_modifytuple(tup, rel,
-                                                               new_record, new_record_nulls, new_record_repl);
+       newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
+                                                               new_record, new_record_nulls,
+                                                               new_record_repl);
 
        simple_heap_update(rel, &tup->t_self, newtuple);
 
@@ -1013,24 +1615,31 @@ AlterDomainDefault(List *names, Node *defaultRaw)
        /* Rebuild dependencies */
        GenerateTypeDependencies(typTup->typnamespace,
                                                         domainoid,
-                                                        typTup->typrelid,
-                                                        0,     /* relation kind is n/a */
+                                                        InvalidOid,            /* typrelid is n/a */
+                                                        0, /* relation kind is n/a */
+                                                        typTup->typowner,
                                                         typTup->typinput,
                                                         typTup->typoutput,
+                                                        typTup->typreceive,
+                                                        typTup->typsend,
+                                                        typTup->typmodin,
+                                                        typTup->typmodout,
+                                                        typTup->typanalyze,
                                                         typTup->typelem,
+                                                        false,         /* a domain isn't an implicit array */
                                                         typTup->typbasetype,
                                                         defaultExpr,
-                                                        true); /* Rebuild is true */
+                                                        true);         /* Rebuild is true */
 
        /* Clean up */
        heap_close(rel, NoLock);
        heap_freetuple(newtuple);
-};
+}
 
 /*
  * AlterDomainNotNull
  *
- * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements. 
+ * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
  */
 void
 AlterDomainNotNull(List *names, bool notNull)
@@ -1039,40 +1648,28 @@ AlterDomainNotNull(List *names, bool notNull)
        Oid                     domainoid;
        Relation        typrel;
        HeapTuple       tup;
-       Form_pg_type    typTup;
+       Form_pg_type typTup;
 
        /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
-
-       /* Lock the type table */
-       typrel = heap_openr(TypeRelationName, RowExclusiveLock);
+       typename = makeTypeNameFromNameList(names);
+       domainoid = typenameTypeId(NULL, typename, NULL);
 
-       /* Use LookupTypeName here so that shell types can be found (why?). */
-       domainoid = LookupTypeName(typename);
-       if (!OidIsValid(domainoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Look up the domain in the type table */
+       typrel = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy(TYPEOID,
                                                         ObjectIdGetDatum(domainoid),
                                                         0, 0, 0);
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "AlterDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               elog(ERROR, "cache lookup failed for type %u", domainoid);
        typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       /* Doesn't return if user isn't allowed to alter the domain */ 
-       domainOwnerCheck(tup, typename);
+       /* Check it's a domain and check user has permission for ALTER DOMAIN */
+       checkDomainOwner(tup, typename);
 
        /* Is the domain already set to the desired constraint? */
        if (typTup->typnotnull == notNull)
        {
-               elog(NOTICE, "AlterDomain: %s is already set to %s",
-                        TypeNameToString(typename),
-                        notNull ? "NOT NULL" : "NULL");
                heap_close(typrel, RowExclusiveLock);
                return;
        }
@@ -1080,15 +1677,15 @@ AlterDomainNotNull(List *names, bool notNull)
        /* Adding a NOT NULL constraint requires checking existing columns */
        if (notNull)
        {
-               List   *rels;
-               List   *rt;
+               List       *rels;
+               ListCell   *rt;
 
                /* Fetch relation list with attributes based on this domain */
                /* ShareLock is sufficient to prevent concurrent data changes */
 
                rels = get_rels_with_domain(domainoid, ShareLock);
 
-               foreach (rt, rels)
+               foreach(rt, rels)
                {
                        RelToCheck *rtc = (RelToCheck *) lfirst(rt);
                        Relation        testrel = rtc->rel;
@@ -1100,21 +1697,19 @@ AlterDomainNotNull(List *names, bool notNull)
                        scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
                        while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
                        {
-                               int             i;
+                               int                     i;
 
                                /* Test attributes that are of the domain */
                                for (i = 0; i < rtc->natts; i++)
                                {
-                                       int             attnum = rtc->atts[i];
-                                       Datum   d;
-                                       bool    isNull;
-
-                                       d = heap_getattr(tuple, attnum, tupdesc, &isNull);
-
-                                       if (isNull)
-                                               elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains NULL values",
-                                                        RelationGetRelationName(testrel),
-                                                        NameStr(tupdesc->attrs[attnum - 1]->attname));
+                                       int                     attnum = rtc->atts[i];
+
+                                       if (heap_attisnull(tuple, attnum))
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_NOT_NULL_VIOLATION),
+                                                                errmsg("column \"%s\" of table \"%s\" contains null values",
+                                                               NameStr(tupdesc->attrs[attnum - 1]->attname),
+                                                                               RelationGetRelationName(testrel))));
                                }
                        }
                        heap_endscan(scan);
@@ -1125,8 +1720,8 @@ AlterDomainNotNull(List *names, bool notNull)
        }
 
        /*
-        * Okay to update pg_type row.  We can scribble on typTup because it's
-        * copy.
+        * Okay to update pg_type row.  We can scribble on typTup because it's a
+        * copy.
         */
        typTup->typnotnull = notNull;
 
@@ -1145,57 +1740,46 @@ AlterDomainNotNull(List *names, bool notNull)
  * Implements the ALTER DOMAIN DROP CONSTRAINT statement
  */
 void
-AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
+AlterDomainDropConstraint(List *names, const char *constrName,
+                                                 DropBehavior behavior)
 {
        TypeName   *typename;
        Oid                     domainoid;
        HeapTuple       tup;
        Relation        rel;
-       Form_pg_type    typTup;
        Relation        conrel;
        SysScanDesc conscan;
        ScanKeyData key[1];
        HeapTuple       contup;
 
        /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
+       typename = makeTypeNameFromNameList(names);
+       domainoid = typenameTypeId(NULL, typename, NULL);
 
-       /* Lock the type table */
-       rel = heap_openr(TypeRelationName, RowExclusiveLock);
-
-       /* Use LookupTypeName here so that shell types can be removed. */
-       domainoid = LookupTypeName(typename);
-       if (!OidIsValid(domainoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Look up the domain in the type table */
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy(TYPEOID,
                                                         ObjectIdGetDatum(domainoid),
                                                         0, 0, 0);
-
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "AlterDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               elog(ERROR, "cache lookup failed for type %u", domainoid);
 
-       /* Doesn't return if user isn't allowed to alter the domain */ 
-       domainOwnerCheck(tup, typename);
+       /* Check it's a domain and check user has permission for ALTER DOMAIN */
+       checkDomainOwner(tup, typename);
 
        /* Grab an appropriate lock on the pg_constraint relation */
-       conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
+       conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
        /* Use the index to scan only constraints of the target relation */
-       ScanKeyEntryInitialize(&key[0], 0x0,
-                                                  Anum_pg_constraint_contypid, F_OIDEQ,
-                                                  ObjectIdGetDatum(HeapTupleGetOid(tup)));
+       ScanKeyInit(&key[0],
+                               Anum_pg_constraint_contypid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(HeapTupleGetOid(tup)));
 
-       conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
+       conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
                                                                 SnapshotNow, 1, key);
 
-       typTup = (Form_pg_type) GETSTRUCT(tup);
-
        /*
         * Scan over the result set, removing any matching entries.
         */
@@ -1207,7 +1791,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior beha
                {
                        ObjectAddress conobj;
 
-                       conobj.classId = RelationGetRelid(conrel);
+                       conobj.classId = ConstraintRelationId;
                        conobj.objectId = HeapTupleGetOid(contup);
                        conobj.objectSubId = 0;
 
@@ -1219,7 +1803,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior beha
        heap_close(conrel, RowExclusiveLock);
 
        heap_close(rel, NoLock);
-};
+}
 
 /*
  * AlterDomainAddConstraint
@@ -1233,102 +1817,92 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
        Oid                     domainoid;
        Relation        typrel;
        HeapTuple       tup;
-       Form_pg_type    typTup;
-       List   *rels;
-       List   *rt;
-       EState *estate;
+       Form_pg_type typTup;
+       List       *rels;
+       ListCell   *rt;
+       EState     *estate;
        ExprContext *econtext;
-       char   *ccbin;
-       Expr   *expr;
-       ExprState *exprstate;
-       int             counter = 0;
+       char       *ccbin;
+       Expr       *expr;
+       ExprState  *exprstate;
        Constraint *constr;
 
        /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
+       typename = makeTypeNameFromNameList(names);
+       domainoid = typenameTypeId(NULL, typename, NULL);
 
-       /* Lock the type table */
-       typrel = heap_openr(TypeRelationName, RowExclusiveLock);
-
-       /* Use LookupTypeName here so that shell types can be found (why?). */
-       domainoid = LookupTypeName(typename);
-       if (!OidIsValid(domainoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Look up the domain in the type table */
+       typrel = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy(TYPEOID,
                                                         ObjectIdGetDatum(domainoid),
                                                         0, 0, 0);
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "AlterDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               elog(ERROR, "cache lookup failed for type %u", domainoid);
        typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       /* Doesn't return if user isn't allowed to alter the domain */ 
-       domainOwnerCheck(tup, typename);
+       /* Check it's a domain and check user has permission for ALTER DOMAIN */
+       checkDomainOwner(tup, typename);
 
        /* Check for unsupported constraint types */
        if (IsA(newConstraint, FkConstraint))
-               elog(ERROR, "ALTER DOMAIN / FOREIGN KEY constraints not supported");
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("foreign key constraints not possible for domains")));
 
-       /* this case should not happen */
+       /* otherwise it should be a plain Constraint */
        if (!IsA(newConstraint, Constraint))
-               elog(ERROR, "AlterDomainAddConstraint: unexpected constraint node type");
+               elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(newConstraint));
 
        constr = (Constraint *) newConstraint;
 
        switch (constr->contype)
        {
-               case CONSTR_DEFAULT:
-                       elog(ERROR, "Use ALTER DOMAIN .. SET DEFAULT instead");
-                       break;
-
-               case CONSTR_NOTNULL:
-               case CONSTR_NULL:
-                       elog(ERROR, "Use ALTER DOMAIN .. [ SET | DROP ] NOT NULL instead");
-                       break;
-
-               case CONSTR_CHECK:
+               case CONSTR_CHECK:
                        /* processed below */
-                       break;
+                       break;
 
                case CONSTR_UNIQUE:
-                       elog(ERROR, "ALTER DOMAIN / UNIQUE indexes not supported");
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("unique constraints not possible for domains")));
                        break;
 
                case CONSTR_PRIMARY:
-                       elog(ERROR, "ALTER DOMAIN / PRIMARY KEY indexes not supported");
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                               errmsg("primary key constraints not possible for domains")));
                        break;
 
                case CONSTR_ATTR_DEFERRABLE:
                case CONSTR_ATTR_NOT_DEFERRABLE:
                case CONSTR_ATTR_DEFERRED:
                case CONSTR_ATTR_IMMEDIATE:
-                       elog(ERROR, "ALTER DOMAIN: DEFERRABLE, NON DEFERRABLE, DEFERRED"
-                                " and IMMEDIATE not supported");
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("specifying constraint deferrability not supported for domains")));
                        break;
 
                default:
-                       elog(ERROR, "AlterDomainAddConstraint: unrecognized constraint node type");
+                       elog(ERROR, "unrecognized constraint subtype: %d",
+                                (int) constr->contype);
                        break;
        }
 
        /*
-        * Since all other constraint types throw errors, this must be
-        * a check constraint.  First, process the constraint expression
-        * and add an entry to pg_constraint.
+        * Since all other constraint types throw errors, this must be a check
+        * constraint.  First, process the constraint expression and add an entry
+        * to pg_constraint.
         */
 
        ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
                                                                typTup->typbasetype, typTup->typtypmod,
-                                                               constr, &counter, NameStr(typTup->typname));
+                                                               constr, NameStr(typTup->typname));
 
        /*
-        * Test all values stored in the attributes based on the domain
-        * the constraint is being added to.
+        * Test all values stored in the attributes based on the domain the
+        * constraint is being added to.
         */
        expr = (Expr *) stringToNode(ccbin);
 
@@ -1344,7 +1918,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 
        rels = get_rels_with_domain(domainoid, ShareLock);
 
-       foreach (rt, rels)
+       foreach(rt, rels)
        {
                RelToCheck *rtc = (RelToCheck *) lfirst(rt);
                Relation        testrel = rtc->rel;
@@ -1356,15 +1930,15 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
                scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
                while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
                {
-                       int             i;
+                       int                     i;
 
                        /* Test attributes that are of the domain */
                        for (i = 0; i < rtc->natts; i++)
                        {
-                               int             attnum = rtc->atts[i];
-                               Datum   d;
-                               bool    isNull;
-                               Datum   conResult;
+                               int                     attnum = rtc->atts[i];
+                               Datum           d;
+                               bool            isNull;
+                               Datum           conResult;
 
                                d = heap_getattr(tuple, attnum, tupdesc, &isNull);
 
@@ -1376,9 +1950,11 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
                                                                                                          &isNull, NULL);
 
                                if (!isNull && !DatumGetBool(conResult))
-                                       elog(ERROR, "ALTER DOMAIN: Relation \"%s\" attribute \"%s\" contains values that fail the new constraint",
-                                                RelationGetRelationName(testrel),
-                                                NameStr(tupdesc->attrs[attnum - 1]->attname));
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_CHECK_VIOLATION),
+                                                        errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
+                                                               NameStr(tupdesc->attrs[attnum - 1]->attname),
+                                                                       RelationGetRelationName(testrel))));
                        }
 
                        ResetExprContext(econtext);
@@ -1405,6 +1981,10 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
  * the domain type.  We have opened each rel and acquired the specified lock
  * type on it.
  *
+ * We support nested domains by including attributes that are of derived
+ * domain types.  Current callers do not need to distinguish between attributes
+ * that are of exactly the given domain and those that are of derived domains.
+ *
  * XXX this is completely broken because there is no way to lock the domain
  * to prevent columns from being added or dropped while our command runs.
  * We can partially protect against column drops by locking relations as we
@@ -1414,9 +1994,11 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
  * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
  * risk by using the weakest suitable lock (ShareLock for most callers).
  *
- * XXX to support domains over domains, we'd need to make this smarter,
- * or make its callers smarter, so that we could find columns of derived
- * domains.  Arrays of domains would be a problem too.
+ * XXX the API for this is not sufficient to support checking domain values
+ * that are inside composite types or arrays.  Currently we just error out
+ * if a composite type containing the target domain is stored anywhere.
+ * There are not currently arrays of domains; if there were, we could take
+ * the same approach, but it'd be nicer to fix it properly.
  *
  * Generally used for retrieving a list of tests when adding
  * new constraints to a domain.
@@ -1424,39 +2006,60 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 static List *
 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
 {
-       List *result = NIL;
+       List       *result = NIL;
        Relation        depRel;
        ScanKeyData key[2];
        SysScanDesc depScan;
        HeapTuple       depTup;
 
+       Assert(lockmode != NoLock);
+
        /*
-        * We scan pg_depend to find those things that depend on the domain.
-        * (We assume we can ignore refobjsubid for a domain.)
+        * We scan pg_depend to find those things that depend on the domain. (We
+        * assume we can ignore refobjsubid for a domain.)
         */
-       depRel = relation_openr(DependRelationName, AccessShareLock);
-
-       ScanKeyEntryInitialize(&key[0], 0x0,
-                                                  Anum_pg_depend_refclassid, F_OIDEQ,
-                                                  ObjectIdGetDatum(RelOid_pg_type));
-       ScanKeyEntryInitialize(&key[1], 0x0,
-                                                  Anum_pg_depend_refobjid, F_OIDEQ,
-                                                  ObjectIdGetDatum(domainOid));
-
-       depScan = systable_beginscan(depRel, DependReferenceIndex, true,
+       depRel = heap_open(DependRelationId, AccessShareLock);
+
+       ScanKeyInit(&key[0],
+                               Anum_pg_depend_refclassid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(TypeRelationId));
+       ScanKeyInit(&key[1],
+                               Anum_pg_depend_refobjid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(domainOid));
+
+       depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
                                                                 SnapshotNow, 2, key);
 
        while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
        {
-               Form_pg_depend          pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
+               Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
                RelToCheck *rtc = NULL;
-               List       *rellist;
-               Form_pg_attribute       pg_att;
+               ListCell   *rellist;
+               Form_pg_attribute pg_att;
                int                     ptr;
 
-               /* Ignore dependees that aren't user columns of tables */
+               /* Check for directly dependent types --- must be domains */
+               if (pg_depend->classid == TypeRelationId)
+               {
+                       Assert(get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN);
+
+                       /*
+                        * Recursively add dependent columns to the output list.  This is
+                        * a bit inefficient since we may fail to combine RelToCheck
+                        * entries when attributes of the same rel have different derived
+                        * domain types, but it's probably not worth improving.
+                        */
+                       result = list_concat(result,
+                                                                get_rels_with_domain(pg_depend->objid,
+                                                                                                         lockmode));
+                       continue;
+               }
+
+               /* Else, ignore dependees that aren't user columns of relations */
                /* (we assume system columns are never of domain types) */
-               if (pg_depend->classid != RelOid_pg_class ||
+               if (pg_depend->classid != RelationRelationId ||
                        pg_depend->objsubid <= 0)
                        continue;
 
@@ -1478,7 +2081,23 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
                        Relation        rel;
 
                        /* Acquire requested lock on relation */
-                       rel = heap_open(pg_depend->objid, lockmode);
+                       rel = relation_open(pg_depend->objid, lockmode);
+
+                       /*
+                        * Check to see if rowtype is stored anyplace as a composite-type
+                        * column; if so we have to fail, for now anyway.
+                        */
+                       if (OidIsValid(rel->rd_rel->reltype))
+                               find_composite_type_dependencies(rel->rd_rel->reltype,
+                                                                                                NULL,
+                                                                                                format_type_be(domainOid));
+
+                       /* Otherwise we can ignore views, composite types, etc */
+                       if (rel->rd_rel->relkind != RELKIND_RELATION)
+                       {
+                               relation_close(rel, lockmode);
+                               continue;
+                       }
 
                        /* Build the RelToCheck entry with enough space for all atts */
                        rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
@@ -1490,9 +2109,9 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
 
                /*
                 * Confirm column has not been dropped, and is of the expected type.
-                * This defends against an ALTER DROP COLUMN occuring just before
-                * we acquired lock ... but if the whole table were dropped, we'd
-                * still have a problem.
+                * This defends against an ALTER DROP COLUMN occuring just before we
+                * acquired lock ... but if the whole table were dropped, we'd still
+                * have a problem.
                 */
                if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
                        continue;
@@ -1501,16 +2120,16 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
                        continue;
 
                /*
-                * Okay, add column to result.  We store the columns in column-number
+                * Okay, add column to result.  We store the columns in column-number
                 * order; this is just a hack to improve predictability of regression
                 * test output ...
                 */
                Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
 
                ptr = rtc->natts++;
-               while (ptr > 0 && rtc->atts[ptr-1] > pg_depend->objsubid)
+               while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
                {
-                       rtc->atts[ptr] = rtc->atts[ptr-1];
+                       rtc->atts[ptr] = rtc->atts[ptr - 1];
                        ptr--;
                }
                rtc->atts[ptr] = pg_depend->objsubid;
@@ -1524,25 +2143,27 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
 }
 
 /*
- * domainOwnerCheck
+ * checkDomainOwner
  *
- * Throw an error if the current user doesn't have permission to modify
- * the domain in an ALTER DOMAIN statement, or if the type isn't actually
- * a domain.
+ * Check that the type is actually a domain and that the current user
+ * has permission to do ALTER DOMAIN on it.  Throw an error if not.
  */
 static void
-domainOwnerCheck(HeapTuple tup, TypeName *typename)
+checkDomainOwner(HeapTuple tup, TypeName *typename)
 {
-       Form_pg_type    typTup = (Form_pg_type) GETSTRUCT(tup);
+       Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
 
        /* Check that this is actually a domain */
-       if (typTup->typtype != 'd')
-               elog(ERROR, "%s is not a domain",
-                        TypeNameToString(typename));
+       if (typTup->typtype != TYPTYPE_DOMAIN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("\"%s\" is not a domain",
+                                               TypeNameToString(typename))));
 
        /* Permission check: must own type */
        if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                                          format_type_be(HeapTupleGetOid(tup)));
 }
 
 /*
@@ -1551,13 +2172,13 @@ domainOwnerCheck(HeapTuple tup, TypeName *typename)
 static char *
 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
                                        int typMod, Constraint *constr,
-                                       int *counter, char *domainName)
+                                       char *domainName)
 {
        Node       *expr;
        char       *ccsrc;
        char       *ccbin;
        ParseState *pstate;
-       CoerceToDomainValue  *domVal;
+       CoerceToDomainValue *domVal;
 
        /*
         * Assign or validate constraint name
@@ -1568,15 +2189,17 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
                                                                 domainOid,
                                                                 domainNamespace,
                                                                 constr->name))
-                       elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
-                                constr->name,
-                                domainName);
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("constraint \"%s\" for domain \"%s\" already exists",
+                                               constr->name, domainName)));
        }
        else
-               constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
-                                                                                         domainOid,
-                                                                                         domainNamespace,
-                                                                                         counter);
+               constr->name = ChooseConstraintName(domainName,
+                                                                                       NULL,
+                                                                                       "check",
+                                                                                       domainNamespace,
+                                                                                       NIL);
 
        /*
         * Convert the A_EXPR in raw_expr into an EXPR
@@ -1584,15 +2207,16 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        pstate = make_parsestate(NULL);
 
        /*
-        * Set up a CoerceToDomainValue to represent the occurrence of VALUE
-        * in the expression.  Note that it will appear to have the type of the
-        * base type, not the domain.  This seems correct since within the
-        * check expression, we should not assume the input value can be considered
-        * member of the domain.
+        * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
+        * the expression.      Note that it will appear to have the type of the base
+        * type, not the domain.  This seems correct since within the check
+        * expression, we should not assume the input value can be considered a
+        * member of the domain.
         */
        domVal = makeNode(CoerceToDomainValue);
        domVal->typeId = baseTypeOid;
        domVal->typeMod = typMod;
+       domVal->location = -1;          /* will be set when/if used */
 
        pstate->p_value_substitute = (Node *) domVal;
 
@@ -1601,29 +2225,40 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        /*
         * Make sure it yields a boolean result.
         */
-       expr = coerce_to_boolean(expr, "CHECK");
+       expr = coerce_to_boolean(pstate, expr, "CHECK");
 
        /*
-        * Make sure no outside relations are
-        * referred to.
+        * Make sure no outside relations are referred to.
         */
-       if (length(pstate->p_rtable) != 0)
-               elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
+       if (list_length(pstate->p_rtable) != 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                 errmsg("cannot use table references in domain check constraint")));
 
        /*
         * Domains don't allow var clauses (this should be redundant with the
         * above check, but make it anyway)
         */
        if (contain_var_clause(expr))
-               elog(ERROR, "cannot use column references in domain CHECK clause");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                 errmsg("cannot use table references in domain check constraint")));
 
        /*
         * No subplans or aggregates, either...
         */
-       if (contain_subplans(expr))
-               elog(ERROR, "cannot use subselect in CHECK constraint expression");
-       if (contain_agg_clause(expr))
-               elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
+       if (pstate->p_hasSubLinks)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot use subquery in check constraint")));
+       if (pstate->p_hasAggs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_GROUPING_ERROR),
+                          errmsg("cannot use aggregate function in check constraint")));
+       if (pstate->p_hasWindowFuncs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                errmsg("cannot use window function in check constraint")));
 
        /*
         * Convert to string form for storage.
@@ -1644,25 +2279,30 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        /*
         * Store the constraint in pg_constraint
         */
-       CreateConstraintEntry(constr->name,             /* Constraint Name */
-                                                 domainNamespace,      /* namespace */
+       CreateConstraintEntry(constr->name, /* Constraint Name */
+                                                 domainNamespace,              /* namespace */
                                                  CONSTRAINT_CHECK,             /* Constraint Type */
                                                  false,        /* Is Deferrable */
                                                  false,        /* Is Deferred */
-                                                 InvalidOid,           /* not a relation constraint */
-                                                 NULL, 
+                                                 InvalidOid,   /* not a relation constraint */
+                                                 NULL,
                                                  0,
                                                  domainOid,    /* domain constraint */
                                                  InvalidOid,   /* Foreign key fields */
                                                  NULL,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL,
                                                  0,
                                                  ' ',
                                                  ' ',
                                                  ' ',
                                                  InvalidOid,
-                                                 expr,         /* Tree form check constraint */
+                                                 expr, /* Tree form check constraint */
                                                  ccbin,        /* Binary form check constraint */
-                                                 ccsrc);       /* Source form check constraint */
+                                                 ccsrc,        /* Source form check constraint */
+                                                 true, /* is local */
+                                                 0);   /* inhcount */
 
        /*
         * Return the compiled constraint expression so the calling routine can
@@ -1679,6 +2319,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
  * This is called by the executor during plan startup for a CoerceToDomain
  * expression node.  The given constraints will be checked for each value
  * passed through the node.
+ *
+ * We allow this to be called for non-domain types, in which case the result
+ * is always NIL.
  */
 List *
 GetDomainConstraints(Oid typeOid)
@@ -1687,7 +2330,7 @@ GetDomainConstraints(Oid typeOid)
        bool            notNull = false;
        Relation        conRel;
 
-       conRel = heap_openr(ConstraintRelationName, AccessShareLock);
+       conRel = heap_open(ConstraintRelationId, AccessShareLock);
 
        for (;;)
        {
@@ -1696,52 +2339,59 @@ GetDomainConstraints(Oid typeOid)
                Form_pg_type typTup;
                ScanKeyData key[1];
                SysScanDesc scan;
-               
+
                tup = SearchSysCache(TYPEOID,
                                                         ObjectIdGetDatum(typeOid),
                                                         0, 0, 0);
                if (!HeapTupleIsValid(tup))
-                       elog(ERROR, "GetDomainConstraints: failed to lookup type %u",
-                                typeOid);
+                       elog(ERROR, "cache lookup failed for type %u", typeOid);
                typTup = (Form_pg_type) GETSTRUCT(tup);
 
+               if (typTup->typtype != TYPTYPE_DOMAIN)
+               {
+                       /* Not a domain, so done */
+                       ReleaseSysCache(tup);
+                       break;
+               }
+
                /* Test for NOT NULL Constraint */
                if (typTup->typnotnull)
                        notNull = true;
 
                /* Look for CHECK Constraints on this domain */
-               ScanKeyEntryInitialize(&key[0], 0x0,
-                                                          Anum_pg_constraint_contypid, F_OIDEQ,
-                                                          ObjectIdGetDatum(typeOid));
+               ScanKeyInit(&key[0],
+                                       Anum_pg_constraint_contypid,
+                                       BTEqualStrategyNumber, F_OIDEQ,
+                                       ObjectIdGetDatum(typeOid));
 
-               scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
+               scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
                                                                  SnapshotNow, 1, key);
 
                while (HeapTupleIsValid(conTup = systable_getnext(scan)))
                {
-                       Form_pg_constraint      c = (Form_pg_constraint) GETSTRUCT(conTup);
-                       Datum   val;
-                       bool    isNull;
-                       Expr   *check_expr;
+                       Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
+                       Datum           val;
+                       bool            isNull;
+                       Expr       *check_expr;
                        DomainConstraintState *r;
 
                        /* Ignore non-CHECK constraints (presently, shouldn't be any) */
                        if (c->contype != CONSTRAINT_CHECK)
                                continue;
 
-                       /* Not expecting conbin to be NULL, but we'll test for it anyway */
+                       /*
+                        * Not expecting conbin to be NULL, but we'll test for it anyway
+                        */
                        val = fastgetattr(conTup, Anum_pg_constraint_conbin,
                                                          conRel->rd_att, &isNull);
                        if (isNull)
-                               elog(ERROR, "GetDomainConstraints: domain %s constraint %s has NULL conbin",
+                               elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
                                         NameStr(typTup->typname), NameStr(c->conname));
 
-                       check_expr = (Expr *)
-                               stringToNode(DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                                val)));
+                       check_expr = (Expr *) stringToNode(TextDatumGetCString(val));
 
-                       /* ExecInitExpr assumes we already fixed opfuncids */
-                       fix_opfuncids((Node *) check_expr);
+                       /* ExecInitExpr assumes we've planned the expression */
+                       check_expr = expression_planner(check_expr);
 
                        r = makeNode(DomainConstraintState);
                        r->constrainttype = DOM_CONSTRAINT_CHECK;
@@ -1749,22 +2399,15 @@ GetDomainConstraints(Oid typeOid)
                        r->check_expr = ExecInitExpr(check_expr, NULL);
 
                        /*
-                        * use lcons() here because constraints of lower domains should
-                        * be applied earlier.
+                        * use lcons() here because constraints of lower domains should be
+                        * applied earlier.
                         */
                        result = lcons(r, result);
                }
 
                systable_endscan(scan);
 
-               if (typTup->typtype != 'd')
-               {
-                       /* Not a domain, so done */
-                       ReleaseSysCache(tup);
-                       break;
-               }
-
-               /* else loop to next domain in stack */
+               /* loop to next domain in stack */
                typeOid = typTup->typbasetype;
                ReleaseSysCache(tup);
        }
@@ -1772,8 +2415,8 @@ GetDomainConstraints(Oid typeOid)
        heap_close(conRel, AccessShareLock);
 
        /*
-        * Only need to add one NOT NULL check regardless of how many domains
-        * in the stack request it.
+        * Only need to add one NOT NULL check regardless of how many domains in
+        * the stack request it.
         */
        if (notNull)
        {
@@ -1790,60 +2433,415 @@ GetDomainConstraints(Oid typeOid)
        return result;
 }
 
+
 /*
- * ALTER DOMAIN .. OWNER TO
- *
- * Eventually this should allow changing ownership of other kinds of types,
- * but some thought must be given to handling complex types.  (A table's
- * rowtype probably shouldn't be allowed as target, but what of a standalone
- * composite type?)
- *
- * Assumes that permission checks have been completed earlier.
+ * Execute ALTER TYPE RENAME
  */
 void
-AlterTypeOwner(List *names, AclId newOwnerSysId)
+RenameType(List *names, const char *newTypeName)
 {
        TypeName   *typename;
        Oid                     typeOid;
        Relation        rel;
        HeapTuple       tup;
-       Form_pg_type    typTup;
+       Form_pg_type typTup;
 
        /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
+       typename = makeTypeNameFromNameList(names);
+       typeOid = typenameTypeId(NULL, typename, NULL);
 
-       /* Lock the type table */
-       rel = heap_openr(TypeRelationName, RowExclusiveLock);
-
-       /* Use LookupTypeName here so that shell types can be processed (why?) */
-       typeOid = LookupTypeName(typename);
-       if (!OidIsValid(typeOid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
+       /* Look up the type in the type table */
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy(TYPEOID,
                                                         ObjectIdGetDatum(typeOid),
                                                         0, 0, 0);
        if (!HeapTupleIsValid(tup))
-               elog(ERROR, "AlterDomain: type \"%s\" does not exist",
-                        TypeNameToString(typename));
+               elog(ERROR, "cache lookup failed for type %u", typeOid);
        typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       /* Check that this is actually a domain */
-       if (typTup->typtype != 'd')
-               elog(ERROR, "%s is not a domain",
-                        TypeNameToString(typename));
+       /* check permissions on type */
+       if (!pg_type_ownercheck(typeOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                                          format_type_be(typeOid));
+
+       /*
+        * If it's a composite type, we need to check that it really is a
+        * free-standing composite type, and not a table's rowtype. We
+        * want people to use ALTER TABLE not ALTER TYPE for that case.
+        */
+       if (typTup->typtype == TYPTYPE_COMPOSITE &&
+               get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("%s is a table's row type",
+                                               format_type_be(typeOid)),
+                                errhint("Use ALTER TABLE instead.")));
+
+       /* don't allow direct alteration of array types, either */
+       if (OidIsValid(typTup->typelem) &&
+               get_array_type(typTup->typelem) == typeOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("cannot alter array type %s",
+                                               format_type_be(typeOid)),
+                                errhint("You can alter type %s, which will alter the array type as well.",
+                                                format_type_be(typTup->typelem))));
+
+       /* 
+        * If type is composite we need to rename associated pg_class entry too.
+        * RenameRelationInternal will call RenameTypeInternal automatically.
+        */
+       if (typTup->typtype == TYPTYPE_COMPOSITE)
+               RenameRelationInternal(typTup->typrelid, newTypeName,
+                                                          typTup->typnamespace);
+       else
+               RenameTypeInternal(typeOid, newTypeName,
+                                                  typTup->typnamespace);
+
+       /* Clean up */
+       heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Change the owner of a type.
+ */
+void
+AlterTypeOwner(List *names, Oid newOwnerId)
+{
+       TypeName   *typename;
+       Oid                     typeOid;
+       Relation        rel;
+       HeapTuple       tup;
+       HeapTuple       newtup;
+       Form_pg_type typTup;
+       AclResult       aclresult;
+
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+       /* Make a TypeName so we can use standard type lookup machinery */
+       typename = makeTypeNameFromNameList(names);
+
+       /* Use LookupTypeName here so that shell types can be processed */
+       tup = LookupTypeName(NULL, typename, NULL);
+       if (tup == NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("type \"%s\" does not exist",
+                                               TypeNameToString(typename))));
+       typeOid = typeTypeId(tup);
+
+       /* Copy the syscache entry so we can scribble on it below */
+       newtup = heap_copytuple(tup);
+       ReleaseSysCache(tup);
+       tup = newtup;
+       typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       /* Modify the owner --- okay to scribble on typTup because it's a copy */
-       typTup->typowner = newOwnerSysId;
+       /*
+        * If it's a composite type, we need to check that it really is a
+        * free-standing composite type, and not a table's rowtype. We want people
+        * to use ALTER TABLE not ALTER TYPE for that case.
+        */
+       if (typTup->typtype == TYPTYPE_COMPOSITE &&
+               get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("%s is a table's row type",
+                                               format_type_be(typeOid)),
+                                errhint("Use ALTER TABLE instead.")));
+
+       /* don't allow direct alteration of array types, either */
+       if (OidIsValid(typTup->typelem) &&
+               get_array_type(typTup->typelem) == typeOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("cannot alter array type %s",
+                                               format_type_be(typeOid)),
+                                errhint("You can alter type %s, which will alter the array type as well.",
+                                                format_type_be(typTup->typelem))));
+
+       /*
+        * If the new owner is the same as the existing owner, consider the
+        * command to have succeeded.  This is for dump restoration purposes.
+        */
+       if (typTup->typowner != newOwnerId)
+       {
+               /* Superusers can always do it */
+               if (!superuser())
+               {
+                       /* Otherwise, must be owner of the existing object */
+                       if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+                               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                                                          format_type_be(HeapTupleGetOid(tup)));
+
+                       /* Must be able to become new owner */
+                       check_is_member_of_role(GetUserId(), newOwnerId);
+
+                       /* New owner must have CREATE privilege on namespace */
+                       aclresult = pg_namespace_aclcheck(typTup->typnamespace,
+                                                                                         newOwnerId,
+                                                                                         ACL_CREATE);
+                       if (aclresult != ACLCHECK_OK)
+                               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                                          get_namespace_name(typTup->typnamespace));
+               }
+
+               /*
+                * If it's a composite type, invoke ATExecChangeOwner so that we fix
+                * up the pg_class entry properly.      That will call back to
+                * AlterTypeOwnerInternal to take care of the pg_type entry(s).
+                */
+               if (typTup->typtype == TYPTYPE_COMPOSITE)
+                       ATExecChangeOwner(typTup->typrelid, newOwnerId, true);
+               else
+               {
+                       /*
+                        * We can just apply the modification directly.
+                        *
+                        * okay to scribble on typTup because it's a copy
+                        */
+                       typTup->typowner = newOwnerId;
+
+                       simple_heap_update(rel, &tup->t_self, tup);
+
+                       CatalogUpdateIndexes(rel, tup);
+
+                       /* Update owner dependency reference */
+                       changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
+
+                       /* If it has an array type, update that too */
+                       if (OidIsValid(typTup->typarray))
+                               AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
+               }
+       }
+
+       /* Clean up */
+       heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * AlterTypeOwnerInternal - change type owner unconditionally
+ *
+ * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a
+ * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
+ * It assumes the caller has done all needed checks.  The function will
+ * automatically recurse to an array type if the type has one.
+ *
+ * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
+ * entry (ie, it's not a table rowtype nor an array type).
+ */
+void
+AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
+                                          bool hasDependEntry)
+{
+       Relation        rel;
+       HeapTuple       tup;
+       Form_pg_type typTup;
+
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+       tup = SearchSysCacheCopy(TYPEOID,
+                                                        ObjectIdGetDatum(typeOid),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "cache lookup failed for type %u", typeOid);
+       typTup = (Form_pg_type) GETSTRUCT(tup);
+
+       /*
+        * Modify the owner --- okay to scribble on typTup because it's a copy
+        */
+       typTup->typowner = newOwnerId;
 
        simple_heap_update(rel, &tup->t_self, tup);
 
        CatalogUpdateIndexes(rel, tup);
 
+       /* Update owner dependency reference, if it has one */
+       if (hasDependEntry)
+               changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
+
+       /* If it has an array type, update that too */
+       if (OidIsValid(typTup->typarray))
+               AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
+
        /* Clean up */
        heap_close(rel, RowExclusiveLock);
 }
+
+/*
+ * Execute ALTER TYPE SET SCHEMA
+ */
+void
+AlterTypeNamespace(List *names, const char *newschema)
+{
+       TypeName   *typename;
+       Oid                     typeOid;
+       Oid                     nspOid;
+       Oid                     elemOid;
+
+       /* Make a TypeName so we can use standard type lookup machinery */
+       typename = makeTypeNameFromNameList(names);
+       typeOid = typenameTypeId(NULL, typename, NULL);
+
+       /* check permissions on type */
+       if (!pg_type_ownercheck(typeOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                                          format_type_be(typeOid));
+
+       /* get schema OID and check its permissions */
+       nspOid = LookupCreationNamespace(newschema);
+
+       /* don't allow direct alteration of array types */
+       elemOid = get_element_type(typeOid);
+       if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("cannot alter array type %s",
+                                               format_type_be(typeOid)),
+                                errhint("You can alter type %s, which will alter the array type as well.",
+                                                format_type_be(elemOid))));
+
+       /* and do the work */
+       AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
+}
+
+/*
+ * Move specified type to new namespace.
+ *
+ * Caller must have already checked privileges.
+ *
+ * The function automatically recurses to process the type's array type,
+ * if any.     isImplicitArray should be TRUE only when doing this internal
+ * recursion (outside callers must never try to move an array type directly).
+ *
+ * If errorOnTableType is TRUE, the function errors out if the type is
+ * a table type.  ALTER TABLE has to be used to move a table to a new
+ * namespace.
+ */
+void
+AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
+                                                  bool isImplicitArray,
+                                                  bool errorOnTableType)
+{
+       Relation        rel;
+       HeapTuple       tup;
+       Form_pg_type typform;
+       Oid                     oldNspOid;
+       Oid                     arrayOid;
+       bool            isCompositeType;
+
+       rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+       tup = SearchSysCacheCopy(TYPEOID,
+                                                        ObjectIdGetDatum(typeOid),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "cache lookup failed for type %u", typeOid);
+       typform = (Form_pg_type) GETSTRUCT(tup);
+
+       oldNspOid = typform->typnamespace;
+       arrayOid = typform->typarray;
+
+       if (oldNspOid == nspOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("type %s is already in schema \"%s\"",
+                                               format_type_be(typeOid),
+                                               get_namespace_name(nspOid))));
+
+       /* disallow renaming into or out of temp schemas */
+       if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                       errmsg("cannot move objects into or out of temporary schemas")));
+
+       /* same for TOAST schema */
+       if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot move objects into or out of TOAST schema")));
+
+       /* check for duplicate name (more friendly than unique-index failure) */
+       if (SearchSysCacheExists(TYPENAMENSP,
+                                                        CStringGetDatum(NameStr(typform->typname)),
+                                                        ObjectIdGetDatum(nspOid),
+                                                        0, 0))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("type \"%s\" already exists in schema \"%s\"",
+                                               NameStr(typform->typname),
+                                               get_namespace_name(nspOid))));
+
+       /* Detect whether type is a composite type (but not a table rowtype) */
+       isCompositeType =
+               (typform->typtype == TYPTYPE_COMPOSITE &&
+                get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
+
+       /* Enforce not-table-type if requested */
+       if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
+               errorOnTableType)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("%s is a table's row type",
+                                               format_type_be(typeOid)),
+                                errhint("Use ALTER TABLE instead.")));
+
+       /* OK, modify the pg_type row */
+
+       /* tup is a copy, so we can scribble directly on it */
+       typform->typnamespace = nspOid;
+
+       simple_heap_update(rel, &tup->t_self, tup);
+       CatalogUpdateIndexes(rel, tup);
+
+       /*
+        * Composite types have pg_class entries.
+        *
+        * We need to modify the pg_class tuple as well to reflect the change of
+        * schema.
+        */
+       if (isCompositeType)
+       {
+               Relation        classRel;
+
+               classRel = heap_open(RelationRelationId, RowExclusiveLock);
+
+               AlterRelationNamespaceInternal(classRel, typform->typrelid,
+                                                                          oldNspOid, nspOid,
+                                                                          false);
+
+               heap_close(classRel, RowExclusiveLock);
+
+               /*
+                * Check for constraints associated with the composite type (we don't
+                * currently support this, but probably will someday).
+                */
+               AlterConstraintNamespaces(typform->typrelid, oldNspOid,
+                                                                 nspOid, false);
+       }
+       else
+       {
+               /* If it's a domain, it might have constraints */
+               if (typform->typtype == TYPTYPE_DOMAIN)
+                       AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
+       }
+
+       /*
+        * Update dependency on schema, if any --- a table rowtype has not got
+        * one, and neither does an implicit array.
+        */
+       if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
+               !isImplicitArray)
+               if (changeDependencyFor(TypeRelationId, typeOid,
+                                                               NamespaceRelationId, oldNspOid, nspOid) != 1)
+                       elog(ERROR, "failed to change schema dependency for type %s",
+                                format_type_be(typeOid));
+
+       heap_freetuple(tup);
+
+       heap_close(rel, RowExclusiveLock);
+
+       /* Recursively alter the associated array type, if any */
+       if (OidIsValid(arrayOid))
+               AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
+}