Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitac61a04

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 cleanup ExecQual to make it clearer that ExecQual *does* follow the SQL specfor boolean nulls. See '[BUGS] (null) != (null)' thread around 10/26/99for more detail.
1 parent6b99fcf commitac61a04

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 issatisified
17+
*ExecQual- return true/false if qualification issatisfied
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
staticDatumExecEvalVar(Var*variable,ExprContext*econtext,bool*isNull);
7272
staticDatumExecMakeFunctionResult(Node*node,List*arguments,
7373
ExprContext*econtext,bool*isNull,bool*isDone);
74-
staticboolExecQualClause(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
caseSUBPLAN_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-
staticbool
1294-
ExecQualClause(Node*clause,ExprContext*econtext)
1295-
{
1296-
Datumexpr_value;
1297-
boolisNull;
1298-
boolisDone;
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+
Datumexpr_value;
1321+
boolisNull;
1322+
boolisDone;
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+
externvoidExecCheckPerms(CmdTypeop,intresRel,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
SubLinkTypesubLinkType=sublink->subLinkType;
35+
booluseor=sublink->useor;
3236
TupleTableSlot*slot;
33-
List*lst;
34-
Datumresult= (Datum) false;
37+
Datumresult;
3538
boolfound= 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
HeapTupletup=slot->val;
7793
TupleDesctdesc=slot->ttc_tupleDescriptor;
78-
inti=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+
Datumrowresult= (Datum) (useor ? false : true);
95+
boolrownull= false;
96+
intcol=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-
boolisnull;
112+
Datumexpresult;
113+
boolexpnull;
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 theexpression.
131+
* Now we can eval thecombining 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+
elseif (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+
elseif (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+
elseif (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+
elseif (DatumGetInt32(rowresult)!=0)
176+
{
177+
result= (Datum) true;
178+
*isNull= false;
179+
break;/* needn't look at any more rows */
180+
}
181+
}
182+
elseif (subLinkType==ALL_SUBLINK)
183+
{
184+
/* combine across rows per AND semantics */
185+
if (rownull)
186+
*isNull= true;
187+
elseif (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-
elseif (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
returnresult;
@@ -152,7 +219,6 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
152219
*
153220
* ----------------------------------------------------------------
154221
*/
155-
externvoidExecCheckPerms(CmdTypeop,intresRel,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-
externDatumExecSubPlan(SubPlan*node,List*pvar,ExprContext*econtext);
12+
externDatumExecSubPlan(SubPlan*node,List*pvar,ExprContext*econtext,
13+
bool*isNull);
1314
externboolExecInitSubPlan(SubPlan*node,EState*estate,Plan*parent);
1415
externvoidExecReScanSetParamPlan(SubPlan*node,Plan*parent);
1516
externvoidExecSetParamPlan(SubPlan*node);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp