From 57b30e8e226014c8d06bae0158e0c7fc679f700b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 20 Feb 2000 21:32:16 +0000 Subject: [PATCH] Create a new expression node type RelabelType, which exists solely to represent the result of a binary-compatible type coercion. At runtime it just evaluates its argument --- but during type resolution, exprType will pick up the output type of the RelabelType node instead of the type of the argument. This solves some longstanding problems with dropped type coercions, an example being 'select now()::abstime::int4' which used to produce date-formatted output, not an integer, because the coercion to int4 was dropped on the floor. --- src/backend/executor/execQual.c | 64 ++++++++++++++++------------ src/backend/nodes/copyfuncs.c | 25 ++++++++++- src/backend/nodes/equalfuncs.c | 17 +++++++- src/backend/nodes/freefuncs.c | 21 ++++++++- src/backend/nodes/outfuncs.c | 18 +++++++- src/backend/nodes/readfuncs.c | 33 +++++++++++++- src/backend/optimizer/util/clauses.c | 45 ++++++++++++++++++- src/backend/parser/parse_coerce.c | 32 ++++++++++---- src/backend/parser/parse_expr.c | 9 +++- src/backend/parser/parse_func.c | 58 +++++++++++++++---------- src/backend/utils/adt/ruleutils.c | 60 +++++++++++++++++--------- src/include/nodes/nodes.h | 3 +- src/include/nodes/primnodes.h | 26 ++++++++++- 13 files changed, 325 insertions(+), 86 deletions(-) diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 91dbde6341..a319e2c2b3 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.67 2000/01/26 05:56:21 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.68 2000/02/20 21:32:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1176,7 +1176,7 @@ ExecEvalExpr(Node *expression, bool *isNull, bool *isDone) { - Datum retDatum = 0; + Datum retDatum; *isNull = false; @@ -1200,36 +1200,33 @@ ExecEvalExpr(Node *expression, switch (nodeTag(expression)) { case T_Var: - retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull); + retDatum = ExecEvalVar((Var *) expression, econtext, isNull); break; case T_Const: { Const *con = (Const *) expression; - if (con->constisnull) - *isNull = true; retDatum = con->constvalue; + *isNull = con->constisnull; break; } case T_Param: - retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull); + retDatum = ExecEvalParam((Param *) expression, econtext, isNull); break; case T_Iter: - retDatum = (Datum) ExecEvalIter((Iter *) expression, - econtext, - isNull, - isDone); + retDatum = ExecEvalIter((Iter *) expression, + econtext, + isNull, + isDone); break; case T_Aggref: - retDatum = (Datum) ExecEvalAggref((Aggref *) expression, - econtext, - isNull); + retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull); break; case T_ArrayRef: - retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression, - econtext, - isNull, - isDone); + retDatum = ExecEvalArrayRef((ArrayRef *) expression, + econtext, + isNull, + isDone); break; case T_Expr: { @@ -1238,37 +1235,48 @@ ExecEvalExpr(Node *expression, switch (expr->opType) { case OP_EXPR: - retDatum = (Datum) ExecEvalOper(expr, econtext, isNull); + retDatum = ExecEvalOper(expr, econtext, isNull); break; case FUNC_EXPR: - retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone); + retDatum = ExecEvalFunc(expr, econtext, + isNull, isDone); break; case OR_EXPR: - retDatum = (Datum) ExecEvalOr(expr, econtext, isNull); + retDatum = ExecEvalOr(expr, econtext, isNull); break; case AND_EXPR: - retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull); + retDatum = ExecEvalAnd(expr, econtext, isNull); break; case NOT_EXPR: - retDatum = (Datum) ExecEvalNot(expr, econtext, isNull); + retDatum = ExecEvalNot(expr, econtext, isNull); break; case SUBPLAN_EXPR: - retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper, - expr->args, econtext, - isNull); + retDatum = ExecSubPlan((SubPlan *) expr->oper, + expr->args, econtext, + isNull); break; default: - elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType); + elog(ERROR, "ExecEvalExpr: unknown expression type %d", + expr->opType); + retDatum = 0; /* keep compiler quiet */ break; } break; } + case T_RelabelType: + retDatum = ExecEvalExpr(((RelabelType *) expression)->arg, + econtext, + isNull, + isDone); + break; case T_CaseExpr: - retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull); + retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull); break; default: - elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression)); + elog(ERROR, "ExecEvalExpr: unknown expression type %d", + nodeTag(expression)); + retDatum = 0; /* keep compiler quiet */ break; } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5bf01e2272..fbef91b35d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.106 2000/02/15 20:49:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.107 2000/02/20 21:32:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -873,6 +873,26 @@ _copySubLink(SubLink *from) return newnode; } +/* ---------------- + * _copyRelabelType + * ---------------- + */ +static RelabelType * +_copyRelabelType(RelabelType *from) +{ + RelabelType *newnode = makeNode(RelabelType); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, arg); + newnode->resulttype = from->resulttype; + newnode->resulttypmod = from->resulttypmod; + + return newnode; +} + /* ---------------- * _copyCaseExpr * ---------------- @@ -1617,6 +1637,9 @@ copyObject(void *from) case T_SubLink: retval = _copySubLink(from); break; + case T_RelabelType: + retval = _copyRelabelType(from); + break; case T_CaseExpr: retval = _copyCaseExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index fadc282d1a..b4f5fc6285 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.61 2000/02/15 20:49:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.62 2000/02/20 21:32:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -262,6 +262,18 @@ _equalSubLink(SubLink *a, SubLink *b) return true; } +static bool +_equalRelabelType(RelabelType *a, RelabelType *b) +{ + if (!equal(a->arg, b->arg)) + return false; + if (a->resulttype != b->resulttype) + return false; + if (a->resulttypmod != b->resulttypmod) + return false; + return true; +} + static bool _equalArray(Array *a, Array *b) { @@ -806,6 +818,9 @@ equal(void *a, void *b) case T_SubLink: retval = _equalSubLink(a, b); break; + case T_RelabelType: + retval = _equalRelabelType(a, b); + break; case T_Func: retval = _equalFunc(a, b); break; diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index 8eed80e61a..daca4a6d96 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.36 2000/02/15 20:49:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.37 2000/02/20 21:32:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -650,6 +650,22 @@ _freeSubLink(SubLink *node) pfree(node); } +/* ---------------- + * _freeRelabelType + * ---------------- + */ +static void +_freeRelabelType(RelabelType *node) +{ + /* ---------------- + * free remainder of node + * ---------------- + */ + freeObject(node->arg); + + pfree(node); +} + /* ---------------- * _freeCaseExpr * ---------------- @@ -1241,6 +1257,9 @@ freeObject(void *node) case T_SubLink: _freeSubLink(node); break; + case T_RelabelType: + _freeRelabelType(node); + break; case T_CaseExpr: _freeCaseExpr(node); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c40ca9ff9c..db785afab9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.108 2000/02/15 20:49:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.109 2000/02/20 21:32:05 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -770,6 +770,19 @@ _outSubLink(StringInfo str, SubLink *node) _outNode(str, node->subselect); } +/* + * RelabelType + */ +static void +_outRelabelType(StringInfo str, RelabelType *node) +{ + appendStringInfo(str, " RELABELTYPE :arg "); + _outNode(str, node->arg); + + appendStringInfo(str, " :resulttype %u :resulttypmod %d ", + node->resulttype, node->resulttypmod); +} + /* * Array is a subclass of Expr */ @@ -1496,6 +1509,9 @@ _outNode(StringInfo str, void *obj) case T_SubLink: _outSubLink(str, obj); break; + case T_RelabelType: + _outRelabelType(str, obj); + break; case T_Array: _outArray(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 7d1e0b4ccc..dfbdbef988 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.84 2000/02/15 20:49:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.85 2000/02/20 21:32:05 tgl Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1191,6 +1191,35 @@ _readSubLink() return local_node; } +/* ---------------- + * _readRelabelType + * + * RelabelType is a subclass of Node + * ---------------- + */ +static RelabelType * +_readRelabelType() +{ + RelabelType *local_node; + char *token; + int length; + + local_node = makeNode(RelabelType); + + token = lsptok(NULL, &length); /* eat :arg */ + local_node->arg = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* eat :resulttype */ + token = lsptok(NULL, &length); /* get resulttype */ + local_node->resulttype = (Oid) atol(token); + + token = lsptok(NULL, &length); /* eat :resulttypmod */ + token = lsptok(NULL, &length); /* get resulttypmod */ + local_node->resulttypmod = atoi(token); + + return local_node; +} + /* * Stuff from execnodes.h */ @@ -1820,6 +1849,8 @@ parsePlanString(void) return_value = _readAggref(); else if (length == 7 && strncmp(token, "SUBLINK", length) == 0) return_value = _readSubLink(); + else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0) + return_value = _readRelabelType(); else if (length == 3 && strncmp(token, "AGG", length) == 0) return_value = _readAgg(); else if (length == 4 && strncmp(token, "HASH", length) == 0) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 664a0dfaaa..4bb84f1521 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.60 2000/02/20 21:32:06 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1150,6 +1150,37 @@ eval_const_expressions_mutator (Node *node, void *context) newexpr->args = args; return (Node *) newexpr; } + if (IsA(node, RelabelType)) + { + /* + * If we can simplify the input to a constant, then we don't need + * the RelabelType node anymore: just change the type field of + * the Const node. Otherwise keep the RelabelType node. + * + * XXX if relabel has a nondefault resulttypmod, do we need to + * keep it to show that? At present I don't think so. + */ + RelabelType *relabel = (RelabelType *) node; + Node *arg; + + arg = eval_const_expressions_mutator(relabel->arg, context); + if (arg && IsA(arg, Const)) + { + Const *con = (Const *) arg; + + con->consttype = relabel->resulttype; + return (Node *) con; + } + else + { + RelabelType *newrelabel = makeNode(RelabelType); + + newrelabel->arg = arg; + newrelabel->resulttype = relabel->resulttype; + newrelabel->resulttypmod = relabel->resulttypmod; + return (Node *) newrelabel; + } + } if (IsA(node, CaseExpr)) { /* @@ -1392,6 +1423,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) return true; } break; + case T_RelabelType: + return walker(((RelabelType *) node)->arg, context); case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -1603,6 +1636,16 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context) return (Node *) newnode; } break; + case T_RelabelType: + { + RelabelType *relabel = (RelabelType *) node; + RelabelType *newnode; + + FLATCOPY(newnode, relabel, RelabelType); + MUTATE(newnode->arg, relabel->arg, Node *); + return (Node *) newnode; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index dbb6cbc581..646f1a046b 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * parse_coerce.c - * handle type coersions/conversions for parser + * handle type coercions/conversions for parser * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.31 2000/02/20 06:28:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.32 2000/02/20 21:32:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,7 @@ Node * coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 atttypmod) { - Node *result = NULL; + Node *result; if (targetTypeId == InvalidOid || targetTypeId == inputTypeId) @@ -44,11 +44,6 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, /* no conversion needed */ result = node; } - else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId)) - { - /* no work if one of the known-good transparent conversions */ - result = node; - } else if (inputTypeId == UNKNOWNOID && IsA(node, Const)) { /* @@ -87,6 +82,27 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, result = (Node *) newcon; } + else if (IS_BINARY_COMPATIBLE(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. + */ + RelabelType *relabel = makeNode(RelabelType); + + relabel->arg = node; + relabel->resulttype = targetTypeId; + /* + * 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. + */ + relabel->resulttypmod = -1; + + result = (Node *) relabel; + } else { /* diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 365378f407..3fd3370672 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.68 2000/02/15 03:37:47 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.69 2000/02/20 21:32:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -516,6 +516,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) case T_Param: case T_Aggref: case T_ArrayRef: + case T_RelabelType: { result = (Node *) expr; break; @@ -627,6 +628,9 @@ exprType(Node *expr) case T_Param: type = ((Param *) expr)->paramtype; break; + case T_RelabelType: + type = ((RelabelType *) expr)->resulttype; + break; case T_SubLink: { SubLink *sublink = (SubLink *) expr; @@ -697,6 +701,9 @@ exprTypmod(Node *expr) } } break; + case T_RelabelType: + return ((RelabelType *) expr)->resulttypmod; + break; default: break; } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 939d7a91ec..71cda76087 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.70 2000/02/20 06:35:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.71 2000/02/20 21:32:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,7 +163,10 @@ agg_get_candidates(char *aggname, } /* agg_get_candidates() */ /* agg_select_candidate() - * Try to choose only one candidate aggregate function from a list of possibles. + * + * Try to choose only one candidate aggregate function from a list of + * possible matches. Return value is Oid of input type of aggregate + * if successful, else InvalidOid. */ static Oid agg_select_candidate(Oid typeid, CandidateList candidates) @@ -175,10 +178,12 @@ agg_select_candidate(Oid typeid, CandidateList candidates) CATEGORY category, current_category; -/* - * First look for exact matches or binary compatible matches. - * (Of course exact matches shouldn't even get here, but anyway.) - */ + /* + * First look for exact matches or binary compatible matches. + * (Of course exact matches shouldn't even get here, but anyway.) + */ + ncandidates = 0; + last_candidate = NULL; for (current_candidate = candidates; current_candidate != NULL; current_candidate = current_candidate->next) @@ -188,15 +193,17 @@ agg_select_candidate(Oid typeid, CandidateList candidates) if (current_typeid == typeid || IS_BINARY_COMPATIBLE(current_typeid, typeid)) { - /* we're home free */ - return current_typeid; + last_candidate = current_candidate; + ncandidates++; } } + if (ncandidates == 1) + return last_candidate->args[0]; -/* - * If no luck that way, look for candidates which allow coersion - * and have a preferred type. Keep all candidates if none match. - */ + /* + * If no luck that way, look for candidates which allow coercion + * and have a preferred type. Keep all candidates if none match. + */ category = TypeCategory(typeid); ncandidates = 0; last_candidate = NULL; @@ -232,7 +239,10 @@ agg_select_candidate(Oid typeid, CandidateList candidates) if (last_candidate) /* terminate rebuilt list */ last_candidate->next = NULL; - return ((ncandidates == 1) ? candidates->args[0] : 0); + if (ncandidates == 1) + return candidates->args[0]; + + return InvalidOid; } /* agg_select_candidate() */ @@ -471,10 +481,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, /* * See if this is a single argument function with the function * name also a type name and the input argument and type name - * binary compatible... This means that you are trying for a - * type conversion which does not need to take place, so we'll - * just pass through the argument itself. (make this clearer - * with some extra brackets - thomas 1998-12-05) + * binary compatible. If so, we do not need to do any real + * conversion, but we do need to build a RelabelType node + * so that exprType() sees the result as being of the output type. */ if (nargs == 1) { @@ -486,8 +495,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, if (HeapTupleIsValid(tp) && IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs)))) { - /* XXX FIXME: probably need to change expression's marked type? */ - return (Node *) lfirst(fargs); + RelabelType *relabel = makeNode(RelabelType); + + relabel->arg = (Node *) lfirst(fargs); + relabel->resulttype = typeTypeId(tp); + relabel->resulttypmod = -1; + + return (Node *) relabel; } } @@ -1128,7 +1142,7 @@ func_get_detail(char *funcname, * inheritance properties of the supplied argv. * * This function is used to disambiguate among functions with the - * same name but different signatures. It takes an array of eight + * same name but different signatures. It takes an array of input * type ids. For each type id in the array that's a complex type * (a class), it walks up the inheritance tree, finding all * superclasses of that type. A vector of new Oid type arrays @@ -1342,7 +1356,7 @@ gen_cross_product(InhPaths *arginh, int nargs) * 2) the input type can be typecast into the function type * Right now, we only typecast unknowns, and that is all we check for. * - * func_get_detail() now can find coersions for function arguments which + * func_get_detail() now can find coercions for function arguments which * will make this function executable. So, we need to recover these * results here too. * - thomas 1998-03-25 @@ -1361,7 +1375,7 @@ make_arguments(ParseState *pstate, i < nargs; i++, current_fargs = lnext(current_fargs)) { - /* types don't match? then force coersion using a function call... */ + /* types don't match? then force coercion using a function call... */ if (input_typeids[i] != function_typeids[i]) { lfirst(current_fargs) = coerce_type(pstate, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index d18cff2789..0267fc2c57 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of its tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.41 2000/02/15 08:24:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.42 2000/02/20 21:32:12 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1006,7 +1006,7 @@ get_select_query_def(Query *query, deparse_context *context) appendStringInfo(buf, "%s", quote_identifier(strVal(lfirst(col)))); } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); } } } @@ -1127,7 +1127,7 @@ get_insert_query_def(Query *query, deparse_context *context) sep = ", "; get_tle_expr(tle, context); } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); } else get_select_query_def(query, context); @@ -1281,7 +1281,7 @@ get_rule_expr(Node *node, deparse_context *context) switch (expr->opType) { case OP_EXPR: - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); if (length(args) == 2) { /* binary operator */ @@ -1320,35 +1320,35 @@ get_rule_expr(Node *node, deparse_context *context) elog(ERROR, "get_rule_expr: bogus oprkind"); } } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); break; case OR_EXPR: - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); get_rule_expr((Node *) lfirst(args), context); while ((args = lnext(args)) != NIL) { appendStringInfo(buf, " OR "); get_rule_expr((Node *) lfirst(args), context); } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); break; case AND_EXPR: - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); get_rule_expr((Node *) lfirst(args), context); while ((args = lnext(args)) != NIL) { appendStringInfo(buf, " AND "); get_rule_expr((Node *) lfirst(args), context); } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); break; case NOT_EXPR: appendStringInfo(buf, "(NOT "); get_rule_expr((Node *) lfirst(args), context); - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); break; case FUNC_EXPR: @@ -1373,7 +1373,7 @@ get_rule_expr(Node *node, deparse_context *context) appendStringInfo(buf, "*"); else get_rule_expr(aggref->target, context); - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); } break; @@ -1405,6 +1405,28 @@ get_rule_expr(Node *node, deparse_context *context) } break; + case T_RelabelType: + { + RelabelType *relabel = (RelabelType *) node; + HeapTuple typetup; + Form_pg_type typeStruct; + char *extval; + + appendStringInfoChar(buf, '('); + get_rule_expr(relabel->arg, context); + typetup = SearchSysCacheTuple(TYPEOID, + ObjectIdGetDatum(relabel->resulttype), + 0, 0, 0); + if (!HeapTupleIsValid(typetup)) + elog(ERROR, "cache lookup of type %u failed", + relabel->resulttype); + typeStruct = (Form_pg_type) GETSTRUCT(typetup); + extval = pstrdup(NameStr(typeStruct->typname)); + appendStringInfo(buf, ")::%s", quote_identifier(extval)); + pfree(extval); + } + break; + case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -1474,14 +1496,14 @@ get_func_expr(Expr *expr, deparse_context *context) { if (!strcmp(proname, "nullvalue")) { - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); get_rule_expr((Node *) lfirst(expr->args), context); appendStringInfo(buf, " ISNULL)"); return; } if (!strcmp(proname, "nonnullvalue")) { - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); get_rule_expr((Node *) lfirst(expr->args), context); appendStringInfo(buf, " NOTNULL)"); return; @@ -1500,7 +1522,7 @@ get_func_expr(Expr *expr, deparse_context *context) sep = ", "; get_rule_expr((Node *) lfirst(l), context); } - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); } @@ -1712,13 +1734,13 @@ get_sublink_expr(Node *node, deparse_context *context) Oper *oper; bool need_paren; - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); if (sublink->lefthand != NIL) { need_paren = (length(sublink->lefthand) > 1); if (need_paren) - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); sep = ""; foreach(l, sublink->lefthand) @@ -1731,7 +1753,7 @@ get_sublink_expr(Node *node, deparse_context *context) if (need_paren) appendStringInfo(buf, ") "); else - appendStringInfo(buf, " "); + appendStringInfoChar(buf, ' '); } need_paren = true; @@ -1768,14 +1790,14 @@ get_sublink_expr(Node *node, deparse_context *context) } if (need_paren) - appendStringInfo(buf, "("); + appendStringInfoChar(buf, '('); get_query_def(query, buf, context->rangetables); if (need_paren) appendStringInfo(buf, "))"); else - appendStringInfo(buf, ")"); + appendStringInfoChar(buf, ')'); } /* ---------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 8dd893e3f1..f42f615c7a 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.65 2000/02/18 09:29:43 inoue Exp $ + * $Id: nodes.h,v 1.66 2000/02/20 21:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,7 @@ typedef enum NodeTag T_Array, T_ArrayRef, T_Iter, + T_RelabelType, /*--------------------- * TAGS FOR PLANNER NODES (relation.h) diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index bdeff737b6..7c3e0c6c4b 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.39 2000/01/26 05:58:16 momjian Exp $ + * $Id: primnodes.h,v 1.40 2000/02/20 21:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -467,4 +467,28 @@ typedef struct ArrayRef Node *refassgnexpr; } ArrayRef; +/* ---------------- + * RelabelType + * arg - input expression + * resulttype - output type of coercion expression + * resulttypmod - output typmod (usually -1) + * + * RelabelType represents a "dummy" type coercion between two binary- + * compatible datatypes, such as reinterpreting the result of an OID + * expression as an int4. It is a no-op at runtime; we only need it + * to provide a place to store the correct type to be attributed to + * the expression result during type resolution. (We can't get away + * with just overwriting the type field of the input expression node, + * so we need a separate node to show the coercion's result type.) + * ---------------- + */ + +typedef struct RelabelType +{ + NodeTag type; + Node *arg; + Oid resulttype; + int32 resulttypmod; +} RelabelType; + #endif /* PRIMNODES_H */