mirror of
https://github.com/postgres/postgres.git
synced 2025-09-06 13:46:51 +03:00
COALESCE() and NULLIF() are now first-class expressions, not macros
that turn into CASE expressions. They evaluate their arguments at most once. Patch by Kris Jurka, review and (very light) editorializing by me.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.403 2003/02/13 05:25:24 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.404 2003/02/16 02:30:38 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -6650,6 +6650,10 @@ in_expr: select_with_parens
|
||||
* 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
|
||||
{
|
||||
@@ -6661,29 +6665,12 @@ case_expr: CASE case_arg when_clause_list case_default END_P
|
||||
}
|
||||
| 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;
|
||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5);
|
||||
}
|
||||
| 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);
|
||||
}
|
||||
CoalesceExpr *c = makeNode(CoalesceExpr);
|
||||
c->args = $3;
|
||||
$$ = (Node *)c;
|
||||
}
|
||||
;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.109 2003/02/13 20:45:21 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.110 2003/02/16 02:30:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -922,17 +922,10 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
|
||||
* 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);
|
||||
CoalesceExpr *c = makeNode(CoalesceExpr);
|
||||
|
||||
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;
|
||||
c->coalescetype = outcoltype;
|
||||
c->args = makeList2(l_node, r_node);
|
||||
res_node = (Node *) c;
|
||||
break;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.145 2003/02/13 18:29:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.146 2003/02/16 02:30:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -277,6 +277,24 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
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:
|
||||
{
|
||||
/*
|
||||
@@ -615,6 +633,43 @@ transformExpr(ParseState *pstate, 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)
|
||||
{
|
||||
Node *e = (Node *) lfirst(args);
|
||||
Node *newe;
|
||||
|
||||
newe = transformExpr(pstate, e);
|
||||
newargs = lappend(newargs, newe);
|
||||
typeids = lappendo(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(e, newc->coalescetype,
|
||||
"COALESCE");
|
||||
newcoercedargs = lappend(newcoercedargs, newe);
|
||||
}
|
||||
|
||||
newc->args = newcoercedargs;
|
||||
result = (Node *) newc;
|
||||
break;
|
||||
}
|
||||
|
||||
case T_NullTest:
|
||||
{
|
||||
NullTest *n = (NullTest *) expr;
|
||||
@@ -680,6 +735,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
case T_FuncExpr:
|
||||
case T_OpExpr:
|
||||
case T_DistinctExpr:
|
||||
case T_NullIfExpr:
|
||||
case T_BoolExpr:
|
||||
case T_FieldSelect:
|
||||
case T_RelabelType:
|
||||
@@ -1020,6 +1076,12 @@ exprType(Node *expr)
|
||||
case T_CaseWhen:
|
||||
type = exprType((Node *) ((CaseWhen *) expr)->result);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
type = ((CoalesceExpr *) expr)->coalescetype;
|
||||
break;
|
||||
case T_NullIfExpr:
|
||||
type = exprType((Node *) lfirst(((NullIfExpr *) expr)->args));
|
||||
break;
|
||||
case T_NullTest:
|
||||
type = BOOLOID;
|
||||
break;
|
||||
@@ -1126,6 +1188,37 @@ exprTypmod(Node *expr)
|
||||
return typmod;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
/*
|
||||
* If all the alternatives agree on type/typmod, return
|
||||
* that typmod, else use -1
|
||||
*/
|
||||
CoalesceExpr *cexpr = (CoalesceExpr *) expr;
|
||||
Oid coalescetype = cexpr->coalescetype;
|
||||
int32 typmod;
|
||||
List *arg;
|
||||
|
||||
typmod = exprTypmod((Node *) lfirst(cexpr->args));
|
||||
foreach(arg, cexpr->args)
|
||||
{
|
||||
Node *e = (Node *) lfirst(arg);
|
||||
|
||||
if (exprType(e) != coalescetype)
|
||||
return -1;
|
||||
if (exprTypmod(e) != typmod)
|
||||
return -1;
|
||||
}
|
||||
return typmod;
|
||||
}
|
||||
break;
|
||||
case T_NullIfExpr:
|
||||
{
|
||||
NullIfExpr *nexpr = (NullIfExpr *) expr;
|
||||
|
||||
return exprTypmod((Node *) lfirst(nexpr->args));
|
||||
}
|
||||
break;
|
||||
case T_CoerceToDomain:
|
||||
return ((CoerceToDomain *) expr)->resulttypmod;
|
||||
case T_CoerceToDomainValue:
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.97 2003/02/13 05:53:46 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.98 2003/02/16 02:30:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -482,6 +482,14 @@ FigureColnameInternal(Node *node, char **name)
|
||||
case T_FuncCall:
|
||||
*name = strVal(llast(((FuncCall *) node)->funcname));
|
||||
return 2;
|
||||
case T_A_Expr:
|
||||
/* make nullif() act like a regular function */
|
||||
if (((A_Expr *) node)->kind == AEXPR_NULLIF)
|
||||
{
|
||||
*name = "nullif";
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case T_A_Const:
|
||||
if (((A_Const *) node)->typename != NULL)
|
||||
{
|
||||
@@ -510,6 +518,10 @@ FigureColnameInternal(Node *node, char **name)
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
/* make coalesce() act like a regular function */
|
||||
*name = "coalesce";
|
||||
return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user