]> granicus.if.org Git - postgresql/commitdiff
Code cleanups: make non-implicit WITHOUT FUNCTION casts work, avoid
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 1 Sep 2002 02:27:32 +0000 (02:27 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 1 Sep 2002 02:27:32 +0000 (02:27 +0000)
redundant pg_cast searches, fix obsolete comments.

src/backend/parser/parse_coerce.c

index 397bd4b8b079e84482b2f5570f9a2c000009baba..18224a7e3fef45e63f0b305cb515318da4c391f4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.81 2002/08/31 22:10:46 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.82 2002/09/01 02:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/syscache.h"
 
 
-Oid                    DemoteType(Oid inType);
-Oid                    PromoteTypeToNext(Oid inType);
-
 static Oid     PreferredType(CATEGORY category, Oid type);
-static Node *build_func_call(Oid funcid, Oid rettype, List *args);
-static Oid     find_coercion_function(Oid targetTypeId, Oid sourceTypeId,
-                                                                  bool isExplicit);
+static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
+                                                                 bool isExplicit,
+                                                                 Oid *funcid);
 static Oid     find_typmod_coercion_function(Oid typeId);
+static Node *build_func_call(Oid funcid, Oid rettype, List *args);
 
 
-/* coerce_type()
- * Convert a function argument to a different type.
+/*
+ * coerce_type()
+ *             Convert a function argument to a different type.
+ *
+ * The caller should already have determined that the coercion is possible;
+ * see can_coerce_type.
  */
 Node *
 coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                        Oid targetTypeId, int32 atttypmod, bool isExplicit)
 {
        Node       *result;
+       Oid                     funcId;
 
        if (targetTypeId == inputTypeId ||
                node == NULL)
@@ -118,25 +121,71 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
                /* assume can_coerce_type verified that implicit coercion is okay */
                result = node;
        }
-       else if (IsBinaryCompatible(inputTypeId, targetTypeId))
+       else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
+                                                                  &funcId))
        {
-               /*
-                * We don't really need to do a conversion, but we do need to
-                * attach a RelabelType node so that the expression will be seen
-                * to have the intended type when inspected by higher-level code.
-                *
-                * Also, domains may have value restrictions beyond the base type
-                * that must be accounted for.
-                */
-               result = coerce_type_constraints(pstate, node, targetTypeId, true);
-               /*
-                * XXX could we label result with exprTypmod(node) instead of
-                * default -1 typmod, to save a possible length-coercion later?
-                * Would work if both types have same interpretation of typmod,
-                * which is likely but not certain (wrong if target is a domain,
-                * in any case).
-                */
-               result = (Node *) makeRelabelType(result, targetTypeId, -1);
+               if (OidIsValid(funcId))
+               {
+                       /*
+                        * Generate an expression tree representing run-time application
+                        * of the conversion function.  If we are dealing with a domain
+                        * target type, the conversion function will yield the base type.
+                        */
+                       Oid             baseTypeId = getBaseType(targetTypeId);
+
+                       result = build_func_call(funcId, baseTypeId, makeList1(node));
+
+                       /*
+                        * If domain, test against domain constraints and relabel with
+                        * domain type ID
+                        */
+                       if (targetTypeId != baseTypeId)
+                       {
+                               result = coerce_type_constraints(pstate, result,
+                                                                                                targetTypeId, true);
+                               result = (Node *) makeRelabelType(result, targetTypeId, -1);
+                       }
+
+                       /*
+                        * If the input is a constant, apply the type conversion function
+                        * now instead of delaying to runtime.  (We could, of course, just
+                        * leave this to be done during planning/optimization; but it's a
+                        * very frequent special case, and we save cycles in the rewriter
+                        * if we fold the expression now.)
+                        *
+                        * Note that no folding will occur if the conversion function is
+                        * not marked 'immutable'.
+                        *
+                        * HACK: if constant is NULL, don't fold it here.  This is needed
+                        * by make_subplan(), which calls this routine on placeholder
+                        * Const nodes that mustn't be collapsed.  (It'd be a lot cleaner
+                        * to make a separate node type for that purpose...)
+                        */
+                       if (IsA(node, Const) &&
+                               !((Const *) node)->constisnull)
+                               result = eval_const_expressions(result);
+               }
+               else
+               {
+                       /*
+                        * We don't need to do a physical conversion, but we do need to
+                        * attach a RelabelType node so that the expression will be seen
+                        * to have the intended type when inspected by higher-level code.
+                        *
+                        * Also, domains may have value restrictions beyond the base type
+                        * that must be accounted for.
+                        */
+                       result = coerce_type_constraints(pstate, node,
+                                                                                        targetTypeId, true);
+                       /*
+                        * XXX could we label result with exprTypmod(node) instead of
+                        * default -1 typmod, to save a possible length-coercion later?
+                        * Would work if both types have same interpretation of typmod,
+                        * which is likely but not certain (wrong if target is a domain,
+                        * in any case).
+                        */
+                       result = (Node *) makeRelabelType(result, targetTypeId, -1);
+               }
        }
        else if (typeInheritsFrom(inputTypeId, targetTypeId))
        {
@@ -149,74 +198,23 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
        }
        else
        {
-               /*
-                * Otherwise, find the appropriate type conversion function
-                * (caller should have determined that there is one), and generate
-                * an expression tree representing run-time application of the
-                * conversion function.
-                *
-                * For domains, we use the coercion function for the base type.
-                */
-               Oid                     baseTypeId = getBaseType(targetTypeId);
-               Oid                     funcId;
-
-               funcId = find_coercion_function(baseTypeId,
-                                                                               getBaseType(inputTypeId),
-                                                                               isExplicit);
-               if (!OidIsValid(funcId))
-                       elog(ERROR, "coerce_type: no conversion function from '%s' to '%s'",
-                                format_type_be(inputTypeId), format_type_be(targetTypeId));
-
-               result = build_func_call(funcId, baseTypeId, makeList1(node));
-
-               /*
-                * If domain, test against domain constraints and relabel with
-                * domain type ID
-                */
-               if (targetTypeId != baseTypeId)
-               {
-                       result = coerce_type_constraints(pstate, result, targetTypeId,
-                                                                                        true);
-                       result = (Node *) makeRelabelType(result, targetTypeId, -1);
-               }
-
-               /*
-                * If the input is a constant, apply the type conversion function
-                * now instead of delaying to runtime.  (We could, of course, just
-                * leave this to be done during planning/optimization; but it's a
-                * very frequent special case, and we save cycles in the rewriter
-                * if we fold the expression now.)
-                *
-                * Note that no folding will occur if the conversion function is not
-                * marked 'iscachable'.
-                *
-                * HACK: if constant is NULL, don't fold it here.  This is needed by
-                * make_subplan(), which calls this routine on placeholder Const
-                * nodes that mustn't be collapsed.  (It'd be a lot cleaner to
-                * make a separate node type for that purpose...)
-                */
-               if (IsA(node, Const) &&
-                       !((Const *) node)->constisnull)
-                       result = eval_const_expressions(result);
+               /* If we get here, caller blew it */
+               elog(ERROR, "coerce_type: no conversion function from %s to %s",
+                        format_type_be(inputTypeId), format_type_be(targetTypeId));
+               result = NULL;                  /* keep compiler quiet */
        }
 
        return result;
 }
 
 
-/* can_coerce_type()
- * Can input_typeids be coerced to func_typeids?
- *
- * There are a few types which are known apriori to be convertible.
- * We will check for those cases first, and then look for possible
- * conversion functions.
+/*
+ * can_coerce_type()
+ *             Can input_typeids be coerced to func_typeids?
  *
  * We must be told whether this is an implicit or explicit coercion
  * (explicit being a CAST construct, explicit function call, etc).
  * We will accept a wider set of coercion cases for an explicit coercion.
- *
- * Notes:
- * This uses the same mechanism as the CAST() SQL construct in gram.y.
  */
 bool
 can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
@@ -278,35 +276,101 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
                }
 
                /*
-                * one of the known-good transparent conversions? then drop
-                * through...
+                * If pg_cast shows that we can coerce, accept.  This test now
+                * covers both binary-compatible and coercion-function cases.
                 */
-               if (IsBinaryCompatible(inputTypeId, targetTypeId))
+               if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
+                                                                 &funcId))
                        continue;
 
                /*
-                * If input is a class type that inherits from target, no problem
+                * If input is a class type that inherits from target, accept
                 */
                if (typeInheritsFrom(inputTypeId, targetTypeId))
                        continue;
 
                /*
-                * Else, try for run-time conversion using functions: look for a
-                * single-argument function named with the target type name and
-                * accepting the source type.
-                *
-                * If either type is a domain, use its base type instead.
+                * Else, cannot coerce at this argument position
                 */
-               funcId = find_coercion_function(getBaseType(targetTypeId),
-                                                                               getBaseType(inputTypeId),
-                                                                               isExplicit);
-               if (!OidIsValid(funcId))
-                       return false;
+               return false;
        }
 
        return true;
 }
 
+
+/*
+ * Create an expression tree to enforce the constraints (if any)
+ * that should be applied by the type.  Currently this is only
+ * interesting for domain types.
+ */
+Node *
+coerce_type_constraints(ParseState *pstate, Node *arg,
+                                               Oid typeId, bool applyTypmod)
+{
+       char   *notNull = NULL;
+       int32   typmod = -1;
+
+       for (;;)
+       {
+               HeapTuple       tup;
+               Form_pg_type typTup;
+
+               tup = SearchSysCache(TYPEOID,
+                                                        ObjectIdGetDatum(typeId),
+                                                        0, 0, 0);
+               if (!HeapTupleIsValid(tup))
+                       elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
+                                typeId);
+               typTup = (Form_pg_type) GETSTRUCT(tup);
+
+               /* Test for NOT NULL Constraint */
+               if (typTup->typnotnull && notNull == NULL)
+                       notNull = pstrdup(NameStr(typTup->typname));
+
+               /* TODO: Add CHECK Constraints to domains */
+
+               if (typTup->typtype != 'd')
+               {
+                       /* Not a domain, so done */
+                       ReleaseSysCache(tup);
+                       break;
+               }
+
+               Assert(typmod < 0);
+
+               typeId = typTup->typbasetype;
+               typmod = typTup->typtypmod;
+               ReleaseSysCache(tup);
+       }
+
+       /*
+        * If domain applies a typmod to its base type, do length coercion.
+        */
+       if (applyTypmod && typmod >= 0)
+               arg = coerce_type_typmod(pstate, arg, typeId, typmod);
+
+       /*
+        * Only need to add one NOT NULL check regardless of how many 
+        * domains in the stack request it.  The topmost domain that
+        * requested it is used as the constraint name.
+        */
+       if (notNull)
+       {
+               ConstraintTest *r = makeNode(ConstraintTest);
+
+               r->arg = arg;
+               r->testtype = CONSTR_TEST_NOTNULL;
+               r->name = notNull;
+               r->check_expr = NULL;
+
+               arg = (Node *) r;
+       }       
+
+       return arg;
+}
+
+
 /* coerce_type_typmod()
  * Force a value to a particular typmod, if meaningful and possible.
  *
@@ -317,21 +381,9 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
  * The caller must have already ensured that the value is of the correct
  * type, typically by applying coerce_type.
  *
- * If the target column type possesses a function named for the type
- * and having parameter signature (columntype, int4), we assume that
- * the type requires coercion to its own length and that the said
- * function should be invoked to do that.
- *
- * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
- *
- * This mechanism may seem pretty grotty and in need of replacement by
- * something in pg_cast, but since typmod is only interesting for datatypes
- * that have special handling in the grammar, there's not really much
- * percentage in making it any easier to apply such coercions ...
- *
  * NOTE: this does not need to work on domain types, because any typmod
  * coercion for a domain is considered to be part of the type coercion
- * needed to produce the domain value in the first place.
+ * needed to produce the domain value in the first place.  So, no getBaseType.
  */
 Node *
 coerce_type_typmod(ParseState *pstate, Node *node,
@@ -600,100 +652,6 @@ TypeCategory(Oid inType)
 }      /* TypeCategory() */
 
 
-/* IsBinaryCompatible()
- *             Check if two types are binary-compatible.
- *
- * This notion allows us to cheat and directly exchange values without
- * going through the trouble of calling a conversion function.
- *
- * XXX This should be moved to system catalog lookups
- * to allow for better type extensibility.
- */
-
-#define TypeIsTextGroup(t) \
-               ((t) == TEXTOID || \
-                (t) == BPCHAROID || \
-                (t) == VARCHAROID)
-
-/* Notice OidGroup is a subset of Int4GroupA */
-#define TypeIsOidGroup(t) \
-               ((t) == OIDOID || \
-                (t) == REGPROCOID || \
-                (t) == REGPROCEDUREOID || \
-                (t) == REGOPEROID || \
-                (t) == REGOPERATOROID || \
-                (t) == REGCLASSOID || \
-                (t) == REGTYPEOID)
-
-/*
- * INT4 is binary-compatible with many types, but we don't want to allow
- * implicit coercion directly between, say, OID and AbsTime.  So we subdivide
- * the categories.
- */
-#define TypeIsInt4GroupA(t) \
-               ((t) == INT4OID || \
-                TypeIsOidGroup(t))
-
-#define TypeIsInt4GroupB(t) \
-               ((t) == INT4OID || \
-                (t) == ABSTIMEOID)
-
-#define TypeIsInt4GroupC(t) \
-               ((t) == INT4OID || \
-                (t) == RELTIMEOID)
-
-#define TypeIsInetGroup(t) \
-               ((t) == INETOID || \
-                (t) == CIDROID)
-
-#define TypeIsBitGroup(t) \
-               ((t) == BITOID || \
-                (t) == VARBITOID)
-
-
-static bool
-DirectlyBinaryCompatible(Oid type1, Oid type2)
-{
-       HeapTuple       tuple;
-       bool            result;
-
-       if (type1 == type2)
-               return true;
-
-       tuple = SearchSysCache(CASTSOURCETARGET, type1, type2, 0, 0);
-       if (HeapTupleIsValid(tuple))
-       {
-               Form_pg_cast caststruct;
-
-               caststruct = (Form_pg_cast) GETSTRUCT(tuple);
-               result = caststruct->castfunc == InvalidOid && caststruct->castimplicit;
-               ReleaseSysCache(tuple);
-       }
-       else
-               result = false;
-
-       return result;
-}
-
-
-bool
-IsBinaryCompatible(Oid type1, Oid type2)
-{
-       if (DirectlyBinaryCompatible(type1, type2))
-               return true;
-       /*
-        * Perhaps the types are domains; if so, look at their base types
-        */
-       if (OidIsValid(type1))
-               type1 = getBaseType(type1);
-       if (OidIsValid(type2))
-               type2 = getBaseType(type2);
-       if (DirectlyBinaryCompatible(type1, type2))
-               return true;
-       return false;
-}
-
-
 /* IsPreferredType()
  * Check if this type is a preferred type.
  * XXX This should be moved to system catalog lookups
@@ -733,7 +691,13 @@ PreferredType(CATEGORY category, Oid type)
                        break;
 
                case (NUMERIC_TYPE):
-                       if (TypeIsOidGroup(type))
+                       if (type == OIDOID ||
+                               type == REGPROCOID ||
+                               type == REGPROCEDUREOID ||
+                               type == REGOPEROID ||
+                               type == REGOPERATOROID ||
+                               type == REGCLASSOID ||
+                               type == REGTYPEOID)
                                result = OIDOID;
                        else if (type == NUMERICOID)
                                result = NUMERICOID;
@@ -768,30 +732,85 @@ PreferredType(CATEGORY category, Oid type)
        return result;
 }      /* PreferredType() */
 
-/*
- * find_coercion_function
- *             Look for a coercion function between two types.
+
+/* IsBinaryCompatible()
+ *             Check if two types are binary-compatible.
  *
- * A coercion function must be named after (the internal name of) its
- * result type, and must accept exactly the specified input type.  We
- * also require it to be defined in the same namespace as its result type.
- * Furthermore, unless we are doing explicit coercion the function must
- * be marked as usable for implicit coercion --- this allows coercion
- * functions to be provided that aren't implicitly invokable.
+ * This notion allows us to cheat and directly exchange values without
+ * going through the trouble of calling a conversion function.
  *
- * This routine is also used to look for length-coercion functions, which
- * are similar but accept a second argument.  secondArgType is the type
- * of the second argument (normally INT4OID), or InvalidOid if we are
- * looking for a regular coercion function.
+ * As of 7.3, binary compatibility isn't hardwired into the code anymore.
+ * We consider two types binary-compatible if there is an implicit,
+ * no-function-needed pg_cast entry.  NOTE that we assume that such
+ * entries are symmetric, ie, it doesn't matter which type we consider
+ * source and which target.  (cf. checks in opr_sanity regression test)
+ */
+bool
+IsBinaryCompatible(Oid type1, Oid type2)
+{
+       HeapTuple       tuple;
+       Form_pg_cast castForm;
+       bool            result;
+
+       /* Fast path if same type */
+       if (type1 == type2)
+               return true;
+
+       /* Perhaps the types are domains; if so, look at their base types */
+       if (OidIsValid(type1))
+               type1 = getBaseType(type1);
+       if (OidIsValid(type2))
+               type2 = getBaseType(type2);
+
+       /* Somewhat-fast path if same base type */
+       if (type1 == type2)
+               return true;
+
+       /* Else look in pg_cast */
+       tuple = SearchSysCache(CASTSOURCETARGET,
+                                                  ObjectIdGetDatum(type1),
+                                                  ObjectIdGetDatum(type2),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               return false;                   /* no cast */
+       castForm = (Form_pg_cast) GETSTRUCT(tuple);
+
+       result = (castForm->castfunc == InvalidOid) && castForm->castimplicit;
+
+       ReleaseSysCache(tuple);
+
+       return result;
+}
+
+
+/*
+ * find_coercion_pathway
+ *             Look for a coercion pathway between two types.
  *
- * If a function is found, return its pg_proc OID; else return InvalidOid.
+ * If we find a matching entry in pg_cast, return TRUE, and set *funcid
+ * to the castfunc value (which may be InvalidOid for a binary-compatible
+ * coercion).
  */
-static Oid
-find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
+static bool
+find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit,
+                                         Oid *funcid)
 {
-       Oid                     funcid = InvalidOid;
+       bool            result = false;
        HeapTuple       tuple;
 
+       *funcid = InvalidOid;
+
+       /* Perhaps the types are domains; if so, look at their base types */
+       if (OidIsValid(sourceTypeId))
+               sourceTypeId = getBaseType(sourceTypeId);
+       if (OidIsValid(targetTypeId))
+               targetTypeId = getBaseType(targetTypeId);
+
+       /* Domains are automatically binary-compatible with their base type */
+       if (sourceTypeId == targetTypeId)
+               return true;
+
+       /* Else look in pg_cast */
        tuple = SearchSysCache(CASTSOURCETARGET,
                                                   ObjectIdGetDatum(sourceTypeId),
                                                   ObjectIdGetDatum(targetTypeId),
@@ -799,18 +818,36 @@ find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
 
        if (HeapTupleIsValid(tuple))
        {
-               Form_pg_cast cform = (Form_pg_cast) GETSTRUCT(tuple);
+               Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
 
-               if (isExplicit || cform->castimplicit)
-                       funcid = cform->castfunc;
+               if (isExplicit || castForm->castimplicit)
+               {
+                       *funcid = castForm->castfunc;
+                       result = true;
+               }
 
                ReleaseSysCache(tuple);
        }
 
-       return funcid;
+       return result;
 }
 
 
+/*
+ * find_typmod_coercion_function -- does the given type need length coercion?
+ *
+ * If the target type possesses a function named for the type
+ * and having parameter signature (targettype, int4), we assume that
+ * the type requires coercion to its own length and that the said
+ * function should be invoked to do that.
+ *
+ * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
+ *
+ * This mechanism may seem pretty grotty and in need of replacement by
+ * something in pg_cast, but since typmod is only interesting for datatypes
+ * that have special handling in the grammar, there's not really much
+ * percentage in making it any easier to apply such coercions ...
+ */
 static Oid
 find_typmod_coercion_function(Oid typeId)
 {
@@ -849,6 +886,7 @@ find_typmod_coercion_function(Oid typeId)
        }
 
        ReleaseSysCache(targetType);
+
        return funcid;
 }
 
@@ -877,74 +915,3 @@ build_func_call(Oid funcid, Oid rettype, List *args)
 
        return (Node *) expr;
 }
-
-/*
- * Create an expression tree to enforce the constraints (if any)
- * that should be applied by the type.  Currently this is only
- * interesting for domain types.
- */
-Node *
-coerce_type_constraints(ParseState *pstate, Node *arg,
-                                               Oid typeId, bool applyTypmod)
-{
-       char   *notNull = NULL;
-       int32   typmod = -1;
-
-       for (;;)
-       {
-               HeapTuple       tup;
-               Form_pg_type typTup;
-
-               tup = SearchSysCache(TYPEOID,
-                                                        ObjectIdGetDatum(typeId),
-                                                        0, 0, 0);
-               if (!HeapTupleIsValid(tup))
-                       elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
-                                typeId);
-               typTup = (Form_pg_type) GETSTRUCT(tup);
-
-               /* Test for NOT NULL Constraint */
-               if (typTup->typnotnull && notNull == NULL)
-                       notNull = pstrdup(NameStr(typTup->typname));
-
-               /* TODO: Add CHECK Constraints to domains */
-
-               if (typTup->typtype != 'd')
-               {
-                       /* Not a domain, so done */
-                       ReleaseSysCache(tup);
-                       break;
-               }
-
-               Assert(typmod < 0);
-
-               typeId = typTup->typbasetype;
-               typmod = typTup->typtypmod;
-               ReleaseSysCache(tup);
-       }
-
-       /*
-        * If domain applies a typmod to its base type, do length coercion.
-        */
-       if (applyTypmod && typmod >= 0)
-               arg = coerce_type_typmod(pstate, arg, typeId, typmod);
-
-       /*
-        * Only need to add one NOT NULL check regardless of how many 
-        * domains in the stack request it.  The topmost domain that
-        * requested it is used as the constraint name.
-        */
-       if (notNull)
-       {
-               ConstraintTest *r = makeNode(ConstraintTest);
-
-               r->arg = arg;
-               r->testtype = CONSTR_TEST_NOTNULL;
-               r->name = notNull;
-               r->check_expr = NULL;
-
-               arg = (Node *) r;
-       }       
-
-       return arg;
-}