Hi! Thank you for your review! Sorryforthe delayin responding.
Irewrotethe patchasyourequested,butnowI'm facedwiththe problemof processingthe elementsof the or_entries list.For somereason, thepointerto thelistis cleared and I couldn't find the place where it happened.MaybeI'mmissingsomethingsimpleinviewof the heavyworkloadright now,butmaybeyou'll seea problem?Ihave displayedpart of stackbelow.
#5 0x00005b0f6d9f6a6a in ExceptionalCondition (conditionName=0x5b0f6dbb74f7 "IsPointerList(list)", fileName=0x5b0f6dbb7418 "list.c", lineNumber=341) at assert.c:66 #6 0x00005b0f6d5dc3ba in lappend (list=0x5b0f6eec5ca0, datum=0x5b0f6eec0d90) at list.c:341 #7 0x00005b0f6d69230c in transform_or_to_any (root=0x5b0f6eeb13c8, orlist=0x5b0f6eec57c0) at initsplan.c:2818 #8 0x00005b0f6d692958 in add_base_clause_to_rel (root=0x5b0f6eeb13c8, relid=1, restrictinfo=0x5b0f6eec5990) at initsplan.c:2982 #9 0x00005b0f6d692e5f in distribute_restrictinfo_to_rels (root=0x5b0f6eeb13c8, restrictinfo=0x5b0f6eec5990) at initsplan.c:3175 #10 0x00005b0f6d691bf2 in distribute_qual_to_rels (root=0x5b0f6eeb13c8, clause=0x5b0f6eec0fc0, jtitem=0x5b0f6eec4330, sjinfo=0x0, security_level=0, qualscope=0x5b0f6eec4730, ojscope=0x0, outerjoin_nonnullable=0x0, incompatible_relids=0x0, allow_equivalence=true, has_clone=false, is_clone=false, postponed_oj_qual_list=0x0) at initsplan.c:2576 #11 0x00005b0f6d69146f in distribute_quals_to_rels (root=0x5b0f6eeb13c8, clauses=0x5b0f6eec0bb0, jtitem=0x5b0f6eec4330, sjinfo=0x0, security_level=0, qualscope=0x5b0f6eec4730, ojscope=0x0, outerjoin_nonnullable=0x0, incompatible_relids=0x0, allow_equivalence=true, has_clone=false, is_clone=false, postponed_oj_qual_list=0x0) at initsplan.c:2144
Thisis stillthe firstiterationof the fixesyouhave proposed,soI have attachedthe patchindiffformat.I rewroteit,asyousuggestedinthe firstletter[0].Icreateda separatefunctionthattriesto forman OrClauseGroup node,butifit failsinthis, it returnsfalse,otherwiseit processesthe generatedelementaccordingtowhat it found-eitheraddsit to thelistasnew,oraddsa constantto anexistingone.
Ialsodividedonegenerallistof suitableforconversionandunsuitableintotwodifferentones:appropriate_entriesandor_entries.Nowweare onlylookinginthe listof suitableelementstoformANYexpr.
Thishelpsusto get ridofrepetitionsinthe codeyoumentioned. Pleasewriteifthisis notthelogicthatyouhave seenbefore.
[0] https://www.postgresql.org/message-id/3381819.e9J7NaK4W3%40thinkpad-pgpro
-- Regards, Alena Rybakina Postgres Professional:http://www.postgrespro.com The Russian Postgres Company
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e2c68fe6f99..1fdada54e23 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -14,9 +14,13 @@ */ #include "postgres.h" +#include "catalog/namespace.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" +#include "common/hashfn.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/queryjumble.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/inherit.h" @@ -29,8 +33,11 @@ #include "optimizer/planner.h" #include "optimizer/restrictinfo.h" #include "parser/analyze.h" +#include "parser/parse_coerce.h" +#include "parser/parse_oper.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" +#include "utils/syscache.h" #include "utils/rel.h" #include "utils/typcache.h" @@ -38,7 +45,6 @@ int from_collapse_limit; int join_collapse_limit; - /* * deconstruct_jointree requires multiple passes over the join tree, because we * need to finish computing JoinDomains before we start distributing quals. @@ -2617,6 +2623,287 @@ check_redundant_nullability_qual(PlannerInfo *root, Node *clause) return false; } +static bool +try_get_appropriable_group(RestrictInfo *rinfo, List **appropriate_group) +{ + OpExpr *orqual; + Node *const_expr; + Node *nconst_expr; + Oid opno; + Oid consttype; + Node *leftop, + *rightop; + ListCell *lc2; + bool found = false; + OrClauseGroup *entry; + + if (!IsA(rinfo, RestrictInfo) || !IsA(rinfo->clause, OpExpr)) + { + return false; + } + + orqual = (OpExpr *) rinfo->clause; + opno = orqual->opno; + if (get_op_rettype(opno) != BOOLOID) + { + /* Only operator returning boolean suits OR -> ANY transformation */ + return false; + } + + /* + * 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) || IsA(leftop, Param)) + { + opno = get_commutator(opno); + + if (!OidIsValid(opno)) + {/* commutator doesn't exist, we can't reverse the order */ + return false; + } + + nconst_expr = get_rightop(orqual); + const_expr = get_leftop(orqual); + } + else if (IsA(rightop, Const) || IsA(rightop, Param)) + { + const_expr = get_rightop(orqual); + nconst_expr = get_leftop(orqual); + } + else + { + return false; + } + + /* + * 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)) + { + return false; + } + + entry = makeNode(OrClauseGroup); + + foreach(lc2, *appropriate_group) + { + + if (!IsA(lfirst(lc2), OrClauseGroup)) + Assert(0); + + entry = (OrClauseGroup *) lfirst(lc2); + + if (entry->opno == opno && + entry->consttype == consttype && + entry->inputcollid == exprCollation(const_expr) && + equal(entry->expr, nconst_expr)) + { + found = true; + break; + } + } + + if (!found) + { + entry->expr = (Expr *) nconst_expr; + entry->exprs = list_make1((void *) orqual); + entry->opno = opno; + entry->inputcollid = exprCollation(const_expr); + entry->consttype = consttype; + entry->consts = list_make1(const_expr); + *appropriate_group = lappend(*appropriate_group, entry); + } + else + { + entry->consts = lappend(entry->consts, const_expr); + entry->exprs = lappend(entry->exprs, (void *) orqual); + } + + return true; +} + +/* + * 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(PlannerInfo *root, List *orlist) +{ + List *appropriate_entries = NIL; + List *or_entries = NIL; + ListCell *lc; + int len_ors = list_length(orlist); + OrClauseGroup *entry = NULL; + bool found; + + Assert(len_ors >= 2); + + foreach(lc, orlist) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + /* + * 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. + */ + found = try_get_appropriable_group(rinfo, &appropriate_entries); + + if (!found) + or_entries = lappend(or_entries, (void *) rinfo->clause); + } + + /* 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, appropriate_entries) + { + Oid scalar_type; + Oid array_type; + + if (!IsA(lfirst(lc), OrClauseGroup)) + { + Assert(0); + } + + entry = (OrClauseGroup *) 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); + or_entries = lappend(or_entries, linitial(entry->exprs)); + continue; + } + + /* + * Do the transformation. + */ + scalar_type = entry->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->opno)); + if (!HeapTupleIsValid(opertup)) + elog(ERROR, "cache lookup failed for operator %u", + entry->opno); + + operform = (Form_pg_operator) GETSTRUCT(opertup); + if (!OperatorIsVisible(entry->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->expr, + (Node *) newa, + -1); + saopexpr->inputcollid = entry->inputcollid; + + or_entries = lappend(or_entries, (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); + or_entries = list_concat(or_entries, entry->exprs); + } + } + list_free(appropriate_entries); + + /* One more trick: assemble correct clause */ + return or_entries; +} + /* * add_base_clause_to_rel * Add 'restrictinfo' as a baserestrictinfo to the base relation denoted @@ -2677,6 +2964,36 @@ add_base_clause_to_rel(PlannerInfo *root, Index relid, } } + if (restriction_is_or_clause(restrictinfo)) + { + BoolExpr *orExpr = (BoolExpr *) restrictinfo->orclause; + + Assert(is_orclause(restrictinfo->orclause)); + + if (list_length(orExpr->args) >= 2) + { + List *or_list = transform_or_to_any(root, orExpr->args); + Expr *clause = list_length(or_list) > 1 ? + makeBoolExpr(OR_EXPR, or_list, + ((BoolExpr *) restrictinfo->clause)->location) : + linitial(or_list); + + RestrictInfo *rinfo; + rinfo = make_restrictinfo(root, + clause, + restrictinfo->is_pushed_down, + restrictinfo->has_clone, + restrictinfo->is_clone, + restrictinfo->pseudoconstant, + restrictinfo->security_level, + restrictinfo->required_relids, + restrictinfo->incompatible_relids, + restrictinfo->outer_relids); + + restrictinfo = rinfo; + } + } + /* Add clause to rel's restriction list */ rel->baserestrictinfo = lappend(rel->baserestrictinfo, restrictinfo); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 14ccfc1ac1c..f48ddb8c385 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2708,6 +2708,37 @@ typedef struct RestrictInfo Oid right_hasheqoperator pg_node_attr(equal_ignore); } RestrictInfo; +/* + * The group of similar operator expressions in transform_or_to_any(). + */ +typedef struct OrClauseGroup +{ + pg_node_attr(nodetag_only) + + 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; + + /* 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; + + Node *const_expr; +} OrClauseGroup; + + /* * This macro embodies the correct way to test whether a RestrictInfo is * "pushed down" to a given outer join, that is, should be treated as a filter diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h index f1c55c8067f..5643ee8f651 100644 --- a/src/include/nodes/queryjumble.h +++ b/src/include/nodes/queryjumble.h @@ -65,6 +65,7 @@ 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; diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index cf6eac57349..421a645fae0 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -1844,18 +1844,73 @@ DROP TABLE onek_with_null; EXPLAIN (COSTS OFF) SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ + 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) + Recheck Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[]))) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[]))) +(4 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) + +EXPLAIN (COSTS OFF) +SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42); + QUERY PLAN +---------------------------------------------------------------------------------------------- + Bitmap Heap Scan on tenk1 + Recheck Cond: ((thousand = 42) AND (tenthous = ANY (ARRAY[1, (InitPlan 1).col1, 42]))) + InitPlan 1 + -> Result + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: ((thousand = 42) AND (tenthous = ANY (ARRAY[1, (InitPlan 1).col1, 42]))) +(6 rows) + +SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) 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) + +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 * 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 = ANY ('{1,3,42}'::integer[]))) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[]))) +(4 rows) SELECT * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); @@ -1867,23 +1922,123 @@ SELECT * FROM tenk1 EXPLAIN (COSTS OFF) SELECT count(*) FROM tenk1 WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); - QUERY PLAN ---------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------ Aggregate -> Bitmap Heap Scan on tenk1 - Recheck Cond: ((hundred = 42) AND ((thousand = 42) OR (thousand = 99))) + 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) + +SELECT count(*) FROM tenk1 + WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand); + count +------- + 10 +(1 row) + +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 = 1)) OR ((thousand = 42) AND (tenthous = 3))) OR (thousand = 41)) + -> BitmapOr -> BitmapOr -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = 42) + Index Cond: ((thousand = 42) AND (tenthous = 1)) -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = 99) + Index Cond: ((thousand = 42) AND (tenthous = 3)) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: (thousand = 41) (11 rows) SELECT count(*) FROM tenk1 - WHERE hundred = 42 AND (thousand = 42 OR thousand = 99); + 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 = 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) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: (tenthous < 2) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: (thousand = 41) +(16 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)))) + 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 = ANY ('{42,41}'::integer[])) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: ((thousand = 99) AND (tenthous = 2)) +(12 rows) + +SELECT count(*) FROM tenk1 + WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2); count ------- 10 diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 9142dab171f..59d2617295f 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -4234,6 +4234,58 @@ select * from tenk1 a join tenk1 b on Index Cond: (unique2 = 7) (19 rows) +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 = 3) OR (a.unique2 = 7)) 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 = 3) OR (unique2 = 7)) + -> 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_unique2 + Index Cond: (unique2 = 7) +(19 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 = 3) OR (a.unique2 = 7)) 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 = 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 = 3) + -> Bitmap Index Scan on tenk1_unique2 + Index Cond: (unique2 = 7) +(17 rows) + -- -- test placement of movable quals in a parameterized join tree -- diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index 8c4da955084..7678744181c 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -1322,19 +1322,19 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND b = ''1'''); estimated | actual -----------+-------- - 99 | 100 + 100 | 100 (1 row) SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 51) AND (b = ''1'' OR b = ''2'')'); estimated | actual -----------+-------- - 99 | 100 + 100 | 100 (1 row) SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE (a = 1 OR a = 2 OR a = 51 OR a = 52) AND (b = ''1'' OR b = ''2'')'); estimated | actual -----------+-------- - 197 | 200 + 200 | 200 (1 row) -- OR clauses referencing different attributes are incompatible @@ -1664,19 +1664,19 @@ SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND upper(b) = ''1'''); estimated | actual -----------+-------- - 99 | 100 + 100 | 100 (1 row) SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 102) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); estimated | actual -----------+-------- - 99 | 100 + 100 | 100 (1 row) SELECT * FROM check_estimated_rows('SELECT * FROM functional_dependencies WHERE ((a * 2) = 2 OR (a * 2) = 4 OR (a * 2) = 102 OR (a * 2) = 104) AND (upper(b) = ''1'' OR upper(b) = ''2'')'); estimated | actual -----------+-------- - 197 | 200 + 200 | 200 (1 row) -- OR clauses referencing different attributes diff --git a/src/test/regress/expected/tidscan.out b/src/test/regress/expected/tidscan.out index f133b5a4ac7..0f6d3d323d4 100644 --- a/src/test/regress/expected/tidscan.out +++ b/src/test/regress/expected/tidscan.out @@ -43,11 +43,12 @@ SELECT ctid, * FROM tidscan WHERE '(0,1)' = ctid; -- OR'd clauses EXPLAIN (COSTS OFF) SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Tid Scan on tidscan - TID Cond: ((ctid = '(0,2)'::tid) OR ('(0,1)'::tid = ctid)) -(2 rows) + TID Cond: (ctid = ANY ('{"(0,2)","(0,1)"}'::tid[])) + Filter: ((ctid = '(0,2)'::tid) OR ('(0,1)'::tid = ctid)) +(3 rows) SELECT ctid, * FROM tidscan WHERE ctid = '(0,2)' OR '(0,1)' = ctid; ctid | id diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index e296891cab8..f74ad415fbf 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -726,6 +726,24 @@ DROP TABLE onek_with_null; -- Check bitmap index path planning -- +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); + +EXPLAIN (COSTS OFF) +SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42); +SELECT * FROM tenk1 + WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42); + +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 * FROM tenk1 WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42); @@ -738,6 +756,30 @@ SELECT count(*) FROM tenk1 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); +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); + -- -- Check behavior with duplicate index column contents -- diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index e3d26520832..cf02cc34ed0 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1409,6 +1409,15 @@ 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 = 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); + -- -- test placement of movable quals in a parameterized join tree -- diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index 442428d937c..3d2ce04f9e2 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -40,6 +40,7 @@ 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'); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 9320e4d8080..4ec9e2ce32f 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1715,6 +1715,7 @@ NumericVar OM_uint32 OP OSAPerGroupState +OrClauseGroup OSAPerQueryState OSInfo OSSLCipher