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:
parent
5a15bdea3b
commit
ff9f72c68f
@ -6304,63 +6304,6 @@ SELECT * FROM parent WHERE key = 2400;
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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">
|
<varlistentry id="guc-plan-cache-mode" xreflabel="plan_cache_mode">
|
||||||
<term><varname>plan_cache_mode</varname> (<type>enum</type>)
|
<term><varname>plan_cache_mode</varname> (<type>enum</type>)
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
@ -141,33 +141,6 @@ JumbleQuery(Query *query)
|
|||||||
return jstate;
|
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.
|
* Enables query identifier computation.
|
||||||
*
|
*
|
||||||
|
@ -31,25 +31,16 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "catalog/namespace.h"
|
|
||||||
#include "catalog/pg_operator.h"
|
|
||||||
#include "common/hashfn.h"
|
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "nodes/queryjumble.h"
|
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
#include "parser/parse_coerce.h"
|
|
||||||
#include "parser/parse_oper.h"
|
|
||||||
#include "utils/lsyscache.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_ands(List *andlist);
|
||||||
static List *pull_ors(List *orlist);
|
static List *pull_ors(List *orlist);
|
||||||
static Expr *find_duplicate_ors(Expr *qual, bool is_check);
|
static Expr *find_duplicate_ors(Expr *qual, bool is_check);
|
||||||
static Expr *process_duplicate_ors(List *orlist);
|
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);
|
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
|
* 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)
|
if (winners == NIL)
|
||||||
{
|
return make_orclause(orlist);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate new OR list consisting of the remaining sub-clauses.
|
* 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
|
* Append reduced OR to the winners list, if it's not degenerate, handling
|
||||||
* the special case of one element correctly (can that really happen?).
|
* the special case of one element correctly (can that really happen?).
|
||||||
|
@ -3668,18 +3668,6 @@ struct config_int ConfigureNamesInt[] =
|
|||||||
NULL, NULL, NULL
|
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 */
|
/* End-of-list marker */
|
||||||
{
|
{
|
||||||
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
|
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
|
||||||
|
@ -392,7 +392,6 @@
|
|||||||
# - Planner Method Configuration -
|
# - Planner Method Configuration -
|
||||||
|
|
||||||
#enable_async_append = on
|
#enable_async_append = on
|
||||||
#or_to_any_transform_limit = 5
|
|
||||||
#enable_bitmapscan = on
|
#enable_bitmapscan = on
|
||||||
#enable_gathermerge = on
|
#enable_gathermerge = on
|
||||||
#enable_hashagg = on
|
#enable_hashagg = on
|
||||||
|
@ -65,7 +65,6 @@ extern PGDLLIMPORT int compute_query_id;
|
|||||||
|
|
||||||
extern const char *CleanQuerytext(const char *query, int *location, int *len);
|
extern const char *CleanQuerytext(const char *query, int *location, int *len);
|
||||||
extern JumbleState *JumbleQuery(Query *query);
|
extern JumbleState *JumbleQuery(Query *query);
|
||||||
extern JumbleState *JumbleExpr(Expr *expr, uint64 *exprId);
|
|
||||||
extern void EnableQueryId(void);
|
extern void EnableQueryId(void);
|
||||||
|
|
||||||
extern PGDLLIMPORT bool query_id_enabled;
|
extern PGDLLIMPORT bool query_id_enabled;
|
||||||
|
@ -133,8 +133,6 @@ extern void extract_query_dependencies(Node *query,
|
|||||||
|
|
||||||
/* in prep/prepqual.c: */
|
/* in prep/prepqual.c: */
|
||||||
|
|
||||||
extern PGDLLIMPORT int or_to_any_transform_limit;
|
|
||||||
|
|
||||||
extern Node *negate_clause(Node *node);
|
extern Node *negate_clause(Node *node);
|
||||||
extern Expr *canonicalize_qual(Expr *qual, bool is_check);
|
extern Expr *canonicalize_qual(Expr *qual, bool is_check);
|
||||||
|
|
||||||
|
@ -1889,165 +1889,6 @@ SELECT count(*) FROM tenk1
|
|||||||
10
|
10
|
||||||
(1 row)
|
(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
|
-- Check behavior with duplicate index column contents
|
||||||
--
|
--
|
||||||
|
@ -4233,56 +4233,6 @@ select * from tenk1 a join tenk1 b on
|
|||||||
Index Cond: (unique2 = 7)
|
Index Cond: (unique2 = 7)
|
||||||
(19 rows)
|
(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
|
-- test placement of movable quals in a parameterized join tree
|
||||||
--
|
--
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
--
|
--
|
||||||
-- Force generic plans to be used for all prepared statements in this file.
|
-- Force generic plans to be used for all prepared statements in this file.
|
||||||
set plan_cache_mode = force_generic_plan;
|
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 (a char) partition by list (a);
|
||||||
create table lp_default partition of lp default;
|
create table lp_default partition of lp default;
|
||||||
create table lp_ef partition of lp for values in ('e', 'f');
|
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)
|
(2 rows)
|
||||||
|
|
||||||
explain (costs off) select * from lp where a = 'a' or a = 'c';
|
explain (costs off) select * from lp where a = 'a' or a = 'c';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------
|
----------------------------------------------------------
|
||||||
Append
|
Append
|
||||||
-> Seq Scan on lp_ad lp_1
|
-> 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
|
-> Seq Scan on lp_bc lp_2
|
||||||
Filter: (a = ANY ('{a,c}'::bpchar[]))
|
Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar))
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
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 is not null and (a = 'a' or a = 'c');
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
---------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
Append
|
Append
|
||||||
-> Seq Scan on lp_ad lp_1
|
-> 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
|
-> 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)
|
(5 rows)
|
||||||
|
|
||||||
explain (costs off) select * from lp where a <> 'g';
|
explain (costs off) select * from lp where a <> 'g';
|
||||||
@ -516,10 +515,10 @@ explain (costs off) select * from rlp where a <= 31;
|
|||||||
(27 rows)
|
(27 rows)
|
||||||
|
|
||||||
explain (costs off) select * from rlp where a = 1 or a = 7;
|
explain (costs off) select * from rlp where a = 1 or a = 7;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------------------
|
--------------------------------
|
||||||
Seq Scan on rlp2 rlp
|
Seq Scan on rlp2 rlp
|
||||||
Filter: (a = ANY ('{1,7}'::integer[]))
|
Filter: ((a = 1) OR (a = 7))
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
explain (costs off) select * from rlp where a = 1 or b = 'ab';
|
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
|
-- where clause contradicts sub-partition's constraint
|
||||||
explain (costs off) select * from rlp where a = 20 or a = 40;
|
explain (costs off) select * from rlp where a = 20 or a = 40;
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------
|
----------------------------------------
|
||||||
Append
|
Append
|
||||||
-> Seq Scan on rlp4_1 rlp_1
|
-> 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
|
-> Seq Scan on rlp5_default rlp_2
|
||||||
Filter: (a = ANY ('{20,40}'::integer[]))
|
Filter: ((a = 20) OR (a = 40))
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
explain (costs off) select * from rlp3 where a = 20; /* empty */
|
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
|
explain (costs off) select * from hp where a = 1 and b = 'abcde' and
|
||||||
(c = 2 or c = 3);
|
(c = 2 or c = 3);
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
--------------------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Seq Scan on hp2 hp
|
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)
|
(2 rows)
|
||||||
|
|
||||||
drop table hp2;
|
drop table hp2;
|
||||||
|
@ -738,51 +738,6 @@ SELECT count(*) FROM tenk1
|
|||||||
SELECT count(*) FROM tenk1
|
SELECT count(*) FROM tenk1
|
||||||
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
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
|
-- Check behavior with duplicate index column contents
|
||||||
--
|
--
|
||||||
|
@ -1409,17 +1409,6 @@ select * from tenk1 a join tenk1 b on
|
|||||||
(a.unique1 = 1 and b.unique1 = 2) or
|
(a.unique1 = 1 and b.unique1 = 2) or
|
||||||
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
((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
|
-- test placement of movable quals in a parameterized join tree
|
||||||
--
|
--
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
-- Force generic plans to be used for all prepared statements in this file.
|
-- Force generic plans to be used for all prepared statements in this file.
|
||||||
set plan_cache_mode = force_generic_plan;
|
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 (a char) partition by list (a);
|
||||||
create table lp_default partition of lp default;
|
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 is null;
|
||||||
explain (costs off) select * from lp where a = 'a' or a = 'c';
|
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 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 <> 'g';
|
||||||
explain (costs off) select * from lp where a <> 'a' and a <> 'd';
|
explain (costs off) select * from lp where a <> 'a' and a <> 'd';
|
||||||
explain (costs off) select * from lp where a not in ('a', 'd');
|
explain (costs off) select * from lp where a not in ('a', 'd');
|
||||||
|
@ -1703,8 +1703,6 @@ NumericVar
|
|||||||
OM_uint32
|
OM_uint32
|
||||||
OP
|
OP
|
||||||
OSAPerGroupState
|
OSAPerGroupState
|
||||||
OrClauseGroupEntry
|
|
||||||
OrClauseGroupKey
|
|
||||||
OSAPerQueryState
|
OSAPerQueryState
|
||||||
OSInfo
|
OSInfo
|
||||||
OSSLCipher
|
OSSLCipher
|
||||||
|
Loading…
x
Reference in New Issue
Block a user