From: Tom Lane Date: Mon, 17 Jan 2000 00:14:49 +0000 (+0000) Subject: Create a new parsetree node type, TypeCast, so that transformation of X-Git-Tag: REL9_0_0~24147 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=49528361f521b432da2cfafcb81cade17eb324d1;p=pg-rex%2Fsyncrep.git Create a new parsetree node type, TypeCast, so that transformation of SQL cast constructs can be performed during expression transformation instead of during parsing. This allows constructs like x::numeric(9,2) and x::int2::float8 to behave as one would expect. --- diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 53bb27e329..241b8ca103 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.99 2000/01/09 00:26:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.100 2000/01/17 00:14:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1385,6 +1385,17 @@ _copyTypeName(TypeName *from) return newnode; } +static TypeCast * +_copyTypeCast(TypeCast *from) +{ + TypeCast *newnode = makeNode(TypeCast); + + Node_Copy(from, newnode, arg); + Node_Copy(from, newnode, typename); + + return newnode; +} + static Query * _copyQuery(Query *from) { @@ -1658,6 +1669,9 @@ copyObject(void *from) case T_TypeName: retval = _copyTypeName(from); break; + case T_TypeCast: + retval = _copyTypeCast(from); + break; /* * VALUE NODES diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index c11677b5b7..83b05821cc 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.30 2000/01/09 00:26:23 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.31 2000/01/17 00:14:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1047,6 +1047,15 @@ _freeTypeName(TypeName *node) } static void +_freeTypeCast(TypeCast *node) +{ + freeObject(node->arg); + freeObject(node->typename); + + pfree(node); +} + +static void _freeQuery(Query *node) { if (node->utilityStmt && nodeTag(node->utilityStmt) == T_NotifyStmt) @@ -1294,6 +1303,9 @@ freeObject(void *node) case T_TypeName: _freeTypeName(node); break; + case T_TypeCast: + _freeTypeCast(node); + break; /* * VALUE NODES diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2bf112e309..08db66e3c6 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.102 2000/01/14 00:53:21 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.103 2000/01/17 00:14:47 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -191,6 +191,15 @@ _outTypeName(StringInfo str, TypeName *node) } static void +_outTypeCast(StringInfo str, TypeCast *node) +{ + appendStringInfo(str, " TYPECAST :arg "); + _outNode(str, node->arg); + appendStringInfo(str, " :typename "); + _outNode(str, node->typename); +} + +static void _outIndexElem(StringInfo str, IndexElem *node) { appendStringInfo(str, " INDEXELEM :name "); @@ -1292,6 +1301,8 @@ _outAConst(StringInfo str, A_Const *node) { appendStringInfo(str, "CONST "); _outValue(str, &(node->val)); + appendStringInfo(str, " :typename "); + _outNode(str, node->typename); } static void @@ -1400,6 +1411,9 @@ _outNode(StringInfo str, void *obj) case T_TypeName: _outTypeName(str, obj); break; + case T_TypeCast: + _outTypeCast(str, obj); + break; case T_IndexElem: _outIndexElem(str, obj); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a3a01d510f..c4901b4c51 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1,4 +1,4 @@ -%{ /* -*-text-*- */ +%{ /*#define YYDEBUG 1*/ /*------------------------------------------------------------------------- @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.127 2000/01/16 20:04:55 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.128 2000/01/17 00:14:48 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -71,6 +71,7 @@ static int pfunc_num_args; static char *xlateSqlFunc(char *); static char *xlateSqlType(char *); static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr); +static Node *makeTypeCast(Node *arg, TypeName *typename); static Node *makeRowExpr(char *opr, List *largs, List *rargs); static void mapTargetColumns(List *source, List *target); static void param_type_init(Oid *typev, int nargs); @@ -274,6 +275,8 @@ static Node *doNegate(Node *n); * This gets annoying when trying to also retain Postgres' nice * type-extensible features, but we don't really have a choice. * - thomas 1997-10-11 + * NOTE: Whenever possible, try to add new keywords to the ColId list, + * or failing that, at least to the ColLabel list. */ /* Keywords (in SQL92 reserved words) */ @@ -3902,23 +3905,7 @@ MathOp: '+' { $$ = "+"; } a_expr: com_expr { $$ = $1; } | a_expr TYPECAST Typename - { - $$ = (Node *)$1; - /* AexprConst can be either A_Const or ParamNo */ - if (nodeTag($1) == T_A_Const) { - ((A_Const *)$1)->typename = $3; - } else if (nodeTag($1) == T_ParamNo) { - ((ParamNo *)$1)->typename = $3; - /* otherwise, try to transform to a function call */ - } else { - FuncCall *n = makeNode(FuncCall); - n->funcname = $3->name; - n->args = lcons($1,NIL); - n->agg_star = false; - n->agg_distinct = false; - $$ = (Node *)n; - } - } + { $$ = makeTypeCast($1, $3); } /* * Can't collapse this into prior rule by using a_expr_or_null; * that creates reduce/reduce conflicts. Grumble. @@ -4149,23 +4136,7 @@ a_expr: com_expr b_expr: com_expr { $$ = $1; } | b_expr TYPECAST Typename - { - $$ = (Node *)$1; - /* AexprConst can be either A_Const or ParamNo */ - if (nodeTag($1) == T_A_Const) { - ((A_Const *)$1)->typename = $3; - } else if (nodeTag($1) == T_ParamNo) { - ((ParamNo *)$1)->typename = $3; - /* otherwise, try to transform to a function call */ - } else { - FuncCall *n = makeNode(FuncCall); - n->funcname = $3->name; - n->args = lcons($1,NIL); - n->agg_star = false; - n->agg_distinct = false; - $$ = (Node *)n; - } - } + { $$ = makeTypeCast($1, $3); } | NULL_P TYPECAST Typename { A_Const *n = makeNode(A_Const); @@ -4243,23 +4214,7 @@ com_expr: attr | '(' a_expr_or_null ')' { $$ = $2; } | CAST '(' a_expr_or_null AS Typename ')' - { - $$ = (Node *)$3; - /* AexprConst can be either A_Const or ParamNo */ - if (nodeTag($3) == T_A_Const) { - ((A_Const *)$3)->typename = $5; - } else if (nodeTag($3) == T_ParamNo) { - ((ParamNo *)$3)->typename = $5; - /* otherwise, try to transform to a function call */ - } else { - FuncCall *n = makeNode(FuncCall); - n->funcname = $5->name; - n->args = lcons($3,NIL); - n->agg_star = false; - n->agg_distinct = false; - $$ = (Node *)n; - } - } + { $$ = makeTypeCast($3, $5); } | case_expr { $$ = $1; } | func_name '(' ')' @@ -5078,6 +5033,7 @@ ColLabel: ColId { $$ = $1; } | CONSTRAINT { $$ = "constraint"; } | COPY { $$ = "copy"; } | CURRENT { $$ = "current"; } + | DECIMAL { $$ = "decimal"; } | DO { $$ = "do"; } | ELSE { $$ = "else"; } | END_TRANS { $$ = "end"; } @@ -5095,6 +5051,7 @@ ColLabel: ColId { $$ = $1; } | NEW { $$ = "new"; } | NONE { $$ = "none"; } | NULLIF { $$ = "nullif"; } + | NUMERIC { $$ = "numeric"; } | ORDER { $$ = "order"; } | POSITION { $$ = "position"; } | PRECISION { $$ = "precision"; } @@ -5139,6 +5096,36 @@ makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr) return (Node *)a; } +static Node * +makeTypeCast(Node *arg, TypeName *typename) +{ + /* + * If arg is an A_Const or ParamNo, just stick the typename into the + * field reserved for it --- unless there's something there already! + * (We don't want to collapse x::type1::type2 into just x::type2.) + * Otherwise, generate a TypeCast node. + */ + if (IsA(arg, A_Const) && + ((A_Const *) arg)->typename == NULL) + { + ((A_Const *) arg)->typename = typename; + return arg; + } + else if (IsA(arg, ParamNo) && + ((ParamNo *) arg)->typename == NULL) + { + ((ParamNo *) arg)->typename = typename; + return arg; + } + else + { + TypeCast *n = makeNode(TypeCast); + n->arg = arg; + n->typename = typename; + return (Node *) n; + } +} + /* makeRowExpr() * Generate separate operator nodes for a single row descriptor expression. * Perhaps this should go deeper in the parser someday... diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 6ddc090bb0..2d0c4b1d2a 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.27 2000/01/10 17:14:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.28 2000/01/17 00:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,8 +32,8 @@ static Oid PreferredType(CATEGORY category, Oid type); * Convert a function argument to a different type. */ Node * -coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, - int32 atttypmod) +coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, + Oid targetTypeId, int32 atttypmod) { Node *result = NULL; @@ -200,6 +200,70 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids) 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) +{ + char *funcname; + Oid oid_array[FUNC_MAX_ARGS]; + HeapTuple ftup; + + /* + * We assume that only typmod values greater than 0 indicate a forced + * conversion is necessary. + */ + if (atttypmod <= 0 || + atttypmod == exprTypmod(node)) + return node; + + funcname = typeidTypeName(targetTypeId); + MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); + oid_array[0] = targetTypeId; + oid_array[1] = INT4OID; + + /* attempt to find with arguments exactly as specified... */ + ftup = SearchSysCacheTuple(PROCNAME, + PointerGetDatum(funcname), + Int32GetDatum(2), + PointerGetDatum(oid_array), + 0); + + if (HeapTupleIsValid(ftup)) + { + A_Const *cons = makeNode(A_Const); + FuncCall *func = makeNode(FuncCall); + + cons->val.type = T_Integer; + cons->val.val.ival = atttypmod; + + func->funcname = funcname; + func->args = lappend(lcons(node, NIL), cons); + func->agg_star = false; + func->agg_distinct = false; + + node = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST); + } + + return node; +} + /* TypeCategory() * Assign a category to the specified OID. diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2b0414f802..6fe43e9e02 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.64 2000/01/16 05:18:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.65 2000/01/17 00:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,9 @@ #include "parser/parse_target.h" #include "utils/builtins.h" -static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod); +static Node *parser_typecast_constant(Value *expr, TypeName *typename); +static Node *parser_typecast_expression(ParseState *pstate, + Node *expr, TypeName *typename); static Node *transformAttr(ParseState *pstate, Attr *att, int precedence); static Node *transformIdent(ParseState *pstate, Ident *ident, int precedence); static Node *transformIndirection(ParseState *pstate, Node *basenode, @@ -63,7 +65,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) Value *val = &con->val; if (con->typename != NULL) - result = parser_typecast(val, con->typename, con->typename->typmod); + result = parser_typecast_constant(val, con->typename); else result = (Node *) make_const(val); break; @@ -85,6 +87,15 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) param->param_tlist = (List *) NULL; result = transformIndirection(pstate, (Node *) param, pno->indirection); + /* XXX what about cast (typename) applied to Param ??? */ + break; + } + case T_TypeCast: + { + TypeCast *tc = (TypeCast *) expr; + Node *arg = transformExpr(pstate, tc->arg, precedence); + + result = parser_typecast_expression(pstate, arg, tc->typename); break; } case T_A_Expr: @@ -689,7 +700,7 @@ exprTypmod(Node *expr) * by the parser and an explicit type name to cast to. */ static Node * -parser_typecast(Value *expr, TypeName *typename, int32 atttypmod) +parser_typecast_constant(Value *expr, TypeName *typename) { Const *con; Type tp; @@ -716,7 +727,7 @@ parser_typecast(Value *expr, TypeName *typename, int32 atttypmod) break; default: elog(ERROR, - "parser_typecast: cannot cast this expression to type '%s'", + "parser_typecast_constant: cannot cast this expression to type '%s'", typename->name); } @@ -733,7 +744,7 @@ parser_typecast(Value *expr, TypeName *typename, int32 atttypmod) if (isNull) datum = (Datum) NULL; else - datum = stringTypeDatum(tp, const_string, atttypmod); + datum = stringTypeDatum(tp, const_string, typename->typmod); con = makeConst(typeTypeId(tp), typeLen(tp), @@ -748,3 +759,52 @@ parser_typecast(Value *expr, TypeName *typename, int32 atttypmod) 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.) + * + * 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) +{ + Oid inputType = exprType(expr); + Type tp; + Oid targetType; + + if (typename->arrayBounds != NIL) + { + char type_string[NAMEDATALEN+2]; + + sprintf(type_string, "_%s", typename->name); + tp = (Type) typenameType(type_string); + } + else + tp = (Type) typenameType(typename->name); + targetType = typeTypeId(tp); + + if (inputType == InvalidOid) + return expr; /* do nothing if NULL input */ + + if (inputType != targetType) + { + expr = CoerceTargetExpr(pstate, expr, + inputType, targetType); + if (expr == NULL) + elog(ERROR, "Cannot cast type '%s' to '%s'", + typeidTypeName(inputType), + typeidTypeName(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); + + return expr; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 503f544e9e..f80b6ef4fb 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.51 2000/01/10 17:14:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.52 2000/01/17 00:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,8 +24,6 @@ #include "utils/syscache.h" -static Node *SizeTargetExpr(ParseState *pstate, Node *expr, - Oid attrtype, int32 attrtypmod); static List *ExpandAllTables(ParseState *pstate); static char *FigureColname(Node *expr, Node *resval); @@ -245,9 +243,7 @@ updateTargetListEntry(ParseState *pstate, * If the target is a fixed-length type, it may need a length * coercion as well as a type coercion. */ - if (attrtypmod > 0 && - attrtypmod != exprTypmod(tle->expr)) - tle->expr = SizeTargetExpr(pstate, tle->expr, + tle->expr = coerce_type_typmod(pstate, tle->expr, attrtype, attrtypmod); } } @@ -301,61 +297,6 @@ CoerceTargetExpr(ParseState *pstate, /* - * SizeTargetExpr() - * - * 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. - * - * Currently, "bpchar" (ie, char(N)) is the only such type, but try - * to be more general than a hard-wired test... - */ -static Node * -SizeTargetExpr(ParseState *pstate, - Node *expr, - Oid attrtype, - int32 attrtypmod) -{ - char *funcname; - Oid oid_array[FUNC_MAX_ARGS]; - HeapTuple ftup; - int i; - - funcname = typeidTypeName(attrtype); - oid_array[0] = attrtype; - oid_array[1] = INT4OID; - for (i = 2; i < FUNC_MAX_ARGS; i++) - oid_array[i] = InvalidOid; - - /* attempt to find with arguments exactly as specified... */ - ftup = SearchSysCacheTuple(PROCNAME, - PointerGetDatum(funcname), - Int32GetDatum(2), - PointerGetDatum(oid_array), - 0); - - if (HeapTupleIsValid(ftup)) - { - A_Const *cons = makeNode(A_Const); - FuncCall *func = makeNode(FuncCall); - - cons->val.type = T_Integer; - cons->val.val.ival = attrtypmod; - - func->funcname = funcname; - func->args = lappend(lcons(expr, NIL), cons); - func->agg_star = false; - func->agg_distinct = false; - - expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST); - } - - return expr; -} - - -/* * checkInsertTargets - * generate a list of column names if not supplied or * test supplied column names to make sure they are in target table. diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 219bcbe6f6..a6df5f1417 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.61 2000/01/16 20:04:58 petere Exp $ + * $Id: nodes.h,v 1.62 2000/01/17 00:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -202,7 +202,7 @@ typedef enum NodeTag T_FuncCall, T_A_Indices, T_ResTarget, - T_ParamString, /* not used anymore */ + T_TypeCast, T_RelExpr, T_SortGroupBy, T_RangeVar, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 53c55be4db..b1fc802d88 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.94 2000/01/16 20:04:58 petere Exp $ + * $Id: parsenodes.h,v 1.95 2000/01/17 00:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -869,6 +869,22 @@ typedef struct A_Const } A_Const; /* + * TypeCast - a CAST expression + * + * NOTE: for mostly historical reasons, A_Const and ParamNo parsenodes contain + * room for a TypeName; we only generate a separate TypeCast node if the + * argument to be casted is neither of those kinds of nodes. In theory either + * representation would work, but it is convenient (especially for A_Const) + * to have the target type immediately available. + */ +typedef struct TypeCast +{ + NodeTag type; + Node *arg; /* the expression being casted */ + TypeName *typename; /* the target type */ +} TypeCast; + +/* * CaseExpr - a CASE expression */ typedef struct CaseExpr diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 02a1eed11e..06736c6dff 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -2,11 +2,11 @@ * * parse_coerce.h * - * + * Routines for type coercion. * * Copyright (c) 1994, Regents of the University of California * - * $Id: parse_coerce.h,v 1.15 1999/07/16 17:07:36 momjian Exp $ + * $Id: parse_coerce.h,v 1.16 2000/01/17 00:14:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -125,6 +125,8 @@ extern CATEGORY TypeCategory(Oid type); extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids); extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, - Oid targetTypeId, int32 atttypmod); + Oid targetTypeId, int32 atttypmod); +extern Node *coerce_type_typmod(ParseState *pstate, Node *node, + Oid targetTypeId, int32 atttypmod); #endif /* PARSE_COERCE_H */