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

Commit 4f06c68

Browse files
committed
Put back planner's ability to cache the results of mergejoinscansel(),
which I had removed in the first cut of the EquivalenceClass rewrite to simplify that patch a little. But it's still important --- in a four-way join problem mergejoinscansel() was eating about 40% of the planning time according to gprof. Also, improve the EquivalenceClass code to re-use join RestrictInfos rather than generating fresh ones for each join considered. This saves some memory space but more importantly improves the effectiveness of caching planning info in RestrictInfos.
1 parent 45e0736 commit 4f06c68

File tree

9 files changed

+285
-65
lines changed

9 files changed

+285
-65
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.362 2007/01/20 20:45:38 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.363 2007/01/22 20:00:39 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -1326,6 +1326,10 @@ _copyRestrictInfo(RestrictInfo *from)
13261326
/* EquivalenceClasses are never copied, so shallow-copy the pointers */
13271327
COPY_SCALAR_FIELD(left_ec);
13281328
COPY_SCALAR_FIELD(right_ec);
1329+
COPY_SCALAR_FIELD(left_em);
1330+
COPY_SCALAR_FIELD(right_em);
1331+
/* MergeScanSelCache isn't a Node, so hard to copy; just reset cache */
1332+
newnode->scansel_cache = NIL;
13291333
COPY_SCALAR_FIELD(outer_is_left);
13301334
COPY_SCALAR_FIELD(hashjoinoperator);
13311335
COPY_SCALAR_FIELD(left_bucketsize);

src/backend/nodes/outfuncs.c

Lines changed: 4 additions & 1 deletion
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.294 2007/01/20 20:45:38 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.295 2007/01/22 20:00:39 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -1304,6 +1304,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node)
13041304
WRITE_NODE_FIELD(ec_opfamilies);
13051305
WRITE_NODE_FIELD(ec_members);
13061306
WRITE_NODE_FIELD(ec_sources);
1307+
WRITE_NODE_FIELD(ec_derives);
13071308
WRITE_BITMAPSET_FIELD(ec_relids);
13081309
WRITE_BOOL_FIELD(ec_has_const);
13091310
WRITE_BOOL_FIELD(ec_has_volatile);
@@ -1354,6 +1355,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
13541355
WRITE_NODE_FIELD(mergeopfamilies);
13551356
WRITE_NODE_FIELD(left_ec);
13561357
WRITE_NODE_FIELD(right_ec);
1358+
WRITE_NODE_FIELD(left_em);
1359+
WRITE_NODE_FIELD(right_em);
13571360
WRITE_BOOL_FIELD(outer_is_left);
13581361
WRITE_OID_FIELD(hashjoinoperator);
13591362
}

src/backend/optimizer/path/costsize.c

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
* Portions Copyright (c) 1994, Regents of the University of California
5555
*
5656
* IDENTIFICATION
57-
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.176 2007/01/22 01:35:20 tgl Exp $
57+
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.177 2007/01/22 20:00:39 tgl Exp $
5858
*
5959
*-------------------------------------------------------------------------
6060
*/
@@ -108,6 +108,9 @@ bool enable_mergejoin = true;
108108
bool enable_hashjoin = true;
109109

110110

111+
static MergeScanSelCache *cached_scansel(PlannerInfo *root,
112+
RestrictInfo *rinfo,
113+
PathKey *pathkey);
111114
static bool cost_qual_eval_walker(Node *node, QualCost *total);
112115
static Selectivity approx_selectivity(PlannerInfo *root, List *quals,
113116
JoinType jointype);
@@ -1349,9 +1352,9 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
13491352
* (unless it's an outer join, in which case the outer side has to be
13501353
* scanned all the way anyway). Estimate fraction of the left and right
13511354
* inputs that will actually need to be scanned. We use only the first
1352-
* (most significant) merge clause for this purpose.
1353-
*
1354-
* XXX mergejoinscansel is a bit expensive, can we cache its results?
1355+
* (most significant) merge clause for this purpose. Since
1356+
* mergejoinscansel() is a fairly expensive computation, we cache the
1357+
* results in the merge clause RestrictInfo.
13551358
*/
13561359
if (mergeclauses && path->jpath.jointype != JOIN_FULL)
13571360
{
@@ -1360,8 +1363,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
13601363
List *ipathkeys;
13611364
PathKey *opathkey;
13621365
PathKey *ipathkey;
1363-
Selectivity leftscansel,
1364-
rightscansel;
1366+
MergeScanSelCache *cache;
13651367

13661368
/* Get the input pathkeys to determine the sort-order details */
13671369
opathkeys = outersortkeys ? outersortkeys : outer_path->pathkeys;
@@ -1376,22 +1378,21 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
13761378
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
13771379
elog(ERROR, "left and right pathkeys do not match in mergejoin");
13781380

1379-
mergejoinscansel(root, (Node *) firstclause->clause,
1380-
opathkey->pk_opfamily, opathkey->pk_strategy,
1381-
&leftscansel, &rightscansel);
1381+
/* Get the selectivity with caching */
1382+
cache = cached_scansel(root, firstclause, opathkey);
13821383

13831384
if (bms_is_subset(firstclause->left_relids,
13841385
outer_path->parent->relids))
13851386
{
13861387
/* left side of clause is outer */
1387-
outerscansel = leftscansel;
1388-
innerscansel = rightscansel;
1388+
outerscansel = cache->leftscansel;
1389+
innerscansel = cache->rightscansel;
13891390
}
13901391
else
13911392
{
13921393
/* left side of clause is inner */
1393-
outerscansel = rightscansel;
1394-
innerscansel = leftscansel;
1394+
outerscansel = cache->rightscansel;
1395+
innerscansel = cache->leftscansel;
13951396
}
13961397
if (path->jpath.jointype == JOIN_LEFT)
13971398
outerscansel = 1.0;
@@ -1493,6 +1494,54 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
14931494
path->jpath.path.total_cost = startup_cost + run_cost;
14941495
}
14951496

1497+
/*
1498+
* run mergejoinscansel() with caching
1499+
*/
1500+
static MergeScanSelCache *
1501+
cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
1502+
{
1503+
MergeScanSelCache *cache;
1504+
ListCell *lc;
1505+
Selectivity leftscansel,
1506+
rightscansel;
1507+
MemoryContext oldcontext;
1508+
1509+
/* Do we have this result already? */
1510+
foreach(lc, rinfo->scansel_cache)
1511+
{
1512+
cache = (MergeScanSelCache *) lfirst(lc);
1513+
if (cache->opfamily == pathkey->pk_opfamily &&
1514+
cache->strategy == pathkey->pk_strategy &&
1515+
cache->nulls_first == pathkey->pk_nulls_first)
1516+
return cache;
1517+
}
1518+
1519+
/* Nope, do the computation */
1520+
mergejoinscansel(root,
1521+
(Node *) rinfo->clause,
1522+
pathkey->pk_opfamily,
1523+
pathkey->pk_strategy,
1524+
pathkey->pk_nulls_first,
1525+
&leftscansel,
1526+
&rightscansel);
1527+
1528+
/* Cache the result in suitably long-lived workspace */
1529+
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
1530+
1531+
cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));
1532+
cache->opfamily = pathkey->pk_opfamily;
1533+
cache->strategy = pathkey->pk_strategy;
1534+
cache->nulls_first = pathkey->pk_nulls_first;
1535+
cache->leftscansel = leftscansel;
1536+
cache->rightscansel = rightscansel;
1537+
1538+
rinfo->scansel_cache = lappend(rinfo->scansel_cache, cache);
1539+
1540+
MemoryContextSwitchTo(oldcontext);
1541+
1542+
return cache;
1543+
}
1544+
14961545
/*
14971546
* cost_hashjoin
14981547
* Determines and returns the cost of joining two relations using the

0 commit comments

Comments
 (0)