mirror of
https://github.com/postgres/postgres.git
synced 2025-08-12 15:23:02 +03:00
Rethink the order of expression preprocessing: eval_const_expressions
really ought to run before canonicalize_qual, because it can now produce forms that canonicalize_qual knows how to improve (eg, NOT clauses). Also, because eval_const_expressions already knows about flattening nested ANDs and ORs into N-argument form, the initial flatten_andors pass in canonicalize_qual is now completely redundant and can be removed. This doesn't save a whole lot of code, but the time and palloc traffic eliminated is a useful gain on large expression trees.
This commit is contained in:
@@ -3,12 +3,29 @@
|
||||
* prepqual.c
|
||||
* Routines for preprocessing qualification expressions
|
||||
*
|
||||
*
|
||||
* The parser regards AND and OR as purely binary operators, so a qual like
|
||||
* (A = 1) OR (A = 2) OR (A = 3) ...
|
||||
* will produce a nested parsetree
|
||||
* (OR (A = 1) (OR (A = 2) (OR (A = 3) ...)))
|
||||
* In reality, the optimizer and executor regard AND and OR as N-argument
|
||||
* operators, so this tree can be flattened to
|
||||
* (OR (A = 1) (A = 2) (A = 3) ...)
|
||||
*
|
||||
* Formerly, this module was responsible for doing the initial flattening,
|
||||
* but now we leave it to eval_const_expressions to do that since it has to
|
||||
* make a complete pass over the expression tree anyway. Instead, we just
|
||||
* have to ensure that our manipulations preserve AND/OR flatness.
|
||||
* pull_ands() and pull_ors() are used to maintain flatness of the AND/OR
|
||||
* tree after local transformations that might introduce nested AND/ORs.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.48 2004/12/31 22:00:20 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.49 2005/03/28 00:58:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -21,7 +38,6 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static Node *flatten_andors_mutator(Node *node, void *context);
|
||||
static List *pull_ands(List *andlist);
|
||||
static List *pull_ors(List *orlist);
|
||||
static Expr *find_nots(Expr *qual);
|
||||
@@ -40,6 +56,11 @@ static Expr *process_duplicate_ors(List *orlist);
|
||||
* actual usefulness, and so now the transformation doesn't involve any
|
||||
* notion of reaching a canonical form.
|
||||
*
|
||||
* NOTE: we assume the input has already been through eval_const_expressions
|
||||
* and therefore possesses AND/OR flatness. Formerly this function included
|
||||
* its own flattening logic, but that requires a useless extra pass over the
|
||||
* tree.
|
||||
*
|
||||
* Returns the modified qualification.
|
||||
*/
|
||||
Expr *
|
||||
@@ -51,18 +72,13 @@ canonicalize_qual(Expr *qual)
|
||||
if (qual == NULL)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Flatten AND and OR groups throughout the expression tree.
|
||||
*/
|
||||
newqual = (Expr *) flatten_andors((Node *) qual);
|
||||
|
||||
/*
|
||||
* Push down NOTs. We do this only in the top-level boolean
|
||||
* expression, without examining arguments of operators/functions. The
|
||||
* main reason for doing this is to expose as much top-level AND/OR
|
||||
* structure as we can, so there's no point in descending further.
|
||||
*/
|
||||
newqual = find_nots(newqual);
|
||||
newqual = find_nots(qual);
|
||||
|
||||
/*
|
||||
* Pull up redundant subclauses in OR-of-AND trees. Again, we do this
|
||||
@@ -74,101 +90,6 @@ canonicalize_qual(Expr *qual)
|
||||
}
|
||||
|
||||
|
||||
/*--------------------
|
||||
* The parser regards AND and OR as purely binary operators, so a qual like
|
||||
* (A = 1) OR (A = 2) OR (A = 3) ...
|
||||
* will produce a nested parsetree
|
||||
* (OR (A = 1) (OR (A = 2) (OR (A = 3) ...)))
|
||||
* In reality, the optimizer and executor regard AND and OR as n-argument
|
||||
* operators, so this tree can be flattened to
|
||||
* (OR (A = 1) (A = 2) (A = 3) ...)
|
||||
* which is the responsibility of the routines below.
|
||||
*
|
||||
* flatten_andors() does the basic transformation with no initial assumptions.
|
||||
* pull_ands() and pull_ors() are used to maintain flatness of the AND/OR
|
||||
* tree after local transformations that might introduce nested AND/ORs.
|
||||
*--------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* flatten_andors
|
||||
* Given an expression tree, simplify nested AND/OR clauses into flat
|
||||
* AND/OR clauses with more arguments. The entire tree is processed.
|
||||
*
|
||||
* Returns the rebuilt expr (note original structure is not touched).
|
||||
*
|
||||
* This is exported so that other modules can perform the part of
|
||||
* canonicalize_qual processing that applies to entire trees, rather
|
||||
* than just the top-level boolean expressions.
|
||||
*/
|
||||
Node *
|
||||
flatten_andors(Node *node)
|
||||
{
|
||||
return flatten_andors_mutator(node, NULL);
|
||||
}
|
||||
|
||||
static Node *
|
||||
flatten_andors_mutator(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, BoolExpr))
|
||||
{
|
||||
BoolExpr *bexpr = (BoolExpr *) node;
|
||||
|
||||
if (bexpr->boolop == AND_EXPR)
|
||||
{
|
||||
List *out_list = NIL;
|
||||
ListCell *arg;
|
||||
|
||||
foreach(arg, bexpr->args)
|
||||
{
|
||||
Node *subexpr = flatten_andors((Node *) lfirst(arg));
|
||||
|
||||
/*
|
||||
* Note: we can destructively concat the subexpression's
|
||||
* arglist because we know the recursive invocation of
|
||||
* flatten_andors will have built a new arglist not shared
|
||||
* with any other expr. Otherwise we'd need a list_copy
|
||||
* here.
|
||||
*/
|
||||
if (and_clause(subexpr))
|
||||
out_list = list_concat(out_list,
|
||||
((BoolExpr *) subexpr)->args);
|
||||
else
|
||||
out_list = lappend(out_list, subexpr);
|
||||
}
|
||||
return (Node *) make_andclause(out_list);
|
||||
}
|
||||
if (bexpr->boolop == OR_EXPR)
|
||||
{
|
||||
List *out_list = NIL;
|
||||
ListCell *arg;
|
||||
|
||||
foreach(arg, bexpr->args)
|
||||
{
|
||||
Node *subexpr = flatten_andors((Node *) lfirst(arg));
|
||||
|
||||
/*
|
||||
* Note: we can destructively concat the subexpression's
|
||||
* arglist because we know the recursive invocation of
|
||||
* flatten_andors will have built a new arglist not shared
|
||||
* with any other expr. Otherwise we'd need a list_copy
|
||||
* here.
|
||||
*/
|
||||
if (or_clause(subexpr))
|
||||
out_list = list_concat(out_list,
|
||||
((BoolExpr *) subexpr)->args);
|
||||
else
|
||||
out_list = lappend(out_list, subexpr);
|
||||
}
|
||||
return (Node *) make_orclause(out_list);
|
||||
}
|
||||
/* else it's a NOT clause, fall through */
|
||||
}
|
||||
return expression_tree_mutator(node, flatten_andors_mutator, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* pull_ands
|
||||
* Recursively flatten nested AND clauses into a single and-clause list.
|
||||
|
Reference in New Issue
Block a user