diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-09-18 21:35:25 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-09-18 21:35:25 +0000 |
commit | b26dfb95222fddd25322bdddf3a5a58d3392d8b1 (patch) | |
tree | 757cf0bafab985d38a5c84d3afebe5edd34c4f27 /src/backend/parser/parse_expr.c | |
parent | cc70ba2e4daa78ba99619770e19beb06de3dfd1c (diff) | |
download | postgresql-b26dfb95222fddd25322bdddf3a5a58d3392d8b1.tar.gz postgresql-b26dfb95222fddd25322bdddf3a5a58d3392d8b1.zip |
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 191 |
1 files changed, 36 insertions, 155 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7be413f6b5b..3873fd37f0d 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.128 2002/09/04 20:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.129 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,6 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" -#include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -40,9 +39,7 @@ static int expr_depth_counter = 0; bool Transform_null_equals = false; -static Node *parser_typecast_constant(Value *expr, TypeName *typename); -static Node *parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename); +static Node *typecast_expression(Node *expr, TypeName *typename); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); @@ -145,10 +142,9 @@ transformExpr(ParseState *pstate, Node *expr) A_Const *con = (A_Const *) expr; Value *val = &con->val; + result = (Node *) make_const(val); if (con->typename != NULL) - result = parser_typecast_constant(val, con->typename); - else - result = (Node *) make_const(val); + result = typecast_expression(result, con->typename); break; } case T_ExprFieldSelect: @@ -175,7 +171,7 @@ transformExpr(ParseState *pstate, Node *expr) TypeCast *tc = (TypeCast *) expr; Node *arg = transformExpr(pstate, tc->arg); - result = parser_typecast_expression(pstate, arg, tc->typename); + result = typecast_expression(arg, tc->typename); break; } case T_A_Expr: @@ -562,8 +558,7 @@ transformExpr(ParseState *pstate, Node *expr) newc->casetype = ptype; /* Convert default result clause, if necessary */ - newc->defresult = coerce_to_common_type(pstate, - newc->defresult, + newc->defresult = coerce_to_common_type(newc->defresult, ptype, "CASE/ELSE"); @@ -572,8 +567,7 @@ transformExpr(ParseState *pstate, Node *expr) { CaseWhen *w = (CaseWhen *) lfirst(args); - w->result = coerce_to_common_type(pstate, - w->result, + w->result = coerce_to_common_type(w->result, ptype, "CASE/WHEN"); } @@ -671,8 +665,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) if (indirection == NIL) return basenode; return (Node *) transformArraySubscripts(pstate, - basenode, exprType(basenode), - indirection, false, NULL); + basenode, + exprType(basenode), + exprTypmod(basenode), + indirection, + false, + NULL); } static Node * @@ -1037,23 +1035,13 @@ exprTypmod(Node *expr) * * If coercedTypmod is not NULL, the typmod is stored there if the expression * is a length-coercion function, else -1 is stored there. - * - * We assume that a two-argument function named for a datatype, whose - * output and first argument types are that datatype, and whose second - * input is an int32 constant, represents a forced length coercion. - * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. */ bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) { Func *func; + int nargs; Const *second_arg; - HeapTuple procTuple; - HeapTuple typeTuple; - Form_pg_proc procStruct; - Form_pg_type typeStruct; if (coercedTypmod != NULL) *coercedTypmod = -1; /* default result on failure */ @@ -1067,62 +1055,26 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) Assert(IsA(func, Func)); /* - * If it's not a two-argument function with the second argument being - * an int4 constant, it can't have been created from a length - * coercion. + * If it didn't come from a coercion context, reject. */ - if (length(((Expr *) expr)->args) != 2) - return false; - second_arg = (Const *) lsecond(((Expr *) expr)->args); - if (!IsA(second_arg, Const) || - second_arg->consttype != INT4OID || - second_arg->constisnull) + if (func->funcformat != COERCE_EXPLICIT_CAST && + func->funcformat != COERCE_IMPLICIT_CAST) return false; /* - * Lookup the function in pg_proc - */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - - /* - * It must be a function with two arguments where the first is of the - * same type as the return value and the second is an int4. Also, just - * to be sure, check return type agrees with expr node. + * If it's not a two-argument or three-argument function with the second + * argument being an int4 constant, it can't have been created from a + * length coercion (it must be a type coercion, instead). */ - if (procStruct->pronargs != 2 || - procStruct->prorettype != procStruct->proargtypes[0] || - procStruct->proargtypes[1] != INT4OID || - procStruct->prorettype != ((Expr *) expr)->typeOid) - { - ReleaseSysCache(procTuple); + nargs = length(((Expr *) expr)->args); + if (nargs < 2 || nargs > 3) return false; - } - /* - * Furthermore, the name and namespace of the function must be the - * same as its result type's name/namespace (cf. - * find_coercion_function). - */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup for type %u failed", - procStruct->prorettype); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - if (strcmp(NameStr(procStruct->proname), - NameStr(typeStruct->typname)) != 0 || - procStruct->pronamespace != typeStruct->typnamespace) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); + second_arg = (Const *) lsecond(((Expr *) expr)->args); + if (!IsA(second_arg, Const) || + second_arg->consttype != INT4OID || + second_arg->constisnull) return false; - } /* * OK, it is indeed a length-coercion function. @@ -1130,79 +1082,17 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) if (coercedTypmod != NULL) *coercedTypmod = DatumGetInt32(second_arg->constvalue); - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); return true; } /* - * Produce an appropriate Const node from a constant value produced - * by the parser and an explicit type name to cast to. - */ -static Node * -parser_typecast_constant(Value *expr, TypeName *typename) -{ - Type tp; - Datum datum; - Const *con; - char *const_string = NULL; - bool string_palloced = false; - bool isNull = false; - - tp = typenameType(typename); - - switch (nodeTag(expr)) - { - case T_Integer: - const_string = DatumGetCString(DirectFunctionCall1(int4out, - Int32GetDatum(expr->val.ival))); - string_palloced = true; - break; - case T_Float: - case T_String: - case T_BitString: - const_string = expr->val.str; - break; - case T_Null: - isNull = true; - break; - default: - elog(ERROR, "Cannot cast this expression to type '%s'", - typeTypeName(tp)); - } - - if (isNull) - datum = (Datum) NULL; - else - datum = stringTypeDatum(tp, const_string, typename->typmod); - - con = makeConst(typeTypeId(tp), - typeLen(tp), - datum, - isNull, - typeByVal(tp), - false, /* not a set */ - true /* is cast */ ); - - if (string_palloced) - pfree(const_string); - - ReleaseSysCache(tp); - - return (Node *) con; -} - -/* - * Handle an explicit CAST applied to a non-constant expression. - * (Actually, this works for constants too, but gram.y won't generate - * a TypeCast node if the argument is just a constant.) + * Handle an explicit CAST construct. * * The given expr has already been transformed, but we need to lookup * the type name and then apply any necessary coercion function(s). */ static Node * -parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename) +typecast_expression(Node *expr, TypeName *typename) { Oid inputType = exprType(expr); Oid targetType; @@ -1212,23 +1102,14 @@ parser_typecast_expression(ParseState *pstate, if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ - if (inputType != targetType) - { - expr = CoerceTargetExpr(pstate, expr, inputType, - targetType, typename->typmod, - true); /* explicit coercion */ - if (expr == NULL) - elog(ERROR, "Cannot cast type '%s' to '%s'", - format_type_be(inputType), - format_type_be(targetType)); - } - - /* - * If the target is a fixed-length type, it may need a length coercion - * as well as a type coercion. - */ - expr = coerce_type_typmod(pstate, expr, - targetType, typename->typmod); + expr = coerce_to_target_type(expr, inputType, + targetType, typename->typmod, + COERCION_EXPLICIT, + COERCE_EXPLICIT_CAST); + if (expr == NULL) + elog(ERROR, "Cannot cast type %s to %s", + format_type_be(inputType), + format_type_be(targetType)); return expr; } |