1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

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.
This commit is contained in:
Tom Lane
2000-01-17 00:14:49 +00:00
parent e0bd60171a
commit 49528361f5
10 changed files with 242 additions and 132 deletions

View File

@@ -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...

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);
}
}
@@ -300,61 +296,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