summaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_coerce.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r--src/backend/parser/parse_coerce.c807
1 files changed, 0 insertions, 807 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
deleted file mode 100644
index 3c441509b1d..00000000000
--- a/src/backend/parser/parse_coerce.c
+++ /dev/null
@@ -1,807 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * parse_coerce.c
- * handle type coercions/conversions for parser
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.74 2002/06/20 20:29:32 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "catalog/pg_proc.h"
-#include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_func.h"
-#include "parser/parse_type.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#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 inputTypeId,
- Oid secondArgType, bool isExplicit);
-
-
-/* coerce_type()
- * Convert a function argument to a different type.
- */
-Node *
-coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
- Oid targetTypeId, int32 atttypmod, bool isExplicit)
-{
- Node *result;
-
- if (targetTypeId == inputTypeId ||
- targetTypeId == InvalidOid ||
- node == NULL)
- {
- /* no conversion needed */
- result = node;
- }
- else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
- {
- /*
- * Input is a string constant with previously undetermined type.
- * Apply the target type's typinput function to it to produce a
- * constant of the target type.
- *
- * NOTE: this case cannot be folded together with the other
- * constant-input case, since the typinput function does not
- * necessarily behave the same as a type conversion function. For
- * example, int4's typinput function will reject "1.2", whereas
- * float-to-int type conversion will round to integer.
- *
- * XXX if the typinput function is not cachable, we really ought to
- * postpone evaluation of the function call until runtime. But
- * there is no way to represent a typinput function call as an
- * expression tree, because C-string values are not Datums.
- */
- Const *con = (Const *) node;
- Const *newcon = makeNode(Const);
- Type targetType = typeidType(targetTypeId);
-
- newcon->consttype = targetTypeId;
- newcon->constlen = typeLen(targetType);
- newcon->constbyval = typeByVal(targetType);
- newcon->constisnull = con->constisnull;
- newcon->constisset = false;
-
- if (!con->constisnull)
- {
- char *val = DatumGetCString(DirectFunctionCall1(unknownout,
- con->constvalue));
-
- newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
- pfree(val);
- }
-
- ReleaseSysCache(targetType);
-
- result = (Node *) newcon;
- }
- else if (IsBinaryCompatible(inputTypeId, targetTypeId))
- {
- /*
- * 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.
- *
- * 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.
- */
- result = (Node *) makeRelabelType(node, targetTypeId, -1);
- }
- else if (typeInheritsFrom(inputTypeId, targetTypeId))
- {
- /*
- * Input class type is a subclass of target, so nothing to do
- * --- except relabel the type. This is binary compatibility
- * for complex types.
- */
- result = (Node *) makeRelabelType(node, targetTypeId, -1);
- }
- 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),
- InvalidOid,
- 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, relabel with domain type ID */
- if (targetTypeId != baseTypeId)
- 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);
- }
-
- 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.
- *
- * 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,
- bool isExplicit)
-{
- int i;
-
- /* run through argument list... */
- for (i = 0; i < nargs; i++)
- {
- Oid inputTypeId = input_typeids[i];
- Oid targetTypeId = func_typeids[i];
- Oid funcId;
-
- /* no problem if same type */
- if (inputTypeId == targetTypeId)
- continue;
-
- /*
- * one of the known-good transparent conversions? then drop
- * through...
- */
- if (IsBinaryCompatible(inputTypeId, targetTypeId))
- continue;
-
- /* don't know what to do for the output type? then quit... */
- if (targetTypeId == InvalidOid)
- return false;
- /* don't know what to do for the input type? then quit... */
- if (inputTypeId == InvalidOid)
- return false;
-
- /*
- * If input is an untyped string constant, assume we can convert
- * it to anything except a class type.
- */
- if (inputTypeId == UNKNOWNOID)
- {
- if (ISCOMPLEX(targetTypeId))
- return false;
- continue;
- }
-
- /*
- * If input is a class type that inherits from target, no problem
- */
- if (typeInheritsFrom(inputTypeId, targetTypeId))
- continue;
-
- /* don't choke on references to no-longer-existing types */
- if (!typeidIsValid(inputTypeId))
- return false;
- if (!typeidIsValid(targetTypeId))
- return false;
-
- /*
- * 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.
- */
- funcId = find_coercion_function(getBaseType(targetTypeId),
- getBaseType(inputTypeId),
- InvalidOid,
- isExplicit);
- if (!OidIsValid(funcId))
- return false;
- }
-
- return true;
-}
-
-/* coerce_type_typmod()
- * Force a value to a particular typmod, if meaningful and possible.
- *
- * This is applied to values that are going to be stored in a relation
- * (where we have an atttypmod for the column) as well as values being
- * explicitly CASTed (where the typmod comes from the target type spec).
- *
- * 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.
- */
-Node *
-coerce_type_typmod(ParseState *pstate, Node *node,
- Oid targetTypeId, int32 atttypmod)
-{
- Oid baseTypeId;
- Oid funcId;
-
- /*
- * A negative typmod is assumed to mean that no coercion is wanted.
- */
- if (atttypmod < 0 || atttypmod == exprTypmod(node))
- return node;
-
- /* If given type is a domain, use base type instead */
- baseTypeId = getBaseType(targetTypeId);
-
- /* Note this is always implicit coercion */
- funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
-
- if (OidIsValid(funcId))
- {
- Const *cons;
-
- cons = makeConst(INT4OID,
- sizeof(int32),
- Int32GetDatum(atttypmod),
- false,
- true,
- false,
- false);
-
- node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
-
- /* relabel if it's domain case */
- if (targetTypeId != baseTypeId)
- node = (Node *) makeRelabelType(node, targetTypeId, atttypmod);
- }
-
- return node;
-}
-
-
-/* coerce_to_boolean()
- * Coerce an argument of a construct that requires boolean input
- * (AND, OR, NOT, etc). Also check that input is not a set.
- *
- * Returns the possibly-transformed node tree.
- */
-Node *
-coerce_to_boolean(Node *node, const char *constructName)
-{
- Oid inputTypeId = exprType(node);
- Oid targetTypeId;
-
- if (inputTypeId != BOOLOID)
- {
- targetTypeId = BOOLOID;
- if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
- {
- /* translator: first %s is name of a SQL construct, eg WHERE */
- elog(ERROR, "Argument of %s must be type boolean, not type %s",
- constructName, format_type_be(inputTypeId));
- }
- node = coerce_type(NULL, node, inputTypeId, targetTypeId, -1,
- false);
- }
-
- if (expression_returns_set(node))
- {
- /* translator: %s is name of a SQL construct, eg WHERE */
- elog(ERROR, "Argument of %s must not be a set function",
- constructName);
- }
-
- return node;
-}
-
-
-/* select_common_type()
- * Determine the common supertype of a list of input expression types.
- * This is used for determining the output type of CASE and UNION
- * constructs.
- *
- * typeids is a nonempty integer list of type OIDs. Note that earlier items
- * in the list will be preferred if there is doubt.
- * 'context' is a phrase to use in the error message if we fail to select
- * a usable type.
- *
- * XXX this code is WRONG, since (for example) given the input (int4,int8)
- * it will select int4, whereas according to SQL92 clause 9.3 the correct
- * answer is clearly int8. To fix this we need a notion of a promotion
- * hierarchy within type categories --- something more complete than
- * just a single preferred type.
- */
-Oid
-select_common_type(List *typeids, const char *context)
-{
- Oid ptype;
- CATEGORY pcategory;
- List *l;
-
- Assert(typeids != NIL);
- ptype = (Oid) lfirsti(typeids);
- pcategory = TypeCategory(ptype);
- foreach(l, lnext(typeids))
- {
- Oid ntype = (Oid) lfirsti(l);
-
- /* move on to next one if no new information... */
- if ((ntype != InvalidOid) && (ntype != UNKNOWNOID) && (ntype != ptype))
- {
- if ((ptype == InvalidOid) || ptype == UNKNOWNOID)
- {
- /* so far, only nulls so take anything... */
- ptype = ntype;
- pcategory = TypeCategory(ptype);
- }
- else if (TypeCategory(ntype) != pcategory)
- {
- /*
- * both types in different categories? then not much
- * hope...
- */
- elog(ERROR, "%s types '%s' and '%s' not matched",
- context, format_type_be(ptype), format_type_be(ntype));
- }
- else if (IsPreferredType(pcategory, ntype)
- && !IsPreferredType(pcategory, ptype)
- && can_coerce_type(1, &ptype, &ntype, false))
- {
- /*
- * new one is preferred and can convert? then take it...
- */
- ptype = ntype;
- pcategory = TypeCategory(ptype);
- }
- }
- }
-
- /*
- * If all the inputs were UNKNOWN type --- ie, unknown-type literals
- * --- then resolve as type TEXT. This situation comes up with
- * constructs like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END);
- * SELECT 'foo' UNION SELECT 'bar'; It might seem desirable to leave
- * the construct's output type as UNKNOWN, but that really doesn't
- * work, because we'd probably end up needing a runtime coercion from
- * UNKNOWN to something else, and we usually won't have it. We need
- * to coerce the unknown literals while they are still literals, so a
- * decision has to be made now.
- */
- if (ptype == UNKNOWNOID)
- ptype = TEXTOID;
-
- return ptype;
-}
-
-/* coerce_to_common_type()
- * Coerce an expression to the given type.
- *
- * This is used following select_common_type() to coerce the individual
- * expressions to the desired type. 'context' is a phrase to use in the
- * error message if we fail to coerce.
- *
- * NOTE: pstate may be NULL.
- */
-Node *
-coerce_to_common_type(ParseState *pstate, Node *node,
- Oid targetTypeId,
- const char *context)
-{
- Oid inputTypeId = exprType(node);
-
- if (inputTypeId == targetTypeId)
- return node; /* no work */
- if (can_coerce_type(1, &inputTypeId, &targetTypeId, false))
- node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
- false);
- else
- {
- elog(ERROR, "%s unable to convert to type %s",
- context, format_type_be(targetTypeId));
- }
- return node;
-}
-
-
-/* TypeCategory()
- * Assign a category to the specified OID.
- * XXX This should be moved to system catalog lookups
- * to allow for better type extensibility.
- * - thomas 2001-09-30
- */
-CATEGORY
-TypeCategory(Oid inType)
-{
- CATEGORY result;
-
- switch (inType)
- {
- case (BOOLOID):
- result = BOOLEAN_TYPE;
- break;
-
- case (CHAROID):
- case (NAMEOID):
- case (BPCHAROID):
- case (VARCHAROID):
- case (TEXTOID):
- result = STRING_TYPE;
- break;
-
- case (BITOID):
- case (VARBITOID):
- result = BITSTRING_TYPE;
- break;
-
- case (OIDOID):
- case (REGPROCOID):
- case (REGPROCEDUREOID):
- case (REGOPEROID):
- case (REGOPERATOROID):
- case (REGCLASSOID):
- case (REGTYPEOID):
- case (INT2OID):
- case (INT4OID):
- case (INT8OID):
- case (FLOAT4OID):
- case (FLOAT8OID):
- case (NUMERICOID):
- case (CASHOID):
- result = NUMERIC_TYPE;
- break;
-
- case (DATEOID):
- case (TIMEOID):
- case (TIMETZOID):
- case (ABSTIMEOID):
- case (TIMESTAMPOID):
- case (TIMESTAMPTZOID):
- result = DATETIME_TYPE;
- break;
-
- case (RELTIMEOID):
- case (TINTERVALOID):
- case (INTERVALOID):
- result = TIMESPAN_TYPE;
- break;
-
- case (POINTOID):
- case (LSEGOID):
- case (PATHOID):
- case (BOXOID):
- case (POLYGONOID):
- case (LINEOID):
- case (CIRCLEOID):
- result = GEOMETRIC_TYPE;
- break;
-
- case (INETOID):
- case (CIDROID):
- result = NETWORK_TYPE;
- break;
-
- case (UNKNOWNOID):
- case (InvalidOid):
- result = UNKNOWN_TYPE;
- break;
-
- default:
- result = USER_TYPE;
- break;
- }
- return result;
-} /* 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)
-{
- if (type1 == type2)
- return true;
- if (TypeIsTextGroup(type1) && TypeIsTextGroup(type2))
- return true;
- if (TypeIsInt4GroupA(type1) && TypeIsInt4GroupA(type2))
- return true;
- if (TypeIsInt4GroupB(type1) && TypeIsInt4GroupB(type2))
- return true;
- if (TypeIsInt4GroupC(type1) && TypeIsInt4GroupC(type2))
- return true;
- if (TypeIsInetGroup(type1) && TypeIsInetGroup(type2))
- return true;
- if (TypeIsBitGroup(type1) && TypeIsBitGroup(type2))
- return true;
- return false;
-}
-
-
-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
- * to allow for better type extensibility.
- * - thomas 2001-09-30
- */
-bool
-IsPreferredType(CATEGORY category, Oid type)
-{
- return (type == PreferredType(category, type));
-} /* IsPreferredType() */
-
-
-/* PreferredType()
- * Return the preferred type OID for the specified category.
- * XXX This should be moved to system catalog lookups
- * to allow for better type extensibility.
- * - thomas 2001-09-30
- */
-static Oid
-PreferredType(CATEGORY category, Oid type)
-{
- Oid result;
-
- switch (category)
- {
- case (BOOLEAN_TYPE):
- result = BOOLOID;
- break;
-
- case (STRING_TYPE):
- result = TEXTOID;
- break;
-
- case (BITSTRING_TYPE):
- result = VARBITOID;
- break;
-
- case (NUMERIC_TYPE):
- if (TypeIsOidGroup(type))
- result = OIDOID;
- else if (type == NUMERICOID)
- result = NUMERICOID;
- else
- result = FLOAT8OID;
- break;
-
- case (DATETIME_TYPE):
- if (type == DATEOID)
- result = TIMESTAMPOID;
- else
- result = TIMESTAMPTZOID;
- break;
-
- case (TIMESPAN_TYPE):
- result = INTERVALOID;
- break;
-
- case (NETWORK_TYPE):
- result = INETOID;
- break;
-
- case (GEOMETRIC_TYPE):
- case (USER_TYPE):
- result = type;
- break;
-
- default:
- result = UNKNOWNOID;
- break;
- }
- return result;
-} /* PreferredType() */
-
-/*
- * find_coercion_function
- * Look for a coercion function between two types.
- *
- * 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 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.
- *
- * If a function is found, return its pg_proc OID; else return InvalidOid.
- */
-static Oid
-find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType,
- bool isExplicit)
-{
- Oid funcid = InvalidOid;
- Type targetType;
- char *typname;
- Oid typnamespace;
- Oid oid_array[FUNC_MAX_ARGS];
- int nargs;
- HeapTuple ftup;
-
- targetType = typeidType(targetTypeId);
- typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname);
- typnamespace = ((Form_pg_type) GETSTRUCT(targetType))->typnamespace;
-
- MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
- oid_array[0] = inputTypeId;
- if (OidIsValid(secondArgType))
- {
- oid_array[1] = secondArgType;
- nargs = 2;
- }
- else
- nargs = 1;
-
- ftup = SearchSysCache(PROCNAMENSP,
- CStringGetDatum(typname),
- Int16GetDatum(nargs),
- PointerGetDatum(oid_array),
- ObjectIdGetDatum(typnamespace));
- if (HeapTupleIsValid(ftup))
- {
- Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
-
- /* Make sure the function's result type is as expected */
- if (pform->prorettype == targetTypeId && !pform->proretset &&
- !pform->proisagg)
- {
- /* If needed, make sure it can be invoked implicitly */
- if (isExplicit || pform->proimplicit)
- {
- /* Okay to use it */
- funcid = ftup->t_data->t_oid;
- }
- }
- ReleaseSysCache(ftup);
- }
-
- ReleaseSysCache(targetType);
- return funcid;
-}
-
-/*
- * Build an expression tree representing a function call.
- *
- * The argument expressions must have been transformed already.
- */
-static Node *
-build_func_call(Oid funcid, Oid rettype, List *args)
-{
- Func *funcnode;
- Expr *expr;
-
- funcnode = makeNode(Func);
- funcnode->funcid = funcid;
- funcnode->funcresulttype = rettype;
- funcnode->funcretset = false; /* only possible case here */
- funcnode->func_fcache = NULL;
-
- expr = makeNode(Expr);
- expr->typeOid = rettype;
- expr->opType = FUNC_EXPR;
- expr->oper = (Node *) funcnode;
- expr->args = args;
-
- return (Node *) expr;
-}