Skip to content

Commit 2abc879

Browse files
committed
Limit the number of index clauses considered in choose_bitmap_and().
classify_index_clause_usage() is O(N^2) in the number of distinct index qual clauses it considers, because of its use of a simple search list to store them. For nearly all queries, that's fine because only a few clauses will be considered. But Alexander Kuzmenkov reported a machine-generated query with 80000 (!) index qual clauses, which caused this code to take forever. Somewhat remarkably, this is the only O(N^2) behavior we now have for such a query, so let's fix it. We can get rid of the O(N^2) runtime for cases like this without much damage to the functionality of choose_bitmap_and() by separating out paths with "too many" qual or pred clauses, and deeming them to always be nonredundant with other paths. Then their clauses needn't go into the search list, so it doesn't get too long, but we don't lose the ability to consider bitmap AND plans altogether. I set the threshold for "too many" to be 100 clauses per path, which should be plenty to ensure no change in planning behavior for normal queries. There are other things we could do to make this go faster, but it's not clear that it's worth any additional effort. 80000 qual clauses require a whole lot of work in many other places, too. The code's been like this for a long time, so back-patch to all supported branches. The troublesome query only works back to 9.5 (in 9.4 it fails with stack overflow in the parser); so I'm not sure that fixing this in 9.4 has any real-world benefit, but perhaps it does. Discussion: https://postgr.es/m/90c5bdfa-d633-dabe-9889-3cf3e1acd443@postgrespro.ru
1 parent 277602d commit 2abc879

File tree

1 file changed

+31
-1
lines changed

1 file changed

+31
-1
lines changed

src/backend/optimizer/path/indxpath.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ typedef struct
6868
List *quals; /* the WHERE clauses it uses */
6969
List *preds; /* predicates of its partial index(es) */
7070
Bitmapset *clauseids; /* quals+preds represented as a bitmapset */
71+
bool unclassifiable; /* has too many quals+preds to process? */
7172
} PathClauseUsage;
7273

7374
/* Callback argument for ec_member_matches_indexcol */
@@ -1379,9 +1380,18 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
13791380
Path *ipath = (Path *) lfirst(l);
13801381

13811382
pathinfo = classify_index_clause_usage(ipath, &clauselist);
1383+
1384+
/* If it's unclassifiable, treat it as distinct from all others */
1385+
if (pathinfo->unclassifiable)
1386+
{
1387+
pathinfoarray[npaths++] = pathinfo;
1388+
continue;
1389+
}
1390+
13821391
for (i = 0; i < npaths; i++)
13831392
{
1384-
if (bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))
1393+
if (!pathinfoarray[i]->unclassifiable &&
1394+
bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))
13851395
break;
13861396
}
13871397
if (i < npaths)
@@ -1416,6 +1426,10 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
14161426
* For each surviving index, consider it as an "AND group leader", and see
14171427
* whether adding on any of the later indexes results in an AND path with
14181428
* cheaper total cost than before. Then take the cheapest AND group.
1429+
*
1430+
* Note: paths that are either clauseless or unclassifiable will have
1431+
* empty clauseids, so that they will not be rejected by the clauseids
1432+
* filter here, nor will they cause later paths to be rejected by it.
14191433
*/
14201434
for (i = 0; i < npaths; i++)
14211435
{
@@ -1629,6 +1643,21 @@ classify_index_clause_usage(Path *path, List **clauselist)
16291643
result->preds = NIL;
16301644
find_indexpath_quals(path, &result->quals, &result->preds);
16311645

1646+
/*
1647+
* Some machine-generated queries have outlandish numbers of qual clauses.
1648+
* To avoid getting into O(N^2) behavior even in this preliminary
1649+
* classification step, we want to limit the number of entries we can
1650+
* accumulate in *clauselist. Treat any path with more than 100 quals +
1651+
* preds as unclassifiable, which will cause calling code to consider it
1652+
* distinct from all other paths.
1653+
*/
1654+
if (list_length(result->quals) + list_length(result->preds) > 100)
1655+
{
1656+
result->clauseids = NULL;
1657+
result->unclassifiable = true;
1658+
return result;
1659+
}
1660+
16321661
/* Build up a bitmapset representing the quals and preds */
16331662
clauseids = NULL;
16341663
foreach(lc, result->quals)
@@ -1646,6 +1675,7 @@ classify_index_clause_usage(Path *path, List **clauselist)
16461675
find_list_position(node, clauselist));
16471676
}
16481677
result->clauseids = clauseids;
1678+
result->unclassifiable = false;
16491679

16501680
return result;
16511681
}

0 commit comments

Comments
 (0)