diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 7b79c298069..fe6fb4fbd3e 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -1339,8 +1339,69 @@ BoolExprNode* InListBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const return node; } +BoolExprNode* InListBoolNode::decompose(CompilerScratch* csb) +{ + // Search for list items depending on record streams. + // If found, decompose expression: + // IN (, , , ...) + // into: + // IN (, , ...) OR = OR = ... + // where the ORed booleans are known to be stream-based (i.e. contain fields inside) + // and thus could use an index, if possible. + // + // See #8109 in the tracker, example: + // + // SELECT e.* + // FROM Employees e + // WHERE :SomeID IN (e.LeaderID, e.DispEmpID) + + auto& pool = csb->csb_pool; + BoolExprNode* boolNode = nullptr; + + for (auto iter = list->items.begin(); iter != list->items.end();) + { + ValueExprNode* const item = *iter; + + SortedStreamList streams; + item->collectStreams(streams); + + if (streams.isEmpty()) + { + iter++; + continue; + } + + list->items.remove(iter); + + const auto cmpNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_eql, arg, item); + + if (boolNode) + boolNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, boolNode, cmpNode); + else + boolNode = cmpNode; + } + + if (boolNode && list->items.hasData()) + { + BoolExprNode* priorNode = this; + + if (list->items.getCount() == 1) + { + // Convert A IN (B) into A = B + priorNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_eql, arg, list->items.front()); + } + + boolNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, boolNode, priorNode); + } + + return boolNode; +} + BoolExprNode* InListBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) { + if (const auto node = decompose(csb)) + return node->pass1(tdbb, csb); + doPass1(tdbb, csb, arg.getAddress()); nodFlags |= FLAG_INVARIANT; diff --git a/src/dsql/BoolNodes.h b/src/dsql/BoolNodes.h index 43ad33fcd04..5cceab24e42 100644 --- a/src/dsql/BoolNodes.h +++ b/src/dsql/BoolNodes.h @@ -187,6 +187,9 @@ class InListBoolNode : public TypedNode process) override; bool execute(thread_db* tdbb, Request* request) const override; +private: + BoolExprNode* decompose(CompilerScratch* csb); + public: NestConst arg; NestConst list;