Skip to content

Commit fd31e52

Browse files
authored
Fix #8109: Plan/Performance regression when using special construct for IN (#8425)
* Fix #8109: Plan/Performance regression when using special construct for IN * Optimize the code a bit: use one loop instead of two loops
1 parent 90e371d commit fd31e52

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

src/dsql/BoolNodes.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,8 +1339,69 @@ BoolExprNode* InListBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const
13391339
return node;
13401340
}
13411341

1342+
BoolExprNode* InListBoolNode::decompose(CompilerScratch* csb)
1343+
{
1344+
// Search for list items depending on record streams.
1345+
// If found, decompose expression:
1346+
// <arg> IN (<item1>, <item2>, <item3>, <item4> ...)
1347+
// into:
1348+
// <arg> IN (<item1>, <item2>, ...) OR <arg> = <item3> OR <arg> = <item4> ...
1349+
// where the ORed booleans are known to be stream-based (i.e. contain fields inside)
1350+
// and thus could use an index, if possible.
1351+
//
1352+
// See #8109 in the tracker, example:
1353+
//
1354+
// SELECT e.*
1355+
// FROM Employees e
1356+
// WHERE :SomeID IN (e.LeaderID, e.DispEmpID)
1357+
1358+
auto& pool = csb->csb_pool;
1359+
BoolExprNode* boolNode = nullptr;
1360+
1361+
for (auto iter = list->items.begin(); iter != list->items.end();)
1362+
{
1363+
ValueExprNode* const item = *iter;
1364+
1365+
SortedStreamList streams;
1366+
item->collectStreams(streams);
1367+
1368+
if (streams.isEmpty())
1369+
{
1370+
iter++;
1371+
continue;
1372+
}
1373+
1374+
list->items.remove(iter);
1375+
1376+
const auto cmpNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_eql, arg, item);
1377+
1378+
if (boolNode)
1379+
boolNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, boolNode, cmpNode);
1380+
else
1381+
boolNode = cmpNode;
1382+
}
1383+
1384+
if (boolNode && list->items.hasData())
1385+
{
1386+
BoolExprNode* priorNode = this;
1387+
1388+
if (list->items.getCount() == 1)
1389+
{
1390+
// Convert A IN (B) into A = B
1391+
priorNode = FB_NEW_POOL(pool) ComparativeBoolNode(pool, blr_eql, arg, list->items.front());
1392+
}
1393+
1394+
boolNode = FB_NEW_POOL(pool) BinaryBoolNode(pool, blr_or, boolNode, priorNode);
1395+
}
1396+
1397+
return boolNode;
1398+
}
1399+
13421400
BoolExprNode* InListBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb)
13431401
{
1402+
if (const auto node = decompose(csb))
1403+
return node->pass1(tdbb, csb);
1404+
13441405
doPass1(tdbb, csb, arg.getAddress());
13451406

13461407
nodFlags |= FLAG_INVARIANT;

src/dsql/BoolNodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ class InListBoolNode : public TypedNode<BoolExprNode, ExprNode::TYPE_IN_LIST_BOO
187187
void pass2Boolean(thread_db* tdbb, CompilerScratch* csb, std::function<void ()> process) override;
188188
bool execute(thread_db* tdbb, Request* request) const override;
189189

190+
private:
191+
BoolExprNode* decompose(CompilerScratch* csb);
192+
190193
public:
191194
NestConst<ValueExprNode> arg;
192195
NestConst<ValueListNode> list;

0 commit comments

Comments
 (0)