Skip to content

Commit fb6d683

Browse files
committed
Fix #8109: Plan/Performance regression when using special construct for IN
1 parent 52a9136 commit fb6d683

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/dsql/BoolNodes.cpp

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

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

13461410
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)