Skip to content

Commit 5590be0

Browse files
committed
Preserve AND/OR flatness during eval_const_expressions(). This seems a
useful improvement in any case, and it keeps the new logic for restrictinfo structures happy. Per report from Kris Jurka.
1 parent 1c15812 commit 5590be0

File tree

1 file changed

+154
-84
lines changed

1 file changed

+154
-84
lines changed

src/backend/optimizer/util/clauses.c

+154-84
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.161 2004/01/10 18:13:53 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.162 2004/01/12 20:48:15 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -61,6 +61,10 @@ static bool contain_mutable_functions_walker(Node *node, void *context);
6161
static bool contain_volatile_functions_walker(Node *node, void *context);
6262
static bool contain_nonstrict_functions_walker(Node *node, void *context);
6363
static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
64+
static List *simplify_or_arguments(List *args,
65+
bool *haveNull, bool *forceTrue);
66+
static List *simplify_and_arguments(List *args,
67+
bool *haveNull, bool *forceFalse);
6468
static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
6569
bool allow_inline, List *active_fns);
6670
static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
@@ -249,6 +253,9 @@ make_andclause(List *andclauses)
249253
* Variant of make_andclause for ANDing two qual conditions together.
250254
* Qual conditions have the property that a NULL nodetree is interpreted
251255
* as 'true'.
256+
*
257+
* NB: this makes no attempt to preserve AND/OR flatness; so it should not
258+
* be used on a qual that has already been run through prepqual.c.
252259
*/
253260
Node *
254261
make_and_qual(Node *qual1, Node *qual2)
@@ -1210,7 +1217,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
12101217
{
12111218
BoolExpr *expr = (BoolExpr *) node;
12121219
List *args;
1213-
Const *const_input;
12141220

12151221
/*
12161222
* Reduce constants in the BoolExpr's arguments. We know args is
@@ -1225,124 +1231,66 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
12251231
{
12261232
case OR_EXPR:
12271233
{
1228-
/*----------
1229-
* OR arguments are handled as follows:
1230-
* non constant: keep
1231-
* FALSE: drop (does not affect result)
1232-
* TRUE: force result to TRUE
1233-
* NULL: keep only one
1234-
* We keep one NULL input because ExecEvalOr returns NULL
1235-
* when no input is TRUE and at least one is NULL.
1236-
*----------
1237-
*/
1238-
FastList newargs;
1239-
List *arg;
1234+
List *newargs;
12401235
bool haveNull = false;
12411236
bool forceTrue = false;
12421237

1243-
FastListInit(&newargs);
1244-
foreach(arg, args)
1245-
{
1246-
if (!IsA(lfirst(arg), Const))
1247-
{
1248-
FastAppend(&newargs, lfirst(arg));
1249-
continue;
1250-
}
1251-
const_input = (Const *) lfirst(arg);
1252-
if (const_input->constisnull)
1253-
haveNull = true;
1254-
else if (DatumGetBool(const_input->constvalue))
1255-
forceTrue = true;
1256-
/* otherwise, we can drop the constant-false input */
1257-
}
1258-
1259-
/*
1260-
* We could return TRUE before falling out of the
1261-
* loop, but this coding method will be easier to
1262-
* adapt if we ever add a notion of non-removable
1263-
* functions. We'd need to check all the inputs for
1264-
* non-removability.
1265-
*/
1238+
newargs = simplify_or_arguments(args,
1239+
&haveNull, &forceTrue);
12661240
if (forceTrue)
12671241
return MAKEBOOLCONST(true, false);
12681242
if (haveNull)
1269-
FastAppend(&newargs, MAKEBOOLCONST(false, true));
1243+
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
12701244
/* If all the inputs are FALSE, result is FALSE */
1271-
if (FastListValue(&newargs) == NIL)
1245+
if (newargs == NIL)
12721246
return MAKEBOOLCONST(false, false);
12731247
/* If only one nonconst-or-NULL input, it's the result */
1274-
if (lnext(FastListValue(&newargs)) == NIL)
1275-
return (Node *) lfirst(FastListValue(&newargs));
1248+
if (lnext(newargs) == NIL)
1249+
return (Node *) lfirst(newargs);
12761250
/* Else we still need an OR node */
1277-
return (Node *) make_orclause(FastListValue(&newargs));
1251+
return (Node *) make_orclause(newargs);
12781252
}
12791253
case AND_EXPR:
12801254
{
1281-
/*----------
1282-
* AND arguments are handled as follows:
1283-
* non constant: keep
1284-
* TRUE: drop (does not affect result)
1285-
* FALSE: force result to FALSE
1286-
* NULL: keep only one
1287-
* We keep one NULL input because ExecEvalAnd returns NULL
1288-
* when no input is FALSE and at least one is NULL.
1289-
*----------
1290-
*/
1291-
FastList newargs;
1292-
List *arg;
1255+
List *newargs;
12931256
bool haveNull = false;
12941257
bool forceFalse = false;
12951258

1296-
FastListInit(&newargs);
1297-
foreach(arg, args)
1298-
{
1299-
if (!IsA(lfirst(arg), Const))
1300-
{
1301-
FastAppend(&newargs, lfirst(arg));
1302-
continue;
1303-
}
1304-
const_input = (Const *) lfirst(arg);
1305-
if (const_input->constisnull)
1306-
haveNull = true;
1307-
else if (!DatumGetBool(const_input->constvalue))
1308-
forceFalse = true;
1309-
/* otherwise, we can drop the constant-true input */
1310-
}
1311-
1312-
/*
1313-
* We could return FALSE before falling out of the
1314-
* loop, but this coding method will be easier to
1315-
* adapt if we ever add a notion of non-removable
1316-
* functions. We'd need to check all the inputs for
1317-
* non-removability.
1318-
*/
1259+
newargs = simplify_and_arguments(args,
1260+
&haveNull, &forceFalse);
13191261
if (forceFalse)
13201262
return MAKEBOOLCONST(false, false);
13211263
if (haveNull)
1322-
FastAppend(&newargs, MAKEBOOLCONST(false, true));
1264+
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
13231265
/* If all the inputs are TRUE, result is TRUE */
1324-
if (FastListValue(&newargs) == NIL)
1266+
if (newargs == NIL)
13251267
return MAKEBOOLCONST(true, false);
13261268
/* If only one nonconst-or-NULL input, it's the result */
1327-
if (lnext(FastListValue(&newargs)) == NIL)
1328-
return (Node *) lfirst(FastListValue(&newargs));
1269+
if (lnext(newargs) == NIL)
1270+
return (Node *) lfirst(newargs);
13291271
/* Else we still need an AND node */
1330-
return (Node *) make_andclause(FastListValue(&newargs));
1272+
return (Node *) make_andclause(newargs);
13311273
}
13321274
case NOT_EXPR:
13331275
Assert(length(args) == 1);
13341276
if (IsA(lfirst(args), Const))
13351277
{
1336-
const_input = (Const *) lfirst(args);
1278+
Const *const_input = (Const *) lfirst(args);
1279+
13371280
/* NOT NULL => NULL */
13381281
if (const_input->constisnull)
13391282
return MAKEBOOLCONST(false, true);
13401283
/* otherwise pretty easy */
13411284
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
13421285
false);
13431286
}
1287+
else if (not_clause((Node *) lfirst(args)))
1288+
{
1289+
/* Cancel NOT/NOT */
1290+
return (Node *) get_notclausearg((Expr *) lfirst(args));
1291+
}
13441292
/* Else we still need a NOT node */
1345-
return (Node *) make_notclause(lfirst(args));
1293+
return (Node *) make_notclause((Expr *) lfirst(args));
13461294
default:
13471295
elog(ERROR, "unrecognized boolop: %d",
13481296
(int) expr->boolop);
@@ -1579,6 +1527,128 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
15791527
(void *) active_fns);
15801528
}
15811529

1530+
/*
1531+
* Subroutine for eval_const_expressions: scan the arguments of an OR clause
1532+
*
1533+
* OR arguments are handled as follows:
1534+
* non constant: keep
1535+
* FALSE: drop (does not affect result)
1536+
* TRUE: force result to TRUE
1537+
* NULL: keep only one
1538+
* We must keep one NULL input because ExecEvalOr returns NULL when no input
1539+
* is TRUE and at least one is NULL.
1540+
*
1541+
* This is split out as a subroutine so that we can recurse to fold sub-ORs
1542+
* into the upper OR clause, thereby preserving AND/OR flatness.
1543+
*
1544+
* The output arguments *haveNull and *forceTrue must be initialized FALSE
1545+
* by the caller. They will be set TRUE if a null constant or true constant,
1546+
* respectively, is detected anywhere in the argument list.
1547+
*/
1548+
static List *
1549+
simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue)
1550+
{
1551+
List *newargs = NIL;
1552+
List *larg;
1553+
1554+
foreach(larg, args)
1555+
{
1556+
Node *arg = (Node *) lfirst(larg);
1557+
1558+
if (IsA(arg, Const))
1559+
{
1560+
Const *const_input = (Const *) arg;
1561+
1562+
if (const_input->constisnull)
1563+
*haveNull = true;
1564+
else if (DatumGetBool(const_input->constvalue))
1565+
{
1566+
*forceTrue = true;
1567+
/*
1568+
* Once we detect a TRUE result we can just exit the loop
1569+
* immediately. However, if we ever add a notion of
1570+
* non-removable functions, we'd need to keep scanning.
1571+
*/
1572+
return NIL;
1573+
}
1574+
/* otherwise, we can drop the constant-false input */
1575+
}
1576+
else if (or_clause(arg))
1577+
{
1578+
newargs = nconc(newargs,
1579+
simplify_or_arguments(((BoolExpr *) arg)->args,
1580+
haveNull, forceTrue));
1581+
}
1582+
else
1583+
{
1584+
newargs = lappend(newargs, arg);
1585+
}
1586+
}
1587+
1588+
return newargs;
1589+
}
1590+
1591+
/*
1592+
* Subroutine for eval_const_expressions: scan the arguments of an AND clause
1593+
*
1594+
* AND arguments are handled as follows:
1595+
* non constant: keep
1596+
* TRUE: drop (does not affect result)
1597+
* FALSE: force result to FALSE
1598+
* NULL: keep only one
1599+
* We must keep one NULL input because ExecEvalAnd returns NULL when no input
1600+
* is FALSE and at least one is NULL.
1601+
*
1602+
* This is split out as a subroutine so that we can recurse to fold sub-ANDs
1603+
* into the upper AND clause, thereby preserving AND/OR flatness.
1604+
*
1605+
* The output arguments *haveNull and *forceFalse must be initialized FALSE
1606+
* by the caller. They will be set TRUE if a null constant or false constant,
1607+
* respectively, is detected anywhere in the argument list.
1608+
*/
1609+
static List *
1610+
simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
1611+
{
1612+
List *newargs = NIL;
1613+
List *larg;
1614+
1615+
foreach(larg, args)
1616+
{
1617+
Node *arg = (Node *) lfirst(larg);
1618+
1619+
if (IsA(arg, Const))
1620+
{
1621+
Const *const_input = (Const *) arg;
1622+
1623+
if (const_input->constisnull)
1624+
*haveNull = true;
1625+
else if (!DatumGetBool(const_input->constvalue))
1626+
{
1627+
*forceFalse = true;
1628+
/*
1629+
* Once we detect a FALSE result we can just exit the loop
1630+
* immediately. However, if we ever add a notion of
1631+
* non-removable functions, we'd need to keep scanning.
1632+
*/
1633+
return NIL;
1634+
}
1635+
/* otherwise, we can drop the constant-true input */
1636+
}
1637+
else if (and_clause(arg))
1638+
{
1639+
newargs = nconc(newargs,
1640+
simplify_and_arguments(((BoolExpr *) arg)->args,
1641+
haveNull, forceFalse));
1642+
}
1643+
else
1644+
{
1645+
newargs = lappend(newargs, arg);
1646+
}
1647+
}
1648+
1649+
return newargs;
1650+
}
1651+
15821652
/*
15831653
* Subroutine for eval_const_expressions: try to simplify a function call
15841654
* (which might originally have been an operator; we don't care)

0 commit comments

Comments
 (0)