Skip to content

Commit 604f339

Browse files
committed
Rework the fix for #7937 and #8036 to address regressions #8535 (OuterJoinConversion = true causes performance regression) and #8821 (natural plan instead of primary/unique index)
1 parent e9b8859 commit 604f339

File tree

2 files changed

+200
-123
lines changed

2 files changed

+200
-123
lines changed

src/jrd/optimizer/Optimizer.cpp

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
965965
river->activate(csb);
966966

967967
bool sortCanBeUsed = true;
968-
SortNode* const orgSortNode = sort;
968+
const auto orgSortNode = sort;
969969

970970
// When DISTINCT and ORDER BY are done on different fields,
971971
// and ORDER BY can be mapped to an index, then the records
@@ -987,6 +987,8 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
987987
}
988988
else
989989
{
990+
fb_assert(isInnerJoin() || isSpecialJoin());
991+
990992
// AB: If previous rsb's are already on the stack we can't use
991993
// a navigational-retrieval for an ORDER BY because the next
992994
// streams are JOINed to the previous ones
@@ -1001,61 +1003,45 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
10011003
;
10021004
}
10031005

1004-
StreamList joinStreams(compileStreams);
1005-
1006-
if (isInnerJoin())
1006+
if (compileStreams.hasData())
10071007
{
1008-
fb_assert(joinStreams.getCount() != 1 || csb->csb_rpt[joinStreams[0]].csb_relation);
1008+
StreamList joinStreams(compileStreams);
10091009

1010-
while (true)
1010+
if (isInnerJoin())
10111011
{
1012-
// AB: Determine which streams have an index relationship
1013-
// with the currently active rivers. This is needed so that
1014-
// no merge is made between a new cross river and the
1015-
// currently active rivers. Where in the new cross river
1016-
// a stream depends (index) on the active rivers.
1017-
StreamList dependentStreams, freeStreams;
1018-
findDependentStreams(joinStreams, dependentStreams, freeStreams);
1019-
1020-
// If we have dependent and free streams then we can't rely on
1021-
// the sort node to be used for index navigation
1022-
if (dependentStreams.hasData() && freeStreams.hasData())
1023-
{
1024-
sort = nullptr;
1025-
sortCanBeUsed = false;
1026-
}
1012+
fb_assert(joinStreams.getCount() != 1 || csb->csb_rpt[joinStreams[0]].csb_relation);
10271013

1028-
if (dependentStreams.hasData())
1029-
{
1030-
// Copy free streams
1031-
joinStreams.assign(freeStreams);
1014+
// Process streams dependent on the priorly created rivers
10321015

1033-
// Make rivers from the dependent streams
1034-
generateInnerJoin(dependentStreams, rivers, &sort, rse->rse_plan);
1016+
if (joinDependentStreams(joinStreams, rivers, &sort))
1017+
{
1018+
// If we have dependent and free streams then we can't rely on
1019+
// the sort node to be used for index navigation
1020+
if (joinStreams.hasData())
1021+
{
1022+
sortCanBeUsed = false;
1023+
sort = nullptr;
1024+
}
10351025

10361026
// Generate one river which holds a cross join rsb between
1037-
// all currently available rivers
1027+
// all currently available rivers. This is needed to exclude
1028+
// dependent rivers from hashing or sort/merging that happens below.
10381029

10391030
rivers.add(FB_NEW_POOL(getPool()) CrossJoin(this, rivers, INNER_JOIN));
10401031
rivers.back()->activate(csb);
10411032
}
1042-
else
1043-
{
1044-
if (freeStreams.hasData())
1045-
{
1046-
// Deactivate streams from rivers on stack, because
1047-
// the remaining streams don't have any indexed relationship with them
1048-
for (const auto river : rivers)
1049-
river->deactivate(csb);
1050-
}
10511033

1052-
break;
1053-
}
1034+
// Now process streams dependent on rivers that are dependent themselves
1035+
1036+
for (const auto depRiver : dependentRivers)
1037+
depRiver->activate(csb);
1038+
1039+
joinDependentStreams(joinStreams, dependentRivers, nullptr);
10541040
}
1055-
}
10561041

1057-
// Attempt to form joins in decreasing order of desirability
1058-
generateInnerJoin(joinStreams, rivers, &sort, rse->rse_plan);
1042+
// Attempt to form joins in decreasing order of desirability
1043+
generateInnerJoin(joinStreams, rivers, &sort, rse->rse_plan);
1044+
}
10591045

10601046
if (rivers.isEmpty() && dependentRivers.isEmpty())
10611047
{
@@ -2357,9 +2343,10 @@ unsigned Optimizer::distributeEqualities(BoolExprNodeStack& orgStack, unsigned b
23572343
// Find the streams that can use an index with the currently active streams
23582344
//
23592345

2360-
void Optimizer::findDependentStreams(const StreamList& streams,
2361-
StreamList& dependent_streams,
2362-
StreamList& free_streams)
2346+
void Optimizer::findDependentStreams(const RiverList& rivers,
2347+
const StreamList& streams,
2348+
StreamList& dependentStreams,
2349+
StreamList& freeStreams)
23632350
{
23642351
#ifdef OPT_DEBUG_RETRIEVAL
23652352
if (streams.hasData())
@@ -2373,7 +2360,7 @@ void Optimizer::findDependentStreams(const StreamList& streams,
23732360
// Set temporary active flag for this stream
23742361
tail->activate();
23752362

2376-
bool indexed_relationship = false;
2363+
bool dependent = false;
23772364

23782365
if (conjuncts.hasData())
23792366
{
@@ -2387,19 +2374,85 @@ void Optimizer::findDependentStreams(const StreamList& streams,
23872374
const auto candidate = retrieval.getInversion();
23882375

23892376
if (candidate->dependentFromStreams.hasData())
2390-
indexed_relationship = true;
2377+
{
2378+
dependent = true;
2379+
2380+
StreamList checkStreams;
2381+
checkStreams.add(stream);
2382+
2383+
for (const auto river : rivers)
2384+
{
2385+
// If some river already depends on this stream,
2386+
// then itself it cannot be dependent
2387+
if (river->isDependent(checkStreams))
2388+
{
2389+
dependent = false;
2390+
break;
2391+
}
2392+
}
2393+
}
23912394
}
23922395

2393-
if (indexed_relationship)
2394-
dependent_streams.add(stream);
2396+
if (dependent)
2397+
dependentStreams.add(stream);
23952398
else
2396-
free_streams.add(stream);
2399+
freeStreams.add(stream);
23972400

23982401
// Reset active flag
23992402
tail->deactivate();
24002403
}
24012404
}
24022405

2406+
//
2407+
// Find streams dependent on the priorly created rivers and make a join from them
2408+
//
2409+
2410+
bool Optimizer::joinDependentStreams(StreamList& joinStreams, RiverList& rivers, SortNode** sort)
2411+
{
2412+
bool hasDependentStreams = false;
2413+
2414+
while (true)
2415+
{
2416+
// AB: Determine which streams have an index relationship
2417+
// with the currently active rivers. This is needed so that
2418+
// no merge is made between a new cross river and the
2419+
// currently active rivers. Where in the new cross river
2420+
// a stream depends (index) on the active rivers.
2421+
StreamList dependentStreams, freeStreams;
2422+
findDependentStreams(rivers, joinStreams, dependentStreams, freeStreams);
2423+
2424+
// If we have dependent and free streams then we can't rely on
2425+
// the sort node to be used for index navigation
2426+
if (dependentStreams.hasData() && freeStreams.hasData())
2427+
sort = nullptr;
2428+
2429+
if (dependentStreams.hasData())
2430+
{
2431+
hasDependentStreams = true;
2432+
2433+
// Copy free streams
2434+
joinStreams.assign(freeStreams);
2435+
2436+
// Make rivers from the dependent streams
2437+
generateInnerJoin(dependentStreams, rivers, sort, rse->rse_plan);
2438+
}
2439+
else
2440+
{
2441+
if (freeStreams.hasData())
2442+
{
2443+
// Deactivate streams from rivers on stack, because
2444+
// the remaining streams don't have any indexed relationship with them
2445+
for (const auto river : rivers)
2446+
river->deactivate(csb);
2447+
}
2448+
2449+
break;
2450+
}
2451+
}
2452+
2453+
return hasDependentStreams;
2454+
};
2455+
24032456

24042457
//
24052458
// Form streams into rivers according to the user-specified plan

0 commit comments

Comments
 (0)