diff --git a/src/backend/gpopt/translate/CTranslatorRelcacheToDXL.cpp b/src/backend/gpopt/translate/CTranslatorRelcacheToDXL.cpp index aa23d3932a2..119dc18f4df 100644 --- a/src/backend/gpopt/translate/CTranslatorRelcacheToDXL.cpp +++ b/src/backend/gpopt/translate/CTranslatorRelcacheToDXL.cpp @@ -2692,11 +2692,14 @@ CTranslatorRelcacheToDXL::RetrievePartKeysAndTypes(CMemoryPool *mp, GPOS_WSZ_LIT("partitioning by expression")); } - if (PARTITION_STRATEGY_HASH == part_type) - { - GPOS_RAISE(gpdxl::ExmaMD, gpdxl::ExmiMDObjUnsupported, - GPOS_WSZ_LIT("hash partitioning")); - } + // Hash-partitioned tables are supported for scanning only. ORCA cannot + // express a hash partition's membership rule (the satisfies_hash_partition + // call, i.e. hash(key) mod modulus = remainder) as a btree interval + // constraint, so it does no static partition pruning for hash partitions + // and simply scans every leaf partition. Range and list partitions keep + // full pruning support. Letting the strategy flow through here (instead of + // raising ExmiMDObjUnsupported) keeps such queries inside ORCA rather than + // falling back to the Postgres planner. (*part_keys)->Append(GPOS_NEW(mp) ULONG(attno - 1)); (*part_types)->Append(GPOS_NEW(mp) CHAR(part_type)); diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CExpressionPreprocessor.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CExpressionPreprocessor.h index 978d2575e42..58965fb2e9b 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CExpressionPreprocessor.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CExpressionPreprocessor.h @@ -215,6 +215,24 @@ class CExpressionPreprocessor CColRefArray *pdrgpcrOutput, ColRefToUlongMap *col_mapping); + // translate a child partition's part-constraint DXL into a CExpression over + // the root table's colrefs (NULL if the child has no stored constraint) + static CExpression *PexprPartConstraintFromChild( + CMemoryPool *mp, const IMDRelation *partrel, CColRefArray *pdrgpcrOutput, + ColRefToUlongMap *root_col_mapping); + + // find a "colref = const" equality among the given conjuncts and return its + // const subexpression (NULL if none) + static CExpression *PexprColumnEqualityConst( + CExpressionArray *pdrgpexprConjuncts, const CColRef *colref); + + // static hash-partition pruning: true if the leaf provably cannot contain + // any row matching the query's equality predicates on the partition key + static BOOL FHashPartitionPruned(CMemoryPool *mp, const IMDRelation *partrel, + CColRefArray *pdrgpcrOutput, + ColRefToUlongMap *root_col_mapping, + CExpressionArray *pdrgpexprConjuncts); + // swap logical select over logical project static CExpression *PexprTransposeSelectAndProject(CMemoryPool *mp, CExpression *pexpr); diff --git a/src/backend/gporca/libgpopt/src/eval/CConstExprEvaluatorDXL.cpp b/src/backend/gporca/libgpopt/src/eval/CConstExprEvaluatorDXL.cpp index 80ac01b8482..47d90f0e101 100644 --- a/src/backend/gporca/libgpopt/src/eval/CConstExprEvaluatorDXL.cpp +++ b/src/backend/gporca/libgpopt/src/eval/CConstExprEvaluatorDXL.cpp @@ -24,6 +24,8 @@ #include "gpopt/operators/CExpression.h" #include "gpopt/operators/CPredicateUtils.h" +#include "naucrates/md/IMDFunction.h" + using namespace gpdxl; using namespace gpmd; using namespace gpopt; @@ -72,7 +74,15 @@ CConstExprEvaluatorDXL::PexprEval(CExpression *pexpr) { GPOS_ASSERT(nullptr != pexpr); - if (!CPredicateUtils::FCompareConstToConstIgnoreCast(pexpr)) + // We can evaluate (a) the (const cmp const) expressions used by the + // comparator, and (b) any column-free IMMUTABLE expression (e.g. + // satisfies_hash_partition() over constants, used for hash partition + // pruning). We must not fold expressions that reference columns, nor + // volatile/stable ones, since their value is not fixed at plan time. + if (!(CPredicateUtils::FCompareConstToConstIgnoreCast(pexpr) || + (0 == pexpr->DeriveUsedColumns()->Size() && + IMDFunction::EfsImmutable == + pexpr->DeriveScalarFunctionProperties()->Efs()))) { GPOS_RAISE(gpopt::ExmaGPOPT, gpopt::ExmiEvalUnsupportedScalarExpr); } diff --git a/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp b/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp index e1c46c9fc8e..6755cc0714e 100644 --- a/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp @@ -22,6 +22,7 @@ #include "gpopt/base/CConstraintInterval.h" #include "gpopt/base/COptCtxt.h" #include "gpopt/base/CUtils.h" +#include "gpopt/eval/IConstExprEvaluator.h" #include "gpopt/exception.h" #include "gpopt/mdcache/CMDAccessor.h" #include "gpopt/operators/CDedupSupersetPreprocessor.h" @@ -49,6 +50,9 @@ #include "gpopt/operators/COrderedAggPreprocessor.h" #include "gpopt/operators/CPredicateUtils.h" #include "gpopt/operators/CScalarCmp.h" +#include "gpopt/operators/CScalarConst.h" +#include "gpopt/operators/CScalarFunc.h" +#include "gpopt/operators/CScalarIdent.h" #include "gpopt/operators/CScalarNAryJoinPredList.h" #include "gpopt/operators/CScalarProjectElement.h" #include "gpopt/operators/CScalarProjectList.h" @@ -59,6 +63,7 @@ #include "gpopt/optimizer/COptimizerConfig.h" #include "gpopt/translate/CTranslatorDXLToExpr.h" #include "gpopt/xforms/CXform.h" +#include "naucrates/base/IDatumBool.h" #include "naucrates/md/IMDScalarOp.h" #include "naucrates/md/IMDType.h" #include "naucrates/statistics/CStatistics.h" @@ -2848,13 +2853,29 @@ CExpressionPreprocessor::PrunePartitions(CMemoryPool *mp, CExpression *expr) CLogicalDynamicGet::PopConvert((*expr)[0]->Pop()); CColRefSetArray *pdrgpcrsChild = nullptr; - // As of now, partition's default opfamily is btree - // ORCA doesn't support hash partition yet + // pred_cnstr captures predicates on the partition key as a btree interval + // constraint, used to statically prune range/list partitions. Hash + // partitions have no btree-interval form and are pruned separately below + // via FHashPartitionPruned(). CConstraint *pred_cnstr = CConstraint::PcnstrFromScalarExpr( mp, filter_pred, &pdrgpcrsChild, false /* infer_nulls_as*/, IMDIndex::EmdindBtree); CRefCount::SafeRelease(pdrgpcrsChild); + // For hash-partitioned tables ORCA cannot build btree interval + // constraints, so static pruning is done separately by evaluating each + // leaf's satisfies_hash_partition() qual against the query's equality + // predicates (see FHashPartitionPruned). + const IMDRelation *root_rel = + mda->RetrieveRel(dyn_get->Ptabdesc()->MDId()); + BOOL fHashPartitioned = + root_rel->IsPartitioned() && + IMDRelation::ErelpartitionHash == root_rel->PartTypeAtLevel(0); + CExpressionArray *pdrgpexprConjuncts = + fHashPartitioned + ? CPredicateUtils::PdrgpexprConjuncts(mp, filter_pred) + : nullptr; + IMdIdArray *selected_partition_mdids = GPOS_NEW(mp) IMdIdArray(mp); CConstraintArray *selected_partition_cnstrs = GPOS_NEW(mp) CConstraintArray(mp); @@ -2866,9 +2887,26 @@ CExpressionPreprocessor::PrunePartitions(CMemoryPool *mp, CExpression *expr) IMDId *part_mdid = (*all_partition_mdids)[ul]; const IMDRelation *partrel = mda->RetrieveRel(part_mdid); - CConstraint *rel_cnstr = PcnstrFromChildPartition( - partrel, dyn_get->PdrgpcrOutput(), - (*dyn_get->GetRootColMappingPerPart())[ul]); + if (fHashPartitioned && + FHashPartitionPruned( + mp, partrel, dyn_get->PdrgpcrOutput(), + (*dyn_get->GetRootColMappingPerPart())[ul], + pdrgpexprConjuncts)) + { + // this hash leaf cannot match the equality predicates: prune it + continue; + } + + // For hash partitions PcnstrFromChildPartition always returns a null + // btree constraint (the qual is satisfies_hash_partition()), so skip + // the wasted DXL translation and keep rel_cnstr null. + CConstraint *rel_cnstr = nullptr; + if (!fHashPartitioned) + { + rel_cnstr = PcnstrFromChildPartition( + partrel, dyn_get->PdrgpcrOutput(), + (*dyn_get->GetRootColMappingPerPart())[ul]); + } CConstraint *pcnstr = nullptr; { @@ -2895,9 +2933,12 @@ CExpressionPreprocessor::PrunePartitions(CMemoryPool *mp, CExpression *expr) foreign_server_mdids->Append(foreign_server_mdid); part_mdid->AddRef(); selected_partition_mdids->Append(part_mdid); - rel_cnstr = PcnstrFromChildPartition( - partrel, dyn_get->PdrgpcrOutput(), - (*dyn_get->GetRootColMappingPerPart())[ul]); + if (!fHashPartitioned) + { + rel_cnstr = PcnstrFromChildPartition( + partrel, dyn_get->PdrgpcrOutput(), + (*dyn_get->GetRootColMappingPerPart())[ul]); + } if (rel_cnstr) { selected_partition_cnstrs->Append(rel_cnstr); @@ -2906,6 +2947,7 @@ CExpressionPreprocessor::PrunePartitions(CMemoryPool *mp, CExpression *expr) CRefCount::SafeRelease(pcnstr); } CRefCount::SafeRelease(pred_cnstr); + CRefCount::SafeRelease(pdrgpexprConjuncts); if (selected_partition_mdids->Size() == 0) { @@ -2958,21 +3000,18 @@ CExpressionPreprocessor::PrunePartitions(CMemoryPool *mp, CExpression *expr) return GPOS_NEW(mp) CExpression(mp, pop, children); } -// Translate the part constraint of a child partition into an ORCA expr using -// corresponding colrefs of the root table, instead of those from the child -// partition. -CConstraint * -CExpressionPreprocessor::PcnstrFromChildPartition( - const IMDRelation *partrel, CColRefArray *pdrgpcrOutput, +// Translate a child partition's stored part-constraint DXL into an ORCA +// CExpression using the corresponding colrefs of the root table, instead of +// those from the child partition. Returns NULL if the child has no stored +// constraint (e.g. a default partition). +CExpression * +CExpressionPreprocessor::PexprPartConstraintFromChild( + CMemoryPool *mp, const IMDRelation *partrel, CColRefArray *pdrgpcrOutput, ColRefToUlongMap *root_col_mapping) { CMDAccessor *md_accessor = COptCtxt::PoctxtFromTLS()->Pmda(); - CMemoryPool *mp = COptCtxt::PoctxtFromTLS()->Pmp(); - - CExpression *part_constraint_expr = nullptr; CDXLNode *dxlnode = partrel->MDPartConstraint(); - if (nullptr == dxlnode) { return nullptr; @@ -2995,25 +3034,216 @@ CExpressionPreprocessor::PcnstrFromChildPartition( } CTranslatorDXLToExpr dxltr(mp, md_accessor); - part_constraint_expr = + CExpression *part_constraint_expr = dxltr.PexprTranslateScalar(dxlnode, pdrgpcrOutput, mapped_colids); mapped_colids->Release(); + return part_constraint_expr; +} + +// Build a CConstraint from a child partition's part constraint, used for static +// pruning of range/list partitions. +CConstraint * +CExpressionPreprocessor::PcnstrFromChildPartition( + const IMDRelation *partrel, CColRefArray *pdrgpcrOutput, + ColRefToUlongMap *root_col_mapping) +{ + CMemoryPool *mp = COptCtxt::PoctxtFromTLS()->Pmp(); + + CExpression *part_constraint_expr = PexprPartConstraintFromChild( + mp, partrel, pdrgpcrOutput, root_col_mapping); + if (nullptr == part_constraint_expr) + { + return nullptr; + } + GPOS_ASSERT(CUtils::FPredicate(part_constraint_expr)); CColRefSetArray *pdrgpcrsChild = nullptr; CConstraint *cnstr; - // As of now, partition's default opfamily is btree - // ORCA doesn't support hash partition yet + // Partition constraints are extracted using the btree opfamily, which + // covers range and list partitions (their bounds are btree comparison + // predicates). A hash partition's constraint is a satisfies_hash_partition() + // function call that has no btree-interval representation, so + // PcnstrFromScalarExpr returns null for it; such partitions are pruned (when + // possible) by FHashPartitionPruned() instead. cnstr = CConstraint::PcnstrFromScalarExpr( mp, part_constraint_expr, &pdrgpcrsChild, true /* infer_nulls_as */, IMDIndex::EmdindBtree); CRefCount::SafeRelease(part_constraint_expr); CRefCount::SafeRelease(pdrgpcrsChild); - GPOS_ASSERT(cnstr); return cnstr; } +// Return the const subexpression of a "colref = const" equality found among the +// given conjuncts (NULL if none). The returned expression is owned by the +// conjunct array; the caller must AddRef it before reusing it. +CExpression * +CExpressionPreprocessor::PexprColumnEqualityConst( + CExpressionArray *pdrgpexprConjuncts, const CColRef *colref) +{ + const ULONG size = pdrgpexprConjuncts->Size(); + for (ULONG ul = 0; ul < size; ++ul) + { + CExpression *pexpr = (*pdrgpexprConjuncts)[ul]; + if (!CPredicateUtils::FPlainEqualityIdentConstWithoutCast(pexpr)) + { + continue; + } + + CExpression *pexprLeft = (*pexpr)[0]; + CExpression *pexprRight = (*pexpr)[1]; + CExpression *pexprIdent = nullptr; + CExpression *pexprConst = nullptr; + if (COperator::EopScalarIdent == pexprLeft->Pop()->Eopid() && + COperator::EopScalarConst == pexprRight->Pop()->Eopid()) + { + pexprIdent = pexprLeft; + pexprConst = pexprRight; + } + else if (COperator::EopScalarIdent == pexprRight->Pop()->Eopid() && + COperator::EopScalarConst == pexprLeft->Pop()->Eopid()) + { + pexprIdent = pexprRight; + pexprConst = pexprLeft; + } + else + { + // ident = ident or const = const: not useful here + continue; + } + + // Skip a NULL constant: "col = NULL" never matches a row, so it must not + // drive pruning (substituting NULL into satisfies_hash_partition does not + // correspond to an equality match on a concrete key value). + if (colref == CScalarIdent::PopConvert(pexprIdent->Pop())->Pcr() && + !CScalarConst::PopConvert(pexprConst->Pop())->GetDatum()->IsNull()) + { + return pexprConst; + } + } + + return nullptr; +} + +// Static pruning of a single HASH partition leaf, mirroring PostgreSQL hash +// pruning by reusing satisfies_hash_partition(). The leaf's part constraint is +// satisfies_hash_partition(parentoid, modulus, remainder, key...). We substitute +// the equality constants for the partition-key columns (taken from the query +// predicates) into that call and evaluate it with the constant-expression +// evaluator. If the call evaluates to false, no row with those key values can +// live in this leaf, so it is pruned. Any uncertainty (no equality on some key, +// non-foldable constant, evaluator unavailable, non-bool result) keeps the leaf, +// so pruning is always safe. +BOOL +CExpressionPreprocessor::FHashPartitionPruned( + CMemoryPool *mp, const IMDRelation *partrel, CColRefArray *pdrgpcrOutput, + ColRefToUlongMap *root_col_mapping, CExpressionArray *pdrgpexprConjuncts) +{ + IConstExprEvaluator *pceeval = COptCtxt::PoctxtFromTLS()->Pceeval(); + if (nullptr == pceeval || !pceeval->FCanEvalExpressions()) + { + return false; + } + + CExpression *pexprCnstr = PexprPartConstraintFromChild( + mp, partrel, pdrgpcrOutput, root_col_mapping); + if (nullptr == pexprCnstr) + { + return false; + } + + // The hash partition qual must be a satisfies_hash_partition() call with at + // least the (parentoid, modulus, remainder, key) arguments. + if (COperator::EopScalarFunc != pexprCnstr->Pop()->Eopid() || + pexprCnstr->Arity() < 4) + { + pexprCnstr->Release(); + return false; + } + + // Rebuild the call, replacing each partition-key argument (a column ident) + // with the constant from a matching "key = const" predicate. If any key + // column lacks such an equality we cannot prune (PostgreSQL likewise needs + // equality on all hash key columns). + CExpressionArray *pdrgpexprArgs = GPOS_NEW(mp) CExpressionArray(mp); + BOOL fAllKeysBound = true; + const ULONG arity = pexprCnstr->Arity(); + for (ULONG ul = 0; ul < arity; ++ul) + { + CExpression *pexprArg = (*pexprCnstr)[ul]; + if (ul < 3) + { + // parentoid, modulus, remainder: carry over unchanged + pexprArg->AddRef(); + pdrgpexprArgs->Append(pexprArg); + continue; + } + + if (COperator::EopScalarIdent != pexprArg->Pop()->Eopid()) + { + fAllKeysBound = false; + break; + } + const CColRef *colref = CScalarIdent::PopConvert(pexprArg->Pop())->Pcr(); + CExpression *pexprConst = + PexprColumnEqualityConst(pdrgpexprConjuncts, colref); + if (nullptr == pexprConst) + { + fAllKeysBound = false; + break; + } + pexprConst->AddRef(); + pdrgpexprArgs->Append(pexprConst); + } + + if (!fAllKeysBound) + { + pdrgpexprArgs->Release(); + pexprCnstr->Release(); + return false; + } + + COperator *popFunc = pexprCnstr->Pop(); + popFunc->AddRef(); + CExpression *pexprTest = GPOS_NEW(mp) CExpression(mp, popFunc, pdrgpexprArgs); + pexprCnstr->Release(); + + // satisfies_hash_partition() over constants should always fold to a boolean, + // but if evaluation fails for any reason we must not abort optimization for + // the whole query: reset the error and conservatively keep the partition + // (pexprResult stays null, so we fall through to return false). + CExpression *pexprResult = nullptr; + GPOS_TRY + { + pexprResult = pceeval->PexprEval(pexprTest); + } + GPOS_CATCH_EX(ex) + { + GPOS_RESET_EX; + } + GPOS_CATCH_END; + pexprTest->Release(); + + BOOL fPruned = false; + if (nullptr != pexprResult && + COperator::EopScalarConst == pexprResult->Pop()->Eopid()) + { + IDatum *datum = CScalarConst::PopConvert(pexprResult->Pop())->GetDatum(); + IDatumBool *pdatumBool = dynamic_cast(datum); + if (nullptr != pdatumBool && !pdatumBool->IsNull() && + !pdatumBool->GetValue()) + { + // satisfies_hash_partition() is false => this leaf cannot hold any + // row with the given key values => prune it. + fPruned = true; + } + } + CRefCount::SafeRelease(pexprResult); + + return fPruned; +} + // Transpose a select over a project // // This preprocessing step enables additional opportunities for predicate push diff --git a/src/backend/gporca/libnaucrates/include/naucrates/md/IMDRelation.h b/src/backend/gporca/libnaucrates/include/naucrates/md/IMDRelation.h index b5e7a8f22ee..f0b5c74f4c0 100644 --- a/src/backend/gporca/libnaucrates/include/naucrates/md/IMDRelation.h +++ b/src/backend/gporca/libnaucrates/include/naucrates/md/IMDRelation.h @@ -75,7 +75,8 @@ class IMDRelation : public IMDCacheObject enum Erelpartitiontype { ErelpartitionRange = 'r', - ErelpartitionList = 'l' + ErelpartitionList = 'l', + ErelpartitionHash = 'h' }; protected: diff --git a/src/test/regress/expected/direct_dispatch_optimizer.out b/src/test/regress/expected/direct_dispatch_optimizer.out index 2da45e7955a..57e499bb091 100644 --- a/src/test/regress/expected/direct_dispatch_optimizer.out +++ b/src/test/regress/expected/direct_dispatch_optimizer.out @@ -1675,15 +1675,11 @@ explain select * from t_14887 where a = 'a'::text; QUERY PLAN ---------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..764.38 rows=158 width=32) - -> Append (cost=0.00..762.26 rows=53 width=32) - -> Seq Scan on t0_14887 t_14887_1 (cost=0.00..254.00 rows=18 width=32) - Filter: ((a)::text = 'a'::text) - -> Seq Scan on t1_14887 t_14887_2 (cost=0.00..254.00 rows=18 width=32) - Filter: ((a)::text = 'a'::text) - -> Seq Scan on t2_14887 t_14887_3 (cost=0.00..254.00 rows=18 width=32) - Filter: ((a)::text = 'a'::text) - Optimizer: Postgres query optimizer -(9 rows) + -> Dynamic Seq Scan on t_14887 + Number of partitions to scan: 3 (out of 3) + Filter: ((a)::text = 'a'::text) + Optimizer: Pivotal Optimizer (GPORCA) +(4 rows) begin; drop table if exists direct_test; diff --git a/src/test/regress/expected/orca_static_pruning_optimizer.out b/src/test/regress/expected/orca_static_pruning_optimizer.out index 522f0dc17f7..9640d004ef0 100644 --- a/src/test/regress/expected/orca_static_pruning_optimizer.out +++ b/src/test/regress/expected/orca_static_pruning_optimizer.out @@ -160,21 +160,18 @@ WHERE b = 42 $query$ AS qry \gset EXPLAIN (COSTS OFF, VERBOSE) :qry ; -INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner -DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: hash partitioning QUERY PLAN ---------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) - Output: hp.a, hp.b - -> Seq Scan on orca_static_pruning.hp0 hp - Output: hp.a, hp.b + Output: a, b + -> Dynamic Seq Scan on orca_static_pruning.hp + Output: a, b + Number of partitions to scan: 1 (out of 2) Filter: (hp.b = 42) - Optimizer: Postgres query optimizer -(6 rows) + Optimizer: Pivotal Optimizer (GPORCA) +(8 rows) :qry ; -INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner -DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: hash partitioning a | b ---+---- 0 | 42 diff --git a/src/test/regress/expected/partition_prune_append.out b/src/test/regress/expected/partition_prune_append.out index 20730d90d0f..75ae62577d8 100644 --- a/src/test/regress/expected/partition_prune_append.out +++ b/src/test/regress/expected/partition_prune_append.out @@ -3502,30 +3502,32 @@ explain (costs off) select * from pph_arrpart where a = '{1}'; QUERY PLAN -------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) - -> Seq Scan on pph_arrpart2 pph_arrpart - Filter: (a = '{1}'::integer[]) - Optimizer: Postgres query optimizer -(4 rows) + -> Append + -> Seq Scan on pph_arrpart2 + Filter: (a = '{1}'::integer[]) + Optimizer: Pivotal Optimizer (GPORCA) +(5 rows) explain (costs off) select * from pph_arrpart where a = '{1, 2}'; QUERY PLAN -------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) - -> Seq Scan on pph_arrpart1 pph_arrpart - Filter: (a = '{1,2}'::integer[]) - Optimizer: Postgres query optimizer -(4 rows) + -> Append + -> Seq Scan on pph_arrpart1 + Filter: (a = '{1,2}'::integer[]) + Optimizer: Pivotal Optimizer (GPORCA) +(5 rows) explain (costs off) select * from pph_arrpart where a in ('{4, 5}', '{1}'); QUERY PLAN ---------------------------------------------------------------------------- - Gather Motion 2:1 (slice1; segments: 2) + Gather Motion 3:1 (slice1; segments: 3) -> Append - -> Seq Scan on pph_arrpart1 pph_arrpart_1 + -> Seq Scan on pph_arrpart1 Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - -> Seq Scan on pph_arrpart2 pph_arrpart_2 + -> Seq Scan on pph_arrpart2 Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - Optimizer: Postgres query optimizer + Optimizer: Pivotal Optimizer (GPORCA) (7 rows) drop table pph_arrpart; diff --git a/src/test/regress/expected/partition_prune_optimizer.out b/src/test/regress/expected/partition_prune_optimizer.out index 72caa769ea3..d3fdaa3493f 100644 --- a/src/test/regress/expected/partition_prune_optimizer.out +++ b/src/test/regress/expected/partition_prune_optimizer.out @@ -3949,31 +3949,31 @@ explain (costs off) select * from pph_arrpart where a = '{1}'; QUERY PLAN -------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) - -> Seq Scan on pph_arrpart2 pph_arrpart + -> Dynamic Seq Scan on pph_arrpart + Number of partitions to scan: 1 (out of 2) Filter: (a = '{1}'::integer[]) - Optimizer: Postgres query optimizer -(4 rows) + Optimizer: Pivotal Optimizer (GPORCA) +(5 rows) explain (costs off) select * from pph_arrpart where a = '{1, 2}'; QUERY PLAN -------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) - -> Seq Scan on pph_arrpart1 pph_arrpart + -> Dynamic Seq Scan on pph_arrpart + Number of partitions to scan: 1 (out of 2) Filter: (a = '{1,2}'::integer[]) - Optimizer: Postgres query optimizer -(4 rows) + Optimizer: Pivotal Optimizer (GPORCA) +(5 rows) explain (costs off) select * from pph_arrpart where a in ('{4, 5}', '{1}'); QUERY PLAN ---------------------------------------------------------------------------- - Gather Motion 2:1 (slice1; segments: 2) - -> Append - -> Seq Scan on pph_arrpart1 pph_arrpart_1 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - -> Seq Scan on pph_arrpart2 pph_arrpart_2 - Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - Optimizer: Postgres query optimizer -(7 rows) + Gather Motion 3:1 (slice1; segments: 3) + -> Dynamic Seq Scan on pph_arrpart + Number of partitions to scan: 2 (out of 2) + Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) + Optimizer: Pivotal Optimizer (GPORCA) +(5 rows) drop table pph_arrpart; -- enum type list partition key diff --git a/src/test/regress/expected/qp_indexscan_optimizer.out b/src/test/regress/expected/qp_indexscan_optimizer.out index 8d187de1d73..ae5405183e9 100644 --- a/src/test/regress/expected/qp_indexscan_optimizer.out +++ b/src/test/regress/expected/qp_indexscan_optimizer.out @@ -2403,14 +2403,13 @@ explain(costs off) select * from tbl_hash order by col1 limit 3; ----------------------------------------------------------------------- Limit -> Gather Motion 3:1 (slice1; segments: 3) - Merge Key: tbl_hash.col1 - -> Limit - -> Merge Append - Sort Key: tbl_hash.col1 - -> Index Scan using p1_col1_idx on p1 tbl_hash_1 - -> Index Scan using p2_col1_idx on p2 tbl_hash_2 - Optimizer: Postgres query optimizer -(9 rows) + Merge Key: col1 + -> Sort + Sort Key: col1 + -> Dynamic Seq Scan on tbl_hash + Number of partitions to scan: 2 (out of 2) + Optimizer: Pivotal Optimizer (GPORCA) +(8 rows) -- Clean Up DROP TABLE tbl_hash;