? src/a.out ? src/interfaces/ecpg/lib/libecpg.so.3.4.1 ? src/interfaces/libpq/libpq.so.2.3 Index: doc/src/sgml/func.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.138 diff -c -r1.138 func.sgml *** doc/src/sgml/func.sgml 2003/02/06 20:25:31 1.138 --- doc/src/sgml/func.sgml 2003/02/12 02:33:20 *************** *** 6292,6308 **** - - - COALESCE and NULLIF are - just shorthand for CASE expressions. They are actually - converted into CASE expressions at a very early stage - of processing, and subsequent processing thinks it is dealing with - CASE. Thus an incorrect COALESCE or - NULLIF usage may draw an error message that - refers to CASE. - - --- 6292,6297 ---- Index: src/backend/catalog/dependency.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/catalog/dependency.c,v retrieving revision 1.21 diff -c -r1.21 dependency.c *** src/backend/catalog/dependency.c 2003/02/09 06:56:26 1.21 --- src/backend/catalog/dependency.c 2003/02/12 02:33:25 *************** *** 933,938 **** --- 933,945 ---- &context->addrs); /* fall through to examine arguments */ } + if (IsA(node, NullIfExpr)) + { + NullIfExpr *nullifexpr = (NullIfExpr *) node; + add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0, + &context->addrs); + /* fall through to examine arguments */ + } if (IsA(node, Aggref)) { Aggref *aggref = (Aggref *) node; Index: src/backend/executor/execQual.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/execQual.c,v retrieving revision 1.124 diff -c -r1.124 execQual.c *** src/backend/executor/execQual.c 2003/02/03 21:15:43 1.124 --- src/backend/executor/execQual.c 2003/02/12 02:33:31 *************** *** 73,78 **** --- 73,82 ---- bool *isNull); static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull); + static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); + static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullTest(GenericExprState *nstate, *************** *** 1370,1375 **** --- 1374,1457 ---- return BoolGetDatum(!AnyNull); } + + static Datum + ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) + { + Datum value; + List *arg; + + /* Simply loop through until something NOT NULL is found. */ + foreach(arg, coalesceExpr->args) + { + ExprState *e = (ExprState *)lfirst(arg); + value = ExecEvalExpr(e, econtext, isNull, NULL); + if (!*isNull) { + return value; + } + } + + *isNull = true; + return (Datum) 0; + } + + static Datum + ExecEvalNullIf(FuncExprState *fcache, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) + { + Datum result; + FunctionCallInfoData fcinfo; + ExprDoneCond argDone; + List *argList; + + /* + * Initialize function cache if first time through + */ + if (fcache->func.fn_oid == InvalidOid) + { + NullIfExpr *op = (NullIfExpr *) fcache->xprstate.expr; + + init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); + Assert(!fcache->func.fn_retset); + } + + /* + * extract info from fcache + */ + argList = fcache->args; + + /* Need to prep callinfo structure */ + MemSet(&fcinfo, 0, sizeof(fcinfo)); + fcinfo.flinfo = &(fcache->func); + argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext); + if (argDone != ExprSingleResult) + elog(ERROR, "NULLIF does not support set arguments"); + Assert(fcinfo.nargs == 2); + + /* if either argument is NULL they can't be equal. */ + if (fcinfo.argnull[0] || fcinfo.argnull[1]) + { + *isNull = fcinfo.argnull[0]; + return fcinfo.arg[0]; + } + else + { + fcinfo.isnull = false; + result = FunctionCallInvoke(&fcinfo); + *isNull = fcinfo.isnull; + /* if the arguments are equal return null. */ + if (DatumGetBool(result)) + { + *isNull = true; + return (Datum) 0; + } + } + + *isNull = fcinfo.argnull[0]; + return fcinfo.arg[0]; + } + /* ---------------------------------------------------------------- * ExecEvalCase * *************** *** 1820,1825 **** --- 1902,1913 ---- isNull, isDone); break; + case T_CoalesceExpr: + retDatum = ExecEvalCoalesce((CoalesceExprState *) expression, econtext, isNull, isDone); + break; + case T_NullIfExpr: + retDatum = ExecEvalNullIf((FuncExprState *) expression, econtext,isNull,isDone); + break; case T_CaseExpr: retDatum = ExecEvalCase((CaseExprState *) expression, econtext, *************** *** 2004,2009 **** --- 2092,2107 ---- state = (ExprState *) fstate; } break; + case T_NullIfExpr: + { + NullIfExpr *nullifexpr = (NullIfExpr *) node; + FuncExprState *fstate = makeNode(FuncExprState); + fstate->args = (List *) + ExecInitExpr((Expr *) nullifexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState *) fstate; + } + break; case T_BoolExpr: { BoolExpr *boolexpr = (BoolExpr *) node; *************** *** 2055,2060 **** --- 2153,2178 ---- gstate->arg = ExecInitExpr(relabel->arg, parent); state = (ExprState *) gstate; + } + break; + case T_CoalesceExpr: + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + CoalesceExprState *cstate = makeNode(CoalesceExprState); + List *outlist = NIL; + List *inlist; + foreach(inlist, coalesceexpr->args) + { + Expr *e = (Expr *) lfirst(inlist); + + ExprState *estate = ExecInitExpr(e, parent); + + outlist = lappend(outlist,estate); + } + + cstate->args = outlist; + + state = (ExprState *) cstate; } break; case T_CaseExpr: Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/copyfuncs.c,v retrieving revision 1.243 diff -c -r1.243 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2003/02/10 04:44:44 1.243 --- src/backend/nodes/copyfuncs.c 2003/02/12 02:33:34 *************** *** 768,804 **** } /* ! * _copyOpExpr */ static OpExpr * ! _copyOpExpr(OpExpr *from) { - OpExpr *newnode = makeNode(OpExpr); - COPY_SCALAR_FIELD(opno); COPY_SCALAR_FIELD(opfuncid); COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opretset); COPY_NODE_FIELD(args); - return newnode; } /* ! * _copyDistinctExpr */ static DistinctExpr * _copyDistinctExpr(DistinctExpr *from) { DistinctExpr *newnode = makeNode(DistinctExpr); ! ! COPY_SCALAR_FIELD(opno); ! COPY_SCALAR_FIELD(opfuncid); ! COPY_SCALAR_FIELD(opresulttype); ! COPY_SCALAR_FIELD(opretset); ! COPY_NODE_FIELD(args); ! return newnode; } /* --- 768,815 ---- } /* ! * Both DistinctExpr and NullIfExpr are typedefs for OpExpr so we'd ! * like to combine some logic here. */ static OpExpr * ! _copyOpExprTypedef(OpExpr *from, OpExpr *newnode) { COPY_SCALAR_FIELD(opno); COPY_SCALAR_FIELD(opfuncid); COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opretset); COPY_NODE_FIELD(args); return newnode; } + + /* + * _copyOpExpr + */ + static OpExpr * + _copyOpExpr(OpExpr *from) + { + OpExpr *newnode = makeNode(OpExpr); + return _copyOpExprTypedef(from,newnode); + } /* ! * _copyDistinctExpr - DistinctExpr is a typedef for OpExpr */ static DistinctExpr * _copyDistinctExpr(DistinctExpr *from) { DistinctExpr *newnode = makeNode(DistinctExpr); ! return (DistinctExpr *)_copyOpExprTypedef(from,newnode); ! } ! /* ! * _copyNullIfExpr - NullIfExpr is a typedef for OpExpr ! */ ! static NullIfExpr * ! _copyNullIfExpr(NullIfExpr *from) ! { ! NullIfExpr *newnode = makeNode(NullIfExpr); ! return (NullIfExpr *)_copyOpExprTypedef(from,newnode); } /* *************** *** 890,895 **** --- 901,920 ---- } /* + * _copyCoalesceExpr + */ + static CoalesceExpr * + _copyCoalesceExpr(CoalesceExpr *from) + { + CoalesceExpr *newnode = makeNode(CoalesceExpr); + + COPY_SCALAR_FIELD(coalescetype); + COPY_NODE_FIELD(args); + + return newnode; + } + + /* * _copyCaseExpr */ static CaseExpr * *************** *** 2477,2482 **** --- 2502,2513 ---- break; case T_RelabelType: retval = _copyRelabelType(from); + break; + case T_CoalesceExpr: + retval = _copyCoalesceExpr(from); + break; + case T_NullIfExpr: + retval = _copyNullIfExpr(from); break; case T_CaseExpr: retval = _copyCaseExpr(from); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/equalfuncs.c,v retrieving revision 1.186 diff -c -r1.186 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2003/02/10 04:44:45 1.186 --- src/backend/nodes/equalfuncs.c 2003/02/12 02:33:37 *************** *** 244,251 **** return true; } static bool ! _equalOpExpr(OpExpr *a, OpExpr *b) { COMPARE_SCALAR_FIELD(opno); /* --- 244,255 ---- return true; } + /* + * NullIfExpr and DistinctExpr are typedefs for OpExpr + * so combine this logic + */ static bool ! _equalOpExprTypedef(OpExpr *a, OpExpr *b) { COMPARE_SCALAR_FIELD(opno); /* *************** *** 267,291 **** } static bool ! _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b) { ! COMPARE_SCALAR_FIELD(opno); ! /* ! * Special-case opfuncid: it is allowable for it to differ if one ! * node contains zero and the other doesn't. This just means that the ! * one node isn't as far along in the parse/plan pipeline and hasn't ! * had the opfuncid cache filled yet. ! */ ! if (a->opfuncid != b->opfuncid && ! a->opfuncid != 0 && ! b->opfuncid != 0) ! return false; ! COMPARE_SCALAR_FIELD(opresulttype); ! COMPARE_SCALAR_FIELD(opretset); ! COMPARE_NODE_FIELD(args); ! return true; } static bool --- 271,293 ---- } static bool ! _equalOpExpr(OpExpr *a, OpExpr *b) { ! return _equalOpExprTypedef(a,b); ! } ! /* DistinctExpr is a typedef for OpExpr */ ! static bool ! _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b) ! { ! return _equalOpExprTypedef(a,b); ! } ! /* NullIfExpr is a typedef for OpExpr */ ! static bool ! _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) ! { ! return _equalOpExprTypedef(a,b); } static bool *************** *** 359,364 **** --- 361,375 ---- } static bool + _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) + { + COMPARE_SCALAR_FIELD(coalescetype); + COMPARE_NODE_FIELD(args); + + return true; + } + + static bool _equalCaseExpr(CaseExpr *a, CaseExpr *b) { COMPARE_SCALAR_FIELD(casetype); *************** *** 1606,1611 **** --- 1617,1628 ---- break; case T_RelabelType: retval = _equalRelabelType(a, b); + break; + case T_CoalesceExpr: + retval = _equalCoalesceExpr(a, b); + break; + case T_NullIfExpr: + retval = _equalNullIfExpr(a, b); break; case T_CaseExpr: retval = _equalCaseExpr(a, b); Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/outfuncs.c,v retrieving revision 1.199 diff -c -r1.199 outfuncs.c *** src/backend/nodes/outfuncs.c 2003/02/10 04:44:45 1.199 --- src/backend/nodes/outfuncs.c 2003/02/12 02:33:40 *************** *** 629,639 **** WRITE_NODE_FIELD(args); } static void ! _outOpExpr(StringInfo str, OpExpr *node) { - WRITE_NODE_TYPE("OPEXPR"); - WRITE_OID_FIELD(opno); WRITE_OID_FIELD(opfuncid); WRITE_OID_FIELD(opresulttype); --- 629,641 ---- WRITE_NODE_FIELD(args); } + /* + * DistinctExpr and NullIfExpr are typedefs for OpExpr + * so combine this logic. + */ static void ! _outOpExprTypedef(StringInfo str, OpExpr *node) { WRITE_OID_FIELD(opno); WRITE_OID_FIELD(opfuncid); WRITE_OID_FIELD(opresulttype); *************** *** 642,656 **** } static void _outDistinctExpr(StringInfo str, DistinctExpr *node) { WRITE_NODE_TYPE("DISTINCTEXPR"); ! WRITE_OID_FIELD(opno); ! WRITE_OID_FIELD(opfuncid); ! WRITE_OID_FIELD(opresulttype); ! WRITE_BOOL_FIELD(opretset); ! WRITE_NODE_FIELD(args); } static void --- 644,669 ---- } static void + _outOpExpr(StringInfo str, OpExpr *node) + { + WRITE_NODE_TYPE("OPEXPR"); + _outOpExprTypedef(str,node); + } + + /* DistinctExpr is a typedef for OpExpr */ + static void _outDistinctExpr(StringInfo str, DistinctExpr *node) { WRITE_NODE_TYPE("DISTINCTEXPR"); + _outOpExprTypedef(str,(DistinctExpr *)node); + } ! /* NullIfExpr is a typedef for OpExpr */ ! static void ! _outNullIfExpr(StringInfo str, NullIfExpr *node) ! { ! WRITE_NODE_TYPE("NULLIF"); ! _outOpExprTypedef(str,(NullIfExpr *)node); } static void *************** *** 734,739 **** --- 747,761 ---- } static void + _outCoalesceExpr(StringInfo str, CoalesceExpr *node) + { + WRITE_NODE_TYPE("COALESCE"); + + WRITE_OID_FIELD(coalescetype); + WRITE_NODE_FIELD(args); + } + + static void _outCaseExpr(StringInfo str, CaseExpr *node) { WRITE_NODE_TYPE("CASE"); *************** *** 1273,1278 **** --- 1295,1304 ---- case AEXPR_NOT: appendStringInfo(str, " NOT"); break; + case AEXPR_NULLIF: + appendStringInfo(str, " NULLIF "); + WRITE_NODE_FIELD(name); + break; case AEXPR_DISTINCT: appendStringInfo(str, " DISTINCT "); WRITE_NODE_FIELD(name); *************** *** 1569,1574 **** --- 1595,1606 ---- break; case T_RelabelType: _outRelabelType(str, obj); + break; + case T_CoalesceExpr: + _outCoalesceExpr(str, obj); + break; + case T_NullIfExpr: + _outNullIfExpr(str, obj); break; case T_CaseExpr: _outCaseExpr(str, obj); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/readfuncs.c,v retrieving revision 1.148 diff -c -r1.148 readfuncs.c *** src/backend/nodes/readfuncs.c 2003/02/09 06:56:27 1.148 --- src/backend/nodes/readfuncs.c 2003/02/12 02:33:42 *************** *** 443,455 **** READ_DONE(); } ! /* ! * _readOpExpr */ ! static OpExpr * ! _readOpExpr(void) { ! READ_LOCALS(OpExpr); READ_OID_FIELD(opno); READ_OID_FIELD(opfuncid); --- 443,456 ---- READ_DONE(); } ! /* ! * DistinctExpr and NullIfExpr are typedefs for OpExpr ! * so combine this logic. */ ! static void ! _readOpExprTypedef(OpExpr *local_node) { ! READ_TEMP_LOCALS(); READ_OID_FIELD(opno); READ_OID_FIELD(opfuncid); *************** *** 466,502 **** READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); READ_DONE(); } /* ! * _readDistinctExpr */ static DistinctExpr * _readDistinctExpr(void) { ! READ_LOCALS(DistinctExpr); ! ! READ_OID_FIELD(opno); ! READ_OID_FIELD(opfuncid); ! /* ! * The opfuncid is stored in the textual format primarily for debugging ! * and documentation reasons. We want to always read it as zero to force ! * it to be re-looked-up in the pg_operator entry. This ensures that ! * stored rules don't have hidden dependencies on operators' functions. ! * (We don't currently support an ALTER OPERATOR command, but might ! * someday.) ! */ ! local_node->opfuncid = InvalidOid; ! ! READ_OID_FIELD(opresulttype); ! READ_BOOL_FIELD(opretset); ! READ_NODE_FIELD(args); READ_DONE(); } /* * _readBoolExpr */ --- 467,509 ---- READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); + } + + /* + * _readOpExpr + */ + static OpExpr * + _readOpExpr(void) + { + READ_LOCALS_NO_FIELDS(OpExpr); + _readOpExprTypedef(local_node); READ_DONE(); } /* ! * _readDistinctExpr - DistinctExpr is a typedef for OpExpr */ static DistinctExpr * _readDistinctExpr(void) { ! READ_LOCALS_NO_FIELDS(DistinctExpr); ! _readOpExprTypedef(local_node); ! READ_DONE(); ! } + /* + * _readNullIfExpr - NullIfExpr is a typedef for OpExpr + */ + static NullIfExpr * + _readNullIfExpr(void) + { + READ_LOCALS_NO_FIELDS(NullIfExpr); + _readOpExprTypedef(local_node); READ_DONE(); } + /* * _readBoolExpr */ *************** *** 577,582 **** --- 584,603 ---- } /* + * _readCoalesceExpr + */ + static CoalesceExpr * + _readCoalesceExpr(void) + { + READ_LOCALS(CoalesceExpr); + + READ_OID_FIELD(coalescetype); + READ_NODE_FIELD(args); + + READ_DONE(); + } + + /* * _readCaseExpr */ static CaseExpr * *************** *** 891,896 **** --- 912,921 ---- return_value = _readFieldSelect(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); + else if (MATCH("COALESCE", 8)) + return_value = _readCoalesceExpr(); + else if (MATCH("NULLIF", 6)) + return_value = _readNullIfExpr(); else if (MATCH("CASE", 4)) return_value = _readCaseExpr(); else if (MATCH("WHEN", 4)) Index: src/backend/optimizer/path/costsize.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/path/costsize.c,v retrieving revision 1.105 diff -c -r1.105 costsize.c *** src/backend/optimizer/path/costsize.c 2003/02/08 20:20:54 1.105 --- src/backend/optimizer/path/costsize.c 2003/02/12 02:33:48 *************** *** 1457,1463 **** */ if (IsA(node, FuncExpr) || IsA(node, OpExpr) || ! IsA(node, DistinctExpr)) { total->per_tuple += cpu_operator_cost; } --- 1457,1464 ---- */ if (IsA(node, FuncExpr) || IsA(node, OpExpr) || ! IsA(node, DistinctExpr) || ! IsA(node, NullIfExpr)) { total->per_tuple += cpu_operator_cost; } Index: src/backend/optimizer/plan/setrefs.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/plan/setrefs.c,v retrieving revision 1.91 diff -c -r1.91 setrefs.c *** src/backend/optimizer/plan/setrefs.c 2003/01/20 18:54:52 1.91 --- src/backend/optimizer/plan/setrefs.c 2003/02/12 02:33:48 *************** *** 284,289 **** --- 284,291 ---- set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ else if (IsA(node, SubPlan)) { SubPlan *sp = (SubPlan *) node; Index: src/backend/optimizer/util/clauses.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/optimizer/util/clauses.c,v retrieving revision 1.129 diff -c -r1.129 clauses.c *** src/backend/optimizer/util/clauses.c 2003/02/09 06:56:27 1.129 --- src/backend/optimizer/util/clauses.c 2003/02/12 02:33:54 *************** *** 705,710 **** --- 705,714 ---- break; } } + if (IsA(node, CoalesceExpr)) + return true; + if (IsA(node, NullIfExpr)) + return true; if (IsA(node, CaseExpr)) return true; if (IsA(node, NullTest)) *************** *** 1048,1053 **** --- 1052,1123 ---- newexpr->args = args; return (Node *) newexpr; } + if (IsA(node, NullIfExpr)) + { + NullIfExpr *expr = (NullIfExpr *) node; + List *arg; + List *args; + Expr *simple; + NullIfExpr *newexpr; + bool only_const_input = true; + + args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, (void *) active_fns); + + foreach(arg, args) + { + if (!IsA(lfirst(arg), Const)) + only_const_input = false; + } + + /* all constants? then we can optimize this out */ + if (only_const_input) + { + set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ + /* + * Code for op/func reduction is pretty bulky, so split it out + * as a separate function. + */ + simple = simplify_function(expr->opfuncid, args, + false, active_fns); + if (simple) /* successfully simplified it */ + { + /* + * Since the semantics are differnt for NullIf + * we must play with the result. + */ + Const *csimple = (Const *) simple; + Const *leftarg = (Const *) lfirst(args); + + Assert(IsA(csimple, Const)); + Assert(IsA(leftarg, Const)); + if (!DatumGetBool(csimple->constvalue)) + { + return (Node *)leftarg; + } else { + Const *nullconst = makeNode(Const); + nullconst->consttype = leftarg->consttype; + nullconst->constlen = leftarg->constlen; + nullconst->constvalue = (Datum) 0; + nullconst->constisnull = true; + nullconst->constbyval = leftarg->constbyval; + return (Node *)nullconst; + } + } + } + + /* + * The expression cannot be simplified any further, so build and + * return a replacement NullIfExpr node using the + * possibly-simplified arguments. + */ + newexpr = makeNode(NullIfExpr); + newexpr->opno = expr->opno; + newexpr->opfuncid = expr->opfuncid; + newexpr->opresulttype = expr->opresulttype; + newexpr->opretset = expr->opretset; + newexpr->args = args; + return (Node *) newexpr; + } if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; *************** *** 1373,1378 **** --- 1443,1494 ---- return (Node *) newrelabel; } } + if (IsA(node, CoalesceExpr)) + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + CoalesceExpr *newcoalesce; + List *newargs = NIL; + List *arg; + bool currently_all_consts; + + currently_all_consts = true; + + foreach(arg, coalesceexpr->args) + { + Expr *e = (Expr *) eval_const_expressions_mutator((Node *) lfirst(arg), active_fns); + /* + * Remove null constants from the list. + * For non null constants, if it has not been preceded by + * any nonconstant expressions then that is the result. + */ + if (IsA(e, Const)) + { + Const *c = (Const *)e; + if (!c->constisnull) + { + if (currently_all_consts) + { + return (Node *)c; + } + else + { + newargs = lappend(newargs,e); + } + } + } + else + { + currently_all_consts = false; + newargs = lappend(newargs,e); + } + } + + newcoalesce = makeNode(CoalesceExpr); + newcoalesce->coalescetype = coalesceexpr->coalescetype; + newcoalesce->args = newargs; + + return (Node *) newcoalesce; + } if (IsA(node, CaseExpr)) { *************** *** 2087,2092 **** --- 2203,2225 ---- return walker(((FieldSelect *) node)->arg, context); case T_RelabelType: return walker(((RelabelType *) node)->arg, context); + case T_CoalesceExpr: + { + CoalesceExpr *expr = (CoalesceExpr *)node; + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; + case T_NullIfExpr: + { + NullIfExpr *expr = (NullIfExpr *) node; + + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; *************** *** 2398,2403 **** --- 2531,2546 ---- return (Node *) newnode; } break; + case T_NullIfExpr: + { + NullIfExpr *expr = (NullIfExpr *) node; + NullIfExpr *newnode; + + FLATCOPY(newnode, expr, NullIfExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; *************** *** 2454,2459 **** --- 2597,2612 ---- FLATCOPY(newnode, relabel, RelabelType); MUTATE(newnode->arg, relabel->arg, Expr *); + return (Node *) newnode; + } + break; + case T_CoalesceExpr: + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + CoalesceExpr *newnode; + + FLATCOPY(newnode, coalesceexpr, CoalesceExpr); + MUTATE(newnode->args, coalesceexpr->args, List *); return (Node *) newnode; } break; Index: src/backend/parser/gram.y =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/gram.y,v retrieving revision 2.401 diff -c -r2.401 gram.y *** src/backend/parser/gram.y 2003/02/10 04:44:45 2.401 --- src/backend/parser/gram.y 2003/02/12 02:34:08 *************** *** 6634,6639 **** --- 6634,6643 ---- * COALESCE(a,b,...) * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END * - thomas 1998-11-09 + * + * NULLIF and COALESCE have become first class nodes to + * prevent double evaluation of arguments. + * - Kris Jurka 2003-02-11 */ case_expr: CASE case_arg when_clause_list case_default END_P { *************** *** 6645,6672 **** } | NULLIF '(' a_expr ',' a_expr ')' { ! CaseExpr *c = makeNode(CaseExpr); ! CaseWhen *w = makeNode(CaseWhen); ! ! w->expr = (Expr *) makeSimpleA_Expr(AEXPR_OP, "=", $3, $5); ! /* w->result is left NULL */ ! c->args = makeList1(w); ! c->defresult = (Expr *) $3; ! $$ = (Node *)c; } | COALESCE '(' expr_list ')' { ! CaseExpr *c = makeNode(CaseExpr); List *l; foreach (l,$3) { ! CaseWhen *w = makeNode(CaseWhen); ! NullTest *n = makeNode(NullTest); ! n->arg = lfirst(l); ! n->nulltesttype = IS_NOT_NULL; ! w->expr = (Expr *) n; ! w->result = lfirst(l); ! c->args = lappend(c->args, w); } $$ = (Node *)c; } --- 6649,6663 ---- } | NULLIF '(' a_expr ',' a_expr ')' { ! $$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3,$5); } | COALESCE '(' expr_list ')' { ! CoalesceExpr *c = makeNode(CoalesceExpr); List *l; foreach (l,$3) { ! c->args = lappend(c->args, lfirst(l)); } $$ = (Node *)c; } Index: src/backend/parser/parse_clause.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/parse_clause.c,v retrieving revision 1.106 diff -c -r1.106 parse_clause.c *** src/backend/parser/parse_clause.c 2003/02/10 04:44:46 1.106 --- src/backend/parser/parse_clause.c 2003/02/12 02:34:11 *************** *** 923,939 **** * Here we must build a COALESCE expression to ensure that * the join output is non-null if either input is. */ ! CaseExpr *c = makeNode(CaseExpr); ! CaseWhen *w = makeNode(CaseWhen); ! NullTest *n = makeNode(NullTest); ! ! n->arg = (Expr *) l_node; ! n->nulltesttype = IS_NOT_NULL; ! w->expr = (Expr *) n; ! w->result = (Expr *) l_node; ! c->casetype = outcoltype; ! c->args = makeList1(w); ! c->defresult = (Expr *) r_node; res_node = (Node *) c; break; } --- 923,931 ---- * Here we must build a COALESCE expression to ensure that * the join output is non-null if either input is. */ ! CoalesceExpr *c = makeNode(CoalesceExpr); ! c->coalescetype = outcoltype; ! c->args = makeList2(l_node, r_node); res_node = (Node *) c; break; } Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/parse_expr.c,v retrieving revision 1.144 diff -c -r1.144 parse_expr.c *** src/backend/parser/parse_expr.c 2003/02/10 04:44:46 1.144 --- src/backend/parser/parse_expr.c 2003/02/12 02:34:14 *************** *** 277,288 **** NodeSetTag(result, T_DistinctExpr); } break; ! case AEXPR_OF: { /* ! * Checking an expression for match to type. ! * Will result in a boolean constant node. */ List *telem; A_Const *n; Oid ltype, --- 277,298 ---- NodeSetTag(result, T_DistinctExpr); } break; ! case AEXPR_NULLIF: { + Node *lexpr = transformExpr(pstate, a->lexpr); + Node *rexpr = transformExpr(pstate, a->rexpr); + + result = (Node *) make_op(a->name,lexpr,rexpr); + if (((OpExpr *) result)->opresulttype != BOOLOID) + elog(ERROR, "NULLIF requires = operator to yield boolean"); /* ! * We rely on NullIfExpr and OpExpr being same struct */ + NodeSetTag(result, T_NullIfExpr); + } + break; + case AEXPR_OF: + { List *telem; A_Const *n; Oid ltype, *************** *** 504,510 **** --- 514,554 ---- result = (Node *) expr; break; } + case T_CoalesceExpr: + { + CoalesceExpr *c = (CoalesceExpr *) expr; + CoalesceExpr *newc = makeNode(CoalesceExpr); + List *newargs = NIL; + List *newcoercedargs = NIL; + List *typeids = NIL; + List *args; + + foreach(args, c->args) + { + Expr *e = (Expr *) lfirst(args); + Expr *newe; + + newe = (Expr *) transformExpr(pstate,(Node *)e); + newargs = lappend(newargs, newe); + typeids = lappendi(typeids, exprType((Node *) newe)); + } + + newc->coalescetype = select_common_type(typeids, "COALESCE"); + + /* Convert arguments if necessary */ + foreach(args, newargs) + { + Expr *e = (Expr *) lfirst(args); + Expr *newe; + + newe = (Expr *) coerce_to_common_type((Node *) e, newc->coalescetype, "COALESCE"); + newcoercedargs = lappend(newcoercedargs, newe); + } + newc->args = newcoercedargs; + result = (Node *) newc; + break; + } case T_CaseExpr: { CaseExpr *c = (CaseExpr *) expr; *************** *** 1003,1008 **** --- 1047,1058 ---- case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; + case T_CoalesceExpr: + type = ((CoalesceExpr *) expr)->coalescetype; + break; + case T_NullIfExpr: + type = exprType((Node *) lfirst(((NullIfExpr *) expr)->args)); + break; case T_CaseExpr: type = ((CaseExpr *) expr)->casetype; break; *************** *** 1084,1089 **** --- 1134,1166 ---- return ((FieldSelect *) expr)->resulttypmod; case T_RelabelType: return ((RelabelType *) expr)->resulttypmod; + case T_CoalesceExpr: + { + CoalesceExpr *cexpr = (CoalesceExpr *) expr; + Oid coalescetype = cexpr->coalescetype; + int32 typmod; + List *arg; + + typmod = exprTypmod((Node *)lfirst(cexpr->args)); + + foreach(arg, cexpr->args) + { + Expr *e = (Expr *) lfirst(arg); + if (exprType((Node *)e) != coalescetype) + return -1; + if (exprTypmod((Node *)e) != typmod) + return -1; + } + + return typmod; + } + break; + case T_NullIfExpr: + { + NullIfExpr *nexpr = (NullIfExpr *) expr; + return exprTypmod((Node *)lfirst(nexpr->args)); + } + break; case T_CaseExpr: { /* Index: src/backend/parser/parse_target.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/parse_target.c,v retrieving revision 1.95 diff -c -r1.95 parse_target.c *** src/backend/parser/parse_target.c 2003/02/09 06:56:28 1.95 --- src/backend/parser/parse_target.c 2003/02/12 02:34:15 *************** *** 501,506 **** --- 501,514 ---- } } break; + case T_CoalesceExpr: + *name = "coalesce"; + return 1; + break; + case T_NullIfExpr: + *name = "nullif"; + return 1; + break; case T_CaseExpr: strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, name); Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.134 diff -c -r1.134 ruleutils.c *** src/backend/utils/adt/ruleutils.c 2003/02/03 21:15:44 1.134 --- src/backend/utils/adt/ruleutils.c 2003/02/12 02:34:27 *************** *** 2176,2181 **** --- 2176,2220 ---- } } break; + case T_CoalesceExpr: + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + List *arg; + char *sep; + + + appendStringInfo(buf, "COALESCE ("); + sep = ""; + foreach(arg, coalesceexpr->args) + { + Expr *e = (Expr *)lfirst(arg); + + appendStringInfo(buf, sep); + get_rule_expr((Node *) e, context, true); + sep = ", "; + } + appendStringInfo(buf, ")"); + } + break; + + case T_NullIfExpr: + { + NullIfExpr *nullifexpr = (NullIfExpr *) node; + List *arg; + char *sep; + + appendStringInfo(buf, "NULLIF ("); + sep = ""; + foreach(arg, nullifexpr->args) + { + Expr *e = (Expr *)lfirst(arg); + appendStringInfo(buf, sep); + get_rule_expr((Node *)e, context, true); + sep = ", "; + } + appendStringInfo(buf, ")"); + } + break; case T_CaseExpr: { Index: src/include/nodes/execnodes.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/execnodes.h,v retrieving revision 1.94 diff -c -r1.94 execnodes.h *** src/include/nodes/execnodes.h 2003/02/09 00:30:39 1.94 --- src/include/nodes/execnodes.h 2003/02/12 02:34:30 *************** *** 441,448 **** /* ---------------- * FuncExprState node * ! * Although named for FuncExpr, this is also used for OpExpr and DistinctExpr ! * nodes; be careful to check what xprstate.expr is actually pointing at! * ---------------- */ typedef struct FuncExprState --- 441,449 ---- /* ---------------- * FuncExprState node * ! * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr, ! * and NullIf nodes; be careful to check what xprstate.expr is actually ! * pointing at! * ---------------- */ typedef struct FuncExprState *************** *** 516,521 **** --- 517,532 ---- AttrNumber *keyColIdx; /* control data for hash tables */ FmgrInfo *eqfunctions; /* comparison functions for hash tables */ } SubPlanState; + + /* ---------------- + * CoalesceExprState node + * ---------------- + */ + typedef struct CoalesceExprState + { + ExprState xprstate; + List *args; /* the arguments */ + } CoalesceExprState; /* ---------------- * CaseExprState node Index: src/include/nodes/nodes.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/nodes.h,v retrieving revision 1.136 diff -c -r1.136 nodes.h *** src/include/nodes/nodes.h 2003/02/03 21:15:44 1.136 --- src/include/nodes/nodes.h 2003/02/12 02:34:31 *************** *** 110,115 **** --- 110,117 ---- T_SubPlan, T_FieldSelect, T_RelabelType, + T_CoalesceExpr, + T_NullIfExpr, T_CaseExpr, T_CaseWhen, T_NullTest, *************** *** 134,139 **** --- 136,142 ---- T_FuncExprState, T_BoolExprState, T_SubPlanState, + T_CoalesceExprState, T_CaseExprState, T_CaseWhenState, T_CoerceToDomainState, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/parsenodes.h,v retrieving revision 1.229 diff -c -r1.229 parsenodes.h *** src/include/nodes/parsenodes.h 2003/02/10 04:44:47 1.229 --- src/include/nodes/parsenodes.h 2003/02/12 02:34:34 *************** *** 173,178 **** --- 173,179 ---- AEXPR_AND, /* booleans - name field is unused */ AEXPR_OR, AEXPR_NOT, + AEXPR_NULLIF, /* NULLIF - name must be "=" */ AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */ AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */ } A_Expr_Kind; Index: src/include/nodes/primnodes.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/primnodes.h,v retrieving revision 1.79 diff -c -r1.79 primnodes.h *** src/include/nodes/primnodes.h 2003/02/09 00:30:40 1.79 --- src/include/nodes/primnodes.h 2003/02/12 02:34:36 *************** *** 516,521 **** --- 516,536 ---- } RelabelType; /* + * CoalesceExpr - a COALESCE expression + */ + typedef struct CoalesceExpr + { + Expr xpr; + Oid coalescetype; /* type of expression result */ + List *args; /* the arguments */ + } CoalesceExpr; + + /* + * NullIfExpr - a NULLIF expression + */ + typedef OpExpr NullIfExpr; + + /* * CaseExpr - a CASE expression */ typedef struct CaseExpr Index: src/test/regress/expected/case.out =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/case.out,v retrieving revision 1.5 diff -c -r1.5 case.out *** src/test/regress/expected/case.out 2000/09/12 21:07:16 1.5 --- src/test/regress/expected/case.out 2003/02/12 02:34:40 *************** *** 154,185 **** SELECT COALESCE(a.f, b.i, b.j) FROM CASE_TBL a, CASE2_TBL b; ! case ! ------- ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! 1 ! 2 ! 3 ! 2 ! 1 ! -6 (24 rows) SELECT * --- 154,185 ---- SELECT COALESCE(a.f, b.i, b.j) FROM CASE_TBL a, CASE2_TBL b; ! coalesce ! ---------- ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 10.1 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! 20.2 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! -30.3 ! 1 ! 2 ! 3 ! 2 ! 1 ! -6 (24 rows) SELECT *