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])