diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index ca24368d77a..8dee391da37 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -37,6 +37,7 @@ #include "../jrd/scl_proto.h" #include "../jrd/Collation.h" #include "../jrd/recsrc/Cursor.h" +#include "../jrd/optimizer/Optimizer.h" using namespace Firebird; using namespace Jrd; @@ -351,7 +352,7 @@ Statement* Statement::makeBoolExpression(thread_db* tdbb, BoolExprNode*& node, return makeStatement(tdbb, csb, internalFlag, [&] { - node = static_cast(csb->csb_node); + node = Optimizer::transformBoolean(tdbb, csb); }); } diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 6ec6e074e34..aa094804feb 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -651,6 +651,17 @@ Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse, } +Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb) + : PermanentStorage(*aTdbb->getDefaultPool()), + tdbb(aTdbb), csb(aCsb), rse(nullptr), + compileStreams(getPool()), + bedStreams(getPool()), + keyStreams(getPool()), + outerStreams(getPool()), + conjuncts(getPool()) +{ +} + // // Destructor // @@ -1274,24 +1285,26 @@ void Optimizer::compileRelation(StreamType stream) // -// Decompose a boolean into a stack of conjuctions. +// Decompose a boolean into a stack of conjunctions. +// +// If the boolean already transformed, don't transform it again. // -unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& stack) +unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& stack, bool transformed) { if (const auto binaryNode = nodeAs(boolNode)) { if (binaryNode->blrOp == blr_and) { - auto count = decomposeBoolean(binaryNode->arg1, stack); - count += decomposeBoolean(binaryNode->arg2, stack); + auto count = decomposeBoolean(binaryNode->arg1, stack, transformed); + count += decomposeBoolean(binaryNode->arg2, stack, transformed); return count; } - else if (binaryNode->blrOp == blr_or) + else if ((binaryNode->blrOp == blr_or) && !transformed) { BoolExprNodeStack or_stack; - if (decomposeBoolean(binaryNode->arg1, or_stack) >= 2) + if (decomposeBoolean(binaryNode->arg1, or_stack, transformed) >= 2) { binaryNode->arg1 = or_stack.pop(); @@ -1308,7 +1321,7 @@ unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& or_stack.clear(); - if (decomposeBoolean(binaryNode->arg2, or_stack) >= 2) + if (decomposeBoolean(binaryNode->arg2, or_stack, transformed) >= 2) { binaryNode->arg2 = or_stack.pop(); @@ -1330,6 +1343,8 @@ unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& if (cmpNode->blrOp == blr_between) { + fb_assert(!transformed); + auto newCmpNode = FB_NEW_POOL(getPool()) ComparativeBoolNode(getPool(), blr_geq); newCmpNode->arg1 = cmpNode->arg1; newCmpNode->arg2 = cmpNode->arg2; @@ -1350,7 +1365,7 @@ unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& ValueExprNode* arg; - if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && + if ((cmpNode->blrOp == blr_like || cmpNode->blrOp == blr_similar) && !transformed && (arg = optimizeLikeSimilar(cmpNode))) { const auto newCmpNode = @@ -1371,6 +1386,57 @@ unsigned Optimizer::decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& } +// +// Transform boolean expression into form better suitable for later matching against +// another expression being optimized. Used with conditional indices. +// + +BoolExprNode* Optimizer::transformBoolean(thread_db* tdbb, CompilerScratch* csb) +{ + fb_assert(csb->csb_node->getKind() == DmlNode::KIND_BOOLEAN); + + auto* node = static_cast(csb->csb_node); + + Optimizer opt(tdbb, csb); + node = opt.transformBoolExpr(tdbb, node); + csb->csb_node = node; + return node; +} + + +BoolExprNode* Optimizer::transformBoolExpr(thread_db* tdbb, BoolExprNode* boolNode) +{ + if (const auto binaryNode = nodeAs(boolNode)) + { + binaryNode->arg1 = transformBoolExpr(tdbb, binaryNode->arg1); + binaryNode->arg2 = transformBoolExpr(tdbb, binaryNode->arg2); + return binaryNode; + } + + if (const auto cmpNode = nodeAs(boolNode)) + { + // turn a between into (a greater than or equal) AND (a less than or equal) + + if (cmpNode->blrOp == blr_between) + { + MemoryPool& pool = getPool(); + + auto geq = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_geq); + geq->arg1 = cmpNode->arg1; + geq->arg2 = cmpNode->arg2; + + auto leq = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_leq); + leq->arg1 = CMP_clone_node_opt(tdbb, csb, cmpNode->arg1); + leq->arg2 = cmpNode->arg3; + + return FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_and, geq, leq); + } + } + + return boolNode; +} + + // // Generate a separate AggregateSort (Aggregate SortedStream Block) for each distinct operation. // Note that this should be optimized to use indices if possible. diff --git a/src/jrd/optimizer/Optimizer.h b/src/jrd/optimizer/Optimizer.h index c567a1748a6..752637b9268 100644 --- a/src/jrd/optimizer/Optimizer.h +++ b/src/jrd/optimizer/Optimizer.h @@ -500,7 +500,10 @@ class Optimizer : public Firebird::PermanentStorage RecordSource* compile(RseNode* subRse, BoolExprNodeStack* parentStack); void compileRelation(StreamType stream); - unsigned decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& stack); + unsigned decomposeBoolean(BoolExprNode* boolNode, BoolExprNodeStack& stack, bool transformed = false); + + static BoolExprNode* transformBoolean(thread_db* tdbb, CompilerScratch* csb); + void generateAggregateDistincts(MapNode* map); RecordSource* generateRetrieval(StreamType stream, SortNode** sortClause, @@ -569,6 +572,7 @@ class Optimizer : public Firebird::PermanentStorage private: Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse, bool parentFirstRows); Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse, const BoolExprNodeStack& stack); + Optimizer(thread_db* aTdbb, CompilerScratch* aCsb); RecordSource* compile(BoolExprNodeStack* parentStack); @@ -603,6 +607,7 @@ class Optimizer : public Firebird::PermanentStorage ValueListNode* list); ValueExprNode* optimizeLikeSimilar(ComparativeBoolNode* cmpNode); + BoolExprNode* transformBoolExpr(thread_db* tdbb, BoolExprNode* boolNode); thread_db* const tdbb; CompilerScratch* const csb; diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 3711a8e9a98..31cb101a900 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -734,7 +734,7 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches auto iter = optimizer->getConjuncts(outerFlag, innerFlag); BoolExprNodeStack idxConjuncts; - const auto conjunctCount = optimizer->decomposeBoolean(idx.idx_condition, idxConjuncts); + const auto conjunctCount = optimizer->decomposeBoolean(idx.idx_condition, idxConjuncts, true); fb_assert(conjunctCount); idx.idx_fraction = MAXIMUM_SELECTIVITY;