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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy