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

Commit43d32d3

Browse files
committed
First cut at doing something reasonable with OR-of-ANDs WHERE
conditions. There are some pretty bogus heuristics in prepqual.c thattry to decide whether to output CNF or DNF format; they need to be replaced,likely. Right now the code is probably too willing to choose DNF form,which might hurt performance in some cases that used to work OK.But at least we have a foundation to build on.
1 parentb705fa3 commit43d32d3

File tree

5 files changed

+269
-21
lines changed

5 files changed

+269
-21
lines changed

‎src/backend/optimizer/path/indxpath.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.70 1999/08/21 03:49:00 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.71 1999/09/13 00:17:19 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -48,6 +48,9 @@ static void match_index_orclauses(RelOptInfo *rel, RelOptInfo *index, int indexk
4848
intxclass,List*restrictinfo_list);
4949
staticList*match_index_orclause(RelOptInfo*rel,RelOptInfo*index,intindexkey,
5050
intxclass,List*or_clauses,List*other_matching_indices);
51+
staticboolmatch_or_subclause_to_indexkey(RelOptInfo*rel,RelOptInfo*index,
52+
intindexkey,intxclass,
53+
Expr*clause);
5154
staticList*group_clauses_by_indexkey(RelOptInfo*rel,RelOptInfo*index,
5255
int*indexkeys,Oid*classes,List*restrictinfo_list);
5356
staticList*group_clauses_by_ikey_for_joins(RelOptInfo*rel,RelOptInfo*index,
@@ -327,8 +330,8 @@ match_index_orclause(RelOptInfo *rel,
327330
{
328331
Expr*clause=lfirst(clist);
329332

330-
if (match_clause_to_indexkey(rel,index,indexkey,xclass,
331-
clause, false))
333+
if (match_or_subclause_to_indexkey(rel,index,indexkey,xclass,
334+
clause))
332335
{
333336
/* OK to add this index to sublist for this subclause */
334337
lfirst(matching_indices)=lcons(index,
@@ -341,6 +344,38 @@ match_index_orclause(RelOptInfo *rel,
341344
returnindex_list;
342345
}
343346

347+
/*
348+
* See if a subclause of an OR clause matches an index.
349+
*
350+
* We accept the subclause if it is an operator clause that matches the
351+
* index, or if it is an AND clause all of whose members are operators
352+
* that match the index. (XXX Would accepting a single match be useful?)
353+
*/
354+
staticbool
355+
match_or_subclause_to_indexkey(RelOptInfo*rel,
356+
RelOptInfo*index,
357+
intindexkey,
358+
intxclass,
359+
Expr*clause)
360+
{
361+
if (and_clause((Node*)clause))
362+
{
363+
List*item;
364+
365+
foreach(item,clause->args)
366+
{
367+
if (!match_clause_to_indexkey(rel,index,indexkey,xclass,
368+
lfirst(item), false))
369+
return false;
370+
}
371+
return true;
372+
}
373+
else
374+
returnmatch_clause_to_indexkey(rel,index,indexkey,xclass,
375+
clause, false);
376+
}
377+
378+
344379
/****************************************************************************
345380
*---- ROUTINES TO CHECK RESTRICTIONS ----
346381
****************************************************************************/
@@ -559,7 +594,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
559594
*
560595
* Returns true if the clause can be used with this index key.
561596
*
562-
* NOTE: returns false if clause is an or_clause; that's handled elsewhere.
597+
* NOTE: returns false if clause is an OR or AND clause; to the extent
598+
* we cope with those at all, it is done by higher-level routines.
563599
*/
564600
staticbool
565601
match_clause_to_indexkey(RelOptInfo*rel,

‎src/backend/optimizer/plan/planmain.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.43 1999/08/22 23:56:45 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.44 1999/09/13 00:17:25 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -75,9 +75,9 @@ query_planner(Query *root,
7575
if (root->hasSubLinks)
7676
qual= (List*)SS_process_sublinks((Node*)qual);
7777

78-
qual=cnfify((Expr*)qual, true);
78+
qual=canonicalize_qual((Expr*)qual, true);
7979
#ifdefOPTIMIZER_DEBUG
80-
printf("Aftercnfify()\n");
80+
printf("Aftercanonicalize_qual()\n");
8181
pprint(qual);
8282
#endif
8383

‎src/backend/optimizer/plan/planner.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.66 1999/08/26 05:07:41 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.67 1999/09/13 00:17:25 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -324,8 +324,9 @@ union_planner(Query *parse)
324324
elog(ERROR,"Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
325325
}
326326

327-
/* convert the havingQual to conjunctive normal form (cnf) */
328-
parse->havingQual= (Node*)cnfify((Expr*)parse->havingQual, true);
327+
/* convert the havingQual to implicit-AND normal form */
328+
parse->havingQual= (Node*)
329+
canonicalize_qual((Expr*)parse->havingQual, true);
329330

330331
/*
331332
* Require an aggregate function to appear in each clause of the

‎src/backend/optimizer/prep/prepqual.c

Lines changed: 220 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.19 1999/09/12 18:08:17 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.20 1999/09/13 00:17:13 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -29,6 +29,9 @@ static Expr *find_ors(Expr *qual);
2929
staticExpr*or_normalize(List*orlist);
3030
staticExpr*find_ands(Expr*qual);
3131
staticExpr*and_normalize(List*andlist);
32+
staticExpr*qual_cleanup(Expr*qual);
33+
staticList*remove_duplicates(List*list);
34+
staticintcount_bool_nodes(Expr*qual);
3235

3336
/*****************************************************************************
3437
*
@@ -37,20 +40,124 @@ static Expr *and_normalize(List *andlist);
3740
*These routines convert an arbitrary boolean expression into
3841
*conjunctive normal form or disjunctive normal form.
3942
*
40-
*The result of these routines differs from a "true" CNF/DNF in that
41-
*we do not bother to detect common subexpressions; e.g., ("AND" A A)
42-
*does not get simplified to A. Testing for identical subexpressions
43-
*is a waste of time if the query is written intelligently, and it
44-
*takes an unreasonable amount of time if there are many subexpressions
45-
*(since it's roughly O(N^2) in the number of subexpressions).
43+
*Normalization is only carried out in the top AND/OR/NOT portion
44+
*of the given tree; we do not attempt to normalize boolean expressions
45+
*that may appear as arguments of operators or functions in the tree.
4646
*
47-
*Because of that restriction, it would be unwise to apply dnfify()
48-
*to the result of cnfify() or vice versa. Instead apply both to
49-
*the original user-written qual expression.
47+
*Query qualifications (WHERE clauses) are ordinarily transformed into
48+
*CNF, ie, AND-of-ORs form, because then the optimizer can use any one
49+
*of the independent AND clauses as a filtering qualification. However,
50+
*quals that are naturally expressed as OR-of-ANDs can suffer an
51+
*exponential growth in size in this transformation, so we also consider
52+
*converting to DNF (OR-of-ANDs), and we may also leave well enough alone
53+
*if both transforms cause unreasonable growth. The OR-of-ANDs format
54+
*is useful for indexscan implementation, so we prefer that format when
55+
*there is just one relation involved.
5056
*
57+
*canonicalize_qual() does "smart" conversion to either CNF or DNF, per
58+
*the above considerations, while cnfify() and dnfify() simply perform
59+
*the demanded transformation. The latter two may become dead code
60+
*eventually.
5161
*****************************************************************************/
5262

5363

64+
/*
65+
* canonicalize_qual
66+
* Convert a qualification to the most useful normalized form.
67+
*
68+
* Returns the modified qualification.
69+
*
70+
* If 'removeAndFlag' is true then it removes explicit AND at the top level,
71+
* producing a list of implicitly-ANDed conditions. Otherwise, a regular
72+
* boolean expression is returned. Since most callers pass 'true', we
73+
* prefer to declare the result as List *, not Expr *.
74+
*
75+
* XXX This code could be much smarter, at the cost of also being slower,
76+
* if we tried to compute selectivities and/or see whether there are
77+
* actually indexes to support an indexscan implementation of a DNF qual.
78+
* We could even try converting the CNF clauses that mention a single
79+
* relation into a single DNF clause to see if that looks cheaper to
80+
* implement. For now, though, we just try to avoid doing anything
81+
* quite as stupid as unconditionally converting to CNF was...
82+
*/
83+
List*
84+
canonicalize_qual(Expr*qual,boolremoveAndFlag)
85+
{
86+
Expr*newqual,
87+
*cnfqual,
88+
*dnfqual;
89+
intqualcnt,
90+
cnfcnt,
91+
dnfcnt;
92+
93+
if (qual==NULL)
94+
returnNIL;
95+
96+
/* Flatten AND and OR groups throughout the tree.
97+
* This improvement is always worthwhile, so do it unconditionally.
98+
*/
99+
qual=flatten_andors(qual);
100+
/* Push down NOTs. We do this only in the top-level boolean
101+
* expression, without examining arguments of operators/functions.
102+
* Even so, it might not be a win if we are unable to find negators
103+
* for all the operators involved; so we keep the flattened-but-not-
104+
* NOT-pushed qual as the reference point for comparsions.
105+
*/
106+
newqual=find_nots(qual);
107+
/*
108+
* Generate both CNF and DNF forms from newqual.
109+
*/
110+
/* Normalize into conjunctive normal form, and clean up the result. */
111+
cnfqual=qual_cleanup(find_ors(newqual));
112+
/* Likewise for DNF */
113+
dnfqual=qual_cleanup(find_ands(newqual));
114+
115+
/*
116+
* Now, choose whether to return qual, cnfqual, or dnfqual.
117+
*
118+
* First heuristic is to forget about either CNF or DNF if it shows
119+
* unreasonable growth compared to the original form of the qual,
120+
* where we define "unreasonable" a tad arbitrarily as 4x more
121+
* operators.
122+
*/
123+
qualcnt=count_bool_nodes(qual);
124+
cnfcnt=count_bool_nodes(cnfqual);
125+
dnfcnt=count_bool_nodes(dnfqual);
126+
if (cnfcnt >=4*qualcnt)
127+
cnfqual=NULL;/* mark CNF not usable */
128+
if (dnfcnt >=4*qualcnt)
129+
dnfqual=NULL;/* mark DNF not usable */
130+
131+
/*
132+
* Second heuristic is to prefer DNF if only one relation is mentioned
133+
* and it is smaller than the CNF representation.
134+
*/
135+
if (dnfqual&&dnfcnt<cnfcnt&&NumRelids((Node*)dnfqual)==1)
136+
cnfqual=NULL;
137+
138+
/*
139+
* Otherwise, we prefer CNF.
140+
*
141+
* XXX obviously, these rules could be improved upon.
142+
*/
143+
144+
/* pick preferred survivor */
145+
if (cnfqual)
146+
newqual=cnfqual;
147+
elseif (dnfqual)
148+
newqual=dnfqual;
149+
else
150+
newqual=qual;
151+
152+
/* Convert to implicit-AND list if requested */
153+
if (removeAndFlag)
154+
{
155+
newqual= (Expr*)make_ands_implicit(newqual);
156+
}
157+
158+
return (List*)newqual;
159+
}
160+
54161
/*
55162
* cnfify
56163
* Convert a qualification to conjunctive normal form by applying
@@ -81,6 +188,8 @@ cnfify(Expr *qual, bool removeAndFlag)
81188
newqual=find_nots(newqual);
82189
/* Normalize into conjunctive normal form. */
83190
newqual=find_ors(newqual);
191+
/* Clean up the result. */
192+
newqual=qual_cleanup(newqual);
84193

85194
if (removeAndFlag)
86195
{
@@ -118,6 +227,8 @@ dnfify(Expr *qual)
118227
newqual=find_nots(newqual);
119228
/* Normalize into disjunctive normal form. */
120229
newqual=find_ands(newqual);
230+
/* Clean up the result. */
231+
newqual=qual_cleanup(newqual);
121232

122233
returnnewqual;
123234
}
@@ -641,3 +752,102 @@ and_normalize(List *andlist)
641752
/* pull_ors is needed in case any sub-and_normalize succeeded */
642753
returnmake_orclause(pull_ors(orclauses));
643754
}
755+
756+
/*
757+
* qual_cleanup
758+
* Fix up a qualification by removing duplicate entries (which could be
759+
* created during normalization, if identical subexpressions from different
760+
* parts of the tree are brought together). Also, check for AND and OR
761+
* clauses with only one remaining subexpression, and simplify.
762+
*
763+
* Returns the modified qualification.
764+
*/
765+
staticExpr*
766+
qual_cleanup(Expr*qual)
767+
{
768+
if (qual==NULL)
769+
returnNULL;
770+
771+
if (and_clause((Node*)qual))
772+
{
773+
List*andlist=NIL;
774+
List*temp;
775+
776+
foreach(temp,qual->args)
777+
andlist=lappend(andlist,qual_cleanup(lfirst(temp)));
778+
779+
andlist=remove_duplicates(pull_ands(andlist));
780+
781+
if (length(andlist)>1)
782+
returnmake_andclause(andlist);
783+
else
784+
returnlfirst(andlist);
785+
}
786+
elseif (or_clause((Node*)qual))
787+
{
788+
List*orlist=NIL;
789+
List*temp;
790+
791+
foreach(temp,qual->args)
792+
orlist=lappend(orlist,qual_cleanup(lfirst(temp)));
793+
794+
orlist=remove_duplicates(pull_ors(orlist));
795+
796+
if (length(orlist)>1)
797+
returnmake_orclause(orlist);
798+
else
799+
returnlfirst(orlist);
800+
}
801+
elseif (not_clause((Node*)qual))
802+
returnmake_notclause(qual_cleanup(get_notclausearg(qual)));
803+
else
804+
returnqual;
805+
}
806+
807+
/*
808+
* remove_duplicates
809+
*/
810+
staticList*
811+
remove_duplicates(List*list)
812+
{
813+
List*result=NIL;
814+
List*i;
815+
816+
if (length(list) <=1)
817+
returnlist;
818+
819+
foreach(i,list)
820+
{
821+
if (!member(lfirst(i),result))
822+
result=lappend(result,lfirst(i));
823+
}
824+
returnresult;
825+
}
826+
827+
/*
828+
* count_bool_nodes
829+
*Support for heuristics in canonicalize_qual(): count the
830+
*number of nodes in the top level AND/OR/NOT part of a qual tree
831+
*/
832+
staticint
833+
count_bool_nodes(Expr*qual)
834+
{
835+
if (qual==NULL)
836+
return0;
837+
838+
if (and_clause((Node*)qual)||
839+
or_clause((Node*)qual))
840+
{
841+
intsum=1;/* for the and/or itself */
842+
List*temp;
843+
844+
foreach(temp,qual->args)
845+
sum+=count_bool_nodes(lfirst(temp));
846+
847+
returnsum;
848+
}
849+
elseif (not_clause((Node*)qual))
850+
returncount_bool_nodes(get_notclausearg(qual))+1;
851+
else
852+
return1;/* anything else counts 1 for my purposes */
853+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp