*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
case T_TypeName:
retval = _copyTypeName(from);
break;
+ case T_TypeCast:
+ retval = _copyTypeCast(from);
+ break;
/*
* VALUE NODES
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
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)
case T_TypeName:
_freeTypeName(node);
break;
+ case T_TypeCast:
+ _freeTypeCast(node);
+ break;
/*
* VALUE NODES
*
* 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
}
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 ");
{
appendStringInfo(str, "CONST ");
_outValue(str, &(node->val));
+ appendStringInfo(str, " :typename ");
+ _outNode(str, node->typename);
}
static void
case T_TypeName:
_outTypeName(str, obj);
break;
+ case T_TypeCast:
+ _outTypeCast(str, obj);
+ break;
case T_IndexElem:
_outIndexElem(str, obj);
break;
-%{ /* -*-text-*- */
+%{
/*#define YYDEBUG 1*/
/*-------------------------------------------------------------------------
*
*
* 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
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);
* 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) */
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.
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);
| '(' 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 '(' ')'
| CONSTRAINT { $$ = "constraint"; }
| COPY { $$ = "copy"; }
| CURRENT { $$ = "current"; }
+ | DECIMAL { $$ = "decimal"; }
| DO { $$ = "do"; }
| ELSE { $$ = "else"; }
| END_TRANS { $$ = "end"; }
| NEW { $$ = "new"; }
| NONE { $$ = "none"; }
| NULLIF { $$ = "nullif"; }
+ | NUMERIC { $$ = "numeric"; }
| ORDER { $$ = "order"; }
| POSITION { $$ = "position"; }
| PRECISION { $$ = "precision"; }
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...
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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,
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;
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:
* 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;
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);
}
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),
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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
* 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);
}
}
/*
- * 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.
*
* 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 $
*
*-------------------------------------------------------------------------
*/
T_FuncCall,
T_A_Indices,
T_ResTarget,
- T_ParamString, /* not used anymore */
+ T_TypeCast,
T_RelExpr,
T_SortGroupBy,
T_RangeVar,
*
* 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 $
*
*-------------------------------------------------------------------------
*/
} 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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */