This patch refactors transformExpr(): rather than being a monsterous 900 line function, it breaks it up into numerous sub-functions that are invoked by transformExpr() for individual expression types, in the style of transformStmt().
I think this patch is reasonably safe for HEAD (it mostly just rearranges code), but I don't mind holding on to it for 8.1. Comments? -Neil
Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /var/lib/cvs/pgsql/src/backend/parser/parse_expr.c,v retrieving revision 1.176 diff -c -r1.176 parse_expr.c *** src/backend/parser/parse_expr.c 29 Aug 2004 05:06:44 -0000 1.176 --- src/backend/parser/parse_expr.c 28 Oct 2004 06:46:54 -0000 *************** *** 37,45 **** --- 37,62 ---- bool Transform_null_equals = false; + static Node *transformParamRef(ParseState *pstate, ParamRef *pref); + static Node *transformAExprOp(ParseState *pstate, A_Expr *a); + static Node *transformAExprAnd(ParseState *pstate, A_Expr *a); + static Node *transformAExprOr(ParseState *pstate, A_Expr *a); + static Node *transformAExprNot(ParseState *pstate, A_Expr *a); + static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a); + static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a); + static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a); + static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a); + static Node *transformAExprOf(ParseState *pstate, A_Expr *a); + static Node *transformFuncCall(ParseState *pstate, FuncCall *fn); + static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c); + static Node *transformSubLink(ParseState *pstate, SubLink *sublink); + static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a); + static Node *transformRowExpr(ParseState *pstate, RowExpr *r); + static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname); + static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); static Node *typecast_expression(ParseState *pstate, Node *expr, *************** *** 90,154 **** switch (nodeTag(expr)) { case T_ColumnRef: ! { ! result = transformColumnRef(pstate, (ColumnRef *) expr); ! break; ! } case T_ParamRef: ! { ! ParamRef *pref = (ParamRef *) expr; ! int paramno = pref->number; ! ParseState *toppstate; ! Param *param; ! ! /* ! * Find topmost ParseState, which is where paramtype info ! * lives. ! */ ! toppstate = pstate; ! while (toppstate->parentParseState != NULL) ! toppstate = toppstate->parentParseState; ! ! /* Check parameter number is in range */ ! if (paramno <= 0) /* probably can't happen? */ ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_PARAMETER), ! errmsg("there is no parameter $%d", paramno))); ! if (paramno > toppstate->p_numparams) ! { ! if (!toppstate->p_variableparams) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_PARAMETER), ! errmsg("there is no parameter $%d", ! paramno))); ! /* Okay to enlarge param array */ ! if (toppstate->p_paramtypes) ! toppstate->p_paramtypes = ! (Oid *) repalloc(toppstate->p_paramtypes, ! paramno * sizeof(Oid)); ! else ! toppstate->p_paramtypes = ! (Oid *) palloc(paramno * sizeof(Oid)); ! /* Zero out the previously-unreferenced slots */ ! MemSet(toppstate->p_paramtypes + toppstate->p_numparams, ! 0, ! (paramno - toppstate->p_numparams) * sizeof(Oid)); ! toppstate->p_numparams = paramno; ! } ! if (toppstate->p_variableparams) ! { ! /* If not seen before, initialize to UNKNOWN type */ ! if (toppstate->p_paramtypes[paramno - 1] == InvalidOid) ! toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID; ! } ! ! param = makeNode(Param); ! param->paramkind = PARAM_NUM; ! param->paramid = (AttrNumber) paramno; ! param->paramtype = toppstate->p_paramtypes[paramno - 1]; ! result = (Node *) param; ! break; ! } case T_A_Const: { A_Const *con = (A_Const *) expr; --- 107,117 ---- switch (nodeTag(expr)) { case T_ColumnRef: ! result = transformColumnRef(pstate, (ColumnRef *) expr); ! break; case T_ParamRef: ! result = transformParamRef(pstate, (ParamRef *) expr); ! break; case T_A_Const: { A_Const *con = (A_Const *) expr; *************** *** 184,961 **** switch (a->kind) { case AEXPR_OP: ! { ! Node *lexpr = a->lexpr; ! Node *rexpr = a->rexpr; ! ! /* ! * Special-case "foo = NULL" and "NULL = foo" ! * for compatibility with standards-broken ! * products (like Microsoft's). Turn these ! * into IS NULL exprs. ! */ ! if (Transform_null_equals && ! list_length(a->name) == 1 && ! strcmp(strVal(linitial(a->name)), "=") == 0 && ! (exprIsNullConstant(lexpr) || ! exprIsNullConstant(rexpr))) ! { ! NullTest *n = makeNode(NullTest); ! ! n->nulltesttype = IS_NULL; ! ! if (exprIsNullConstant(lexpr)) ! n->arg = (Expr *) rexpr; ! else ! n->arg = (Expr *) lexpr; ! ! result = transformExpr(pstate, ! (Node *) n); ! } ! else if (lexpr && IsA(lexpr, RowExpr) && ! rexpr && IsA(rexpr, SubLink) && ! ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK) ! { ! /* ! * Convert "row op subselect" into a ! * MULTIEXPR sublink. Formerly the ! * grammar did this, but now that a row ! * construct is allowed anywhere in ! * expressions, it's easier to do it here. ! */ ! SubLink *s = (SubLink *) rexpr; ! ! s->subLinkType = MULTIEXPR_SUBLINK; ! s->lefthand = ((RowExpr *) lexpr)->args; ! s->operName = a->name; ! result = transformExpr(pstate, (Node *) s); ! } ! else if (lexpr && IsA(lexpr, RowExpr) && ! rexpr && IsA(rexpr, RowExpr)) ! { ! /* "row op row" */ ! result = make_row_op(pstate, a->name, ! lexpr, rexpr); ! } ! else ! { ! /* Ordinary scalar operator */ ! lexpr = transformExpr(pstate, lexpr); ! rexpr = transformExpr(pstate, rexpr); ! ! result = (Node *) make_op(pstate, ! a->name, ! lexpr, ! rexpr); ! } ! } break; case AEXPR_AND: ! { ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! lexpr = coerce_to_boolean(pstate, lexpr, "AND"); ! rexpr = coerce_to_boolean(pstate, rexpr, "AND"); ! ! result = (Node *) makeBoolExpr(AND_EXPR, ! list_make2(lexpr, ! rexpr)); ! } break; case AEXPR_OR: ! { ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! lexpr = coerce_to_boolean(pstate, lexpr, "OR"); ! rexpr = coerce_to_boolean(pstate, rexpr, "OR"); ! ! result = (Node *) makeBoolExpr(OR_EXPR, ! list_make2(lexpr, ! rexpr)); ! } break; case AEXPR_NOT: ! { ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! rexpr = coerce_to_boolean(pstate, rexpr, "NOT"); ! ! result = (Node *) makeBoolExpr(NOT_EXPR, ! list_make1(rexpr)); ! } break; case AEXPR_OP_ANY: ! { ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! result = (Node *) make_scalar_array_op(pstate, ! a->name, ! true, ! lexpr, ! rexpr); ! } break; case AEXPR_OP_ALL: ! { ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! result = (Node *) make_scalar_array_op(pstate, ! a->name, ! false, ! lexpr, ! rexpr); ! } break; case AEXPR_DISTINCT: ! { ! Node *lexpr = a->lexpr; ! Node *rexpr = a->rexpr; ! ! if (lexpr && IsA(lexpr, RowExpr) && ! rexpr && IsA(rexpr, RowExpr)) ! { ! /* "row op row" */ ! result = make_row_distinct_op(pstate, a->name, ! lexpr, rexpr); ! } ! else ! { ! /* Ordinary scalar operator */ ! lexpr = transformExpr(pstate, lexpr); ! rexpr = transformExpr(pstate, rexpr); ! ! result = (Node *) make_distinct_op(pstate, ! a->name, ! lexpr, ! rexpr); ! } ! } break; case AEXPR_NULLIF: ! { ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! Node *rexpr = transformExpr(pstate, ! a->rexpr); ! ! result = (Node *) make_op(pstate, ! a->name, ! lexpr, ! rexpr); ! if (((OpExpr *) result)->opresulttype != BOOLOID) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("NULLIF requires = operator to yield boolean"))); ! ! /* ! * We rely on NullIfExpr and OpExpr being same ! * struct ! */ ! NodeSetTag(result, T_NullIfExpr); ! } break; case AEXPR_OF: ! { ! /* ! * Checking an expression for match to type. ! * Will result in a boolean constant node. ! */ ! ListCell *telem; ! A_Const *n; ! Oid ltype, ! rtype; ! bool matched = FALSE; ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! ! ltype = exprType(lexpr); ! foreach(telem, (List *) a->rexpr) ! { ! rtype = LookupTypeName(lfirst(telem)); ! matched = (rtype == ltype); ! if (matched) ! break; ! } ! ! /* ! * Expect two forms: equals or not equals. ! * Flip the sense of the result for not ! * equals. ! */ ! if (strcmp(strVal(linitial(a->name)), "!=") == 0) ! matched = (!matched); ! ! n = makeNode(A_Const); ! n->val.type = T_String; ! n->val.val.str = (matched ? "t" : "f"); ! n->typename = SystemTypeName("bool"); ! ! result = transformExpr(pstate, (Node *) n); ! } break; } break; } case T_FuncCall: { ! FuncCall *fn = (FuncCall *) expr; ! List *targs; ! ListCell *args; ! /* ! * Transform the list of arguments. We use a shallow list ! * copy and then transform-in-place to avoid O(N^2) ! * behavior from repeated lappend's. ! * ! * XXX: repeated lappend() would no longer result in O(n^2) ! * behavior; worth reconsidering this design? ! */ ! targs = list_copy(fn->args); ! foreach(args, targs) ! { ! lfirst(args) = transformExpr(pstate, ! (Node *) lfirst(args)); ! } ! result = ParseFuncOrColumn(pstate, ! fn->funcname, ! targs, ! fn->agg_star, ! fn->agg_distinct, ! false); break; } ! case T_SubLink: { ! SubLink *sublink = (SubLink *) expr; ! List *qtrees; ! Query *qtree; ! /* If we already transformed this node, do nothing */ ! if (IsA(sublink->subselect, Query)) ! { ! result = expr; ! break; ! } ! pstate->p_hasSubLinks = true; ! qtrees = parse_sub_analyze(sublink->subselect, pstate); ! if (list_length(qtrees) != 1) ! elog(ERROR, "bad query in sub-select"); ! qtree = (Query *) linitial(qtrees); ! if (qtree->commandType != CMD_SELECT || ! qtree->resultRelation != 0) ! elog(ERROR, "bad query in sub-select"); ! sublink->subselect = (Node *) qtree; ! if (sublink->subLinkType == EXISTS_SUBLINK) ! { ! /* ! * EXISTS needs no lefthand or combining operator. ! * These fields should be NIL already, but make sure. ! */ ! sublink->lefthand = NIL; ! sublink->operName = NIL; ! sublink->operOids = NIL; ! sublink->useOr = FALSE; ! } ! else if (sublink->subLinkType == EXPR_SUBLINK || ! sublink->subLinkType == ARRAY_SUBLINK) ! { ! ListCell *tlist_item = list_head(qtree->targetList); ! /* ! * Make sure the subselect delivers a single column ! * (ignoring resjunk targets). ! */ ! if (tlist_item == NULL || ! ((TargetEntry *) lfirst(tlist_item))->resdom->resjunk) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery must return a column"))); ! while ((tlist_item = lnext(tlist_item)) != NULL) ! { ! if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery must return only one column"))); ! } ! /* ! * EXPR and ARRAY need no lefthand or combining ! * operator. These fields should be NIL already, but ! * make sure. ! */ ! sublink->lefthand = NIL; ! sublink->operName = NIL; ! sublink->operOids = NIL; ! sublink->useOr = FALSE; ! } ! else ! { ! /* ALL, ANY, or MULTIEXPR: generate operator list */ ! List *left_list = sublink->lefthand; ! List *right_list = qtree->targetList; ! int row_length = list_length(left_list); ! bool needNot = false; ! List *op = sublink->operName; ! char *opname = strVal(llast(op)); ! ListCell *l; ! ListCell *ll_item; ! ! /* transform lefthand expressions */ ! foreach(l, left_list) ! lfirst(l) = transformExpr(pstate, lfirst(l)); ! /* ! * If the expression is "<> ALL" (with unqualified ! * opname) then convert it to "NOT IN". This is a ! * hack to improve efficiency of expressions output by ! * pre-7.4 Postgres. ! */ ! if (sublink->subLinkType == ALL_SUBLINK && ! list_length(op) == 1 && strcmp(opname, "<>") == 0) ! { ! sublink->subLinkType = ANY_SUBLINK; ! opname = pstrdup("="); ! op = list_make1(makeString(opname)); ! sublink->operName = op; ! needNot = true; ! } ! /* Set useOr if op is "<>" (possibly qualified) */ ! if (strcmp(opname, "<>") == 0) ! sublink->useOr = TRUE; ! else ! sublink->useOr = FALSE; ! /* Combining operators other than =/<> is dubious... */ ! if (row_length != 1 && ! strcmp(opname, "=") != 0 && ! strcmp(opname, "<>") != 0) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("row comparison cannot use operator %s", ! opname))); ! /* ! * To build the list of combining operator OIDs, we ! * must scan subquery's targetlist to find values that ! * will be matched against lefthand values. We need ! * to ignore resjunk targets, so doing the outer ! * iteration over right_list is easier than doing it ! * over left_list. ! */ ! sublink->operOids = NIL; ! ll_item = list_head(left_list); ! foreach(l, right_list) ! { ! TargetEntry *tent = (TargetEntry *) lfirst(l); ! Node *lexpr; ! Operator optup; ! Form_pg_operator opform; ! if (tent->resdom->resjunk) ! continue; ! if (ll_item == NULL) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery has too many columns"))); ! lexpr = lfirst(ll_item); ! ll_item = lnext(ll_item); ! ! /* ! * It's OK to use oper() not compatible_oper() ! * here, because make_subplan() will insert type ! * coercion calls if needed. ! */ ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); ! opform = (Form_pg_operator) GETSTRUCT(optup); ! if (opform->oprresult != BOOLOID) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("operator %s must return type boolean, not type %s", ! opname, ! format_type_be(opform->oprresult)), ! errhint("The operator of a quantified predicate subquery must return type boolean."))); ! if (get_func_retset(opform->oprcode)) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("operator %s must not return a set", ! opname), ! errhint("The operator of a quantified predicate subquery must return type boolean."))); ! sublink->operOids = lappend_oid(sublink->operOids, ! oprid(optup)); ! ReleaseSysCache(optup); ! } ! if (ll_item != NULL) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery has too few columns"))); ! if (needNot) ! { ! expr = coerce_to_boolean(pstate, expr, "NOT"); ! expr = (Node *) makeBoolExpr(NOT_EXPR, ! list_make1(expr)); ! } ! } ! result = (Node *) expr; ! break; ! } ! case T_CaseExpr: ! { ! CaseExpr *c = (CaseExpr *) expr; ! CaseExpr *newc; ! Node *arg; ! CaseTestExpr *placeholder; ! List *newargs; ! List *typeids; ! ListCell *l; ! Node *defresult; ! Oid ptype; ! /* If we already transformed this node, do nothing */ ! if (OidIsValid(c->casetype)) ! { ! result = expr; ! break; ! } ! newc = makeNode(CaseExpr); ! /* transform the test expression, if any */ ! arg = transformExpr(pstate, (Node *) c->arg); ! newc->arg = (Expr *) arg; ! /* generate placeholder for test expression */ ! if (arg) ! { ! placeholder = makeNode(CaseTestExpr); ! placeholder->typeId = exprType(arg); ! placeholder->typeMod = exprTypmod(arg); ! } ! else ! placeholder = NULL; ! /* transform the list of arguments */ ! newargs = NIL; ! typeids = NIL; ! foreach(l, c->args) ! { ! CaseWhen *w = (CaseWhen *) lfirst(l); ! CaseWhen *neww = makeNode(CaseWhen); ! Node *warg; ! Assert(IsA(w, CaseWhen)); ! warg = (Node *) w->expr; ! if (placeholder) ! { ! /* shorthand form was specified, so expand... */ ! warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", ! (Node *) placeholder, ! warg); ! } ! neww->expr = (Expr *) transformExpr(pstate, warg); ! neww->expr = (Expr *) coerce_to_boolean(pstate, ! (Node *) neww->expr, ! "CASE/WHEN"); ! warg = (Node *) w->result; ! neww->result = (Expr *) transformExpr(pstate, warg); ! newargs = lappend(newargs, neww); ! typeids = lappend_oid(typeids, exprType((Node *) neww->result)); ! } ! newc->args = newargs; ! /* transform the default clause */ ! defresult = (Node *) c->defresult; ! if (defresult == NULL) ! { ! A_Const *n = makeNode(A_Const); ! n->val.type = T_Null; ! defresult = (Node *) n; ! } ! newc->defresult = (Expr *) transformExpr(pstate, defresult); ! /* ! * Note: default result is considered the most significant ! * type in determining preferred type. This is how the ! * code worked before, but it seems a little bogus to me ! * --- tgl ! */ ! typeids = lcons_oid(exprType((Node *) newc->defresult), typeids); ! ptype = select_common_type(typeids, "CASE"); ! Assert(OidIsValid(ptype)); ! newc->casetype = ptype; ! ! /* Convert default result clause, if necessary */ ! newc->defresult = (Expr *) ! coerce_to_common_type(pstate, ! (Node *) newc->defresult, ! ptype, ! "CASE/ELSE"); ! /* Convert when-clause results, if necessary */ ! foreach(l, newc->args) ! { ! CaseWhen *w = (CaseWhen *) lfirst(l); ! w->result = (Expr *) ! coerce_to_common_type(pstate, ! (Node *) w->result, ! ptype, ! "CASE/WHEN"); ! } ! result = (Node *) newc; ! break; ! } ! case T_ArrayExpr: ! { ! ArrayExpr *a = (ArrayExpr *) expr; ! ArrayExpr *newa = makeNode(ArrayExpr); ! List *newelems = NIL; ! List *newcoercedelems = NIL; ! List *typeids = NIL; ! ListCell *element; ! Oid array_type; ! Oid element_type; ! /* Transform the element expressions */ ! foreach(element, a->elements) ! { ! Node *e = (Node *) lfirst(element); ! Node *newe; ! newe = transformExpr(pstate, e); ! newelems = lappend(newelems, newe); ! typeids = lappend_oid(typeids, exprType(newe)); ! } ! /* Select a common type for the elements */ ! element_type = select_common_type(typeids, "ARRAY"); ! /* Coerce arguments to common type if necessary */ ! foreach(element, newelems) ! { ! Node *e = (Node *) lfirst(element); ! Node *newe; ! newe = coerce_to_common_type(pstate, e, ! element_type, ! "ARRAY"); ! newcoercedelems = lappend(newcoercedelems, newe); ! } ! /* Do we have an array type to use? */ ! array_type = get_array_type(element_type); ! if (array_type != InvalidOid) ! { ! /* Elements are presumably of scalar type */ ! newa->multidims = false; ! } ! else ! { ! /* Must be nested array expressions */ ! newa->multidims = true; ! array_type = element_type; ! element_type = get_element_type(array_type); ! if (!OidIsValid(element_type)) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_OBJECT), ! errmsg("could not find array type for data type %s", ! format_type_be(array_type)))); ! } ! newa->array_typeid = array_type; ! newa->element_typeid = element_type; ! newa->elements = newcoercedelems; ! result = (Node *) newa; ! break; ! } ! case T_RowExpr: ! { ! RowExpr *r = (RowExpr *) expr; ! RowExpr *newr = makeNode(RowExpr); ! List *newargs = NIL; ! ListCell *arg; ! /* Transform the field expressions */ ! foreach(arg, r->args) ! { ! Node *e = (Node *) lfirst(arg); ! Node *newe; ! newe = transformExpr(pstate, e); ! newargs = lappend(newargs, newe); ! } ! newr->args = newargs; ! /* Barring later casting, we consider the type RECORD */ ! newr->row_typeid = RECORDOID; ! newr->row_format = COERCE_IMPLICIT_CAST; ! result = (Node *) newr; ! break; ! } ! case T_CoalesceExpr: ! { ! CoalesceExpr *c = (CoalesceExpr *) expr; ! CoalesceExpr *newc = makeNode(CoalesceExpr); ! List *newargs = NIL; ! List *newcoercedargs = NIL; ! List *typeids = NIL; ! ListCell *args; ! foreach(args, c->args) ! { ! Node *e = (Node *) lfirst(args); ! Node *newe; ! newe = transformExpr(pstate, e); ! newargs = lappend(newargs, newe); ! typeids = lappend_oid(typeids, exprType(newe)); ! } ! newc->coalescetype = select_common_type(typeids, "COALESCE"); ! /* Convert arguments if necessary */ ! foreach(args, newargs) ! { ! Node *e = (Node *) lfirst(args); ! Node *newe; ! newe = coerce_to_common_type(pstate, e, ! newc->coalescetype, ! "COALESCE"); ! newcoercedargs = lappend(newcoercedargs, newe); ! } ! newc->args = newcoercedargs; ! result = (Node *) newc; ! break; ! } ! case T_NullTest: ! { ! NullTest *n = (NullTest *) expr; ! n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg); ! /* the argument can be any type, so don't coerce it */ ! result = expr; ! break; ! } ! case T_BooleanTest: ! { ! BooleanTest *b = (BooleanTest *) expr; ! const char *clausename; ! switch (b->booltesttype) ! { ! case IS_TRUE: ! clausename = "IS TRUE"; ! break; ! case IS_NOT_TRUE: ! clausename = "IS NOT TRUE"; ! break; ! case IS_FALSE: ! clausename = "IS FALSE"; ! break; ! case IS_NOT_FALSE: ! clausename = "IS NOT FALSE"; ! break; ! case IS_UNKNOWN: ! clausename = "IS UNKNOWN"; ! break; ! case IS_NOT_UNKNOWN: ! clausename = "IS NOT UNKNOWN"; ! break; ! default: ! elog(ERROR, "unrecognized booltesttype: %d", ! (int) b->booltesttype); ! clausename = NULL; /* keep compiler quiet */ ! } ! b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg); ! b->arg = (Expr *) coerce_to_boolean(pstate, ! (Node *) b->arg, ! clausename); ! result = expr; ! break; ! } ! /********************************************* ! * Quietly accept node types that may be presented when we are ! * called on an already-transformed tree. ! * ! * Do any other node types need to be accepted? For now we are ! * taking a conservative approach, and only accepting node ! * types that are demonstrably necessary to accept. ! *********************************************/ ! case T_Var: ! case T_Const: ! case T_Param: ! case T_Aggref: ! case T_ArrayRef: ! case T_FuncExpr: ! case T_OpExpr: ! case T_DistinctExpr: ! case T_ScalarArrayOpExpr: ! case T_NullIfExpr: ! case T_BoolExpr: ! case T_FieldSelect: ! case T_FieldStore: ! case T_RelabelType: ! case T_CaseTestExpr: ! case T_CoerceToDomain: ! case T_CoerceToDomainValue: ! case T_SetToDefault: ! { ! result = (Node *) expr; ! break; ! } ! default: ! /* should not reach here */ ! elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); ! break; } ! return result; } static Node * --- 147,984 ---- switch (a->kind) { case AEXPR_OP: ! result = transformAExprOp(pstate, a); break; case AEXPR_AND: ! result = transformAExprAnd(pstate, a); break; case AEXPR_OR: ! result = transformAExprOr(pstate, a); break; case AEXPR_NOT: ! result = transformAExprNot(pstate, a); break; case AEXPR_OP_ANY: ! result = transformAExprOpAny(pstate, a); break; case AEXPR_OP_ALL: ! result = transformAExprOpAll(pstate, a); break; case AEXPR_DISTINCT: ! result = transformAExprDistinct(pstate, a); break; case AEXPR_NULLIF: ! result = transformAExprNullIf(pstate, a); break; case AEXPR_OF: ! result = transformAExprOf(pstate, a); break; + default: + elog(ERROR, "unrecognized A_Expr kind: %d", a->kind); } break; } case T_FuncCall: + result = transformFuncCall(pstate, (FuncCall *) expr); + break; + case T_SubLink: + result = transformSubLink(pstate, (SubLink *) expr); + break; + + case T_CaseExpr: + result = transformCaseExpr(pstate, (CaseExpr *) expr); + break; + + case T_ArrayExpr: + result = transformArrayExpr(pstate, (ArrayExpr *) expr); + break; + + case T_RowExpr: + result = transformRowExpr(pstate, (RowExpr *) expr); + break; + + case T_CoalesceExpr: + result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr); + break; + + case T_NullTest: { ! NullTest *n = (NullTest *) expr; ! n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg); ! /* the argument can be any type, so don't coerce it */ ! result = expr; break; } ! ! case T_BooleanTest: ! result = transformBooleanTest(pstate, (BooleanTest *) expr); ! break; ! ! /********************************************* ! * Quietly accept node types that may be presented when we are ! * called on an already-transformed tree. ! * ! * Do any other node types need to be accepted? For now we are ! * taking a conservative approach, and only accepting node ! * types that are demonstrably necessary to accept. ! *********************************************/ ! case T_Var: ! case T_Const: ! case T_Param: ! case T_Aggref: ! case T_ArrayRef: ! case T_FuncExpr: ! case T_OpExpr: ! case T_DistinctExpr: ! case T_ScalarArrayOpExpr: ! case T_NullIfExpr: ! case T_BoolExpr: ! case T_FieldSelect: ! case T_FieldStore: ! case T_RelabelType: ! case T_CaseTestExpr: ! case T_CoerceToDomain: ! case T_CoerceToDomainValue: ! case T_SetToDefault: { ! result = (Node *) expr; ! break; ! } ! default: ! /* should not reach here */ ! elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); ! break; ! } ! return result; ! } ! static Node * ! transformParamRef(ParseState *pstate, ParamRef *pref) ! { ! int paramno = pref->number; ! ParseState *toppstate; ! Param *param; ! /* ! * Find topmost ParseState, which is where paramtype info lives. ! */ ! toppstate = pstate; ! while (toppstate->parentParseState != NULL) ! toppstate = toppstate->parentParseState; ! /* Check parameter number is in range */ ! if (paramno <= 0) /* probably can't happen? */ ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_PARAMETER), ! errmsg("there is no parameter $%d", paramno))); ! if (paramno > toppstate->p_numparams) ! { ! if (!toppstate->p_variableparams) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_PARAMETER), ! errmsg("there is no parameter $%d", ! paramno))); ! /* Okay to enlarge param array */ ! if (toppstate->p_paramtypes) ! toppstate->p_paramtypes = ! (Oid *) repalloc(toppstate->p_paramtypes, ! paramno * sizeof(Oid)); ! else ! toppstate->p_paramtypes = ! (Oid *) palloc(paramno * sizeof(Oid)); ! /* Zero out the previously-unreferenced slots */ ! MemSet(toppstate->p_paramtypes + toppstate->p_numparams, ! 0, ! (paramno - toppstate->p_numparams) * sizeof(Oid)); ! toppstate->p_numparams = paramno; ! } ! if (toppstate->p_variableparams) ! { ! /* If not seen before, initialize to UNKNOWN type */ ! if (toppstate->p_paramtypes[paramno - 1] == InvalidOid) ! toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID; ! } ! param = makeNode(Param); ! param->paramkind = PARAM_NUM; ! param->paramid = (AttrNumber) paramno; ! param->paramtype = toppstate->p_paramtypes[paramno - 1]; ! return (Node *) param; ! } ! static Node * ! transformAExprOp(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = a->lexpr; ! Node *rexpr = a->rexpr; ! Node *result; ! /* ! * Special-case "foo = NULL" and "NULL = foo" for compatibility ! * with standards-broken products (like Microsoft's). Turn these ! * into IS NULL exprs. ! */ ! if (Transform_null_equals && ! list_length(a->name) == 1 && ! strcmp(strVal(linitial(a->name)), "=") == 0 && ! (exprIsNullConstant(lexpr) || ! exprIsNullConstant(rexpr))) ! { ! NullTest *n = makeNode(NullTest); ! n->nulltesttype = IS_NULL; ! if (exprIsNullConstant(lexpr)) ! n->arg = (Expr *) rexpr; ! else ! n->arg = (Expr *) lexpr; ! result = transformExpr(pstate, ! (Node *) n); ! } ! else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, SubLink) && ! ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK) ! { ! /* ! * Convert "row op subselect" into a MULTIEXPR sublink. ! * Formerly the grammar did this, but now that a row construct ! * is allowed anywhere in expressions, it's easier to do it ! * here. ! */ ! SubLink *s = (SubLink *) rexpr; ! ! s->subLinkType = MULTIEXPR_SUBLINK; ! s->lefthand = ((RowExpr *) lexpr)->args; ! s->operName = a->name; ! result = transformExpr(pstate, (Node *) s); ! } ! else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, RowExpr)) ! { ! /* "row op row" */ ! result = make_row_op(pstate, a->name, ! lexpr, rexpr); ! } ! else ! { ! /* Ordinary scalar operator */ ! lexpr = transformExpr(pstate, lexpr); ! rexpr = transformExpr(pstate, rexpr); ! ! result = (Node *) make_op(pstate, ! a->name, ! lexpr, ! rexpr); ! } ! return result; ! } ! static Node * ! transformAExprAnd(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = transformExpr(pstate, a->lexpr); ! Node *rexpr = transformExpr(pstate, a->rexpr); ! lexpr = coerce_to_boolean(pstate, lexpr, "AND"); ! rexpr = coerce_to_boolean(pstate, rexpr, "AND"); ! return (Node *) makeBoolExpr(AND_EXPR, ! list_make2(lexpr, ! rexpr)); ! } ! static Node * ! transformAExprOr(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = transformExpr(pstate, a->lexpr); ! Node *rexpr = transformExpr(pstate, a->rexpr); ! lexpr = coerce_to_boolean(pstate, lexpr, "OR"); ! rexpr = coerce_to_boolean(pstate, rexpr, "OR"); ! return (Node *) makeBoolExpr(OR_EXPR, ! list_make2(lexpr, ! rexpr)); ! } ! static Node * ! transformAExprNot(ParseState *pstate, A_Expr *a) ! { ! Node *rexpr = transformExpr(pstate, a->rexpr); ! rexpr = coerce_to_boolean(pstate, rexpr, "NOT"); ! return (Node *) makeBoolExpr(NOT_EXPR, ! list_make1(rexpr)); ! } ! static Node * ! transformAExprOpAny(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = transformExpr(pstate, a->lexpr); ! Node *rexpr = transformExpr(pstate, a->rexpr); ! return (Node *) make_scalar_array_op(pstate, ! a->name, ! true, ! lexpr, ! rexpr); ! } ! static Node * ! transformAExprOpAll(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = transformExpr(pstate, a->lexpr); ! Node *rexpr = transformExpr(pstate, a->rexpr); ! return (Node *) make_scalar_array_op(pstate, ! a->name, ! false, ! lexpr, ! rexpr); ! } ! static Node * ! transformAExprDistinct(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = a->lexpr; ! Node *rexpr = a->rexpr; ! Node *result; ! if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, RowExpr)) ! { ! /* "row op row" */ ! result = make_row_distinct_op(pstate, a->name, ! lexpr, rexpr); ! } ! else ! { ! /* Ordinary scalar operator */ ! lexpr = transformExpr(pstate, lexpr); ! rexpr = transformExpr(pstate, rexpr); ! ! result = (Node *) make_distinct_op(pstate, ! a->name, ! lexpr, ! rexpr); ! } ! return result; ! } ! static Node * ! transformAExprNullIf(ParseState *pstate, A_Expr *a) ! { ! Node *lexpr = transformExpr(pstate, a->lexpr); ! Node *rexpr = transformExpr(pstate, a->rexpr); ! Node *result; ! result = (Node *) make_op(pstate, ! a->name, ! lexpr, ! rexpr); ! if (((OpExpr *) result)->opresulttype != BOOLOID) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("NULLIF requires = operator to yield boolean"))); ! /* ! * We rely on NullIfExpr and OpExpr being same struct ! */ ! NodeSetTag(result, T_NullIfExpr); ! return result; ! } ! static Node * ! transformAExprOf(ParseState *pstate, A_Expr *a) ! { ! /* ! * Checking an expression for match to type. Will result in a ! * boolean constant node. ! */ ! ListCell *telem; ! A_Const *n; ! Oid ltype, ! rtype; ! bool matched = FALSE; ! Node *lexpr = transformExpr(pstate, ! a->lexpr); ! ltype = exprType(lexpr); ! foreach(telem, (List *) a->rexpr) ! { ! rtype = LookupTypeName(lfirst(telem)); ! matched = (rtype == ltype); ! if (matched) ! break; ! } ! /* ! * Expect two forms: equals or not equals. Flip the sense of the ! * result for not equals. ! */ ! if (strcmp(strVal(linitial(a->name)), "!=") == 0) ! matched = (!matched); ! n = makeNode(A_Const); ! n->val.type = T_String; ! n->val.val.str = (matched ? "t" : "f"); ! n->typename = SystemTypeName("bool"); ! return transformExpr(pstate, (Node *) n); ! } ! static Node * ! transformFuncCall(ParseState *pstate, FuncCall *fn) ! { ! List *targs; ! ListCell *args; ! /* ! * Transform the list of arguments. We use a shallow list copy ! * and then transform-in-place to avoid O(N^2) behavior from ! * repeated lappend's. ! * ! * XXX: repeated lappend() would no longer result in O(n^2) ! * behavior; worth reconsidering this design? ! */ ! targs = list_copy(fn->args); ! foreach(args, targs) ! { ! lfirst(args) = transformExpr(pstate, ! (Node *) lfirst(args)); ! } ! return ParseFuncOrColumn(pstate, ! fn->funcname, ! targs, ! fn->agg_star, ! fn->agg_distinct, ! false); ! } ! static Node * ! transformSubLink(ParseState *pstate, SubLink *sublink) ! { ! List *qtrees; ! Query *qtree; ! Node *result = (Node *) sublink; ! ! /* If we already transformed this node, do nothing */ ! if (IsA(sublink->subselect, Query)) ! return result; ! ! pstate->p_hasSubLinks = true; ! qtrees = parse_sub_analyze(sublink->subselect, pstate); ! if (list_length(qtrees) != 1) ! elog(ERROR, "bad query in sub-select"); ! qtree = (Query *) linitial(qtrees); ! if (qtree->commandType != CMD_SELECT || ! qtree->resultRelation != 0) ! elog(ERROR, "bad query in sub-select"); ! sublink->subselect = (Node *) qtree; ! if (sublink->subLinkType == EXISTS_SUBLINK) ! { ! /* ! * EXISTS needs no lefthand or combining operator. These ! * fields should be NIL already, but make sure. ! */ ! sublink->lefthand = NIL; ! sublink->operName = NIL; ! sublink->operOids = NIL; ! sublink->useOr = FALSE; ! } ! else if (sublink->subLinkType == EXPR_SUBLINK || ! sublink->subLinkType == ARRAY_SUBLINK) ! { ! ListCell *tlist_item = list_head(qtree->targetList); ! /* ! * Make sure the subselect delivers a single column (ignoring ! * resjunk targets). ! */ ! if (tlist_item == NULL || ! ((TargetEntry *) lfirst(tlist_item))->resdom->resjunk) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery must return a column"))); ! while ((tlist_item = lnext(tlist_item)) != NULL) ! { ! if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery must return only one column"))); ! } ! /* ! * EXPR and ARRAY need no lefthand or combining ! * operator. These fields should be NIL already, but make ! * sure. ! */ ! sublink->lefthand = NIL; ! sublink->operName = NIL; ! sublink->operOids = NIL; ! sublink->useOr = FALSE; ! } ! else ! { ! /* ALL, ANY, or MULTIEXPR: generate operator list */ ! List *left_list = sublink->lefthand; ! List *right_list = qtree->targetList; ! int row_length = list_length(left_list); ! bool needNot = false; ! List *op = sublink->operName; ! char *opname = strVal(llast(op)); ! ListCell *l; ! ListCell *ll_item; ! ! /* transform lefthand expressions */ ! foreach(l, left_list) ! lfirst(l) = transformExpr(pstate, lfirst(l)); ! ! /* ! * If the expression is "<> ALL" (with unqualified opname) ! * then convert it to "NOT IN". This is a hack to improve ! * efficiency of expressions output by pre-7.4 Postgres. ! */ ! if (sublink->subLinkType == ALL_SUBLINK && ! list_length(op) == 1 && strcmp(opname, "<>") == 0) ! { ! sublink->subLinkType = ANY_SUBLINK; ! opname = pstrdup("="); ! op = list_make1(makeString(opname)); ! sublink->operName = op; ! needNot = true; ! } ! /* Set useOr if op is "<>" (possibly qualified) */ ! if (strcmp(opname, "<>") == 0) ! sublink->useOr = TRUE; ! else ! sublink->useOr = FALSE; ! /* Combining operators other than =/<> is dubious... */ ! if (row_length != 1 && ! strcmp(opname, "=") != 0 && ! strcmp(opname, "<>") != 0) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("row comparison cannot use operator %s", ! opname))); ! ! /* ! * To build the list of combining operator OIDs, we must scan ! * subquery's targetlist to find values that will be matched ! * against lefthand values. We need to ignore resjunk ! * targets, so doing the outer iteration over right_list is ! * easier than doing it over left_list. ! */ ! sublink->operOids = NIL; ! ll_item = list_head(left_list); ! foreach(l, right_list) ! { ! TargetEntry *tent = (TargetEntry *) lfirst(l); ! Node *lexpr; ! Operator optup; ! Form_pg_operator opform; ! ! if (tent->resdom->resjunk) ! continue; ! ! if (ll_item == NULL) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery has too many columns"))); ! lexpr = lfirst(ll_item); ! ll_item = lnext(ll_item); ! /* ! * It's OK to use oper() not compatible_oper() here, ! * because make_subplan() will insert type coercion calls ! * if needed. ! */ ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); ! opform = (Form_pg_operator) GETSTRUCT(optup); ! ! if (opform->oprresult != BOOLOID) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("operator %s must return type boolean, not type %s", ! opname, ! format_type_be(opform->oprresult)), ! errhint("The operator of a quantified predicate subquery must return type boolean."))); ! ! if (get_func_retset(opform->oprcode)) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("operator %s must not return a set", ! opname), ! errhint("The operator of a quantified predicate subquery must return type boolean."))); ! sublink->operOids = lappend_oid(sublink->operOids, ! oprid(optup)); ! ReleaseSysCache(optup); ! } ! if (ll_item != NULL) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("subquery has too few columns"))); ! if (needNot) ! { ! result = coerce_to_boolean(pstate, result, "NOT"); ! result = (Node *) makeBoolExpr(NOT_EXPR, ! list_make1(result)); ! } ! } ! return result; ! } ! static Node * ! transformCaseExpr(ParseState *pstate, CaseExpr *c) ! { ! CaseExpr *newc; ! Node *arg; ! CaseTestExpr *placeholder; ! List *newargs; ! List *typeids; ! ListCell *l; ! Node *defresult; ! Oid ptype; ! ! /* If we already transformed this node, do nothing */ ! if (OidIsValid(c->casetype)) ! return (Node *) c; ! ! newc = makeNode(CaseExpr); ! ! /* transform the test expression, if any */ ! arg = transformExpr(pstate, (Node *) c->arg); ! newc->arg = (Expr *) arg; ! /* generate placeholder for test expression */ ! if (arg) ! { ! placeholder = makeNode(CaseTestExpr); ! placeholder->typeId = exprType(arg); ! placeholder->typeMod = exprTypmod(arg); ! } ! else ! placeholder = NULL; ! /* transform the list of arguments */ ! newargs = NIL; ! typeids = NIL; ! foreach(l, c->args) ! { ! CaseWhen *w = (CaseWhen *) lfirst(l); ! CaseWhen *neww = makeNode(CaseWhen); ! Node *warg; ! Assert(IsA(w, CaseWhen)); ! warg = (Node *) w->expr; ! if (placeholder) ! { ! /* shorthand form was specified, so expand... */ ! warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", ! (Node *) placeholder, ! warg); ! } ! neww->expr = (Expr *) transformExpr(pstate, warg); ! neww->expr = (Expr *) coerce_to_boolean(pstate, ! (Node *) neww->expr, ! "CASE/WHEN"); ! warg = (Node *) w->result; ! neww->result = (Expr *) transformExpr(pstate, warg); ! newargs = lappend(newargs, neww); ! typeids = lappend_oid(typeids, exprType((Node *) neww->result)); ! } ! newc->args = newargs; ! /* transform the default clause */ ! defresult = (Node *) c->defresult; ! if (defresult == NULL) ! { ! A_Const *n = makeNode(A_Const); ! n->val.type = T_Null; ! defresult = (Node *) n; ! } ! newc->defresult = (Expr *) transformExpr(pstate, defresult); ! /* ! * Note: default result is considered the most significant type in ! * determining preferred type. This is how the code worked before, ! * but it seems a little bogus to me ! * --- tgl ! */ ! typeids = lcons_oid(exprType((Node *) newc->defresult), typeids); ! ! ptype = select_common_type(typeids, "CASE"); ! Assert(OidIsValid(ptype)); ! newc->casetype = ptype; ! ! /* Convert default result clause, if necessary */ ! newc->defresult = (Expr *) coerce_to_common_type(pstate, ! (Node *) newc->defresult, ! ptype, ! "CASE/ELSE"); ! ! /* Convert when-clause results, if necessary */ ! foreach(l, newc->args) ! { ! CaseWhen *w = (CaseWhen *) lfirst(l); ! ! w->result = (Expr *) coerce_to_common_type(pstate, ! (Node *) w->result, ! ptype, ! "CASE/WHEN"); } ! return (Node *) newc; ! } ! ! static Node * ! transformArrayExpr(ParseState *pstate, ArrayExpr *a) ! { ! ArrayExpr *newa = makeNode(ArrayExpr); ! List *newelems = NIL; ! List *newcoercedelems = NIL; ! List *typeids = NIL; ! ListCell *element; ! Oid array_type; ! Oid element_type; ! ! /* Transform the element expressions */ ! foreach(element, a->elements) ! { ! Node *e = (Node *) lfirst(element); ! Node *newe; ! ! newe = transformExpr(pstate, e); ! newelems = lappend(newelems, newe); ! typeids = lappend_oid(typeids, exprType(newe)); ! } ! ! /* Select a common type for the elements */ ! element_type = select_common_type(typeids, "ARRAY"); ! ! /* Coerce arguments to common type if necessary */ ! foreach(element, newelems) ! { ! Node *e = (Node *) lfirst(element); ! Node *newe; ! ! newe = coerce_to_common_type(pstate, e, ! element_type, ! "ARRAY"); ! newcoercedelems = lappend(newcoercedelems, newe); ! } ! ! /* Do we have an array type to use? */ ! array_type = get_array_type(element_type); ! if (array_type != InvalidOid) ! { ! /* Elements are presumably of scalar type */ ! newa->multidims = false; ! } ! else ! { ! /* Must be nested array expressions */ ! newa->multidims = true; ! ! array_type = element_type; ! element_type = get_element_type(array_type); ! if (!OidIsValid(element_type)) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_OBJECT), ! errmsg("could not find array type for data type %s", ! format_type_be(array_type)))); ! } ! ! newa->array_typeid = array_type; ! newa->element_typeid = element_type; ! newa->elements = newcoercedelems; ! ! return (Node *) newa; ! } ! ! static Node * ! transformRowExpr(ParseState *pstate, RowExpr *r) ! { ! RowExpr *newr = makeNode(RowExpr); ! List *newargs = NIL; ! ListCell *arg; ! ! /* Transform the field expressions */ ! foreach(arg, r->args) ! { ! Node *e = (Node *) lfirst(arg); ! Node *newe; ! ! newe = transformExpr(pstate, e); ! newargs = lappend(newargs, newe); ! } ! newr->args = newargs; ! ! /* Barring later casting, we consider the type RECORD */ ! newr->row_typeid = RECORDOID; ! newr->row_format = COERCE_IMPLICIT_CAST; ! ! return (Node *) newr; ! } ! ! static Node * ! transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) ! { ! CoalesceExpr *newc = makeNode(CoalesceExpr); ! List *newargs = NIL; ! List *newcoercedargs = NIL; ! List *typeids = NIL; ! ListCell *args; ! ! foreach(args, c->args) ! { ! Node *e = (Node *) lfirst(args); ! Node *newe; ! ! newe = transformExpr(pstate, e); ! newargs = lappend(newargs, newe); ! typeids = lappend_oid(typeids, exprType(newe)); ! } ! ! newc->coalescetype = select_common_type(typeids, "COALESCE"); ! ! /* Convert arguments if necessary */ ! foreach(args, newargs) ! { ! Node *e = (Node *) lfirst(args); ! Node *newe; ! ! newe = coerce_to_common_type(pstate, e, ! newc->coalescetype, ! "COALESCE"); ! newcoercedargs = lappend(newcoercedargs, newe); ! } ! ! newc->args = newcoercedargs; ! return (Node *) newc; } static Node * *************** *** 1278,1283 **** --- 1301,1346 ---- return result; } + static Node * + transformBooleanTest(ParseState *pstate, BooleanTest *b) + { + const char *clausename; + + switch (b->booltesttype) + { + case IS_TRUE: + clausename = "IS TRUE"; + break; + case IS_NOT_TRUE: + clausename = "IS NOT TRUE"; + break; + case IS_FALSE: + clausename = "IS FALSE"; + break; + case IS_NOT_FALSE: + clausename = "IS NOT FALSE"; + break; + case IS_UNKNOWN: + clausename = "IS UNKNOWN"; + break; + case IS_NOT_UNKNOWN: + clausename = "IS NOT UNKNOWN"; + break; + default: + elog(ERROR, "unrecognized booltesttype: %d", + (int) b->booltesttype); + clausename = NULL; /* keep compiler quiet */ + } + + b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg); + + b->arg = (Expr *) coerce_to_boolean(pstate, + (Node *) b->arg, + clausename); + + return (Node *) b; + } + /* * exprType - * returns the Oid of the type of the expression. (Used for typechecking.)
---------------------------(end of broadcast)--------------------------- TIP 2: you can get off all lists at once with the unregister command (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])