revert: Transform OR clauses to ANY expression

This commit reverts 72bd38cc99 due to implementation and design issues.

Reported-by: Tom Lane
Discussion: https://postgr.es/m/3604469.1712628736%40sss.pgh.pa.us
This commit is contained in:
Alexander Korotkov 2024-04-10 02:07:34 +03:00
parent 5a15bdea3b
commit ff9f72c68f
14 changed files with 20 additions and 786 deletions

View File

@ -6304,63 +6304,6 @@ SELECT * FROM parent WHERE key = 2400;
</listitem>
</varlistentry>
<varlistentry id="guc-or-to-any-transform-limit" xreflabel="or_to_any_transform_limit">
<term><varname>or_to_any_transform_limit</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>or_to_any_transform_limit</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the minimum length of arguments in an <literal>OR</literal>
expression exceeding which planner will try to lookup and group
multiple similar <literal>OR</literal> expressions to
<literal>ANY</literal> (<xref linkend="functions-comparisons-any-some"/>)
expressions. The grouping technique of this transformation is based
on the equivalence of variable sides. One side of such an expression
must be a constant clause, and the other must contain a variable
clause. The default value is <literal>5</literal>. The value of
<literal>-1</literal> completely disables the transformation.
</para>
<para>
The advantage of this <literal>OR-to-ANY</literal> transformation is
faster query planning and execution. In certain cases, this
transformation also leads to more effective plans containing
a single index scan instead of multiple bitmap scans. However, it
may also cause a planning regression when distinct
<literal>OR</literal> arguments are better to match to distinct indexes.
This may happen when they have different matching partial indexes or
have different distributions of other columns used in the query.
Generally, more groupable <literal>OR</literal> arguments mean that
transformation will be more likely to win than to lose.
</para>
<para>
For example, this query has its set of five <literal>OR</literal>
expressions transformed to <literal>ANY</literal> with the default
value of <varname>or_to_any_transform_limit</varname>. But not with
the increased value.
<programlisting>
# EXPLAIN SELECT * FROM tbl WHERE key = 1 OR key = 2 OR key = 3 OR key = 4 OR key = 5;
QUERY PLAN
-----------------------------------------------------
Seq Scan on tbl (cost=0.00..51.44 rows=64 width=4)
Filter: (key = ANY ('{1,2,3,4,5}'::integer[]))
(2 rows)
# SET or_to_any_transform_limit = 6;
SET
# EXPLAIN SELECT * FROM tbl WHERE key = 1 OR key = 2 OR key = 3 OR key = 4 OR key = 5;
QUERY PLAN
---------------------------------------------------------------------------
Seq Scan on tbl (cost=0.00..67.38 rows=63 width=4)
Filter: ((key = 1) OR (key = 2) OR (key = 3) OR (key = 4) OR (key = 5))
(2 rows)
</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-plan-cache-mode" xreflabel="plan_cache_mode">
<term><varname>plan_cache_mode</varname> (<type>enum</type>)
<indexterm>

View File

@ -141,33 +141,6 @@ JumbleQuery(Query *query)
return jstate;
}
JumbleState *
JumbleExpr(Expr *expr, uint64 *exprId)
{
JumbleState *jstate = NULL;
Assert(exprId != NULL);
jstate = (JumbleState *) palloc(sizeof(JumbleState));
/* Set up workspace for query jumbling */
jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
jstate->jumble_len = 0;
jstate->clocations_buf_size = 32;
jstate->clocations = (LocationLen *)
palloc(jstate->clocations_buf_size * sizeof(LocationLen));
jstate->clocations_count = 0;
jstate->highest_extern_param_id = 0;
/* Compute query ID */
_jumbleNode(jstate, (Node *) expr);
*exprId = DatumGetUInt64(hash_any_extended(jstate->jumble,
jstate->jumble_len,
0));
return jstate;
}
/*
* Enables query identifier computation.
*

View File

@ -31,25 +31,16 @@
#include "postgres.h"
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
#include "common/hashfn.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/queryjumble.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_oper.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
int or_to_any_transform_limit = 5;
static List *pull_ands(List *andlist);
static List *pull_ors(List *orlist);
static Expr *find_duplicate_ors(Expr *qual, bool is_check);
static Expr *process_duplicate_ors(List *orlist);
static List *transform_or_to_any(List *orlist);
/*
@ -275,376 +266,6 @@ negate_clause(Node *node)
return (Node *) make_notclause((Expr *) node);
}
/*
* The key for grouping similar operator expressions in transform_or_to_any().
*/
typedef struct OrClauseGroupKey
{
/* We need this to put this structure into list together with other nodes */
NodeTag type;
/* The expression of the variable side of operator */
Expr *expr;
/* The operator of the operator expression */
Oid opno;
/* The collation of the operator expression */
Oid inputcollid;
/* The type of constant side of operator */
Oid consttype;
} OrClauseGroupKey;
/*
* The group of similar operator expressions in transform_or_to_any().
*/
typedef struct OrClauseGroupEntry
{
OrClauseGroupKey key;
/* The list of constant sides of operators */
List *consts;
/*
* List of source expressions. We need this for convenience in case we
* will give up on transformation.
*/
List *exprs;
} OrClauseGroupEntry;
/*
* The hash function for OrClauseGroupKey.
*/
static uint32
orclause_hash(const void *data, Size keysize)
{
OrClauseGroupKey *key = (OrClauseGroupKey *) data;
uint64 exprHash;
Assert(keysize == sizeof(OrClauseGroupKey));
Assert(IsA(data, Invalid));
(void) JumbleExpr(key->expr, &exprHash);
return hash_combine((uint32) exprHash,
hash_combine((uint32) key->opno,
hash_combine((uint32) key->consttype,
(uint32) key->inputcollid)));
}
/*
* The copy function for OrClauseGroupKey.
*/
static void *
orclause_keycopy(void *dest, const void *src, Size keysize)
{
OrClauseGroupKey *src_key = (OrClauseGroupKey *) src;
OrClauseGroupKey *dst_key = (OrClauseGroupKey *) dest;
Assert(sizeof(OrClauseGroupKey) == keysize);
Assert(IsA(src, Invalid));
dst_key->type = T_Invalid;
dst_key->expr = src_key->expr;
dst_key->opno = src_key->opno;
dst_key->consttype = src_key->consttype;
dst_key->inputcollid = src_key->inputcollid;
return dst_key;
}
/*
* The equality function for OrClauseGroupKey.
*/
static int
orclause_match(const void *data1, const void *data2, Size keysize)
{
OrClauseGroupKey *key1 = (OrClauseGroupKey *) data1;
OrClauseGroupKey *key2 = (OrClauseGroupKey *) data2;
Assert(sizeof(OrClauseGroupKey) == keysize);
Assert(IsA(key1, Invalid));
Assert(IsA(key2, Invalid));
if (key1->opno == key2->opno &&
key1->consttype == key2->consttype &&
key1->inputcollid == key2->inputcollid &&
equal(key1->expr, key2->expr))
return 0;
return 1;
}
/*
* transform_or_to_any -
* Discover the args of an OR expression and try to group similar OR
* expressions to SAOP expressions.
*
* This transformation groups two-sided equality expression. One side of
* such an expression must be a plain constant or constant expression. The
* other side must be a variable expression without volatile functions.
* To group quals, opno, inputcollid of variable expression, and type of
* constant expression must be equal too.
*
* The grouping technique is based on the equivalence of variable sides of
* the expression: using exprId and equal() routine, it groups constant sides
* of similar clauses into an array. After the grouping procedure, each
* couple ('variable expression' and 'constant array') forms a new SAOP
* operation, which is added to the args list of the returning expression.
*/
static List *
transform_or_to_any(List *orlist)
{
List *neworlist = NIL;
List *entries = NIL;
ListCell *lc;
HASHCTL info;
HTAB *or_group_htab = NULL;
int len_ors = list_length(orlist);
OrClauseGroupEntry *entry = NULL;
Assert(or_to_any_transform_limit >= 0 &&
len_ors >= or_to_any_transform_limit);
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(OrClauseGroupKey);
info.entrysize = sizeof(OrClauseGroupEntry);
info.hash = orclause_hash;
info.keycopy = orclause_keycopy;
info.match = orclause_match;
or_group_htab = hash_create("OR Groups",
len_ors,
&info,
HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
foreach(lc, orlist)
{
Node *orqual = lfirst(lc);
Node *const_expr;
Node *nconst_expr;
OrClauseGroupKey hashkey;
bool found;
Oid opno;
Oid consttype;
Node *leftop,
*rightop;
if (!IsA(orqual, OpExpr))
{
entries = lappend(entries, orqual);
continue;
}
opno = ((OpExpr *) orqual)->opno;
if (get_op_rettype(opno) != BOOLOID)
{
/* Only operator returning boolean suits OR -> ANY transformation */
entries = lappend(entries, orqual);
continue;
}
/*
* Detect the constant side of the clause. Recall non-constant
* expression can be made not only with Vars, but also with Params,
* which is not bonded with any relation. Thus, we detect the const
* side - if another side is constant too, the orqual couldn't be an
* OpExpr. Get pointers to constant and expression sides of the qual.
*/
leftop = get_leftop(orqual);
if (IsA(leftop, RelabelType))
leftop = (Node *) ((RelabelType *) leftop)->arg;
rightop = get_rightop(orqual);
if (IsA(rightop, RelabelType))
rightop = (Node *) ((RelabelType *) rightop)->arg;
if (IsA(leftop, Const))
{
opno = get_commutator(opno);
if (!OidIsValid(opno))
{
/* commutator doesn't exist, we can't reverse the order */
entries = lappend(entries, orqual);
continue;
}
nconst_expr = get_rightop(orqual);
const_expr = get_leftop(orqual);
}
else if (IsA(rightop, Const))
{
const_expr = get_rightop(orqual);
nconst_expr = get_leftop(orqual);
}
else
{
entries = lappend(entries, orqual);
continue;
}
/*
* Forbid transformation for composite types, records, and volatile
* expressions.
*/
consttype = exprType(const_expr);
if (type_is_rowtype(exprType(const_expr)) ||
type_is_rowtype(consttype) ||
contain_volatile_functions((Node *) nconst_expr))
{
entries = lappend(entries, orqual);
continue;
}
/*
* At this point we definitely have a transformable clause. Classify
* it and add into specific group of clauses, or create new group.
*/
hashkey.type = T_Invalid;
hashkey.expr = (Expr *) nconst_expr;
hashkey.opno = opno;
hashkey.consttype = consttype;
hashkey.inputcollid = exprCollation(const_expr);
entry = hash_search(or_group_htab, &hashkey, HASH_ENTER, &found);
if (unlikely(found))
{
entry->consts = lappend(entry->consts, const_expr);
entry->exprs = lappend(entry->exprs, orqual);
}
else
{
entry->consts = list_make1(const_expr);
entry->exprs = list_make1(orqual);
/*
* Add the entry to the list. It is needed exclusively to manage
* the problem with the order of transformed clauses in explain.
* Hash value can depend on the platform and version. Hence,
* sequental scan of the hash table would prone to change the
* order of clauses in lists and, as a result, break regression
* tests accidentially.
*/
entries = lappend(entries, entry);
}
}
/* Let's convert each group of clauses to an ANY expression. */
/*
* Go through the list of groups and convert each, where number of consts
* more than 1. trivial groups move to OR-list again
*/
foreach(lc, entries)
{
Oid scalar_type;
Oid array_type;
if (!IsA(lfirst(lc), Invalid))
{
neworlist = lappend(neworlist, lfirst(lc));
continue;
}
entry = (OrClauseGroupEntry *) lfirst(lc);
Assert(list_length(entry->consts) > 0);
Assert(list_length(entry->exprs) == list_length(entry->consts));
if (list_length(entry->consts) == 1)
{
/*
* Only one element returns origin expression into the BoolExpr
* args list unchanged.
*/
list_free(entry->consts);
neworlist = list_concat(neworlist, entry->exprs);
continue;
}
/*
* Do the transformation.
*/
scalar_type = entry->key.consttype;
array_type = OidIsValid(scalar_type) ? get_array_type(scalar_type) :
InvalidOid;
if (OidIsValid(array_type))
{
/*
* OK: coerce all the right-hand non-Var inputs to the common type
* and build an ArrayExpr for them.
*/
List *aexprs = NIL;
ArrayExpr *newa = NULL;
ScalarArrayOpExpr *saopexpr = NULL;
HeapTuple opertup;
Form_pg_operator operform;
List *namelist = NIL;
ListCell *lc2;
foreach(lc2, entry->consts)
{
Node *node = (Node *) lfirst(lc2);
node = coerce_to_common_type(NULL, node, scalar_type,
"OR ANY Transformation");
aexprs = lappend(aexprs, node);
}
newa = makeNode(ArrayExpr);
/* array_collid will be set by parse_collate.c */
newa->element_typeid = scalar_type;
newa->array_typeid = array_type;
newa->multidims = false;
newa->elements = aexprs;
newa->location = -1;
/*
* Try to cast this expression to Const. Due to current strict
* transformation rules it should be done [almost] every time.
*/
newa = (ArrayExpr *) eval_const_expressions(NULL, (Node *) newa);
opertup = SearchSysCache1(OPEROID,
ObjectIdGetDatum(entry->key.opno));
if (!HeapTupleIsValid(opertup))
elog(ERROR, "cache lookup failed for operator %u",
entry->key.opno);
operform = (Form_pg_operator) GETSTRUCT(opertup);
if (!OperatorIsVisible(entry->key.opno))
namelist = lappend(namelist, makeString(get_namespace_name(operform->oprnamespace)));
namelist = lappend(namelist, makeString(pstrdup(NameStr(operform->oprname))));
ReleaseSysCache(opertup);
saopexpr =
(ScalarArrayOpExpr *)
make_scalar_array_op(NULL,
namelist,
true,
(Node *) entry->key.expr,
(Node *) newa,
-1);
saopexpr->inputcollid = entry->key.inputcollid;
neworlist = lappend(neworlist, (void *) saopexpr);
}
else
{
/*
* If the const node's (right side of operator expression) type
* don't have true array type, then we cannnot do the
* transformation. We simply concatenate the expression node.
*/
list_free(entry->consts);
neworlist = list_concat(neworlist, entry->exprs);
}
}
hash_destroy(or_group_htab);
list_free(entries);
/* One more trick: assemble correct clause */
return neworlist;
}
/*
* canonicalize_qual
@ -980,22 +601,10 @@ process_duplicate_ors(List *orlist)
}
/*
* If no winners, we can't do OR-to-ANY transformation.
* If no winners, we can't transform the OR
*/
if (winners == NIL)
{
/*
* Make an attempt to group similar OR clauses into SAOP if the list
* is lengthy enough.
*/
if (or_to_any_transform_limit >= 0 &&
list_length(orlist) >= or_to_any_transform_limit)
orlist = transform_or_to_any(orlist);
/* Transformation could group all OR clauses to a single SAOP */
return (list_length(orlist) == 1) ?
(Expr *) linitial(orlist) : make_orclause(orlist);
}
return make_orclause(orlist);
/*
* Generate new OR list consisting of the remaining sub-clauses.
@ -1042,11 +651,6 @@ process_duplicate_ors(List *orlist)
}
}
/* Make an attempt to group similar OR clauses into ANY operation */
if (or_to_any_transform_limit >= 0 &&
list_length(neworlist) >= or_to_any_transform_limit)
neworlist = transform_or_to_any(neworlist);
/*
* Append reduced OR to the winners list, if it's not degenerate, handling
* the special case of one element correctly (can that really happen?).

View File

@ -3668,18 +3668,6 @@ struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
{"or_to_any_transform_limit", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the minimum length of the list of OR clauses to attempt the OR-to-ANY transformation."),
gettext_noop("Once the limit is reached, the planner will try to replace expression like "
"'x=c1 OR x=c2 ..' to the expression 'x = ANY(ARRAY[c1,c2,..])'"),
GUC_EXPLAIN
},
&or_to_any_transform_limit,
5, -1, INT_MAX,
NULL, NULL, NULL
},
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL

View File

@ -392,7 +392,6 @@
# - Planner Method Configuration -
#enable_async_append = on
#or_to_any_transform_limit = 5
#enable_bitmapscan = on
#enable_gathermerge = on
#enable_hashagg = on

View File

@ -65,7 +65,6 @@ extern PGDLLIMPORT int compute_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
extern JumbleState *JumbleQuery(Query *query);
extern JumbleState *JumbleExpr(Expr *expr, uint64 *exprId);
extern void EnableQueryId(void);
extern PGDLLIMPORT bool query_id_enabled;

View File

@ -133,8 +133,6 @@ extern void extract_query_dependencies(Node *query,
/* in prep/prepqual.c: */
extern PGDLLIMPORT int or_to_any_transform_limit;
extern Node *negate_clause(Node *node);
extern Expr *canonicalize_qual(Expr *qual, bool is_check);

View File

@ -1889,165 +1889,6 @@ SELECT count(*) FROM tenk1
10
(1 row)
SET or_to_any_transform_limit = 0;
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
QUERY PLAN
------------------------------------------------------------------------------
Index Scan using tenk1_thous_tenthous on tenk1
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[])))
(2 rows)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
(1 row)
SET or_to_any_transform_limit = 3;
EXPLAIN (COSTS OFF) -- or_transformation still works
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
QUERY PLAN
------------------------------------------------------------------------------
Index Scan using tenk1_thous_tenthous on tenk1
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[])))
(2 rows)
SET or_to_any_transform_limit = 4;
EXPLAIN (COSTS OFF) -- or_transformation must be disabled
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1
Recheck Cond: (((thousand = 42) AND (tenthous = 1)) OR ((thousand = 42) AND (tenthous = 3)) OR ((thousand = 42) AND (tenthous = 42)))
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = 1))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = 3))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = 42))
(9 rows)
RESET or_to_any_transform_limit;
SET or_to_any_transform_limit = 0;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
QUERY PLAN
------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: ((hundred = 42) AND (thousand = ANY ('{42,99}'::integer[])))
-> BitmapAnd
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 42)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = ANY ('{42,99}'::integer[]))
(8 rows)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
count
-------
10
(1 row)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
QUERY PLAN
------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: ((hundred = 42) AND (thousand < ANY ('{42,99,43,42}'::integer[])))
-> BitmapAnd
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 42)
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand < ANY ('{42,99,43,42}'::integer[]))
(8 rows)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: (((thousand = 42) AND (tenthous = ANY ('{1,3}'::integer[]))) OR (thousand = 41))
-> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3}'::integer[])))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 41)
(8 rows)
SELECT count(*) FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
count
-------
10
(1 row)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: (((hundred = 42) AND ((thousand = ANY ('{42,99}'::integer[])) 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 = 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)
(14 rows)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
count
-------
20
(1 row)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1
Recheck Cond: ((hundred = 42) AND ((thousand = ANY ('{42,41}'::integer[])) 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 = ANY ('{42,41}'::integer[]))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: ((thousand = 99) AND (tenthous = 2))
(11 rows)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
count
-------
10
(1 row)
RESET or_to_any_transform_limit;
--
-- Check behavior with duplicate index column contents
--

View File

@ -4233,56 +4233,6 @@ select * from tenk1 a join tenk1 b on
Index Cond: (unique2 = 7)
(19 rows)
SET or_to_any_transform_limit = 0;
explain (costs off)
select * from tenk1 a join tenk1 b on
(a.unique1 = 1 and b.unique1 = 2) or
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Nested Loop
Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = ANY ('{3,7}'::integer[])) AND (b.hundred = 4)))
-> Bitmap Heap Scan on tenk1 b
Recheck Cond: ((unique1 = 2) OR (hundred = 4))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 2)
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred = 4)
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 = 1) OR (unique2 = ANY ('{3,7}'::integer[])))
-> BitmapOr
-> Bitmap Index Scan on tenk1_unique1
Index Cond: (unique1 = 1)
-> Bitmap Index Scan on tenk1_unique2
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
(17 rows)
explain (costs off)
select * from tenk1 a join tenk1 b on
(a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop
Join Filter: ((a.unique1 < 20) OR (a.unique1 = 3) OR ((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = ANY ('{3,7}'::integer[])) AND (b.hundred = 4)))
-> Seq Scan on tenk1 b
-> Materialize
-> Bitmap Heap Scan on tenk1 a
Recheck Cond: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR (unique2 = ANY ('{3,7}'::integer[])))
-> 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[]))
(15 rows)
RESET or_to_any_transform_limit;
--
-- test placement of movable quals in a parameterized join tree
--

View File

@ -3,7 +3,6 @@
--
-- Force generic plans to be used for all prepared statements in this file.
set plan_cache_mode = force_generic_plan;
set or_to_any_transform_limit = 0;
create table lp (a char) partition by list (a);
create table lp_default partition of lp default;
create table lp_ef partition of lp for values in ('e', 'f');
@ -83,23 +82,23 @@ explain (costs off) select * from lp where a is null;
(2 rows)
explain (costs off) select * from lp where a = 'a' or a = 'c';
QUERY PLAN
-----------------------------------------------
QUERY PLAN
----------------------------------------------------------
Append
-> Seq Scan on lp_ad lp_1
Filter: (a = ANY ('{a,c}'::bpchar[]))
Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar))
-> Seq Scan on lp_bc lp_2
Filter: (a = ANY ('{a,c}'::bpchar[]))
Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar))
(5 rows)
explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c');
QUERY PLAN
---------------------------------------------------------------------
QUERY PLAN
--------------------------------------------------------------------------------
Append
-> Seq Scan on lp_ad lp_1
Filter: ((a IS NOT NULL) AND (a = ANY ('{a,c}'::bpchar[])))
Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar)))
-> Seq Scan on lp_bc lp_2
Filter: ((a IS NOT NULL) AND (a = ANY ('{a,c}'::bpchar[])))
Filter: ((a IS NOT NULL) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar)))
(5 rows)
explain (costs off) select * from lp where a <> 'g';
@ -516,10 +515,10 @@ explain (costs off) select * from rlp where a <= 31;
(27 rows)
explain (costs off) select * from rlp where a = 1 or a = 7;
QUERY PLAN
------------------------------------------
QUERY PLAN
--------------------------------
Seq Scan on rlp2 rlp
Filter: (a = ANY ('{1,7}'::integer[]))
Filter: ((a = 1) OR (a = 7))
(2 rows)
explain (costs off) select * from rlp where a = 1 or b = 'ab';
@ -597,13 +596,13 @@ explain (costs off) select * from rlp where a < 1 or (a > 20 and a < 25);
-- where clause contradicts sub-partition's constraint
explain (costs off) select * from rlp where a = 20 or a = 40;
QUERY PLAN
--------------------------------------------------
QUERY PLAN
----------------------------------------
Append
-> Seq Scan on rlp4_1 rlp_1
Filter: (a = ANY ('{20,40}'::integer[]))
Filter: ((a = 20) OR (a = 40))
-> Seq Scan on rlp5_default rlp_2
Filter: (a = ANY ('{20,40}'::integer[]))
Filter: ((a = 20) OR (a = 40))
(5 rows)
explain (costs off) select * from rlp3 where a = 20; /* empty */
@ -2073,10 +2072,10 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde';
explain (costs off) select * from hp where a = 1 and b = 'abcde' and
(c = 2 or c = 3);
QUERY PLAN
--------------------------------------------------------------------------------
QUERY PLAN
----------------------------------------------------------------------
Seq Scan on hp2 hp
Filter: ((c = ANY ('{2,3}'::integer[])) AND (a = 1) AND (b = 'abcde'::text))
Filter: ((a = 1) AND (b = 'abcde'::text) AND ((c = 2) OR (c = 3)))
(2 rows)
drop table hp2;

View File

@ -738,51 +738,6 @@ SELECT count(*) FROM tenk1
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
SET or_to_any_transform_limit = 0;
EXPLAIN (COSTS OFF)
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
SET or_to_any_transform_limit = 3;
EXPLAIN (COSTS OFF) -- or_transformation still works
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
SET or_to_any_transform_limit = 4;
EXPLAIN (COSTS OFF) -- or_transformation must be disabled
SELECT * FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
RESET or_to_any_transform_limit;
SET or_to_any_transform_limit = 0;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
SELECT count(*) FROM tenk1
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
SELECT count(*) FROM tenk1
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
RESET or_to_any_transform_limit;
--
-- Check behavior with duplicate index column contents
--

View File

@ -1409,17 +1409,6 @@ select * from tenk1 a join tenk1 b on
(a.unique1 = 1 and b.unique1 = 2) or
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
SET or_to_any_transform_limit = 0;
explain (costs off)
select * from tenk1 a join tenk1 b on
(a.unique1 = 1 and b.unique1 = 2) or
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
explain (costs off)
select * from tenk1 a join tenk1 b on
(a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
RESET or_to_any_transform_limit;
--
-- test placement of movable quals in a parameterized join tree
--

View File

@ -4,7 +4,6 @@
-- Force generic plans to be used for all prepared statements in this file.
set plan_cache_mode = force_generic_plan;
set or_to_any_transform_limit = 0;
create table lp (a char) partition by list (a);
create table lp_default partition of lp default;
@ -22,7 +21,6 @@ explain (costs off) select * from lp where a is not null;
explain (costs off) select * from lp where a is null;
explain (costs off) select * from lp where a = 'a' or a = 'c';
explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c');
explain (costs off) select * from lp where a <> 'g';
explain (costs off) select * from lp where a <> 'a' and a <> 'd';
explain (costs off) select * from lp where a not in ('a', 'd');

View File

@ -1703,8 +1703,6 @@ NumericVar
OM_uint32
OP
OSAPerGroupState
OrClauseGroupEntry
OrClauseGroupKey
OSAPerQueryState
OSInfo
OSSLCipher