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

Commit 08ccdf0

Browse files
committed
Fix oversight in planning for multiple indexscans driven by
ScalarArrayOpExpr index quals: we were estimating the right total number of rows returned, but treating the index-access part of the cost as if a single scan were fetching that many consecutive index tuples. Actually we should treat it as a multiple indexscan, and if there are enough of 'em the Mackert-Lohman discount should kick in.
1 parent cffd89c commit 08ccdf0

File tree

3 files changed

+84
-39
lines changed

3 files changed

+84
-39
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 1 addition & 30 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.159 2006/07/01 18:38:32 tgl Exp $
57+
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.160 2006/07/01 22:07:23 tgl Exp $
5858
*
5959
*-------------------------------------------------------------------------
6060
*/
@@ -112,7 +112,6 @@ bool enable_hashjoin = true;
112112

113113

114114
static bool cost_qual_eval_walker(Node *node, QualCost *total);
115-
static int estimate_array_length(Node *arrayexpr);
116115
static Selectivity approx_selectivity(PlannerInfo *root, List *quals,
117116
JoinType jointype);
118117
static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root);
@@ -691,34 +690,6 @@ cost_tidscan(Path *path, PlannerInfo *root,
691690
path->total_cost = startup_cost + run_cost;
692691
}
693692

694-
/*
695-
* Estimate number of elements in the array yielded by an expression.
696-
*/
697-
static int
698-
estimate_array_length(Node *arrayexpr)
699-
{
700-
if (arrayexpr && IsA(arrayexpr, Const))
701-
{
702-
Datum arraydatum = ((Const *) arrayexpr)->constvalue;
703-
bool arrayisnull = ((Const *) arrayexpr)->constisnull;
704-
ArrayType *arrayval;
705-
706-
if (arrayisnull)
707-
return 0;
708-
arrayval = DatumGetArrayTypeP(arraydatum);
709-
return ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
710-
}
711-
else if (arrayexpr && IsA(arrayexpr, ArrayExpr))
712-
{
713-
return list_length(((ArrayExpr *) arrayexpr)->elements);
714-
}
715-
else
716-
{
717-
/* default guess */
718-
return 10;
719-
}
720-
}
721-
722693
/*
723694
* cost_subqueryscan
724695
* Determines and returns the cost of scanning a subquery RTE.

src/backend/utils/adt/selfuncs.c

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.207 2006/06/06 17:59:57 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.208 2006/07/01 22:07:23 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -1394,7 +1394,6 @@ scalararraysel(PlannerInfo *root,
13941394
{
13951395
oprsel = get_oprjoin(operator);
13961396
selarg4 = Int16GetDatum(jointype);
1397-
13981397
}
13991398
else
14001399
{
@@ -1519,6 +1518,7 @@ scalararraysel(PlannerInfo *root,
15191518
s1 = useOr ? 0.0 : 1.0;
15201519
/*
15211520
* Arbitrarily assume 10 elements in the eventual array value
1521+
* (see also estimate_array_length)
15221522
*/
15231523
for (i = 0; i < 10; i++)
15241524
{
@@ -1535,6 +1535,37 @@ scalararraysel(PlannerInfo *root,
15351535
return s1;
15361536
}
15371537

1538+
/*
1539+
* Estimate number of elements in the array yielded by an expression.
1540+
*
1541+
* It's important that this agree with scalararraysel.
1542+
*/
1543+
int
1544+
estimate_array_length(Node *arrayexpr)
1545+
{
1546+
if (arrayexpr && IsA(arrayexpr, Const))
1547+
{
1548+
Datum arraydatum = ((Const *) arrayexpr)->constvalue;
1549+
bool arrayisnull = ((Const *) arrayexpr)->constisnull;
1550+
ArrayType *arrayval;
1551+
1552+
if (arrayisnull)
1553+
return 0;
1554+
arrayval = DatumGetArrayTypeP(arraydatum);
1555+
return ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
1556+
}
1557+
else if (arrayexpr && IsA(arrayexpr, ArrayExpr) &&
1558+
!((ArrayExpr *) arrayexpr)->multidims)
1559+
{
1560+
return list_length(((ArrayExpr *) arrayexpr)->elements);
1561+
}
1562+
else
1563+
{
1564+
/* default guess --- see also scalararraysel */
1565+
return 10;
1566+
}
1567+
}
1568+
15381569
/*
15391570
* rowcomparesel - Selectivity of RowCompareExpr Node.
15401571
*
@@ -4473,10 +4504,14 @@ genericcostestimate(PlannerInfo *root,
44734504
double *indexCorrelation)
44744505
{
44754506
double numIndexPages;
4507+
double num_sa_scans;
4508+
double num_outer_scans;
4509+
double num_scans;
44764510
QualCost index_qual_cost;
44774511
double qual_op_cost;
44784512
double qual_arg_cost;
44794513
List *selectivityQuals;
4514+
ListCell *l;
44804515

44814516
/*
44824517
* If the index is partial, AND the index predicate with the explicitly
@@ -4513,6 +4548,25 @@ genericcostestimate(PlannerInfo *root,
45134548
else
45144549
selectivityQuals = indexQuals;
45154550

4551+
/*
4552+
* Check for ScalarArrayOpExpr index quals, and estimate the number
4553+
* of index scans that will be performed.
4554+
*/
4555+
num_sa_scans = 1;
4556+
foreach(l, indexQuals)
4557+
{
4558+
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
4559+
4560+
if (IsA(rinfo->clause, ScalarArrayOpExpr))
4561+
{
4562+
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
4563+
int alength = estimate_array_length(lsecond(saop->args));
4564+
4565+
if (alength > 1)
4566+
num_sa_scans *= alength;
4567+
}
4568+
}
4569+
45164570
/* Estimate the fraction of main-table tuples that will be visited */
45174571
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
45184572
index->rel->relid,
@@ -4527,8 +4581,15 @@ genericcostestimate(PlannerInfo *root,
45274581
numIndexTuples = *indexSelectivity * index->rel->tuples;
45284582

45294583
/*
4530-
* We can bound the number of tuples by the index size in any case. Also,
4531-
* always estimate at least one tuple is touched, even when
4584+
* The estimate obtained so far counts all the tuples returned by all
4585+
* scans of ScalarArrayOpExpr calls. We want to consider the per-scan
4586+
* number, so adjust. This is a handy place to round to integer, too.
4587+
*/
4588+
numIndexTuples = rint(numIndexTuples / num_sa_scans);
4589+
4590+
/*
4591+
* We can bound the number of tuples by the index size in any case.
4592+
* Also, always estimate at least one tuple is touched, even when
45324593
* indexSelectivity estimate is tiny.
45334594
*/
45344595
if (numIndexTuples > index->tuples)
@@ -4556,7 +4617,8 @@ genericcostestimate(PlannerInfo *root,
45564617
*
45574618
* The above calculations are all per-index-scan. However, if we are
45584619
* in a nestloop inner scan, we can expect the scan to be repeated (with
4559-
* different search keys) for each row of the outer relation. This
4620+
* different search keys) for each row of the outer relation. Likewise,
4621+
* ScalarArrayOpExpr quals result in multiple index scans. This
45604622
* creates the potential for cache effects to reduce the number of
45614623
* disk page fetches needed. We want to estimate the average per-scan
45624624
* I/O cost in the presence of caching.
@@ -4569,7 +4631,17 @@ genericcostestimate(PlannerInfo *root,
45694631
*/
45704632
if (outer_rel != NULL && outer_rel->rows > 1)
45714633
{
4572-
double num_scans = outer_rel->rows;
4634+
num_outer_scans = outer_rel->rows;
4635+
num_scans = num_sa_scans * num_outer_scans;
4636+
}
4637+
else
4638+
{
4639+
num_outer_scans = 1;
4640+
num_scans = num_sa_scans;
4641+
}
4642+
4643+
if (num_scans > 1)
4644+
{
45734645
double pages_fetched;
45744646

45754647
/* total page fetches ignoring cache effects */
@@ -4582,9 +4654,10 @@ genericcostestimate(PlannerInfo *root,
45824654

45834655
/*
45844656
* Now compute the total disk access cost, and then report a
4585-
* pro-rated share for one index scan.
4657+
* pro-rated share for each outer scan. (Don't pro-rate for
4658+
* ScalarArrayOpExpr, since that's internal to the indexscan.)
45864659
*/
4587-
*indexTotalCost = (pages_fetched * random_page_cost) / num_scans;
4660+
*indexTotalCost = (pages_fetched * random_page_cost) / num_outer_scans;
45884661
}
45894662
else
45904663
{

src/include/utils/selfuncs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.33 2006/05/02 11:28:55 teodor Exp $
11+
* $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.34 2006/07/01 22:07:23 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -151,6 +151,7 @@ extern Selectivity scalararraysel(PlannerInfo *root,
151151
ScalarArrayOpExpr *clause,
152152
bool is_join_clause,
153153
int varRelid, JoinType jointype);
154+
extern int estimate_array_length(Node *arrayexpr);
154155
extern Selectivity rowcomparesel(PlannerInfo *root,
155156
RowCompareExpr *clause,
156157
int varRelid, JoinType jointype);

0 commit comments

Comments
 (0)