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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy