Commit c607bd69 authored by Tom Lane's avatar Tom Lane

Clean up the usage of canonicalize_qual(): in particular, be consistent

about whether it is applied before or after eval_const_expressions().
I believe there were some corner cases where the system would fail to
recognize that a partial index is applicable because of the previous
inconsistency.  Store normal rather than 'implicit AND' representations
of constraints and index predicates in the catalogs.
initdb forced due to representation change of constraints/predicates.
parent d167fb10
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.256 2003/11/29 19:51:42 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.257 2003/12/28 21:57:36 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1344,10 +1344,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) ...@@ -1344,10 +1344,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
int16 *attNos; int16 *attNos;
/* /*
* Convert condition to a normal boolean expression tree. * Convert condition to an expression tree.
*/ */
expr = stringToNode(ccbin); expr = stringToNode(ccbin);
expr = (Node *) make_ands_explicit((List *) expr);
/* /*
* deparse it * deparse it
...@@ -1651,14 +1650,6 @@ AddRelationRawConstraints(Relation rel, ...@@ -1651,14 +1650,6 @@ AddRelationRawConstraints(Relation rel,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in check constraint"))); errmsg("cannot use aggregate function in check constraint")));
/*
* Constraints are evaluated with execQual, which expects an
* implicit-AND list, so convert expression to implicit-AND form.
* (We could go so far as to convert to CNF, but that's probably
* overkill...)
*/
expr = (Node *) make_ands_implicit((Expr *) expr);
/* /*
* OK, store it. * OK, store it.
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.223 2003/11/29 19:51:43 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.224 2003/12/28 21:57:36 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -397,13 +397,14 @@ UpdateIndexRelation(Oid indexoid, ...@@ -397,13 +397,14 @@ UpdateIndexRelation(Oid indexoid,
exprsDatum = (Datum) 0; exprsDatum = (Datum) 0;
/* /*
* Convert the index predicate (if any) to a text datum * Convert the index predicate (if any) to a text datum. Note we
* convert implicit-AND format to normal explicit-AND for storage.
*/ */
if (indexInfo->ii_Predicate != NIL) if (indexInfo->ii_Predicate != NIL)
{ {
char *predString; char *predString;
predString = nodeToString(indexInfo->ii_Predicate); predString = nodeToString(make_ands_explicit(indexInfo->ii_Predicate));
predDatum = DirectFunctionCall1(textin, predDatum = DirectFunctionCall1(textin,
CStringGetDatum(predString)); CStringGetDatum(predString));
pfree(predString); pfree(predString);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.116 2003/11/29 19:51:47 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.117 2003/12/28 21:57:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
/* non-export function prototypes */ /* non-export function prototypes */
static void CheckPredicate(List *predList); static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
List *attList, List *attList,
Oid relId, Oid relId,
...@@ -80,7 +80,6 @@ DefineIndex(RangeVar *heapRelation, ...@@ -80,7 +80,6 @@ DefineIndex(RangeVar *heapRelation,
Form_pg_am accessMethodForm; Form_pg_am accessMethodForm;
IndexInfo *indexInfo; IndexInfo *indexInfo;
int numberOfAttributes; int numberOfAttributes;
List *cnfPred = NIL;
/* /*
* count attributes in index * count attributes in index
...@@ -172,16 +171,10 @@ DefineIndex(RangeVar *heapRelation, ...@@ -172,16 +171,10 @@ DefineIndex(RangeVar *heapRelation,
} }
/* /*
* Convert the partial-index predicate from parsetree form to an * Validate predicate, if given
* implicit-AND qual expression, for easier evaluation at runtime.
* While we are at it, we reduce it to a canonical (CNF or DNF) form
* to simplify the task of proving implications.
*/ */
if (predicate) if (predicate)
{ CheckPredicate(predicate);
cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
CheckPredicate(cnfPred);
}
/* /*
* Check that all of the attributes in a primary key are marked as not * Check that all of the attributes in a primary key are marked as not
...@@ -237,13 +230,13 @@ DefineIndex(RangeVar *heapRelation, ...@@ -237,13 +230,13 @@ DefineIndex(RangeVar *heapRelation,
/* /*
* Prepare arguments for index_create, primarily an IndexInfo * Prepare arguments for index_create, primarily an IndexInfo
* structure * structure. Note that ii_Predicate must be in implicit-AND format.
*/ */
indexInfo = makeNode(IndexInfo); indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = numberOfAttributes; indexInfo->ii_NumIndexAttrs = numberOfAttributes;
indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_Expressions = NIL; /* for now */
indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = cnfPred; indexInfo->ii_Predicate = make_ands_implicit(predicate);
indexInfo->ii_PredicateState = NIL; indexInfo->ii_PredicateState = NIL;
indexInfo->ii_Unique = unique; indexInfo->ii_Unique = unique;
...@@ -268,7 +261,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -268,7 +261,7 @@ DefineIndex(RangeVar *heapRelation,
/* /*
* CheckPredicate * CheckPredicate
* Checks that the given list of partial-index predicates is valid. * Checks that the given partial-index predicate is valid.
* *
* This used to also constrain the form of the predicate to forms that * This used to also constrain the form of the predicate to forms that
* indxpath.c could do something with. However, that seems overly * indxpath.c could do something with. However, that seems overly
...@@ -278,18 +271,18 @@ DefineIndex(RangeVar *heapRelation, ...@@ -278,18 +271,18 @@ DefineIndex(RangeVar *heapRelation,
* (except ones requiring a plan), and let indxpath.c fend for itself. * (except ones requiring a plan), and let indxpath.c fend for itself.
*/ */
static void static void
CheckPredicate(List *predList) CheckPredicate(Expr *predicate)
{ {
/* /*
* We don't currently support generation of an actual query plan for a * We don't currently support generation of an actual query plan for a
* predicate, only simple scalar expressions; hence these * predicate, only simple scalar expressions; hence these
* restrictions. * restrictions.
*/ */
if (contain_subplans((Node *) predList)) if (contain_subplans((Node *) predicate))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use subquery in index predicate"))); errmsg("cannot use subquery in index predicate")));
if (contain_agg_clause((Node *) predList)) if (contain_agg_clause((Node *) predicate))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate in index predicate"))); errmsg("cannot use aggregate in index predicate")));
...@@ -298,7 +291,7 @@ CheckPredicate(List *predList) ...@@ -298,7 +291,7 @@ CheckPredicate(List *predList)
* A predicate using mutable functions is probably wrong, for the same * A predicate using mutable functions is probably wrong, for the same
* reasons that we don't allow an index expression to use one. * reasons that we don't allow an index expression to use one.
*/ */
if (contain_mutable_functions((Node *) predList)) if (contain_mutable_functions((Node *) predicate))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index predicate must be marked IMMUTABLE"))); errmsg("functions in index predicate must be marked IMMUTABLE")));
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.223 2003/12/01 22:07:58 momjian Exp $ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.224 2003/12/28 21:57:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "executor/execdefs.h" #include "executor/execdefs.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h" #include "optimizer/var.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/acl.h" #include "utils/acl.h"
...@@ -1658,7 +1659,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ...@@ -1658,7 +1659,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
(List **) palloc(ncheck * sizeof(List *)); (List **) palloc(ncheck * sizeof(List *));
for (i = 0; i < ncheck; i++) for (i = 0; i < ncheck; i++)
{ {
qual = (List *) stringToNode(check[i].ccbin); /* ExecQual wants implicit-AND form */
qual = make_ands_implicit(stringToNode(check[i].ccbin));
resultRelInfo->ri_ConstraintExprs[i] = (List *) resultRelInfo->ri_ConstraintExprs[i] = (List *)
ExecPrepareExpr((Expr *) qual, estate); ExecPrepareExpr((Expr *) qual, estate);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.162 2003/11/29 19:51:50 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.163 2003/12/28 21:57:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -377,23 +377,13 @@ preprocess_expression(Query *parse, Node *expr, int kind) ...@@ -377,23 +377,13 @@ preprocess_expression(Query *parse, Node *expr, int kind)
expr = flatten_join_alias_vars(parse, expr); expr = flatten_join_alias_vars(parse, expr);
/* /*
* Simplify constant expressions. * If it's a qual or havingQual, canonicalize it. It seems most useful
* * to do this before applying eval_const_expressions, since the latter
* Note that at this point quals have not yet been converted to * can optimize flattened AND/ORs better than unflattened ones.
* implicit-AND form, so we can apply eval_const_expressions directly.
*/
expr = eval_const_expressions(expr);
/*
* If it's a qual or havingQual, canonicalize it, and convert it to
* implicit-AND format.
*
* XXX Is there any value in re-applying eval_const_expressions after
* canonicalize_qual?
*/ */
if (kind == EXPRKIND_QUAL) if (kind == EXPRKIND_QUAL)
{ {
expr = (Node *) canonicalize_qual((Expr *) expr, true); expr = (Node *) canonicalize_qual((Expr *) expr);
#ifdef OPTIMIZER_DEBUG #ifdef OPTIMIZER_DEBUG
printf("After canonicalize_qual()\n"); printf("After canonicalize_qual()\n");
...@@ -401,6 +391,19 @@ preprocess_expression(Query *parse, Node *expr, int kind) ...@@ -401,6 +391,19 @@ preprocess_expression(Query *parse, Node *expr, int kind)
#endif #endif
} }
/*
* Simplify constant expressions.
*/
expr = eval_const_expressions(expr);
/*
* If it's a qual or havingQual, convert it to implicit-AND format.
* (We don't want to do this before eval_const_expressions, since the
* latter would be unable to simplify a top-level AND correctly.)
*/
if (kind == EXPRKIND_QUAL)
expr = (Node *) make_ands_implicit((Expr *) expr);
/* Expand SubLinks to SubPlans */ /* Expand SubLinks to SubPlans */
if (parse->hasSubLinks) if (parse->hasSubLinks)
expr = SS_process_sublinks(expr, (kind == EXPRKIND_QUAL)); expr = SS_process_sublinks(expr, (kind == EXPRKIND_QUAL));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.39 2003/11/29 19:51:51 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.40 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static Expr *flatten_andors(Expr *qual); static Node *flatten_andors_mutator(Node *node, void *context);
static void flatten_andors_and_walker(FastList *out_list, List *andlist); static void flatten_andors_and_walker(FastList *out_list, List *andlist);
static void flatten_andors_or_walker(FastList *out_list, List *orlist); static void flatten_andors_or_walker(FastList *out_list, List *orlist);
static List *pull_ands(List *andlist); static List *pull_ands(List *andlist);
...@@ -61,8 +61,7 @@ static void count_bool_nodes(Expr *qual, double *nodes, ...@@ -61,8 +61,7 @@ static void count_bool_nodes(Expr *qual, double *nodes,
* *
* canonicalize_qual() does "smart" conversion to either CNF or DNF, per * canonicalize_qual() does "smart" conversion to either CNF or DNF, per
* the above considerations, while cnfify() and dnfify() simply perform * the above considerations, while cnfify() and dnfify() simply perform
* the demanded transformation. The latter two may become dead code * the demanded transformation. The latter two are presently dead code.
* eventually.
*****************************************************************************/ *****************************************************************************/
...@@ -72,11 +71,6 @@ static void count_bool_nodes(Expr *qual, double *nodes, ...@@ -72,11 +71,6 @@ static void count_bool_nodes(Expr *qual, double *nodes,
* *
* Returns the modified qualification. * Returns the modified qualification.
* *
* If 'removeAndFlag' is true then it removes explicit AND at the top level,
* producing a list of implicitly-ANDed conditions. Otherwise, a regular
* boolean expression is returned. Since most callers pass 'true', we
* prefer to declare the result as List *, not Expr *.
*
* XXX This code could be much smarter, at the cost of also being slower, * XXX This code could be much smarter, at the cost of also being slower,
* if we tried to compute selectivities and/or see whether there are * if we tried to compute selectivities and/or see whether there are
* actually indexes to support an indexscan implementation of a DNF qual. * actually indexes to support an indexscan implementation of a DNF qual.
...@@ -85,8 +79,8 @@ static void count_bool_nodes(Expr *qual, double *nodes, ...@@ -85,8 +79,8 @@ static void count_bool_nodes(Expr *qual, double *nodes,
* implement. For now, though, we just try to avoid doing anything * implement. For now, though, we just try to avoid doing anything
* quite as stupid as unconditionally converting to CNF was... * quite as stupid as unconditionally converting to CNF was...
*/ */
List * Expr *
canonicalize_qual(Expr *qual, bool removeAndFlag) canonicalize_qual(Expr *qual)
{ {
Expr *newqual; Expr *newqual;
double nodes, double nodes,
...@@ -95,23 +89,23 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) ...@@ -95,23 +89,23 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
bool cnfok, bool cnfok,
dnfok; dnfok;
/* Quick exit for empty qual */
if (qual == NULL) if (qual == NULL)
return NIL; return NULL;
/* /*
* Flatten AND and OR groups throughout the tree. This improvement is * Flatten AND and OR groups throughout the tree. This improvement is
* always worthwhile, so do it unconditionally. * always worthwhile, so do it unconditionally.
*/ */
qual = flatten_andors(qual); newqual = (Expr *) flatten_andors((Node *) qual);
/* /*
* Push down NOTs. We do this only in the top-level boolean * Push down NOTs. We do this only in the top-level boolean
* expression, without examining arguments of operators/functions. * expression, without examining arguments of operators/functions.
* Even so, it might not be a win if we are unable to find negators * The main reason for doing this is to expose as much top-level AND/OR
* for all the operators involved; perhaps we should compare before- * structure as we can, so there's no point in descending further.
* and-after tree sizes?
*/ */
newqual = find_nots(qual); newqual = find_nots(newqual);
/* /*
* Choose whether to convert to CNF, or DNF, or leave well enough * Choose whether to convert to CNF, or DNF, or leave well enough
...@@ -175,13 +169,10 @@ canonicalize_qual(Expr *qual, bool removeAndFlag) ...@@ -175,13 +169,10 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
newqual = qual_cleanup(find_ands(newqual)); newqual = qual_cleanup(find_ands(newqual));
} }
/* Convert to implicit-AND list if requested */ return newqual;
if (removeAndFlag)
newqual = (Expr *) make_ands_implicit(newqual);
return (List *) newqual;
} }
#ifdef NOT_USED
/* /*
* cnfify * cnfify
* Convert a qualification to conjunctive normal form by applying * Convert a qualification to conjunctive normal form by applying
...@@ -223,6 +214,7 @@ cnfify(Expr *qual, bool removeAndFlag) ...@@ -223,6 +214,7 @@ cnfify(Expr *qual, bool removeAndFlag)
return (List *) newqual; return (List *) newqual;
} }
#endif
#ifdef NOT_USED #ifdef NOT_USED
/* /*
...@@ -281,50 +273,46 @@ dnfify(Expr *qual) ...@@ -281,50 +273,46 @@ dnfify(Expr *qual)
/*-------------------- /*--------------------
* flatten_andors * flatten_andors
* Given a qualification, simplify nested AND/OR clauses into flat * Given an expression tree, simplify nested AND/OR clauses into flat
* AND/OR clauses with more arguments. * AND/OR clauses with more arguments. The entire tree is processed.
* *
* Returns the rebuilt expr (note original list structure is not touched). * Returns the rebuilt expr (note original structure is not touched).
*-------------------- *--------------------
*/ */
static Expr * Node *
flatten_andors(Expr *qual) flatten_andors(Node *node)
{ {
if (qual == NULL) return flatten_andors_mutator(node, NULL);
return NULL; }
if (and_clause((Node *) qual)) static Node *
flatten_andors_mutator(Node *node, void *context)
{
if (node == NULL)
return NULL;
if (IsA(node, BoolExpr))
{ {
FastList out_list; BoolExpr *bexpr = (BoolExpr *) node;
FastListInit(&out_list); if (bexpr->boolop == AND_EXPR)
flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args); {
return make_andclause(FastListValue(&out_list)); FastList out_list;
}
else if (or_clause((Node *) qual))
{
FastList out_list;
FastListInit(&out_list); FastListInit(&out_list);
flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args); flatten_andors_and_walker(&out_list, bexpr->args);
return make_orclause(FastListValue(&out_list)); return (Node *) make_andclause(FastListValue(&out_list));
} }
else if (not_clause((Node *) qual)) if (bexpr->boolop == OR_EXPR)
return make_notclause(flatten_andors(get_notclausearg(qual))); {
else if (is_opclause(qual)) FastList out_list;
{
OpExpr *opexpr = (OpExpr *) qual; FastListInit(&out_list);
Expr *left = (Expr *) get_leftop(qual); flatten_andors_or_walker(&out_list, bexpr->args);
Expr *right = (Expr *) get_rightop(qual); return (Node *) make_orclause(FastListValue(&out_list));
}
return make_opclause(opexpr->opno, /* else it's a NOT clause, fall through */
opexpr->opresulttype,
opexpr->opretset,
flatten_andors(left),
flatten_andors(right));
} }
else return expression_tree_mutator(node, flatten_andors_mutator, context);
return qual;
} }
static void static void
...@@ -334,9 +322,9 @@ flatten_andors_and_walker(FastList *out_list, List *andlist) ...@@ -334,9 +322,9 @@ flatten_andors_and_walker(FastList *out_list, List *andlist)
foreach(arg, andlist) foreach(arg, andlist)
{ {
Expr *subexpr = (Expr *) lfirst(arg); Node *subexpr = (Node *) lfirst(arg);
if (and_clause((Node *) subexpr)) if (and_clause(subexpr))
flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args); flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args);
else else
FastAppend(out_list, flatten_andors(subexpr)); FastAppend(out_list, flatten_andors(subexpr));
...@@ -350,9 +338,9 @@ flatten_andors_or_walker(FastList *out_list, List *orlist) ...@@ -350,9 +338,9 @@ flatten_andors_or_walker(FastList *out_list, List *orlist)
foreach(arg, orlist) foreach(arg, orlist)
{ {
Expr *subexpr = (Expr *) lfirst(arg); Node *subexpr = (Node *) lfirst(arg);
if (or_clause((Node *) subexpr)) if (or_clause(subexpr))
flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args); flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args);
else else
FastAppend(out_list, flatten_andors(subexpr)); FastAppend(out_list, flatten_andors(subexpr));
...@@ -383,9 +371,9 @@ pull_ands_walker(FastList *out_list, List *andlist) ...@@ -383,9 +371,9 @@ pull_ands_walker(FastList *out_list, List *andlist)
foreach(arg, andlist) foreach(arg, andlist)
{ {
Expr *subexpr = (Expr *) lfirst(arg); Node *subexpr = (Node *) lfirst(arg);
if (and_clause((Node *) subexpr)) if (and_clause(subexpr))
pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args); pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args);
else else
FastAppend(out_list, subexpr); FastAppend(out_list, subexpr);
...@@ -416,9 +404,9 @@ pull_ors_walker(FastList *out_list, List *orlist) ...@@ -416,9 +404,9 @@ pull_ors_walker(FastList *out_list, List *orlist)
foreach(arg, orlist) foreach(arg, orlist)
{ {
Expr *subexpr = (Expr *) lfirst(arg); Node *subexpr = (Node *) lfirst(arg);
if (or_clause((Node *) subexpr)) if (or_clause(subexpr))
pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args); pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args);
else else
FastAppend(out_list, subexpr); FastAppend(out_list, subexpr);
...@@ -427,9 +415,10 @@ pull_ors_walker(FastList *out_list, List *orlist) ...@@ -427,9 +415,10 @@ pull_ors_walker(FastList *out_list, List *orlist)
/* /*
* find_nots * find_nots
* Traverse the qualification, looking for 'NOT's to take care of. * Traverse the qualification, looking for NOTs to take care of.
* For 'NOT' clauses, apply push_not() to try to push down the 'NOT'. * For NOT clauses, apply push_nots() to try to push down the NOT.
* For all other clause types, simply recurse. * For AND and OR clause types, simply recurse. Otherwise stop
* recursing (we do not worry about structure below the top AND/OR tree).
* *
* Returns the modified qualification. AND/OR flatness is preserved. * Returns the modified qualification. AND/OR flatness is preserved.
*/ */
...@@ -439,21 +428,6 @@ find_nots(Expr *qual) ...@@ -439,21 +428,6 @@ find_nots(Expr *qual)
if (qual == NULL) if (qual == NULL)
return NULL; return NULL;
#ifdef NOT_USED
/* recursing into operator expressions is probably not worth it. */
if (is_opclause(qual))
{
OpExpr *opexpr = (OpExpr *) qual;
Expr *left = (Expr *) get_leftop(qual);
Expr *right = (Expr *) get_rightop(qual);
return make_opclause(opexpr->opno,
opexpr->opresulttype,
opexpr->opretset,
find_nots(left),
find_nots(right));
}
#endif
if (and_clause((Node *) qual)) if (and_clause((Node *) qual))
{ {
FastList t_list; FastList t_list;
...@@ -482,7 +456,7 @@ find_nots(Expr *qual) ...@@ -482,7 +456,7 @@ find_nots(Expr *qual)
/* /*
* push_nots * push_nots
* Push down a 'NOT' as far as possible. * Push down a NOT as far as possible.
* *
* Input is an expression to be negated (e.g., the argument of a NOT clause). * Input is an expression to be negated (e.g., the argument of a NOT clause).
* Returns a new qual equivalent to the negation of the given qual. * Returns a new qual equivalent to the negation of the given qual.
...@@ -496,7 +470,7 @@ push_nots(Expr *qual) ...@@ -496,7 +470,7 @@ push_nots(Expr *qual)
/* /*
* Negate an operator clause if possible: ("NOT" (< A B)) => (> A B) * Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)
* Otherwise, retain the clause as it is (the 'not' can't be pushed * Otherwise, retain the clause as it is (the NOT can't be pushed
* down any farther). * down any farther).
*/ */
if (is_opclause(qual)) if (is_opclause(qual))
...@@ -543,16 +517,15 @@ push_nots(Expr *qual) ...@@ -543,16 +517,15 @@ push_nots(Expr *qual)
else if (not_clause((Node *) qual)) else if (not_clause((Node *) qual))
{ {
/* /*
* Another 'not' cancels this 'not', so eliminate the 'not' and * Another NOT cancels this NOT, so eliminate the NOT and
* stop negating this branch. But search the subexpression for * stop negating this branch.
* more 'not's to simplify.
*/ */
return find_nots(get_notclausearg(qual)); return get_notclausearg(qual);
} }
else else
{ {
/* /*
* We don't know how to negate anything else, place a 'not' at * We don't know how to negate anything else, place a NOT at
* this level. * this level.
*/ */
return make_notclause(qual); return make_notclause(qual);
...@@ -561,12 +534,13 @@ push_nots(Expr *qual) ...@@ -561,12 +534,13 @@ push_nots(Expr *qual)
/* /*
* find_ors * find_ors
* Given a qualification tree with the 'not's pushed down, convert it * Given a qualification tree with the NOTs pushed down, convert it
* to a tree in CNF by repeatedly applying the rule: * to a tree in CNF by repeatedly applying the rule:
* ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C)) * ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
* *
* Note that 'or' clauses will always be turned into 'and' clauses * Note that 'or' clauses will always be turned into 'and' clauses
* if they contain any 'and' subclauses. * if they contain any 'and' subclauses. Also, we do not descend
* below the top-level AND/OR structure.
* *
* Returns the modified qualification. AND/OR flatness is preserved. * Returns the modified qualification. AND/OR flatness is preserved.
*/ */
...@@ -576,7 +550,6 @@ find_ors(Expr *qual) ...@@ -576,7 +550,6 @@ find_ors(Expr *qual)
if (qual == NULL) if (qual == NULL)
return NULL; return NULL;
/* We used to recurse into opclauses here, but I see no reason to... */
if (and_clause((Node *) qual)) if (and_clause((Node *) qual))
{ {
List *andlist = NIL; List *andlist = NIL;
...@@ -595,8 +568,6 @@ find_ors(Expr *qual) ...@@ -595,8 +568,6 @@ find_ors(Expr *qual)
orlist = lappend(orlist, find_ors(lfirst(temp))); orlist = lappend(orlist, find_ors(lfirst(temp)));
return or_normalize(pull_ors(orlist)); return or_normalize(pull_ors(orlist));
} }
else if (not_clause((Node *) qual))
return make_notclause(find_ors(get_notclausearg(qual)));
else else
return qual; return qual;
} }
...@@ -688,12 +659,13 @@ or_normalize(List *orlist) ...@@ -688,12 +659,13 @@ or_normalize(List *orlist)
/* /*
* find_ands * find_ands
* Given a qualification tree with the 'not's pushed down, convert it * Given a qualification tree with the NOTs pushed down, convert it
* to a tree in DNF by repeatedly applying the rule: * to a tree in DNF by repeatedly applying the rule:
* ("AND" A ("OR" B C)) => ("OR" ("AND" A B) ("AND" A C)) * ("AND" A ("OR" B C)) => ("OR" ("AND" A B) ("AND" A C))
* *
* Note that 'and' clauses will always be turned into 'or' clauses * Note that 'and' clauses will always be turned into 'or' clauses
* if they contain any 'or' subclauses. * if they contain any 'or' subclauses. Also, we do not descend
* below the top-level AND/OR structure.
* *
* Returns the modified qualification. AND/OR flatness is preserved. * Returns the modified qualification. AND/OR flatness is preserved.
*/ */
...@@ -703,7 +675,6 @@ find_ands(Expr *qual) ...@@ -703,7 +675,6 @@ find_ands(Expr *qual)
if (qual == NULL) if (qual == NULL)
return NULL; return NULL;
/* We used to recurse into opclauses here, but I see no reason to... */
if (or_clause((Node *) qual)) if (or_clause((Node *) qual))
{ {
List *orlist = NIL; List *orlist = NIL;
...@@ -722,8 +693,6 @@ find_ands(Expr *qual) ...@@ -722,8 +693,6 @@ find_ands(Expr *qual)
andlist = lappend(andlist, find_ands(lfirst(temp))); andlist = lappend(andlist, find_ands(lfirst(temp)));
return and_normalize(pull_ands(andlist)); return and_normalize(pull_ands(andlist));
} }
else if (not_clause((Node *) qual))
return make_notclause(find_ands(get_notclausearg(qual)));
else else
return qual; return qual;
} }
...@@ -860,8 +829,6 @@ qual_cleanup(Expr *qual) ...@@ -860,8 +829,6 @@ qual_cleanup(Expr *qual)
else else
return lfirst(orlist); return lfirst(orlist);
} }
else if (not_clause((Node *) qual))
return make_notclause(qual_cleanup(get_notclausearg(qual)));
else else
return qual; return qual;
} }
...@@ -889,14 +856,14 @@ remove_duplicates(List *list) ...@@ -889,14 +856,14 @@ remove_duplicates(List *list)
/* /*
* count_bool_nodes * count_bool_nodes
* Support for heuristics in canonicalize_qual(): count the * Support for heuristics in canonicalize_qual(): count the
* number of nodes that are inputs to the top level AND/OR/NOT * number of nodes that are inputs to the top level AND/OR
* part of a qual tree, and estimate how many nodes will appear * part of a qual tree, and estimate how many nodes will appear
* in the CNF'ified or DNF'ified equivalent of the expression. * in the CNF'ified or DNF'ified equivalent of the expression.
* *
* This is just an approximate calculation; it doesn't deal with NOTs * This is just an approximate calculation; it cannot detect possible
* very well, and of course it cannot detect possible simplifications * simplifications from eliminating duplicate subclauses. The idea is just to
* from eliminating duplicate subclauses. The idea is just to cheaply * cheaply determine whether CNF will be markedly worse than DNF or vice
* determine whether CNF will be markedly worse than DNF or vice versa. * versa.
* *
* The counts/estimates are represented as doubles to avoid risk of overflow. * The counts/estimates are represented as doubles to avoid risk of overflow.
*/ */
...@@ -953,11 +920,6 @@ count_bool_nodes(Expr *qual, ...@@ -953,11 +920,6 @@ count_bool_nodes(Expr *qual,
if (*cnfnodes < *dnfnodes) if (*cnfnodes < *dnfnodes)
*cnfnodes = *dnfnodes; *cnfnodes = *dnfnodes;
} }
else if (not_clause((Node *) qual))
{
count_bool_nodes(get_notclausearg(qual),
nodes, cnfnodes, dnfnodes);
}
else if (contain_subplans((Node *) qual)) else if (contain_subplans((Node *) qual))
{ {
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.156 2003/12/09 01:56:20 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.157 2003/12/28 21:57:37 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -261,8 +261,8 @@ make_and_qual(Node *qual1, Node *qual2) ...@@ -261,8 +261,8 @@ make_and_qual(Node *qual1, Node *qual2)
} }
/* /*
* Sometimes (such as in the result of canonicalize_qual or the input of * Sometimes (such as in the input of ExecQual), we use lists of expression
* ExecQual), we use lists of expression nodes with implicit AND semantics. * nodes with implicit AND semantics.
* *
* These functions convert between an AND-semantics expression list and the * These functions convert between an AND-semantics expression list and the
* ordinary representation of a boolean expression. * ordinary representation of a boolean expression.
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.160 2003/11/29 19:51:59 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.161 2003/12/28 21:57:37 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -810,13 +810,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) ...@@ -810,13 +810,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
node = (Node *) stringToNode(predString); node = (Node *) stringToNode(predString);
pfree(predString); pfree(predString);
/*
* If top level is a List, assume it is an implicit-AND
* structure, and convert to explicit AND. This is needed for
* partial index predicates.
*/
if (node && IsA(node, List))
node = (Node *) make_ands_explicit((List *) node);
/* Deparse */ /* Deparse */
str = deparse_expression_pretty(node, context, false, false, str = deparse_expression_pretty(node, context, false, false,
prettyFlags, 0); prettyFlags, 0);
...@@ -1060,14 +1053,6 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags) ...@@ -1060,14 +1053,6 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags)
conbin = DatumGetCString(DirectFunctionCall1(textout, val)); conbin = DatumGetCString(DirectFunctionCall1(textout, val));
expr = stringToNode(conbin); expr = stringToNode(conbin);
/*
* If top level is a List, assume it is an implicit-AND
* structure, and convert to explicit AND. This is needed
* for partial index predicates.
*/
if (expr && IsA(expr, List))
expr = (Node *) make_ands_explicit((List *) expr);
/* Set up deparsing context for Var nodes in constraint */ /* Set up deparsing context for Var nodes in constraint */
if (conForm->conrelid != InvalidOid) if (conForm->conrelid != InvalidOid)
{ {
...@@ -1212,14 +1197,6 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags) ...@@ -1212,14 +1197,6 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
/* Convert expression to node tree */ /* Convert expression to node tree */
node = (Node *) stringToNode(exprstr); node = (Node *) stringToNode(exprstr);
/*
* If top level is a List, assume it is an implicit-AND structure, and
* convert to explicit AND. This is needed for partial index
* predicates.
*/
if (node && IsA(node, List))
node = (Node *) make_ands_explicit((List *) node);
/* Deparse */ /* Deparse */
context = deparse_context_for(relname, relid); context = deparse_context_for(relname, relid);
str = deparse_expression_pretty(node, context, false, false, str = deparse_expression_pretty(node, context, false, false,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.150 2003/12/07 04:14:10 joe Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.151 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3902,12 +3902,12 @@ genericcostestimate(Query *root, RelOptInfo *rel, ...@@ -3902,12 +3902,12 @@ genericcostestimate(Query *root, RelOptInfo *rel,
* If the index is partial, AND the index predicate with the * If the index is partial, AND the index predicate with the
* explicitly given indexquals to produce a more accurate idea of the * explicitly given indexquals to produce a more accurate idea of the
* index restriction. This may produce redundant clauses, which we * index restriction. This may produce redundant clauses, which we
* hope that cnfify and clauselist_selectivity will deal with * hope that canonicalize_qual and clauselist_selectivity will deal with
* intelligently. * intelligently.
* *
* Note that index->indpred and indexQuals are both in implicit-AND form * Note that index->indpred and indexQuals are both in implicit-AND form
* to start with, which we have to make explicit to hand to * to start with, which we have to make explicit to hand to
* canonicalize_qual, and then we get back implicit-AND form again. * canonicalize_qual, and then we convert back to implicit-AND form.
*/ */
if (index->indpred != NIL) if (index->indpred != NIL)
{ {
...@@ -3915,7 +3915,8 @@ genericcostestimate(Query *root, RelOptInfo *rel, ...@@ -3915,7 +3915,8 @@ genericcostestimate(Query *root, RelOptInfo *rel,
andedQuals = make_ands_explicit(nconc(listCopy(index->indpred), andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
indexQuals)); indexQuals));
selectivityQuals = canonicalize_qual(andedQuals, true); andedQuals = canonicalize_qual(andedQuals);
selectivityQuals = make_ands_implicit(andedQuals);
} }
/* Estimate the fraction of main-table tuples that will be visited */ /* Estimate the fraction of main-table tuples that will be visited */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.193 2003/11/29 19:52:00 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.194 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/catcache.h" #include "utils/catcache.h"
...@@ -2770,11 +2771,13 @@ RelationGetIndexExpressions(Relation relation) ...@@ -2770,11 +2771,13 @@ RelationGetIndexExpressions(Relation relation)
pfree(exprsString); pfree(exprsString);
/* /*
* Run the expressions through eval_const_expressions. This is not * Run the expressions through flatten_andors and eval_const_expressions.
* just an optimization, but is necessary, because the planner will be * This is not just an optimization, but is necessary, because the planner
* comparing them to const-folded qual clauses, and may fail to detect * will be comparing them to similarly-processed qual clauses, and may
* valid matches without this. * fail to detect valid matches without this.
*/ */
result = (List *) flatten_andors((Node *) result);
result = (List *) eval_const_expressions((Node *) result); result = (List *) eval_const_expressions((Node *) result);
/* May as well fix opfuncids too */ /* May as well fix opfuncids too */
...@@ -2791,7 +2794,8 @@ RelationGetIndexExpressions(Relation relation) ...@@ -2791,7 +2794,8 @@ RelationGetIndexExpressions(Relation relation)
/* /*
* RelationGetIndexPredicate -- get the index predicate for an index * RelationGetIndexPredicate -- get the index predicate for an index
* *
* We cache the result of transforming pg_index.indpred into a node tree. * We cache the result of transforming pg_index.indpred into an implicit-AND
* node tree (suitable for ExecQual).
* If the rel is not an index or has no predicate, we return NIL. * If the rel is not an index or has no predicate, we return NIL.
* Otherwise, the returned tree is copied into the caller's memory context. * Otherwise, the returned tree is copied into the caller's memory context.
* (We don't want to return a pointer to the relcache copy, since it could * (We don't want to return a pointer to the relcache copy, since it could
...@@ -2835,13 +2839,18 @@ RelationGetIndexPredicate(Relation relation) ...@@ -2835,13 +2839,18 @@ RelationGetIndexPredicate(Relation relation)
pfree(predString); pfree(predString);
/* /*
* Run the expression through eval_const_expressions. This is not * Run the expression through canonicalize_qual and eval_const_expressions.
* just an optimization, but is necessary, because the planner will be * This is not just an optimization, but is necessary, because the planner
* comparing it to const-folded qual clauses, and may fail to detect * will be comparing it to similarly-processed qual clauses, and may fail
* valid matches without this. * to detect valid matches without this.
*/ */
result = (List *) canonicalize_qual((Expr *) result);
result = (List *) eval_const_expressions((Node *) result); result = (List *) eval_const_expressions((Node *) result);
/* Also convert to implicit-AND format */
result = make_ands_implicit((Expr *) result);
/* May as well fix opfuncids too */ /* May as well fix opfuncids too */
fix_opfuncids((Node *) result); fix_opfuncids((Node *) result);
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.213 2003/12/03 18:53:52 joe Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.214 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200312031 #define CATALOG_VERSION_NO 200312281
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.86 2003/11/29 22:41:06 pgsql Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.87 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -245,7 +245,8 @@ typedef struct RelOptInfo ...@@ -245,7 +245,8 @@ typedef struct RelOptInfo
* zero entry, rather than looking at ncolumns. * zero entry, rather than looking at ncolumns.
* *
* The indexprs and indpred expressions have been run through * The indexprs and indpred expressions have been run through
* eval_const_expressions() for ease of matching to WHERE clauses. * prepqual.c and eval_const_expressions() for ease of matching to
* WHERE clauses. indpred is in implicit-AND form.
*/ */
typedef struct IndexOptInfo typedef struct IndexOptInfo
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.42 2003/11/29 22:41:07 pgsql Exp $ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.43 2003/12/28 21:57:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,8 +36,8 @@ extern Relids get_relids_for_join(Query *parse, int joinrelid); ...@@ -36,8 +36,8 @@ extern Relids get_relids_for_join(Query *parse, int joinrelid);
/* /*
* prototypes for prepqual.c * prototypes for prepqual.c
*/ */
extern List *canonicalize_qual(Expr *qual, bool removeAndFlag); extern Expr *canonicalize_qual(Expr *qual);
extern List *cnfify(Expr *qual, bool removeAndFlag); extern Node *flatten_andors(Node *node);
/* /*
* prototypes for preptlist.c * prototypes for preptlist.c
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment