@@ -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+
13421403BoolExprNode* 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;
0 commit comments