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 */