Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ac61a04

Browse files
committed
Fix ExecSubPlan to handle nulls per the SQL spec --- it didn't combine
nulls with non-nulls using proper three-valued boolean logic. Also clean up ExecQual to make it clearer that ExecQual *does* follow the SQL spec for boolean nulls. See '[BUGS] (null) != (null)' thread around 10/26/99 for more detail.
1 parent 6b99fcf commit ac61a04

File tree

3 files changed

+147
-106
lines changed

3 files changed

+147
-106
lines changed

src/backend/executor/execQual.c

Lines changed: 33 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.63 1999/10/08 03:49:55 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.64 1999/11/12 06:39:34 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
1414
/*
1515
* INTERFACE ROUTINES
1616
* ExecEvalExpr - evaluate an expression and return a datum
17-
* ExecQual - return true/false if qualification is satisified
17+
* ExecQual - return true/false if qualification is satisfied
1818
* ExecTargetList - form a new tuple by projecting the given tuple
1919
*
2020
* NOTES
@@ -71,7 +71,6 @@ static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
7171
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
7272
static Datum ExecMakeFunctionResult(Node *node, List *arguments,
7373
ExprContext *econtext, bool *isNull, bool *isDone);
74-
static bool ExecQualClause(Node *clause, ExprContext *econtext);
7574

7675
/*
7776
* ExecEvalArrayRef
@@ -1253,7 +1252,9 @@ ExecEvalExpr(Node *expression,
12531252
retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
12541253
break;
12551254
case SUBPLAN_EXPR:
1256-
retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper, expr->args, econtext);
1255+
retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper,
1256+
expr->args, econtext,
1257+
isNull);
12571258
break;
12581259
default:
12591260
elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType);
@@ -1279,46 +1280,6 @@ ExecEvalExpr(Node *expression,
12791280
* ----------------------------------------------------------------
12801281
*/
12811282

1282-
/* ----------------------------------------------------------------
1283-
* ExecQualClause
1284-
*
1285-
* this is a workhorse for ExecQual. ExecQual has to deal
1286-
* with a list of qualifications, so it passes each qualification
1287-
* in the list to this function one at a time. ExecQualClause
1288-
* returns true when the qualification *fails* and false if
1289-
* the qualification succeeded (meaning we have to test the
1290-
* rest of the qualification)
1291-
* ----------------------------------------------------------------
1292-
*/
1293-
static bool
1294-
ExecQualClause(Node *clause, ExprContext *econtext)
1295-
{
1296-
Datum expr_value;
1297-
bool isNull;
1298-
bool isDone;
1299-
1300-
/* when there is a null clause, consider the qualification to fail */
1301-
if (clause == NULL)
1302-
return true;
1303-
1304-
/*
1305-
* pass isDone, but ignore it. We don't iterate over multiple returns
1306-
* in the qualifications.
1307-
*/
1308-
expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone);
1309-
1310-
/*
1311-
* remember, we return true when the qualification fails;
1312-
* NULL is considered failure.
1313-
*/
1314-
if (isNull)
1315-
return true;
1316-
if (DatumGetInt32(expr_value) == 0)
1317-
return true;
1318-
1319-
return false;
1320-
}
1321-
13221283
/* ----------------------------------------------------------------
13231284
* ExecQual
13241285
*
@@ -1329,7 +1290,7 @@ ExecQualClause(Node *clause, ExprContext *econtext)
13291290
bool
13301291
ExecQual(List *qual, ExprContext *econtext)
13311292
{
1332-
List *clause;
1293+
List *qlist;
13331294

13341295
/*
13351296
* debugging stuff
@@ -1340,25 +1301,38 @@ ExecQual(List *qual, ExprContext *econtext)
13401301

13411302
IncrProcessed();
13421303

1343-
/*
1344-
* return true immediately if no qual
1345-
*/
1346-
if (qual == NIL)
1347-
return true;
1348-
13491304
/*
13501305
* a "qual" is a list of clauses. To evaluate the qual, we evaluate
1351-
* each of the clauses in the list.
1306+
* each of the clauses in the list. (For an empty list, we'll return
1307+
* TRUE.)
13521308
*
1353-
* ExecQualClause returns true when we know the qualification *failed*
1354-
* so we just pass each clause in qual to it until we know the qual
1355-
* failed or there are no more clauses.
1309+
* If any of the clauses return NULL, we treat this as FALSE. This
1310+
* is correct per the SQL spec: if any ANDed conditions are NULL, then
1311+
* the AND result is either FALSE or NULL, and in either case the
1312+
* WHERE condition fails. NOTE: it would NOT be correct to use this
1313+
* simplified logic in a sub-clause; ExecEvalAnd must do the full
1314+
* three-state condition evaluation. We can get away with simpler
1315+
* logic here because we know how the result will be used.
13561316
*/
1357-
1358-
foreach(clause, qual)
1317+
foreach(qlist, qual)
13591318
{
1360-
if (ExecQualClause((Node *) lfirst(clause), econtext))
1361-
return false; /* qual failed, so return false */
1319+
Node *clause = (Node *) lfirst(qlist);
1320+
Datum expr_value;
1321+
bool isNull;
1322+
bool isDone;
1323+
1324+
/* if there is a null clause, consider the qualification to fail */
1325+
if (clause == NULL)
1326+
return false;
1327+
/*
1328+
* pass isDone, but ignore it. We don't iterate over multiple returns
1329+
* in the qualifications.
1330+
*/
1331+
expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone);
1332+
if (isNull)
1333+
return false; /* treat NULL as FALSE */
1334+
if (DatumGetInt32(expr_value) == 0)
1335+
return false;
13621336
}
13631337

13641338
return true;

src/backend/executor/nodeSubplan.c

Lines changed: 112 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,81 +18,99 @@
1818
#include "executor/nodeSubplan.h"
1919
#include "tcop/pquery.h"
2020

21+
/* should be exported by execMain.c */
22+
extern void ExecCheckPerms(CmdType op, int resRel, List *rtable, Query *q);
23+
2124
/* ----------------------------------------------------------------
2225
* ExecSubPlan(node)
2326
*
2427
* ----------------------------------------------------------------
2528
*/
2629
Datum
27-
ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
30+
ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
2831
{
2932
Plan *plan = node->plan;
3033
SubLink *sublink = node->sublink;
3134
SubLinkType subLinkType = sublink->subLinkType;
35+
bool useor = sublink->useor;
3236
TupleTableSlot *slot;
33-
List *lst;
34-
Datum result = (Datum) false;
37+
Datum result;
3538
bool found = false; /* TRUE if got at least one subplan tuple */
39+
List *lst;
3640

37-
if (node->setParam != NULL)
41+
if (node->setParam != NIL)
3842
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
3943

4044
/*
4145
* Set Params of this plan from parent plan correlation Vars
4246
*/
43-
if (node->parParam != NULL)
47+
if (node->parParam != NIL)
4448
{
4549
foreach(lst, node->parParam)
4650
{
4751
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
4852

53+
Assert(pvar != NIL);
4954
prm->value = ExecEvalExpr((Node *) lfirst(pvar),
5055
econtext,
5156
&(prm->isnull), NULL);
5257
pvar = lnext(pvar);
5358
}
5459
plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
5560
}
61+
Assert(pvar == NIL);
5662

5763
ExecReScan(plan, (ExprContext *) NULL, plan);
5864

5965
/*
60-
* For all sublink types except EXPR_SUBLINK, the result type is
61-
* boolean, and we have a fairly clear idea of how to combine multiple
62-
* subitems and deal with NULL values or an empty subplan result.
66+
* For all sublink types except EXPR_SUBLINK, the result is boolean
67+
* as are the results of the combining operators. We combine results
68+
* within a tuple (if there are multiple columns) using OR semantics
69+
* if "useor" is true, AND semantics if not. We then combine results
70+
* across tuples (if the subplan produces more than one) using OR
71+
* semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK. NULL
72+
* results from the combining operators are handled according to the
73+
* usual SQL semantics for OR and AND. The result for no input
74+
* tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK.
6375
*
64-
* For EXPR_SUBLINK, the result type is whatever the combining operator
65-
* returns. We have no way to deal with more than one column in the
66-
* subplan result --- hopefully the parser forbids that. More
67-
* seriously, it's unclear what to do with NULL values or an empty
68-
* subplan result. For now, we error out, but should something else
69-
* happen?
76+
* For EXPR_SUBLINK we require the subplan to produce no more than one
77+
* tuple, else an error is raised. If zero tuples are produced, we
78+
* return NULL. (XXX it would probably be more correct to evaluate
79+
* the combining operator with a NULL input?) Assuming we get a tuple:
80+
* if there is only one column then we just return its result as-is, NULL
81+
* or otherwise. If there is more than one column we combine the results
82+
* per "useor" --- this only makes sense if the combining operators yield
83+
* boolean, and we assume the parser has checked that.
7084
*/
85+
result = (Datum) (subLinkType == ALL_SUBLINK ? true : false);
86+
*isNull = false;
7187

7288
for (slot = ExecProcNode(plan, plan);
7389
!TupIsNull(slot);
7490
slot = ExecProcNode(plan, plan))
7591
{
7692
HeapTuple tup = slot->val;
7793
TupleDesc tdesc = slot->ttc_tupleDescriptor;
78-
int i = 1;
79-
80-
if (subLinkType == EXPR_SUBLINK && found)
81-
{
82-
elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
83-
return (Datum) false;
84-
}
94+
Datum rowresult = (Datum) (useor ? false : true);
95+
bool rownull = false;
96+
int col = 1;
8597

8698
if (subLinkType == EXISTS_SUBLINK)
8799
return (Datum) true;
88100

101+
/* cannot allow multiple input tuples for EXPR sublink */
102+
if (subLinkType == EXPR_SUBLINK && found)
103+
elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
104+
89105
found = true;
90106

107+
/* iterate over combining operators for columns of tuple */
91108
foreach(lst, sublink->oper)
92109
{
93110
Expr *expr = (Expr *) lfirst(lst);
94111
Const *con = lsecond(expr->args);
95-
bool isnull;
112+
Datum expresult;
113+
bool expnull;
96114

97115
/*
98116
* The righthand side of the expression should be either a Const
@@ -107,41 +125,90 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
107125
con = lfirst(((Expr *) con)->args);
108126
Assert(IsA(con, Const));
109127
}
110-
con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
128+
con->constvalue = heap_getattr(tup, col, tdesc,
129+
&(con->constisnull));
111130
/*
112-
* Now we can eval the expression.
131+
* Now we can eval the combining operator for this column.
113132
*/
114-
result = ExecEvalExpr((Node *) expr, econtext, &isnull,
115-
(bool *) NULL);
116-
if (isnull)
133+
expresult = ExecEvalExpr((Node *) expr, econtext, &expnull,
134+
(bool *) NULL);
135+
/*
136+
* Combine the result into the row result as appropriate.
137+
*/
138+
if (col == 1)
117139
{
118-
if (subLinkType == EXPR_SUBLINK)
119-
elog(ERROR, "ExecSubPlan: null value returned by expression subselect");
120-
else
121-
result = (Datum) false;
140+
rowresult = expresult;
141+
rownull = expnull;
122142
}
123-
if (subLinkType != EXPR_SUBLINK)
143+
else if (useor)
124144
{
125-
if ((!(bool) result && !(sublink->useor)) ||
126-
((bool) result && sublink->useor))
127-
break;
145+
/* combine within row per OR semantics */
146+
if (expnull)
147+
rownull = true;
148+
else if (DatumGetInt32(expresult) != 0)
149+
{
150+
rowresult = (Datum) true;
151+
rownull = false;
152+
break; /* needn't look at any more columns */
153+
}
128154
}
129-
i++;
155+
else
156+
{
157+
/* combine within row per AND semantics */
158+
if (expnull)
159+
rownull = true;
160+
else if (DatumGetInt32(expresult) == 0)
161+
{
162+
rowresult = (Datum) false;
163+
rownull = false;
164+
break; /* needn't look at any more columns */
165+
}
166+
}
167+
col++;
130168
}
131169

132-
if (subLinkType == ALL_SUBLINK && !(bool) result)
133-
break;
134-
if (subLinkType == ANY_SUBLINK && (bool) result)
135-
break;
170+
if (subLinkType == ANY_SUBLINK)
171+
{
172+
/* combine across rows per OR semantics */
173+
if (rownull)
174+
*isNull = true;
175+
else if (DatumGetInt32(rowresult) != 0)
176+
{
177+
result = (Datum) true;
178+
*isNull = false;
179+
break; /* needn't look at any more rows */
180+
}
181+
}
182+
else if (subLinkType == ALL_SUBLINK)
183+
{
184+
/* combine across rows per AND semantics */
185+
if (rownull)
186+
*isNull = true;
187+
else if (DatumGetInt32(rowresult) == 0)
188+
{
189+
result = (Datum) false;
190+
*isNull = false;
191+
break; /* needn't look at any more rows */
192+
}
193+
}
194+
else
195+
{
196+
/* must be EXPR_SUBLINK */
197+
result = rowresult;
198+
*isNull = rownull;
199+
}
136200
}
137201

138202
if (!found)
139203
{
140-
/* deal with empty subplan result. Note default result is 'false' */
141-
if (subLinkType == ALL_SUBLINK)
142-
result = (Datum) true;
143-
else if (subLinkType == EXPR_SUBLINK)
144-
elog(ERROR, "ExecSubPlan: no tuples returned by expression subselect");
204+
/* deal with empty subplan result. result/isNull were previously
205+
* initialized correctly for all sublink types except EXPR.
206+
*/
207+
if (subLinkType == EXPR_SUBLINK)
208+
{
209+
result = (Datum) false;
210+
*isNull = true;
211+
}
145212
}
146213

147214
return result;
@@ -152,7 +219,6 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
152219
*
153220
* ----------------------------------------------------------------
154221
*/
155-
extern void ExecCheckPerms(CmdType op, int resRel, List *rtable, Query *q);
156222
bool
157223
ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
158224
{

src/include/executor/nodeSubplan.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
#include "nodes/plannodes.h"
1111

12-
extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext);
12+
extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext,
13+
bool *isNull);
1314
extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
1415
extern void ExecReScanSetParamPlan(SubPlan *node, Plan *parent);
1516
extern void ExecSetParamPlan(SubPlan *node);

0 commit comments

Comments
 (0)