Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b78f626

Browse files
committed
Rework join-removal logic as per recent discussion. In particular this
fixes things so that it works for cases where nested removals are possible. The overhead of the optimization should be significantly less, as well.
1 parent a760893 commit b78f626

File tree

17 files changed

+585
-317
lines changed

17 files changed

+585
-317
lines changed

src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.383 2010/02/16 22:34:43 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.384 2010/03/28 22:59:32 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1478,16 +1478,6 @@ _outUniquePath(StringInfo str, UniquePath *node)
14781478
WRITE_FLOAT_FIELD(rows, "%.0f");
14791479
}
14801480

1481-
static void
1482-
_outNoOpPath(StringInfo str, NoOpPath *node)
1483-
{
1484-
WRITE_NODE_TYPE("NOOPPATH");
1485-
1486-
_outPathInfo(str, (Path *) node);
1487-
1488-
WRITE_NODE_FIELD(subpath);
1489-
}
1490-
14911481
static void
14921482
_outNestPath(StringInfo str, NestPath *node)
14931483
{
@@ -2740,9 +2730,6 @@ _outNode(StringInfo str, void *obj)
27402730
case T_UniquePath:
27412731
_outUniquePath(str, obj);
27422732
break;
2743-
case T_NoOpPath:
2744-
_outNoOpPath(str, obj);
2745-
break;
27462733
case T_NestPath:
27472734
_outNestPath(str, obj);
27482735
break;

src/backend/optimizer/README

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.52 2009/09/29 01:20:34 tgl Exp $
1+
$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.53 2010/03/28 22:59:32 tgl Exp $
22

33
Optimizer
44
=========
@@ -354,7 +354,6 @@ RelOptInfo - a relation or joined relations
354354
NestPath - nested-loop joins
355355
MergePath - merge joins
356356
HashPath - hash joins
357-
NoOpPath - same as its input path (used when a join is removed)
358357

359358
EquivalenceClass - a data structure representing a set of values known equal
360359

src/backend/optimizer/path/allpaths.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.193 2010/02/26 02:00:44 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.194 2010/03/28 22:59:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1396,10 +1396,6 @@ print_path(PlannerInfo *root, Path *path, int indent)
13961396
ptype = "Unique";
13971397
subpath = ((UniquePath *) path)->subpath;
13981398
break;
1399-
case T_NoOpPath:
1400-
ptype = "NoOp";
1401-
subpath = ((NoOpPath *) path)->subpath;
1402-
break;
14031399
case T_NestPath:
14041400
ptype = "NestLoop";
14051401
join = true;

src/backend/optimizer/path/joinpath.c

Lines changed: 1 addition & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.131 2010/03/22 13:57:15 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.132 2010/03/28 22:59:32 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -22,11 +22,6 @@
2222
#include "optimizer/paths.h"
2323

2424

25-
static bool join_is_removable(PlannerInfo *root, RelOptInfo *joinrel,
26-
RelOptInfo *outerrel, RelOptInfo *innerrel,
27-
List *restrictlist, JoinType jointype);
28-
static void generate_outer_only(PlannerInfo *root, RelOptInfo *joinrel,
29-
RelOptInfo *outerrel);
3025
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
3126
RelOptInfo *outerrel, RelOptInfo *innerrel,
3227
List *restrictlist, List *mergeclause_list,
@@ -83,27 +78,11 @@ add_paths_to_joinrel(PlannerInfo *root,
8378
{
8479
List *mergeclause_list = NIL;
8580

86-
/*
87-
* 0. Consider join removal. This is always the most efficient strategy,
88-
* so if it works, there's no need to consider anything further.
89-
*/
90-
if (join_is_removable(root, joinrel, outerrel, innerrel,
91-
restrictlist, jointype))
92-
{
93-
generate_outer_only(root, joinrel, outerrel);
94-
return;
95-
}
96-
9781
/*
9882
* Find potential mergejoin clauses. We can skip this if we are not
9983
* interested in doing a mergejoin. However, mergejoin is currently our
10084
* only way of implementing full outer joins, so override mergejoin
10185
* disable if it's a full join.
102-
*
103-
* Note: do this after join_is_removable(), because this sets the
104-
* outer_is_left flags in the mergejoin clauses, while join_is_removable
105-
* uses those flags for its own purposes. Currently, they set the flags
106-
* the same way anyway, but let's avoid unnecessary entanglement.
10786
*/
10887
if (enable_mergejoin || jointype == JOIN_FULL)
10988
mergeclause_list = select_mergejoin_clauses(root,
@@ -185,188 +164,6 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel,
185164
return false; /* no good for these input relations */
186165
}
187166

188-
/*
189-
* join_is_removable
190-
* Determine whether we need not perform the join at all, because
191-
* it will just duplicate its left input.
192-
*
193-
* This is true for a left join for which the join condition cannot match
194-
* more than one inner-side row. (There are other possibly interesting
195-
* cases, but we don't have the infrastructure to prove them.) We also
196-
* have to check that the inner side doesn't generate any variables needed
197-
* above the join.
198-
*
199-
* Note: there is no need to consider the symmetrical case of duplicating the
200-
* right input, because add_paths_to_joinrel() will be called with each rel
201-
* on the outer side.
202-
*/
203-
static bool
204-
join_is_removable(PlannerInfo *root,
205-
RelOptInfo *joinrel,
206-
RelOptInfo *outerrel,
207-
RelOptInfo *innerrel,
208-
List *restrictlist,
209-
JoinType jointype)
210-
{
211-
List *clause_list = NIL;
212-
ListCell *l;
213-
int attroff;
214-
215-
/*
216-
* Currently, we only know how to remove left joins to a baserel with
217-
* unique indexes. We can check most of these criteria pretty trivially
218-
* to avoid doing useless extra work. But checking whether any of the
219-
* indexes are unique would require iterating over the indexlist, so for
220-
* now we just make sure there are indexes of some sort or other. If none
221-
* of them are unique, join removal will still fail, just slightly later.
222-
*/
223-
if (jointype != JOIN_LEFT ||
224-
innerrel->reloptkind == RELOPT_JOINREL ||
225-
innerrel->rtekind != RTE_RELATION ||
226-
innerrel->indexlist == NIL)
227-
return false;
228-
229-
/*
230-
* We can't remove the join if any inner-rel attributes are used above the
231-
* join.
232-
*
233-
* Note that this test only detects use of inner-rel attributes in higher
234-
* join conditions and the target list. There might be such attributes in
235-
* pushed-down conditions at this join, too. We check that case below.
236-
*
237-
* As a micro-optimization, it seems better to start with max_attr and
238-
* count down rather than starting with min_attr and counting up, on the
239-
* theory that the system attributes are somewhat less likely to be wanted
240-
* and should be tested last.
241-
*/
242-
for (attroff = innerrel->max_attr - innerrel->min_attr;
243-
attroff >= 0;
244-
attroff--)
245-
{
246-
if (!bms_is_subset(innerrel->attr_needed[attroff], joinrel->relids))
247-
return false;
248-
}
249-
250-
/*
251-
* Similarly check that the inner rel doesn't produce any PlaceHolderVars
252-
* that will be used above the join.
253-
*/
254-
foreach(l, root->placeholder_list)
255-
{
256-
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
257-
258-
if (bms_is_subset(phinfo->ph_eval_at, innerrel->relids) &&
259-
!bms_is_subset(phinfo->ph_needed, joinrel->relids))
260-
return false;
261-
}
262-
263-
/*
264-
* Search for mergejoinable clauses that constrain the inner rel against
265-
* either the outer rel or a pseudoconstant. If an operator is
266-
* mergejoinable then it behaves like equality for some btree opclass, so
267-
* it's what we want. The mergejoinability test also eliminates clauses
268-
* containing volatile functions, which we couldn't depend on.
269-
*/
270-
foreach(l, restrictlist)
271-
{
272-
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
273-
274-
/*
275-
* If we find a pushed-down clause, it must have come from above the
276-
* outer join and it must contain references to the inner rel. (If it
277-
* had only outer-rel variables, it'd have been pushed down into the
278-
* outer rel.) Therefore, we can conclude that join removal is unsafe
279-
* without any examination of the clause contents.
280-
*/
281-
if (restrictinfo->is_pushed_down)
282-
return false;
283-
284-
/* Ignore if it's not a mergejoinable clause */
285-
if (!restrictinfo->can_join ||
286-
restrictinfo->mergeopfamilies == NIL)
287-
continue; /* not mergejoinable */
288-
289-
/*
290-
* Check if clause has the form "outer op inner" or "inner op outer".
291-
*/
292-
if (!clause_sides_match_join(restrictinfo, outerrel, innerrel))
293-
continue; /* no good for these input relations */
294-
295-
/* OK, add to list */
296-
clause_list = lappend(clause_list, restrictinfo);
297-
}
298-
299-
/* Now examine the rel's restriction clauses for var = const clauses */
300-
foreach(l, innerrel->baserestrictinfo)
301-
{
302-
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
303-
304-
/*
305-
* Note: can_join won't be set for a restriction clause, but
306-
* mergeopfamilies will be if it has a mergejoinable operator and
307-
* doesn't contain volatile functions.
308-
*/
309-
if (restrictinfo->mergeopfamilies == NIL)
310-
continue; /* not mergejoinable */
311-
312-
/*
313-
* The clause certainly doesn't refer to anything but the given rel.
314-
* If either side is pseudoconstant then we can use it.
315-
*/
316-
if (bms_is_empty(restrictinfo->left_relids))
317-
{
318-
/* righthand side is inner */
319-
restrictinfo->outer_is_left = true;
320-
}
321-
else if (bms_is_empty(restrictinfo->right_relids))
322-
{
323-
/* lefthand side is inner */
324-
restrictinfo->outer_is_left = false;
325-
}
326-
else
327-
continue;
328-
329-
/* OK, add to list */
330-
clause_list = lappend(clause_list, restrictinfo);
331-
}
332-
333-
/* Now examine the indexes to see if we have a matching unique index */
334-
if (relation_has_unique_index_for(root, innerrel, clause_list))
335-
return true;
336-
337-
/*
338-
* Some day it would be nice to check for other methods of establishing
339-
* distinctness.
340-
*/
341-
return false;
342-
}
343-
344-
/*
345-
* generate_outer_only
346-
* Generate "join" paths when we have found the join is removable.
347-
*/
348-
static void
349-
generate_outer_only(PlannerInfo *root, RelOptInfo *joinrel,
350-
RelOptInfo *outerrel)
351-
{
352-
ListCell *lc;
353-
354-
/*
355-
* For the moment, replicate all of the outerrel's paths as join paths.
356-
* Some of them might not really be interesting above the join, if they
357-
* have sort orderings that have no real use except to do a mergejoin for
358-
* the join we've just found we don't need. But distinguishing that case
359-
* probably isn't worth the extra code it would take.
360-
*/
361-
foreach(lc, outerrel->pathlist)
362-
{
363-
Path *outerpath = (Path *) lfirst(lc);
364-
365-
add_path(joinrel, (Path *)
366-
create_noop_path(root, joinrel, outerpath));
367-
}
368-
}
369-
370167
/*
371168
* sort_inner_and_outer
372169
* Create mergejoin join paths by explicitly sorting both the outer and

src/backend/optimizer/plan/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
# Makefile for optimizer/plan
55
#
66
# IDENTIFICATION
7-
# $PostgreSQL: pgsql/src/backend/optimizer/plan/Makefile,v 1.15 2008/02/19 10:30:07 petere Exp $
7+
# $PostgreSQL: pgsql/src/backend/optimizer/plan/Makefile,v 1.16 2010/03/28 22:59:32 tgl Exp $
88
#
99
#-------------------------------------------------------------------------
1010

1111
subdir = src/backend/optimizer/plan
1212
top_builddir = ../../../..
1313
include $(top_builddir)/src/Makefile.global
1414

15-
OBJS = createplan.o initsplan.o planagg.o planmain.o planner.o \
15+
OBJS = analyzejoins.o createplan.o initsplan.o planagg.o planmain.o planner.o \
1616
setrefs.o subselect.o
1717

1818
include $(top_srcdir)/src/backend/common.mk

0 commit comments

Comments
 (0)