Optimizer.cpp (Firebird-3.0.2.32703-0.tar.bz2) | : | Optimizer.cpp (Firebird-3.0.4.33054-0.tar.bz2) | ||
---|---|---|---|---|
skipping to change at line 472 | skipping to change at line 472 | |||
const OptimizerBlk::opt_conjunct* const opt_end = | const OptimizerBlk::opt_conjunct* const opt_end = | |||
innerFlag ? optimizer->opt_conjuncts.begin() + optimizer->opt_bas e_missing_conjuncts : | innerFlag ? optimizer->opt_conjuncts.begin() + optimizer->opt_bas e_missing_conjuncts : | |||
optimizer->opt_conjuncts.end(); | optimizer->opt_conjuncts.end(); | |||
InversionCandidate* invCandidate = NULL; | InversionCandidate* invCandidate = NULL; | |||
if (relation && !relation->rel_file && !relation->isVirtual()) | if (relation && !relation->rel_file && !relation->isVirtual()) | |||
{ | { | |||
InversionCandidateList inversions; | InversionCandidateList inversions; | |||
// Check for any DB_KEY comparisons | ||||
for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end | ||||
; tail++) | ||||
{ | ||||
BoolExprNode* const node = tail->opt_conjunct_node; | ||||
if (!(tail->opt_conjunct_flags & opt_conjunct_used) && no | ||||
de) | ||||
{ | ||||
invCandidate = matchDbKey(node); | ||||
if (invCandidate) | ||||
inversions.add(invCandidate); | ||||
} | ||||
} | ||||
// First, handle "AND" comparisons (all nodes except OR) | // First, handle "AND" comparisons (all nodes except OR) | |||
for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end ; tail++) | for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end ; tail++) | |||
{ | { | |||
BoolExprNode* const node = tail->opt_conjunct_node; | BoolExprNode* const node = tail->opt_conjunct_node; | |||
BinaryBoolNode* booleanNode = node->as<BinaryBoolNode>(); | BinaryBoolNode* booleanNode = node->as<BinaryBoolNode>(); | |||
if (!(tail->opt_conjunct_flags & opt_conjunct_used) && no de && | if (!(tail->opt_conjunct_flags & opt_conjunct_used) && no de && | |||
(!booleanNode || booleanNode->blrOp != blr_or)) | (!booleanNode || booleanNode->blrOp != blr_or)) | |||
{ | { | |||
matchOnIndexes(&indexScratches, node, 1); | invCandidate = matchOnIndexes(&indexScratches, no | |||
de, 1); | ||||
if (invCandidate) | ||||
inversions.add(invCandidate); | ||||
} | } | |||
} | } | |||
getInversionCandidates(&inversions, &indexScratches, 1); | getInversionCandidates(&inversions, &indexScratches, 1); | |||
// Second, handle "OR" comparisons | // Second, handle "OR" comparisons | |||
for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end ; tail++) | for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end ; tail++) | |||
{ | { | |||
BoolExprNode* const node = tail->opt_conjunct_node; | BoolExprNode* const node = tail->opt_conjunct_node; | |||
BinaryBoolNode* booleanNode = node->as<BinaryBoolNode>(); | BinaryBoolNode* booleanNode = node->as<BinaryBoolNode>(); | |||
skipping to change at line 705 | skipping to change at line 694 | |||
const index_desc::idx_repeat* idx_tail = idx->idx_rpt; | const index_desc::idx_repeat* idx_tail = idx->idx_rpt; | |||
const index_desc::idx_repeat* const idx_end = idx_tail + idx->idx _count; | const index_desc::idx_repeat* const idx_end = idx_tail + idx->idx _count; | |||
NestConst<ValueExprNode>* ptr = sort->expressions.begin(); | NestConst<ValueExprNode>* ptr = sort->expressions.begin(); | |||
const bool* descending = sort->descending.begin(); | const bool* descending = sort->descending.begin(); | |||
const int* nullOrder = sort->nullOrder.begin(); | const int* nullOrder = sort->nullOrder.begin(); | |||
for (const NestConst<ValueExprNode>* const end = sort->expression s.end(); | for (const NestConst<ValueExprNode>* const end = sort->expression s.end(); | |||
ptr != end; | ptr != end; | |||
++ptr, ++descending, ++nullOrder, ++idx_tail) | ++ptr, ++descending, ++nullOrder, ++idx_tail) | |||
{ | { | |||
ValueExprNode* const node = *ptr; | ValueExprNode* const orgNode = *ptr; | |||
FieldNode* fieldNode; | FieldNode* fieldNode; | |||
bool nodeMatched = false; | ||||
// Collect nodes equivalent to the given sort node | ||||
if (idx->idx_flags & idx_expressn) | HalfStaticArray<ValueExprNode*, OPT_STATIC_ITEMS> nodes; | |||
nodes.add(orgNode); | ||||
for (const OptimizerBlk::opt_conjunct* tail = optimizer-> | ||||
opt_conjuncts.begin(); | ||||
tail < optimizer->opt_conjuncts.end(); tail++) | ||||
{ | { | |||
if (!checkExpressionIndex(idx, node, stream)) | BoolExprNode* const boolean = tail->opt_conjunct_ | |||
node; | ||||
fb_assert(boolean); | ||||
ComparativeBoolNode* const cmpNode = boolean->as< | ||||
ComparativeBoolNode>(); | ||||
if (cmpNode && (cmpNode->blrOp == blr_eql || cmpN | ||||
ode->blrOp == blr_equiv)) | ||||
{ | { | |||
usableIndex = false; | ValueExprNode* const node1 = cmpNode->arg | |||
break; | 1; | |||
ValueExprNode* const node2 = cmpNode->arg | ||||
2; | ||||
if (node1->sameAs(orgNode, false)) | ||||
nodes.add(node2); | ||||
if (node2->sameAs(orgNode, false)) | ||||
nodes.add(node1); | ||||
} | } | |||
} | } | |||
else if (!(fieldNode = node->as<FieldNode>()) || fieldNod | ||||
e->fieldStream != stream) | // Check whether any of the equivalent nodes is suitable | |||
{ | for index navigation | |||
usableIndex = false; | ||||
break; | for (ValueExprNode** iter = nodes.begin(); iter != nodes. | |||
} | end(); ++iter) | |||
else | ||||
{ | { | |||
for (; idx_tail < idx_end && fieldNode->fieldId ! | ValueExprNode* const node = *iter; | |||
= idx_tail->idx_field; idx_tail++) | ||||
{ | ||||
const int segmentNumber = idx_tail - idx- | ||||
>idx_rpt; | ||||
if (segmentNumber >= equalSegments) | if (idx->idx_flags & idx_expressn) | |||
break; | { | |||
if (!checkExpressionIndex(idx, node, stre | ||||
am)) | ||||
continue; | ||||
} | } | |||
else if (!(fieldNode = node->as<FieldNode>()) || | ||||
if (idx_tail >= idx_end || fieldNode->fieldId != | fieldNode->fieldStream != stream) | |||
idx_tail->idx_field) | ||||
{ | { | |||
usableIndex = false; | continue; | |||
break; | ||||
} | } | |||
} | else | |||
{ | ||||
for (; idx_tail < idx_end && fieldNode->f | ||||
ieldId != idx_tail->idx_field; idx_tail++) | ||||
{ | ||||
const int segmentNumber = idx_tai | ||||
l - idx->idx_rpt; | ||||
if ((*descending && !(idx->idx_flags & idx_descending)) | | if (segmentNumber >= equalSegment | |||
| | s) | |||
(!*descending && (idx->idx_flags & idx_descending | break; | |||
)) || | } | |||
((*nullOrder == rse_nulls_first && *descending) | | ||||
| | if (idx_tail >= idx_end || fieldNode->fie | |||
(*nullOrder == rse_nulls_last && !*descending))) | ldId != idx_tail->idx_field) | |||
{ | continue; | |||
usableIndex = false; | } | |||
break; | ||||
} | ||||
dsc desc; | if ((*descending && !(idx->idx_flags & idx_descen | |||
node->getDesc(tdbb, csb, &desc); | ding)) || | |||
(!*descending && (idx->idx_flags & idx_de | ||||
scending)) || | ||||
((*nullOrder == rse_nulls_first && *desce | ||||
nding) || | ||||
(*nullOrder == rse_nulls_last && !*desce | ||||
nding))) | ||||
{ | ||||
continue; | ||||
} | ||||
// ASF: "desc.dsc_ttype() > ttype_last_internal" is to av | dsc desc; | |||
oid recursion | node->getDesc(tdbb, csb, &desc); | |||
// when looking for charsets/collations | ||||
if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.dsc_ttype() > t | // ASF: "desc.dsc_ttype() > ttype_last_internal" | |||
type_last_internal) | is to avoid recursion | |||
{ | // when looking for charsets/collations | |||
const TextType* const tt = INTL_texttype_lookup(t | ||||
dbb, desc.dsc_ttype()); | ||||
if (idx->idx_flags & idx_unique) | if (DTYPE_IS_TEXT(desc.dsc_dtype) && desc.dsc_tty pe() > ttype_last_internal) | |||
{ | { | |||
if (tt->getFlags() & TEXTTYPE_UNSORTED_UN | const TextType* const tt = INTL_texttype_ | |||
IQUE) | lookup(tdbb, desc.dsc_ttype()); | |||
if (idx->idx_flags & idx_unique) | ||||
{ | { | |||
usableIndex = false; | if (tt->getFlags() & TEXTTYPE_UNS | |||
break; | ORTED_UNIQUE) | |||
continue; | ||||
} | } | |||
} | else | |||
else | ||||
{ | ||||
// ASF: We currently can't use non-unique | ||||
index for GROUP BY and DISTINCT with | ||||
// multi-level and insensitive collation. | ||||
In NAV, keys are verified with memcmp | ||||
// but there we don't know length of each | ||||
level. | ||||
if (sort->unique && (tt->getFlags() & TEX | ||||
TTYPE_SEPARATE_UNIQUE)) | ||||
{ | { | |||
usableIndex = false; | // ASF: We currently can't use no | |||
break; | n-unique index for GROUP BY and DISTINCT with | |||
// multi-level and insensitive co | ||||
llation. In NAV, keys are verified with memcmp | ||||
// but there we don't know length | ||||
of each level. | ||||
if (sort->unique && (tt->getFlags | ||||
() & TEXTTYPE_SEPARATE_UNIQUE)) | ||||
continue; | ||||
} | } | |||
} | } | |||
nodeMatched = true; | ||||
break; | ||||
} | ||||
if (!nodeMatched) | ||||
{ | ||||
usableIndex = false; | ||||
break; | ||||
} | } | |||
} | } | |||
if (!usableIndex) | if (!usableIndex) | |||
continue; | continue; | |||
// Looks like we can do a navigational walk. Remember this candid ate | // Looks like we can do a navigational walk. Remember this candid ate | |||
// and compare it against other possible candidates. | // and compare it against other possible candidates. | |||
if (!navigationCandidate) | if (!navigationCandidate) | |||
skipping to change at line 1997 | skipping to change at line 2015 | |||
************************************** | ************************************** | |||
* | * | |||
* Functional description | * Functional description | |||
* Check whether a boolean is a DB_KEY based comparison. | * Check whether a boolean is a DB_KEY based comparison. | |||
* | * | |||
**************************************/ | **************************************/ | |||
// If this isn't an equality, it isn't even interesting | // If this isn't an equality, it isn't even interesting | |||
ComparativeBoolNode* cmpNode = boolean->as<ComparativeBoolNode>(); | ComparativeBoolNode* cmpNode = boolean->as<ComparativeBoolNode>(); | |||
if (!cmpNode || cmpNode->blrOp != blr_eql) | if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv )) | |||
return NULL; | return NULL; | |||
// Find the side of the equality that is potentially a dbkey. If | // Find the side of the equality that is potentially a dbkey. If | |||
// neither, make the obvious deduction | // neither, make the obvious deduction | |||
ValueExprNode* dbkey = cmpNode->arg1; | ValueExprNode* dbkey = cmpNode->arg1; | |||
ValueExprNode* value = cmpNode->arg2; | ValueExprNode* value = cmpNode->arg2; | |||
const RecordKeyNode* keyNode = dbkey->as<RecordKeyNode>(); | const RecordKeyNode* keyNode = dbkey->as<RecordKeyNode>(); | |||
skipping to change at line 2088 | skipping to change at line 2106 | |||
* If the boolean is an "OR" node then a | * If the boolean is an "OR" node then a | |||
* inversion candidate could be returned. | * inversion candidate could be returned. | |||
* | * | |||
**************************************/ | **************************************/ | |||
BinaryBoolNode* binaryNode = boolean->as<BinaryBoolNode>(); | BinaryBoolNode* binaryNode = boolean->as<BinaryBoolNode>(); | |||
// Handle the "OR" case up front | // Handle the "OR" case up front | |||
if (binaryNode && binaryNode->blrOp == blr_or) | if (binaryNode && binaryNode->blrOp == blr_or) | |||
{ | { | |||
InversionCandidateList inversions; | InversionCandidateList inversions; | |||
inversions.shrink(0); | ||||
// Make list for index matches | // Make list for index matches | |||
IndexScratchList indexOrScratches; | IndexScratchList indexOrScratches; | |||
// Copy information from caller | // Copy information from caller | |||
FB_SIZE_T i = 0; | FB_SIZE_T i = 0; | |||
for (; i < inputIndexScratches->getCount(); i++) | for (; i < inputIndexScratches->getCount(); i++) | |||
{ | { | |||
skipping to change at line 2204 | skipping to change at line 2221 | |||
} | } | |||
} | } | |||
return invCandidate; | return invCandidate; | |||
} | } | |||
if (invCandidate1) | if (invCandidate1) | |||
{ | { | |||
BoolExprNode* condition = binaryNode->arg2; | BoolExprNode* condition = binaryNode->arg2; | |||
if (invCandidate1->condition) | if (condition->computable(csb, INVALID_STREAM, false) && !condition->findStream(stream)) | |||
{ | { | |||
BinaryBoolNode* const newNode = | if (invCandidate1->condition) | |||
FB_NEW_POOL(*tdbb->getDefaultPool()) Bina | { | |||
ryBoolNode(*tdbb->getDefaultPool(), blr_or); | BinaryBoolNode* const newNode = | |||
newNode->arg1 = invCandidate1->condition; | FB_NEW_POOL(*tdbb->getDefaultPool | |||
newNode->arg2 = condition; | ()) | |||
condition = newNode; | BinaryBoolNode(*tdbb->get | |||
} | DefaultPool(), blr_or); | |||
newNode->arg1 = invCandidate1->condition; | ||||
newNode->arg2 = condition; | ||||
condition = newNode; | ||||
} | ||||
invCandidate1->condition = condition; | invCandidate1->condition = condition; | |||
return invCandidate1; | return invCandidate1; | |||
} | ||||
} | } | |||
if (invCandidate2) | if (invCandidate2) | |||
{ | { | |||
BoolExprNode* condition = binaryNode->arg1; | BoolExprNode* condition = binaryNode->arg1; | |||
if (invCandidate2->condition) | if (condition->computable(csb, INVALID_STREAM, false) && !condition->findStream(stream)) | |||
{ | { | |||
BinaryBoolNode* const newNode = | if (invCandidate2->condition) | |||
FB_NEW_POOL(*tdbb->getDefaultPool()) Bina | { | |||
ryBoolNode(*tdbb->getDefaultPool(), blr_or); | BinaryBoolNode* const newNode = | |||
newNode->arg1 = invCandidate2->condition; | FB_NEW_POOL(*tdbb->getDefaultPool | |||
newNode->arg2 = condition; | ()) | |||
condition = newNode; | BinaryBoolNode(*tdbb->get | |||
} | DefaultPool(), blr_or); | |||
newNode->arg1 = invCandidate2->condition; | ||||
newNode->arg2 = condition; | ||||
condition = newNode; | ||||
} | ||||
invCandidate2->condition = condition; | invCandidate2->condition = condition; | |||
return invCandidate2; | return invCandidate2; | |||
} | ||||
} | } | |||
return NULL; | return NULL; | |||
} | } | |||
if (binaryNode && binaryNode->blrOp == blr_and) | if (binaryNode && binaryNode->blrOp == blr_and) | |||
{ | { | |||
// Recursively call this procedure for every boolean | // Recursively call this procedure for every boolean | |||
// and finally get candidate inversions. | // and finally get candidate inversions. | |||
// Normally we come here from within a OR conjunction. | // Normally we come here from within a OR conjunction. | |||
InversionCandidateList inversions; | InversionCandidateList inversions; | |||
inversions.shrink(0); | ||||
InversionCandidate* invCandidate = matchOnIndexes( | InversionCandidate* invCandidate = matchOnIndexes( | |||
inputIndexScratches, binaryNode->arg1, scope); | inputIndexScratches, binaryNode->arg1, scope); | |||
if (invCandidate) | if (invCandidate) | |||
inversions.add(invCandidate); | inversions.add(invCandidate); | |||
invCandidate = matchOnIndexes(inputIndexScratches, binaryNode->ar g2, scope); | invCandidate = matchOnIndexes(inputIndexScratches, binaryNode->ar g2, scope); | |||
if (invCandidate) | if (invCandidate) | |||
inversions.add(invCandidate); | inversions.add(invCandidate); | |||
return makeInversion(&inversions); | return makeInversion(&inversions); | |||
} | } | |||
// Check for DB_KEY comparison | ||||
InversionCandidate* const invCandidate = matchDbKey(boolean); | ||||
// Walk through indexes | // Walk through indexes | |||
for (FB_SIZE_T i = 0; i < inputIndexScratches->getCount(); i++) | for (FB_SIZE_T i = 0; i < inputIndexScratches->getCount(); i++) | |||
{ | { | |||
IndexScratch& indexScratch = (*inputIndexScratches)[i]; | IndexScratch& indexScratch = (*inputIndexScratches)[i]; | |||
// Try to match the boolean against a index. | // Try to match the boolean against a index. | |||
if (!(indexScratch.idx->idx_runtime_flags & idx_plan_dont_use) || | if (!(indexScratch.idx->idx_runtime_flags & idx_plan_dont_use) || | |||
(indexScratch.idx->idx_runtime_flags & idx_plan_navigate) ) | (indexScratch.idx->idx_runtime_flags & idx_plan_navigate) ) | |||
{ | { | |||
matchBoolean(&indexScratch, boolean, scope); | matchBoolean(&indexScratch, boolean, scope); | |||
} | } | |||
} | } | |||
return NULL; | return invCandidate; | |||
} | } | |||
#ifdef OPT_DEBUG_RETRIEVAL | #ifdef OPT_DEBUG_RETRIEVAL | |||
void OptimizerRetrieval::printCandidate(const InversionCandidate* candidate) con st | void OptimizerRetrieval::printCandidate(const InversionCandidate* candidate) con st | |||
{ | { | |||
/************************************** | /************************************** | |||
* | * | |||
* p r i n t C a n d i d a t e | * p r i n t C a n d i d a t e | |||
* | * | |||
************************************** | ************************************** | |||
End of changes. 34 change blocks. | ||||
105 lines changed or deleted | 143 lines changed or added |