Skip to content

Commit 893488b

Browse files
committed
(Simplified but still) cost-based choice between LOOP and HASH semi-joins
1 parent f7068cd commit 893488b

3 files changed

Lines changed: 77 additions & 13 deletions

File tree

src/jrd/RecordSourceNodes.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3559,8 +3559,11 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
35593559
conjunctStack.push(iter);
35603560
}
35613561

3562-
if (opt->isSpecialJoin() && !opt->deliverJoinConjuncts(conjunctStack))
3562+
if (opt->isSpecialJoin() && !opt->deliverJoinConjuncts(this, conjunctStack))
3563+
{
35633564
conjunctStack.clear();
3565+
firstRows = false;
3566+
}
35643567
}
35653568
else
35663569
{

src/jrd/optimizer/Optimizer.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ namespace
587587

588588

589589
//
590-
// Constructor
590+
// Constructors
591591
//
592592

593593
Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
@@ -622,6 +622,37 @@ Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
622622
}
623623

624624

625+
Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
626+
const BoolExprNodeStack& stack)
627+
: PermanentStorage(*aTdbb->getDefaultPool()),
628+
tdbb(aTdbb), csb(aCsb), rse(aRse),
629+
compileStreams(getPool()),
630+
bedStreams(getPool()),
631+
keyStreams(getPool()),
632+
outerStreams(getPool()),
633+
conjuncts(getPool())
634+
{
635+
for (BoolExprNodeStack::const_iterator iter(stack); iter.hasData(); ++iter)
636+
{
637+
const auto boolean = iter.object();
638+
639+
conjuncts.add({boolean, 0});
640+
baseConjuncts++;
641+
baseParentConjuncts++;
642+
baseMissingConjuncts++;
643+
}
644+
645+
StreamList rseStreams;
646+
rse->computeRseStreams(rseStreams);
647+
648+
for (const auto stream : rseStreams)
649+
{
650+
if (csb->csb_rpt[stream].csb_relation)
651+
compileRelation(stream);
652+
}
653+
}
654+
655+
625656
//
626657
// Destructor
627658
//
@@ -1179,6 +1210,29 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
11791210
}
11801211

11811212

1213+
//
1214+
// Calculate selectivity of the dependent booleans
1215+
//
1216+
1217+
double Optimizer::getDependentSelectivity()
1218+
{
1219+
StreamStateHolder stateHolder(csb, compileStreams);
1220+
stateHolder.activate();
1221+
1222+
double selectivity = MAXIMUM_SELECTIVITY;
1223+
for (const auto stream : compileStreams)
1224+
{
1225+
Retrieval retrieval(tdbb, this, stream, false, false, nullptr, true);
1226+
const auto candidate = retrieval.getInversion();
1227+
1228+
if (candidate->dependencies)
1229+
selectivity *= candidate->matchSelectivity;
1230+
}
1231+
1232+
return selectivity;
1233+
}
1234+
1235+
11821236
//
11831237
// Prepare relation and its indices for optimization
11841238
//

src/jrd/optimizer/Optimizer.h

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -439,21 +439,26 @@ class Optimizer final : public Firebird::PermanentStorage
439439
selectivity = MIN(selectivity * factor, MAXIMUM_SELECTIVITY);
440440
}
441441

442-
bool deliverJoinConjuncts(const BoolExprNodeStack& conjuncts) const
442+
double getDependentSelectivity();
443+
444+
bool deliverJoinConjuncts(RseNode* subRse, const BoolExprNodeStack& stack) const
443445
{
444-
fb_assert(conjuncts.hasData());
446+
// Determine whether the join conjunct(s) should be delivered to the inner RSE being joined.
447+
// The decision is based on the parent (outer) cardinality and selectivity of the conjunct(s).
448+
449+
fb_assert(stack.hasData());
450+
451+
const auto selectivity = Optimizer(tdbb, csb, subRse, stack).getDependentSelectivity();
445452

446-
// Look at cardinality of the priorly joined streams. If it's known to be
447-
// not very small, give up a possible nested loop join in favor of a hash join.
448-
// Here we assume every equi-join condition having a default selectivity (0.1).
449-
// TODO: replace with a proper cost-based decision in the future.
453+
if (selectivity < MAXIMUM_SELECTIVITY)
454+
{
455+
if (cardinality)
456+
return (cardinality * selectivity < MINIMUM_CARDINALITY);
450457

451-
double subSelectivity = MAXIMUM_SELECTIVITY;
452-
for (auto count = conjuncts.getCount(); count; count--)
453-
subSelectivity *= DEFAULT_SELECTIVITY;
454-
const auto thresholdCardinality = MINIMUM_CARDINALITY / subSelectivity;
458+
return true;
459+
}
455460

456-
return (cardinality && cardinality <= thresholdCardinality);
461+
return false;
457462
}
458463

459464
static RecordSource* compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse)
@@ -557,6 +562,8 @@ class Optimizer final : public Firebird::PermanentStorage
557562
private:
558563
Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
559564
bool parentFirstRows);
565+
Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
566+
const BoolExprNodeStack& stack);
560567

561568
RecordSource* compile(BoolExprNodeStack* parentStack);
562569

0 commit comments

Comments
 (0)