8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:03:03 +01:00

Postfix for #7804: The partial index is not involved when filtering conditions through OR. Use the full index scan instead of multiple range scans ORed afterwards.

This commit is contained in:
Dmitry Yemanov 2025-01-19 15:06:26 +03:00
parent 63e6ead37b
commit 90401f7210

View File

@ -1414,17 +1414,37 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
for (auto inversion : inversions) for (auto inversion : inversions)
{ {
const auto indexScratch = inversion->scratch; if (const auto indexScratch = inversion->scratch)
// If the explicit plan doesn't mention this index, fake it as used
// thus excluding it from the cost-based algorithm. Otherwise,
// given this index is suitable for navigation, also mark it as used.
if ((indexScratch &&
(indexScratch->index->idx_runtime_flags & idx_plan_dont_use)) ||
(!customPlan && inversion == navigationCandidate))
{ {
inversion->used = true; const auto idx = indexScratch->index;
// If the explicit plan doesn't mention this index, fake it as used
// thus excluding it from the cost-based algorithm. Otherwise,
// given this index is suitable for navigation, also mark it as used.
if (((idx->idx_runtime_flags & idx_plan_dont_use)) ||
(!customPlan && inversion == navigationCandidate))
{
inversion->used = true;
}
// If the index is conditional and its condition is also present in
// some other inversion as a boolean (it represents the OR operation),
// fake these other inversions as used, so that the full index scan would
// be preferred to multiple range scans. The cost-based algorithm below
// cannot handle it currently.
if (idx->idx_flags & idx_condition)
{
for (auto otherInversion : inversions)
{
if (otherInversion->boolean &&
idx->idx_condition->sameAs(otherInversion->boolean, true))
{
otherInversion->used = true;
}
}
}
} }
} }
@ -1460,11 +1480,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
if (!invCandidate) if (!invCandidate)
invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
if (!currentInv->inversion && currentInv->scratch) const auto inversionNode = (!currentInv->inversion && currentInv->scratch) ?
invCandidate->inversion = makeIndexScanNode(currentInv->scratch); makeIndexScanNode(currentInv->scratch) : currentInv->inversion;
else invCandidate->inversion = inversionNode;
invCandidate->inversion = currentInv->inversion;
invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges); invCandidate->dbkeyRanges.assign(currentInv->dbkeyRanges);
invCandidate->unique = currentInv->unique; invCandidate->unique = currentInv->unique;
invCandidate->selectivity = currentInv->selectivity; invCandidate->selectivity = currentInv->selectivity;
@ -1653,12 +1671,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
if (!invCandidate) if (!invCandidate)
{ {
invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool()); invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
if (!bestCandidate->inversion && bestCandidate->scratch) { const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ?
invCandidate->inversion = makeIndexScanNode(bestCandidate->scratch); makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion;
} invCandidate->inversion = inversionNode;
else {
invCandidate->inversion = bestCandidate->inversion;
}
invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges); invCandidate->dbkeyRanges.assign(bestCandidate->dbkeyRanges);
invCandidate->unique = bestCandidate->unique; invCandidate->unique = bestCandidate->unique;
invCandidate->selectivity = bestCandidate->selectivity; invCandidate->selectivity = bestCandidate->selectivity;
@ -1685,17 +1700,10 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
} }
else if (!bestCandidate->condition) else if (!bestCandidate->condition)
{ {
if (!bestCandidate->inversion && bestCandidate->scratch) const auto inversionNode = (!bestCandidate->inversion && bestCandidate->scratch) ?
{ makeIndexScanNode(bestCandidate->scratch) : bestCandidate->inversion;
invCandidate->inversion = composeInversion(invCandidate->inversion, invCandidate->inversion = composeInversion(invCandidate->inversion,
makeIndexScanNode(bestCandidate->scratch), InversionNode::TYPE_AND); inversionNode, InversionNode::TYPE_AND);
}
else
{
invCandidate->inversion = composeInversion(invCandidate->inversion,
bestCandidate->inversion, InversionNode::TYPE_AND);
}
invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges); invCandidate->dbkeyRanges.join(bestCandidate->dbkeyRanges);
invCandidate->unique = (invCandidate->unique || bestCandidate->unique); invCandidate->unique = (invCandidate->unique || bestCandidate->unique);
invCandidate->selectivity = totalSelectivity; invCandidate->selectivity = totalSelectivity;
@ -1770,6 +1778,16 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch,
if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE)
return false; return false;
const auto idx = indexScratch->index;
if (idx->idx_flags & idx_condition)
{
// If index condition matches the boolean, this should not be
// considered a match. Full index scan will be used instead.
if (idx->idx_condition->sameAs(boolean, true))
return false;
}
const auto cmpNode = nodeAs<ComparativeBoolNode>(boolean); const auto cmpNode = nodeAs<ComparativeBoolNode>(boolean);
const auto missingNode = nodeAs<MissingBoolNode>(boolean); const auto missingNode = nodeAs<MissingBoolNode>(boolean);
const auto listNode = nodeAs<InListBoolNode>(boolean); const auto listNode = nodeAs<InListBoolNode>(boolean);
@ -1804,16 +1822,6 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch,
ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ? ValueExprNode* value2 = (cmpNode && cmpNode->blrOp == blr_between) ?
cmpNode->arg3 : nullptr; cmpNode->arg3 : nullptr;
const auto idx = indexScratch->index;
if (idx->idx_flags & idx_condition)
{
// If index condition matches the boolean, this should not be
// considered a match. Full index scan will be used instead.
if (idx->idx_condition->sameAs(boolean, true))
return false;
}
if (idx->idx_flags & idx_expression) if (idx->idx_flags & idx_expression)
{ {
// see if one side or the other is matchable to the index expression // see if one side or the other is matchable to the index expression