1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00

Teach bitmap path generation about transforming OR-clauses to SAOP's

When optimizer generates bitmap paths, it considers breaking OR-clause
arguments one-by-one.  But now, a group of similar OR-clauses can be
transformed into SAOP during index matching.  So, bitmap paths should
keep up.

This commit teaches bitmap paths generation machinery to group similar
OR-clauses into dedicated RestrictInfos.  Those RestrictInfos are considered
both to match index as a whole (as SAOP), or to match as a set of individual
OR-clause argument one-by-one (the old way).

Therefore, bitmap path generation will takes advantage of OR-clauses to SAOP's
transformation.  The old way of handling them is also considered.  So, there
shouldn't be planning regression.

Discussion: https://postgr.es/m/CAPpHfdu5iQOjF93vGbjidsQkhHvY2NSm29duENYH_cbhC6x%2BMg%40mail.gmail.com
Author: Alexander Korotkov, Andrey Lepikhov
Reviewed-by: Alena Rybakina, Andrei Lepikhov, Jian he, Robert Haas
Reviewed-by: Peter Geoghegan
This commit is contained in:
Alexander Korotkov 2024-11-24 01:41:45 +02:00
parent d4378c0005
commit ae4569161a
7 changed files with 664 additions and 104 deletions

View File

@ -1173,6 +1173,383 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
return result;
}
/*
* Utility structure used to group similar OR-clause arguments in
* group_similar_or_args(). It represents information about the OR-clause
* argument and its matching index key.
*/
typedef struct
{
int indexnum; /* index of the matching index, or -1 if no
* matching index */
int colnum; /* index of the matching column, or -1 if no
* matching index */
Oid opno; /* OID of the OpClause operator, or InvalidOid
* if not an OpExpr */
Oid inputcollid; /* OID of the OpClause input collation */
int argindex; /* index of the clause in the list of
* arguments */
} OrArgIndexMatch;
/*
* Comparison function for OrArgIndexMatch which provides sort order placing
* similar OR-clause arguments together.
*/
static int
or_arg_index_match_cmp(const void *a, const void *b)
{
const OrArgIndexMatch *match_a = (const OrArgIndexMatch *) a;
const OrArgIndexMatch *match_b = (const OrArgIndexMatch *) b;
if (match_a->indexnum < match_b->indexnum)
return -1;
else if (match_a->indexnum > match_b->indexnum)
return 1;
if (match_a->colnum < match_b->colnum)
return -1;
else if (match_a->colnum > match_b->colnum)
return 1;
if (match_a->opno < match_b->opno)
return -1;
else if (match_a->opno > match_b->opno)
return 1;
if (match_a->inputcollid < match_b->inputcollid)
return -1;
else if (match_a->inputcollid > match_b->inputcollid)
return 1;
if (match_a->argindex < match_b->argindex)
return -1;
else if (match_a->argindex > match_b->argindex)
return 1;
return 0;
}
/*
* group_similar_or_args
* Transform incoming OR-restrictinfo into a list of sub-restrictinfos,
* each of them containing a subset of similar OR-clause arguments from
* the source rinfo.
*
* Similar OR-clause arguments are of the form "indexkey op constant" having
* the same indexkey, operator, and collation. Constant may comprise either
* Const or Param. It may be employed later, during the
* match_clause_to_indexcol() to transform the whole OR-sub-rinfo to an SAOP
* clause.
*
* Returns the processed list of OR-clause arguments.
*/
static List *
group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
{
int n;
int i;
int group_start;
OrArgIndexMatch *matches;
bool matched = false;
ListCell *lc;
ListCell *lc2;
List *orargs;
List *result = NIL;
Assert(IsA(rinfo->orclause, BoolExpr));
orargs = ((BoolExpr *) rinfo->orclause)->args;
n = list_length(orargs);
/*
* To avoid N^2 behavior, take utility pass along the list of OR-clause
* arguments. For each argument, fill the OrArgIndexMatch structure,
* which will be used to sort these arguments at the next step.
*/
i = -1;
matches = (OrArgIndexMatch *) palloc(sizeof(OrArgIndexMatch) * n);
foreach(lc, orargs)
{
Node *arg = lfirst(lc);
RestrictInfo *argrinfo;
OpExpr *clause;
Oid opno;
Node *leftop,
*rightop;
Node *nonConstExpr;
int indexnum;
int colnum;
i++;
matches[i].argindex = i;
matches[i].indexnum = -1;
matches[i].colnum = -1;
matches[i].opno = InvalidOid;
matches[i].inputcollid = InvalidOid;
if (!IsA(arg, RestrictInfo))
continue;
argrinfo = castNode(RestrictInfo, arg);
/* Only operator clauses can match */
if (!IsA(argrinfo->clause, OpExpr))
continue;
clause = (OpExpr *) argrinfo->clause;
opno = clause->opno;
/* Only binary operators can match */
if (list_length(clause->args) != 2)
continue;
/*
* Ignore any RelabelType node above the operands. This is needed to
* be able to apply indexscanning in binary-compatible-operator cases.
* Note: we can assume there is at most one RelabelType node;
* eval_const_expressions() will have simplified if more than one.
*/
leftop = get_leftop(clause);
if (IsA(leftop, RelabelType))
leftop = (Node *) ((RelabelType *) leftop)->arg;
rightop = get_rightop(clause);
if (IsA(rightop, RelabelType))
rightop = (Node *) ((RelabelType *) rightop)->arg;
/*
* Check for clauses of the form: (indexkey operator constant) or
* (constant operator indexkey). But we don't know a particular index
* yet. First check for a constant, which must be Const or Param.
* That's cheaper than search for an index key among all indexes.
*/
if (IsA(leftop, Const) || IsA(leftop, Param))
{
opno = get_commutator(opno);
if (!OidIsValid(opno))
{
/* commutator doesn't exist, we can't reverse the order */
continue;
}
nonConstExpr = rightop;
}
else if (IsA(rightop, Const) || IsA(rightop, Param))
{
nonConstExpr = leftop;
}
else
{
continue;
}
/*
* Match non-constant part to the index key. It's possible that a
* single non-constant part matches multiple index keys. It's OK, we
* just stop with first matching index key. Given that this choice is
* determined the same for every clause, we will group similar clauses
* together anyway.
*/
indexnum = 0;
foreach(lc2, rel->indexlist)
{
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc2);
/* Ignore index if it doesn't support bitmap scans */
if (!index->amhasgetbitmap)
continue;
for (colnum = 0; colnum < index->nkeycolumns; colnum++)
{
if (match_index_to_operand(nonConstExpr, colnum, index))
{
matches[i].indexnum = indexnum;
matches[i].colnum = colnum;
matches[i].opno = opno;
matches[i].inputcollid = clause->inputcollid;
matched = true;
break;
}
}
/*
* Stop looping through the indexes, if we managed to match
* nonConstExpr to any index column.
*/
if (matches[i].indexnum >= 0)
break;
indexnum++;
}
}
/*
* Fast-path check: if no clause is matching to the index column, we can
* just give up at this stage and return the clause list as-is.
*/
if (!matched)
{
pfree(matches);
return orargs;
}
/* Sort clauses to make similar clauses go together */
qsort(matches, n, sizeof(OrArgIndexMatch), or_arg_index_match_cmp);
/*
* Group similar clauses into single sub-restrictinfo. Side effect: the
* resulting list of restrictions will be sorted by indexnum and colnum.
*/
group_start = 0;
for (i = 1; i <= n; i++)
{
/* Check if it's a group boundary */
if (group_start >= 0 &&
(i == n ||
matches[i].indexnum != matches[group_start].indexnum ||
matches[i].colnum != matches[group_start].colnum ||
matches[i].opno != matches[group_start].opno ||
matches[i].inputcollid != matches[group_start].inputcollid ||
matches[i].indexnum == -1))
{
/*
* One clause in group: add it "as is" to the upper-level OR.
*/
if (i - group_start == 1)
{
result = lappend(result,
list_nth(orargs,
matches[group_start].argindex));
}
else
{
/*
* Two or more clauses in a group: create a nested OR.
*/
List *args = NIL;
List *rargs = NIL;
RestrictInfo *subrinfo;
int j;
Assert(i - group_start >= 2);
/* Construct the list of nested OR arguments */
for (j = group_start; j < i; j++)
{
Node *arg = list_nth(orargs, matches[j].argindex);
rargs = lappend(rargs, arg);
if (IsA(arg, RestrictInfo))
args = lappend(args, ((RestrictInfo *) arg)->clause);
else
args = lappend(args, arg);
}
/* Construct the nested OR and wrap it with RestrictInfo */
subrinfo = make_plain_restrictinfo(root,
make_orclause(args),
make_orclause(rargs),
rinfo->is_pushed_down,
rinfo->has_clone,
rinfo->is_clone,
rinfo->pseudoconstant,
rinfo->security_level,
rinfo->required_relids,
rinfo->incompatible_relids,
rinfo->outer_relids);
result = lappend(result, subrinfo);
}
group_start = i;
}
}
pfree(matches);
return result;
}
/*
* make_bitmap_paths_for_or_group
* Generate bitmap paths for a group of similar OR-clause arguments
* produced by group_similar_or_args().
*
* This function considers two cases: (1) matching a group of clauses to
* the index as a whole, and (2) matching the individual clauses one-by-one.
* (1) typically comprises an optimal solution. If not, (2) typically
* comprises fair alternative.
*
* Ideally, we could consider all arbitrary splits of arguments into
* subgroups, but that could lead to unacceptable computational complexity.
* This is why we only consider two cases of above.
*/
static List *
make_bitmap_paths_for_or_group(PlannerInfo *root, RelOptInfo *rel,
RestrictInfo *ri, List *other_clauses)
{
List *jointlist = NIL;
List *splitlist = NIL;
ListCell *lc;
List *orargs;
List *args = ((BoolExpr *) ri->orclause)->args;
Cost jointcost = 0.0,
splitcost = 0.0;
Path *bitmapqual;
List *indlist;
/*
* First, try to match the whole group to the one index.
*/
orargs = list_make1(ri);
indlist = build_paths_for_OR(root, rel,
orargs,
other_clauses);
if (indlist != NIL)
{
bitmapqual = choose_bitmap_and(root, rel, indlist);
jointcost = bitmapqual->total_cost;
jointlist = list_make1(bitmapqual);
}
/*
* If we manage to find a bitmap scan, which uses the group of OR-clause
* arguments as a whole, we can skip matching OR-clause arguments
* one-by-one as long as there are no other clauses, which can bring more
* efficiency to one-by-one case.
*/
if (jointlist != NIL && other_clauses == NIL)
return jointlist;
/*
* Also try to match all containing clauses one-by-one.
*/
foreach(lc, args)
{
orargs = list_make1(lfirst(lc));
indlist = build_paths_for_OR(root, rel,
orargs,
other_clauses);
if (indlist == NIL)
{
splitlist = NIL;
break;
}
bitmapqual = choose_bitmap_and(root, rel, indlist);
splitcost += bitmapqual->total_cost;
splitlist = lappend(splitlist, bitmapqual);
}
/*
* Pick the best option.
*/
if (splitlist == NIL)
return jointlist;
else if (jointlist == NIL)
return splitlist;
else
return (jointcost < splitcost) ? jointlist : splitlist;
}
/*
* generate_bitmap_or_paths
* Look through the list of clauses to find OR clauses, and generate
@ -1203,6 +1580,8 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
List *pathlist;
Path *bitmapqual;
ListCell *j;
List *groupedArgs;
List *inner_other_clauses = NIL;
/* Ignore RestrictInfos that aren't ORs */
if (!restriction_is_or_clause(rinfo))
@ -1213,7 +1592,29 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
* the OR, else we can't use it.
*/
pathlist = NIL;
foreach(j, ((BoolExpr *) rinfo->orclause)->args)
/*
* Group the similar OR-clause arguments into dedicated RestrictInfos,
* because each of those RestrictInfos has a chance to match the index
* as a whole.
*/
groupedArgs = group_similar_or_args(root, rel, rinfo);
if (groupedArgs != ((BoolExpr *) rinfo->orclause)->args)
{
/*
* Some parts of the rinfo were probably grouped. In this case,
* we have a set of sub-rinfos that together are an exact
* duplicate of rinfo. Thus, we need to remove the rinfo from
* other clauses. match_clauses_to_index detects duplicated
* iclauses by comparing pointers to original rinfos that would be
* different. So, we must delete rinfo to avoid de-facto
* duplicated clauses in the index clauses list.
*/
inner_other_clauses = list_delete(list_copy(all_clauses), rinfo);
}
foreach(j, groupedArgs)
{
Node *orarg = (Node *) lfirst(j);
List *indlist;
@ -1233,12 +1634,34 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
andargs,
all_clauses));
}
else if (restriction_is_or_clause(castNode(RestrictInfo, orarg)))
{
RestrictInfo *ri = castNode(RestrictInfo, orarg);
/*
* Generate bitmap paths for the group of similar OR-clause
* arguments.
*/
indlist = make_bitmap_paths_for_or_group(root,
rel, ri,
inner_other_clauses);
if (indlist == NIL)
{
pathlist = NIL;
break;
}
else
{
pathlist = list_concat(pathlist, indlist);
continue;
}
}
else
{
RestrictInfo *ri = castNode(RestrictInfo, orarg);
List *orargs;
Assert(!restriction_is_or_clause(ri));
orargs = list_make1(ri);
indlist = build_paths_for_OR(root, rel,
@ -1264,6 +1687,9 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
pathlist = lappend(pathlist, bitmapqual);
}
if (inner_other_clauses != NIL)
list_free(inner_other_clauses);
/*
* If we have a match for every arm, then turn them into a
* BitmapOrPath, and add to result list.

View File

@ -21,17 +21,6 @@
#include "optimizer/restrictinfo.h"
static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root,
Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool has_clone,
bool is_clone,
bool pseudoconstant,
Index security_level,
Relids required_relids,
Relids incompatible_relids,
Relids outer_relids);
static Expr *make_sub_restrictinfos(PlannerInfo *root,
Expr *clause,
bool is_pushed_down,
@ -90,36 +79,38 @@ make_restrictinfo(PlannerInfo *root,
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
Assert(!is_andclause(clause));
return make_restrictinfo_internal(root,
clause,
NULL,
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
return make_plain_restrictinfo(root,
clause,
NULL,
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
}
/*
* make_restrictinfo_internal
* make_plain_restrictinfo
*
* Common code for the main entry points and the recursive cases.
* Common code for the main entry points and the recursive cases. Also,
* useful while contrucitng RestrictInfos above OR clause, which already has
* RestrictInfos above its subclauses.
*/
static RestrictInfo *
make_restrictinfo_internal(PlannerInfo *root,
Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool has_clone,
bool is_clone,
bool pseudoconstant,
Index security_level,
Relids required_relids,
Relids incompatible_relids,
Relids outer_relids)
RestrictInfo *
make_plain_restrictinfo(PlannerInfo *root,
Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool has_clone,
bool is_clone,
bool pseudoconstant,
Index security_level,
Relids required_relids,
Relids incompatible_relids,
Relids outer_relids)
{
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
Relids baserels;
@ -296,17 +287,17 @@ make_sub_restrictinfos(PlannerInfo *root,
NULL,
incompatible_relids,
outer_relids));
return (Expr *) make_restrictinfo_internal(root,
clause,
make_orclause(orlist),
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
return (Expr *) make_plain_restrictinfo(root,
clause,
make_orclause(orlist),
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
}
else if (is_andclause(clause))
{
@ -328,17 +319,17 @@ make_sub_restrictinfos(PlannerInfo *root,
return make_andclause(andlist);
}
else
return (Expr *) make_restrictinfo_internal(root,
clause,
NULL,
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
return (Expr *) make_plain_restrictinfo(root,
clause,
NULL,
is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
security_level,
required_relids,
incompatible_relids,
outer_relids);
}
/*

View File

@ -22,6 +22,17 @@
make_restrictinfo(root, clause, true, false, false, false, 0, \
NULL, NULL, NULL)
extern RestrictInfo *make_plain_restrictinfo(PlannerInfo *root,
Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool has_clone,
bool is_clone,
bool pseudoconstant,
Index security_level,
Relids required_relids,
Relids incompatible_relids,
Relids outer_relids);
extern RestrictInfo *make_restrictinfo(PlannerInfo *root,
Expr *clause,
bool is_pushed_down,

View File

@ -1875,6 +1875,60 @@ SELECT * FROM tenk1
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
(1 row)
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42 OR tenthous IS NULL);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1
Recheck Cond: (((thousand = 42) AND (tenthous IS NULL)) OR ((thousand = 42) AND ((tenthous = 1) OR (tenthous = 3) OR (tenthous = 42))))
Filter: ((tenthous = 1) OR (tenthous = 3) OR (tenthous = 42) OR (tenthous IS NULL))
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous IS NULL))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[])))
(8 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous::int2 = 3::int8 OR tenthous = 42::int8);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1
Recheck Cond: (thousand = 42)
Filter: ((tenthous = '1'::smallint) OR ((tenthous)::smallint = '3'::bigint) OR (tenthous = '42'::bigint))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 42)
(5 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous::int2 = 3::int8 OR tenthous::int2 = 42::int8);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1
Recheck Cond: (thousand = 42)
Filter: ((tenthous = '1'::smallint) OR ((tenthous)::smallint = '3'::bigint) OR ((tenthous)::smallint = '42'::bigint))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 42)
(5 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous = 3::int8 OR tenthous = 42::int8);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1
Recheck Cond: (((thousand = 42) AND ((tenthous = '3'::bigint) OR (tenthous = '42'::bigint))) OR ((thousand = 42) AND (tenthous = '1'::smallint)))
Filter: ((tenthous = '1'::smallint) OR (tenthous = '3'::bigint) OR (tenthous = '42'::bigint))
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{3,42}'::bigint[])))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = '1'::smallint))
(8 rows)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
@ -2003,25 +2057,24 @@ SELECT count(*) FROM tenk1
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: (((hundred = 42) AND ((thousand = 42) OR (thousand = 99) OR (tenthous < 2))) OR (thousand = 41))
Recheck Cond: (((hundred = 42) AND (((thousand = 42) OR (thousand = 99)) OR (tenthous < 2))) OR (thousand = 41))
Filter: (((hundred = 42) AND ((thousand = 42) OR (thousand = 99) OR (tenthous < 2))) OR (thousand = 41))
-> BitmapOr
-> BitmapAnd
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 42)
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 42)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 99)
Index Cond: (thousand = ANY ('{42,99}'::integer[]))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (tenthous < 2)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 41)
(16 rows)
(15 rows)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
@ -2033,22 +2086,21 @@ SELECT count(*) FROM tenk1
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: ((hundred = 42) AND ((thousand = 42) OR (thousand = 41) OR ((thousand = 99) AND (tenthous = 2))))
Recheck Cond: ((hundred = 42) AND (((thousand = 99) AND (tenthous = 2)) OR ((thousand = 42) OR (thousand = 41))))
Filter: ((thousand = 42) OR (thousand = 41) OR ((thousand = 99) AND (tenthous = 2)))
-> BitmapAnd
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 42)
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 42)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 41)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 99) AND (tenthous = 2))
(13 rows)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = ANY ('{42,41}'::integer[]))
(12 rows)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
@ -3144,6 +3196,49 @@ SELECT b.relname,
(2 rows)
DROP TABLE concur_temp_tab_1, concur_temp_tab_2, reindex_temp_before;
-- Check bitmap scan can consider similar OR arguments separately without
-- grouping them into SAOP.
CREATE TABLE bitmap_split_or (a int NOT NULL, b int NOT NULL, c int NOT NULL);
INSERT INTO bitmap_split_or (SELECT 1, 1, i FROM generate_series(1, 1000) i);
INSERT INTO bitmap_split_or (select i, 2, 2 FROM generate_series(1, 1000) i);
VACUUM ANALYZE bitmap_split_or;
CREATE INDEX t_b_partial_1_idx ON bitmap_split_or (b) WHERE a = 1;
CREATE INDEX t_b_partial_2_idx ON bitmap_split_or (b) WHERE a = 2;
EXPLAIN (COSTS OFF)
SELECT * FROM bitmap_split_or WHERE (a = 1 OR a = 2) AND b = 2;
QUERY PLAN
------------------------------------------------------------------
Bitmap Heap Scan on bitmap_split_or
Recheck Cond: (((b = 2) AND (a = 1)) OR ((b = 2) AND (a = 2)))
-> BitmapOr
-> Bitmap Index Scan on t_b_partial_1_idx
Index Cond: (b = 2)
-> Bitmap Index Scan on t_b_partial_2_idx
Index Cond: (b = 2)
(7 rows)
DROP INDEX t_b_partial_1_idx;
DROP INDEX t_b_partial_2_idx;
CREATE INDEX t_a_b_idx ON bitmap_split_or (a, b);
CREATE INDEX t_b_c_idx ON bitmap_split_or (b, c);
CREATE STATISTICS t_a_b_stat (mcv) ON a, b FROM bitmap_split_or;
CREATE STATISTICS t_b_c_stat (mcv) ON b, c FROM bitmap_split_or;
ANALYZE bitmap_split_or;
EXPLAIN (COSTS OFF)
SELECT * FROM bitmap_split_or WHERE a = 1 AND (b = 1 OR b = 2) AND c = 2;
QUERY PLAN
------------------------------------------------------------------
Bitmap Heap Scan on bitmap_split_or
Recheck Cond: (((b = 1) AND (c = 2)) OR ((a = 1) AND (b = 2)))
Filter: ((a = 1) AND (c = 2))
-> BitmapOr
-> Bitmap Index Scan on t_b_c_idx
Index Cond: ((b = 1) AND (c = 2))
-> Bitmap Index Scan on t_a_b_idx
Index Cond: ((a = 1) AND (b = 2))
(8 rows)
DROP TABLE bitmap_split_or;
--
-- REINDEX SCHEMA
--

View File

@ -4296,20 +4296,20 @@ select * from tenk1 a join tenk1 b on
Nested Loop
Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = 3) AND (b.hundred = 4)))
-> Bitmap Heap Scan on tenk1 b
Recheck Cond: ((unique1 = 2) OR (hundred = 4))
Recheck Cond: ((hundred = 4) OR (unique1 = 2))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 4)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 = 1) OR (unique2 = 3))
Recheck Cond: ((unique2 = 3) OR (unique1 = 1))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = 3)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
(17 rows)
explain (costs off)
@ -4323,12 +4323,12 @@ select * from tenk1 a join tenk1 b on
Filter: ((unique1 = 2) OR (ten = 4))
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 = 1) OR (unique2 = 3))
Recheck Cond: ((unique2 = 3) OR (unique1 = 1))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = 3)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
(12 rows)
explain (costs off)
@ -4340,21 +4340,21 @@ select * from tenk1 a join tenk1 b on
Nested Loop
Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4)))
-> Bitmap Heap Scan on tenk1 b
Recheck Cond: ((unique1 = 2) OR (hundred = 4))
Recheck Cond: ((hundred = 4) OR (unique1 = 2))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 4)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
Recheck Cond: (((unique2 = 3) OR (unique2 = 7)) OR (unique1 = 1))
Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
(18 rows)
explain (costs off)
@ -4366,21 +4366,21 @@ select * from tenk1 a join tenk1 b on
Nested Loop
Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4)))
-> Bitmap Heap Scan on tenk1 b
Recheck Cond: ((unique1 = 2) OR (hundred = 4))
Recheck Cond: ((hundred = 4) OR (unique1 = 2))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 4)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
Recheck Cond: (((unique2 = 3) OR (unique2 = 7)) OR (unique1 = 1))
Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
(18 rows)
explain (costs off)
@ -4394,18 +4394,16 @@ select * from tenk1 a join tenk1 b on
-> Seq Scan on tenk1 b
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
Recheck Cond: (((unique2 = 3) OR (unique2 = 7)) OR ((unique1 = 3) OR (unique1 = 1)) OR (unique1 < 20))
Filter: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 < 20)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 3)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
(16 rows)
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = ANY ('{3,1}'::integer[]))
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 < 20)
(14 rows)
--
-- test placement of movable quals in a parameterized join tree

View File

@ -738,6 +738,23 @@ SELECT * FROM tenk1
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42);
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42 OR tenthous IS NULL);
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous::int2 = 3::int8 OR tenthous = 42::int8);
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous::int2 = 3::int8 OR tenthous::int2 = 42::int8);
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1::int2 OR tenthous = 3::int8 OR tenthous = 42::int8);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
@ -1321,6 +1338,27 @@ SELECT b.relname,
ORDER BY 1;
DROP TABLE concur_temp_tab_1, concur_temp_tab_2, reindex_temp_before;
-- Check bitmap scan can consider similar OR arguments separately without
-- grouping them into SAOP.
CREATE TABLE bitmap_split_or (a int NOT NULL, b int NOT NULL, c int NOT NULL);
INSERT INTO bitmap_split_or (SELECT 1, 1, i FROM generate_series(1, 1000) i);
INSERT INTO bitmap_split_or (select i, 2, 2 FROM generate_series(1, 1000) i);
VACUUM ANALYZE bitmap_split_or;
CREATE INDEX t_b_partial_1_idx ON bitmap_split_or (b) WHERE a = 1;
CREATE INDEX t_b_partial_2_idx ON bitmap_split_or (b) WHERE a = 2;
EXPLAIN (COSTS OFF)
SELECT * FROM bitmap_split_or WHERE (a = 1 OR a = 2) AND b = 2;
DROP INDEX t_b_partial_1_idx;
DROP INDEX t_b_partial_2_idx;
CREATE INDEX t_a_b_idx ON bitmap_split_or (a, b);
CREATE INDEX t_b_c_idx ON bitmap_split_or (b, c);
CREATE STATISTICS t_a_b_stat (mcv) ON a, b FROM bitmap_split_or;
CREATE STATISTICS t_b_c_stat (mcv) ON b, c FROM bitmap_split_or;
ANALYZE bitmap_split_or;
EXPLAIN (COSTS OFF)
SELECT * FROM bitmap_split_or WHERE a = 1 AND (b = 1 OR b = 2) AND c = 2;
DROP TABLE bitmap_split_or;
--
-- REINDEX SCHEMA
--

View File

@ -1767,6 +1767,7 @@ OprCacheKey
OprInfo
OprProofCacheEntry
OprProofCacheKey
OrArgIndexMatch
OuterJoinClauseInfo
OutputPluginCallbacks
OutputPluginOptions