yjhjstz commented on code in PR #1868:
URL: https://github.com/apache/cloudberry/pull/1868#discussion_r3688038515
##########
src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp:
##########
@@ -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;
Review Comment:
ignore exceptions ?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]