#include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
-#include "utils/rangetypes.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
 /* Potentially set by contrib/pg_upgrade_support functions */
 Oid            binary_upgrade_next_array_pg_type_oid = InvalidOid;
 
+static void makeRangeConstructors(const char *name, Oid namespace,
+                     Oid rangeOid, Oid subtype);
 static Oid findTypeInputFunction(List *procname, Oid typeOid);
 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
 static Oid findTypeTypmodinFunction(List *procname);
 static Oid findTypeTypmodoutFunction(List *procname);
 static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findRangeSubOpclass(List *opcname, Oid subtype);
 static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
-static Oid findRangeSubOpclass(List *procname, Oid typeOid);
-static Oid findRangeSubtypeDiffFunction(List *procname, Oid typeOid);
+static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
 static void validateDomainConstraint(Oid domainoid, char *ccbin);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
 static void checkDomainOwner(HeapTuple tup);
                    Oid baseTypeOid,
                    int typMod, Constraint *constr,
                    char *domainName);
-static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
-                    Oid subtype);
 
 
 /*
    pfree(enumArrayName);
 }
 
+/*
+ * AlterEnum
+ *     Adds a new label to an existing enum.
+ */
+void
+AlterEnum(AlterEnumStmt *stmt)
+{
+   Oid         enum_type_oid;
+   TypeName   *typename;
+   HeapTuple   tup;
+
+   /* Make a TypeName so we can use standard type lookup machinery */
+   typename = makeTypeNameFromNameList(stmt->typeName);
+   enum_type_oid = typenameTypeId(NULL, typename);
+
+   tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
+   if (!HeapTupleIsValid(tup))
+       elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
+
+   /* Check it's an enum and check user has permission to ALTER the enum */
+   checkEnumOwner(tup);
+
+   /* Add the new label */
+   AddEnumLabel(enum_type_oid, stmt->newVal,
+                stmt->newValNeighbor, stmt->newValIsAfter);
+
+   ReleaseSysCache(tup);
+}
+
+
+/*
+ * checkEnumOwner
+ *
+ * Check that the type is actually an enum and that the current user
+ * has permission to do ALTER TYPE on it.  Throw an error if not.
+ */
+static void
+checkEnumOwner(HeapTuple tup)
+{
+   Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
+
+   /* Check that this is actually an enum */
+   if (typTup->typtype != TYPTYPE_ENUM)
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("%s is not an enum",
+                       format_type_be(HeapTupleGetOid(tup)))));
+
+   /* Permission check: must own type */
+   if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+                      format_type_be(HeapTupleGetOid(tup)));
+}
+
+
 /*
  * DefineRange
  *     Registers a new range type.
 DefineRange(CreateRangeStmt *stmt)
 {
    char       *typeName;
-   char       *rangeArrayName;
    Oid         typeNamespace;
    Oid         typoid;
+   char       *rangeArrayName;
    Oid         rangeArrayOid;
-   List       *parameters = stmt->params;
+   Oid         rangeSubtype = InvalidOid;
    List       *rangeSubOpclassName = NIL;
-   List       *rangeSubtypeDiffName = NIL;
    List       *rangeCollationName = NIL;
-   Oid         rangeCollation = InvalidOid;
-   regproc     rangeAnalyze = InvalidOid;
-   Oid         rangeSubtype = InvalidOid;
-   regproc     rangeSubOpclass = InvalidOid;
-   regproc     rangeCanonical = InvalidOid;
-   regproc     rangeSubtypeDiff = InvalidOid;
+   List       *rangeCanonicalName = NIL;
+   List       *rangeSubtypeDiffName = NIL;
+   List       *rangeAnalyzeName = NIL;
+   Oid         rangeSubOpclass;
+   Oid         rangeCollation;
+   regproc     rangeCanonical;
+   regproc     rangeSubtypeDiff;
+   regproc     rangeAnalyze;
    int16       subtyplen;
    bool        subtypbyval;
    char        subtypalign;
                       get_namespace_name(typeNamespace));
 
    /*
-    * Look to see if type already exists (presumably as a shell; if not,
-    * TypeCreate will complain).
+    * Look to see if type already exists.
     */
    typoid = GetSysCacheOid2(TYPENAMENSP,
                             CStringGetDatum(typeName),
    {
        if (moveArrayTypeName(typoid, typeName, typeNamespace))
            typoid = InvalidOid;
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_DUPLICATE_OBJECT),
+                    errmsg("type \"%s\" already exists", typeName)));
    }
 
    /*
     * If it doesn't exist, create it as a shell, so that the OID is known for
-    * use in the I/O function definitions.
+    * use in the range function definitions.
     */
    if (!OidIsValid(typoid))
    {
        typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
        /* Make new shell type visible for modification below */
        CommandCounterIncrement();
-
-       /*
-        * 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)));
    }
 
+   /* Extract the parameters from the parameter list */
    foreach(lc, stmt->params)
    {
-       DefElem    *defel = lfirst(lc);
+       DefElem    *defel = (DefElem *) lfirst(lc);
 
        if (pg_strcasecmp(defel->defname, "subtype") == 0)
        {
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
+           /* we can look up the subtype name immediately */
            rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
        }
-       else if (pg_strcasecmp(defel->defname, "canonical") == 0)
+       else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
        {
-           if (OidIsValid(rangeCanonical))
+           if (rangeSubOpclassName != NIL)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           rangeCanonical = findRangeCanonicalFunction(
-                                        defGetQualifiedName(defel), typoid);
+           rangeSubOpclassName = defGetQualifiedName(defel);
        }
        else if (pg_strcasecmp(defel->defname, "collation") == 0)
        {
                         errmsg("conflicting or redundant options")));
            rangeCollationName = defGetQualifiedName(defel);
        }
-       else if (pg_strcasecmp(defel->defname, "analyze") == 0)
+       else if (pg_strcasecmp(defel->defname, "canonical") == 0)
        {
-           if (OidIsValid(rangeAnalyze))
+           if (rangeCanonicalName != NIL)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel),
-                                                  typoid);
+           rangeCanonicalName = defGetQualifiedName(defel);
        }
-       else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
+       else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0)
        {
-           if (rangeSubOpclassName != NIL)
+           if (rangeSubtypeDiffName != NIL)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           rangeSubOpclassName = defGetQualifiedName(defel);
+           rangeSubtypeDiffName = defGetQualifiedName(defel);
        }
-       else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0)
+       else if (pg_strcasecmp(defel->defname, "analyze") == 0)
        {
-           if (rangeSubtypeDiffName != NIL)
+           if (rangeAnalyzeName != NIL)
                ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("conflicting or redundant options")));
-           rangeSubtypeDiffName = defGetQualifiedName(defel);
+           rangeAnalyzeName = defGetQualifiedName(defel);
        }
        else
-       {
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("type attribute \"%s\" not recognized",
                            defel->defname)));
-           continue;
-       }
    }
 
+   /* Must have a subtype */
    if (!OidIsValid(rangeSubtype))
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("type attribute \"subtype\" is required")));
+   /* disallow ranges of pseudotypes */
+   if (get_typtype(rangeSubtype) == TYPTYPE_PSEUDO)
+       ereport(ERROR,
+               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                errmsg("range subtype cannot be %s",
+                       format_type_be(rangeSubtype))));
 
+   /* Identify subopclass */
+   rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
+
+   /* Identify collation to use, if any */
    if (type_is_collatable(rangeSubtype))
    {
-       if (rangeCollationName == NIL)
-           rangeCollation = get_typcollation(rangeSubtype);
-       else
+       if (rangeCollationName != NIL)
            rangeCollation = get_collation_oid(rangeCollationName, false);
+       else
+           rangeCollation = get_typcollation(rangeSubtype);
+   }
+   else
+   {
+       if (rangeCollationName != NIL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                    errmsg("range collation specified but subtype does not support collation")));
+       rangeCollation = InvalidOid;
    }
-   else if (rangeCollationName != NIL)
-       ereport(ERROR,
-               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("range collation specified but subtype does not support collation")));
 
-   rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
+   /* Identify support functions, if provided */
+   if (rangeCanonicalName != NIL)
+       rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName,
+                                                   typoid);
+   else
+       rangeCanonical = InvalidOid;
 
    if (rangeSubtypeDiffName != NIL)
        rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
                                                        rangeSubtype);
+   else
+       rangeSubtypeDiff = InvalidOid;
+
+   if (rangeAnalyzeName != NIL)
+       rangeAnalyze = findTypeAnalyzeFunction(rangeAnalyzeName,
+                                              typoid);
+   else
+       rangeAnalyze = InvalidOid;
 
    get_typlenbyvalalign(rangeSubtype,
                         &subtyplen, &subtypbyval, &subtypalign);
                   rangeArrayOid,       /* 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 */
+                  NULL,        /* no binary form available either */
                   false,       /* never passed by value */
                   alignment,   /* alignment */
                   'x',         /* TOAST strategy (always extended) */
                   -1,          /* typMod (Domains only) */
                   0,           /* Array dimensions of typbasetype */
                   false,       /* Type NOT NULL */
-                  InvalidOid); /* typcollation */
+                  InvalidOid); /* type's collation (ranges never have one) */
 
-   /* create the entry in pg_range */
+   /* Create the entry in pg_range */
    RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
                rangeCanonical, rangeSubtypeDiff);
 
    pfree(rangeArrayName);
 
    /* And create the constructor functions for this range type */
-   makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
+   makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
 }
 
 /*
- * Because there may exist several range types over one subtype, the range type
- * can't be determined from the subtype. This means that constructors can't be
- * polymorphic, and so we must generate a new constructor for every range type
- * defined.
+ * Because there may exist several range types over the same subtype, the
+ * range type can't be uniquely determined from the subtype.  So it's
+ * impossible to define a polymorphic constructor; we have to generate new
+ * constructor functions explicitly for each range type.
  *
- * We actually define 4 functions with 0 through 3 arguments. This is just to
- * offer more convenience for the user.
+ * We actually define 4 functions, with 0 through 3 arguments.  This is just
+ * to offer more convenience for the user.
  */
 static void
-makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
+makeRangeConstructors(const char *name, Oid namespace,
+                     Oid rangeOid, Oid subtype)
 {
-   ObjectAddress referenced;
+   static const char * const prosrc[4] = {"range_constructor0",
+                                          "range_constructor1",
+                                          "range_constructor2",
+                                          "range_constructor3"};
+   static const int pronargs[4] = {0, 1, 2, 3};
+
    Oid         constructorArgTypes[3];
+   ObjectAddress myself,
+               referenced;
    int         i;
 
-   referenced.classId = TypeRelationId;
-   referenced.objectId = rangeOid;
-   referenced.objectSubId = 0;
-
    constructorArgTypes[0] = subtype;
    constructorArgTypes[1] = subtype;
    constructorArgTypes[2] = TEXTOID;
 
-   for (i = 0; i < 4; i++)
+   referenced.classId = TypeRelationId;
+   referenced.objectId = rangeOid;
+   referenced.objectSubId = 0;
+
+   for (i = 0; i < lengthof(prosrc); i++)
    {
        oidvector  *constructorArgTypesVector;
-       ObjectAddress myself;
        Oid         procOid;
-       char       *prosrc[4] = {"range_constructor0",
-           "range_constructor1",
-           "range_constructor2",
-       "range_constructor3"};
 
-       constructorArgTypesVector = buildoidvector(constructorArgTypes, i);
+       constructorArgTypesVector = buildoidvector(constructorArgTypes,
+                                                  pronargs[i]);
 
-       procOid = ProcedureCreate(
-                                 name, /* name */
+       procOid = ProcedureCreate(name,         /* name: same as range type */
                                  namespace,    /* namespace */
                                  false,        /* replace */
-                                 false,        /* return set */
+                                 false,        /* returns set */
                                  rangeOid,     /* return type */
                                  INTERNALlanguageId,   /* language */
                                  F_FMGR_INTERNAL_VALIDATOR,    /* language validator */
                                  prosrc[i],    /* prosrc */
-                                 NULL, /* probin */
-                                 false,        /* agg */
-                                 false,        /* window */
-                                 false,        /* security definer */
-                                 false,        /* strict */
+                                 NULL,         /* probin */
+                                 false,        /* isAgg */
+                                 false,        /* isWindowFunc */
+                                 false,        /* security_definer */
+                                 false,        /* isStrict */
                                  PROVOLATILE_IMMUTABLE,        /* volatility */
-                                 constructorArgTypesVector,    /* param types */
+                                 constructorArgTypesVector,    /* parameterTypes */
                                  PointerGetDatum(NULL),        /* allParameterTypes */
                                  PointerGetDatum(NULL),        /* parameterModes */
                                  PointerGetDatum(NULL),        /* parameterNames */
        myself.classId = ProcedureRelationId;
        myself.objectId = procOid;
        myself.objectSubId = 0;
+
        recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
    }
 }
 
-/*
- * AlterEnum
- *     Adds a new label to an existing enum.
- */
-void
-AlterEnum(AlterEnumStmt *stmt)
-{
-   Oid         enum_type_oid;
-   TypeName   *typename;
-   HeapTuple   tup;
-
-   /* Make a TypeName so we can use standard type lookup machinery */
-   typename = makeTypeNameFromNameList(stmt->typeName);
-   enum_type_oid = typenameTypeId(NULL, typename);
-
-   tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
-   if (!HeapTupleIsValid(tup))
-       elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
-
-   /* Check it's an enum and check user has permission to ALTER the enum */
-   checkEnumOwner(tup);
-
-   /* Add the new label */
-   AddEnumLabel(enum_type_oid, stmt->newVal,
-                stmt->newValNeighbor, stmt->newValIsAfter);
-
-   ReleaseSysCache(tup);
-}
-
-
-/*
- * checkEnumOwner
- *
- * Check that the type is actually an enum and that the current user
- * has permission to do ALTER TYPE on it.  Throw an error if not.
- */
-static void
-checkEnumOwner(HeapTuple tup)
-{
-   Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
-
-   /* Check that this is actually an enum */
-   if (typTup->typtype != TYPTYPE_ENUM)
-       ereport(ERROR,
-               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("%s is not an enum",
-                       format_type_be(HeapTupleGetOid(tup)))));
-
-   /* Permission check: must own type */
-   if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
-                      format_type_be(HeapTupleGetOid(tup)));
-}
-
 
 /*
  * Find suitable I/O functions for a type.
    return procOid;
 }
 
+/*
+ * Find suitable support functions and opclasses for a range type.
+ */
+
 /*
  * Find named btree opclass for subtype, or default btree opclass if
- * opcname is NIL. This will be used for comparing values of subtype.
+ * opcname is NIL.
  */
 static Oid
 findRangeSubOpclass(List *opcname, Oid subtype)
 {
    Oid         opcid;
+   Oid         opInputType;
+
+   if (opcname != NIL)
+   {
+       opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
 
-   if (opcname == NIL)
+       /*
+        * Verify that the operator class accepts this datatype. Note we will
+        * accept binary compatibility.
+        */
+       opInputType = get_opclass_input_type(opcid);
+       if (!IsBinaryCoercible(subtype, opInputType))
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                    errmsg("operator class \"%s\" does not accept data type %s",
+                           NameListToString(opcname),
+                           format_type_be(subtype))));
+   }
+   else
    {
        opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
        if (!OidIsValid(opcid))
        {
+           /* We spell the error message identically to GetIndexOpClass */
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_OBJECT),
-                    errmsg("data type %s has no default operator class for access method \"btree\"",
-                           format_type_be(subtype)),
-                    errhint("You must specify an operator class for the data type or define a default operator class for the data type.")));
+                    errmsg("data type %s has no default operator class for access method \"%s\"",
+                           format_type_be(subtype), "btree"),
+                    errhint("You must specify an operator class for the range type or define a default operator class for the subtype.")));
        }
-       return opcid;
    }
 
-   opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
-
    return opcid;
 }
 
-/*
- * Used to find a range's 'canonical' function.
- */
 static Oid
-findRangeSubtypeDiffFunction(List *procname, Oid typeOid)
+findRangeCanonicalFunction(List *procname, Oid typeOid)
 {
-   Oid         argList[2];
+   Oid         argList[1];
    Oid         procOid;
 
+   /*
+    * Range canonical functions must take and return the range type, and must
+    * be immutable.
+    */
    argList[0] = typeOid;
-   argList[1] = typeOid;
 
-   procOid = LookupFuncName(procname, 2, argList, true);
+   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, 2, NIL, argList))));
+                       func_signature_string(procname, 1, NIL, argList))));
 
-   if (get_func_rettype(procOid) != FLOAT8OID)
+   if (get_func_rettype(procOid) != typeOid)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-        errmsg("range subtype diff function %s must return type \"float8\"",
-               func_signature_string(procname, 2, NIL, argList))));
+                errmsg("range canonical function %s must return range type",
+                       func_signature_string(procname, 1, NIL, argList))));
 
    if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                errmsg("range subtype diff function %s must be immutable",
-                       func_signature_string(procname, 2, NIL, argList))));
+                errmsg("range canonical function %s must be immutable",
+                       func_signature_string(procname, 1, NIL, argList))));
 
    return procOid;
 }
 
-/*
- * Used to find a range's 'canonical' function.
- */
 static Oid
-findRangeCanonicalFunction(List *procname, Oid typeOid)
+findRangeSubtypeDiffFunction(List *procname, Oid subtype)
 {
-   Oid         argList[1];
+   Oid         argList[2];
    Oid         procOid;
 
-   argList[0] = typeOid;
+   /*
+    * Range subtype diff functions must take two arguments of the subtype,
+    * must return float8, and must be immutable.
+    */
+   argList[0] = subtype;
+   argList[1] = subtype;
 
-   procOid = LookupFuncName(procname, 1, argList, true);
+   procOid = LookupFuncName(procname, 2, argList, true);
 
    if (!OidIsValid(procOid))
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(procname, 1, NIL, argList))));
+                       func_signature_string(procname, 2, NIL, argList))));
 
-   if (get_func_rettype(procOid) != typeOid)
+   if (get_func_rettype(procOid) != FLOAT8OID)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                errmsg("range canonical function %s must return range type",
-                       NameListToString(procname))));
+                errmsg("range subtype diff function %s must return type double precision",
+                       func_signature_string(procname, 2, NIL, argList))));
 
    if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                errmsg("range canonical function %s must be immutable",
-                       func_signature_string(procname, 1, NIL, argList))));
+                errmsg("range subtype diff function %s must be immutable",
+                       func_signature_string(procname, 2, NIL, argList))));
 
    return procOid;
 }