Skip to content

Commit 9a36aea

Browse files
committed
Postfixes for special joins
1 parent d282004 commit 9a36aea

File tree

3 files changed

+32
-33
lines changed

3 files changed

+32
-33
lines changed

src/jrd/RecordSourceNodes.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -3469,15 +3469,15 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
34693469

34703470
// Pass RseNode boolean only to inner substreams because join condition
34713471
// should never exclude records from outer substreams
3472-
if (opt->isInnerJoin() || (opt->isLeftJoin() && innerSubStream))
3472+
if (opt->isInnerJoin() || ((opt->isLeftJoin() || opt->isSpecialJoin()) && innerSubStream))
34733473
{
34743474
// AB: For an (X LEFT JOIN Y) mark the outer-streams (X) as
34753475
// active because the inner-streams (Y) are always "dependent"
34763476
// on the outer-streams. So that index retrieval nodes could be made.
34773477
//
34783478
// dimitr: the same for lateral derived tables in inner joins
34793479

3480-
if (opt->isLeftJoin() || isLateral())
3480+
if (!opt->isInnerJoin() || isLateral())
34813481
stateHolder.activate();
34823482

34833483
// For the LEFT JOIN, push all conjuncts except "missing" ones (e.g. IS NULL)
@@ -3486,6 +3486,9 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
34863486
if (iter->containsAnyStream(rseStreams))
34873487
conjunctStack.push(iter);
34883488
}
3489+
3490+
if (opt->isSpecialJoin() && !opt->deliverJoinConjuncts(conjunctStack))
3491+
conjunctStack.clear();
34893492
}
34903493
else
34913494
{

src/jrd/optimizer/Optimizer.cpp

+8-29
Original file line numberDiff line numberDiff line change
@@ -591,11 +591,10 @@ namespace
591591
//
592592

593593
Optimizer::Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
594-
bool parentFirstRows, double parentCardinality)
594+
bool parentFirstRows)
595595
: PermanentStorage(*aTdbb->getDefaultPool()),
596596
tdbb(aTdbb), csb(aCsb), rse(aRse),
597597
firstRows(rse->firstRows.valueOr(parentFirstRows)),
598-
cardinality(parentCardinality),
599598
compileStreams(getPool()),
600599
bedStreams(getPool()),
601600
keyStreams(getPool()),
@@ -651,7 +650,7 @@ RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack
651650
// if we're going to sort/aggregate the resultset afterwards
652651
const bool subFirstRows = firstRows && !rse->rse_sorted && !rse->rse_aggregate;
653652

654-
Optimizer subOpt(tdbb, csb, subRse, subFirstRows, cardinality);
653+
Optimizer subOpt(tdbb, csb, subRse, subFirstRows);
655654
const auto rsb = subOpt.compile(parentStack);
656655

657656
if (parentStack && subOpt.isInnerJoin())
@@ -702,31 +701,11 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
702701

703702
conjunctCount += distributeEqualities(conjunctStack, conjunctCount);
704703

705-
if (parentStack)
706-
{
707-
// AB: If we have limit our retrieval with FIRST / SKIP syntax then
708-
// we may not deliver above conditions (from higher rse's) to this
709-
// rse, because the results should be consistent.
710-
if (rse->rse_skip || rse->rse_first)
711-
parentStack = nullptr;
712-
713-
if (isSpecialJoin() && parentStack->hasData())
714-
{
715-
// We have a semi-join, look at the parent (priorly joined streams) cardinality.
716-
// If it's known to be not very small, nullify the parent conjuncts
717-
// to give up a possible nested loop join in favor of a hash join.
718-
// Here we assume every equi-join condition having a default selectivity (0.1).
719-
// TODO: replace with a proper cost-based decision in the future.
720-
721-
double subSelectivity = MAXIMUM_SELECTIVITY;
722-
for (auto count = parentStack->getCount(); count; count--)
723-
subSelectivity *= DEFAULT_SELECTIVITY;
724-
const auto thresholdCardinality = MINIMUM_CARDINALITY / subSelectivity;
725-
726-
if (!cardinality || cardinality > thresholdCardinality)
727-
parentStack = nullptr;
728-
}
729-
}
704+
// AB: If we have limit our retrieval with FIRST / SKIP syntax then
705+
// we may not deliver above conditions (from higher rse's) to this
706+
// rse, because the results should be consistent.
707+
if (rse->rse_skip || rse->rse_first)
708+
parentStack = nullptr;
730709

731710
// Set base-point before the parent/distributed nodes begin.
732711
const unsigned baseCount = conjunctCount;
@@ -905,7 +884,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
905884
bool computable = false;
906885

907886
// AB: Save all outer-part streams
908-
if (isInnerJoin() || (isLeftJoin() && !innerSubStream))
887+
if (isInnerJoin() || ((isLeftJoin() || isSpecialJoin()) && !innerSubStream))
909888
{
910889
if (node->computable(csb, INVALID_STREAM, false))
911890
computable = true;

src/jrd/optimizer/Optimizer.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,23 @@ class Optimizer : public Firebird::PermanentStorage
438438
selectivity = minSelectivity + diffSelectivity * factor;
439439
}
440440

441+
bool deliverJoinConjuncts(const BoolExprNodeStack& conjuncts)
442+
{
443+
fb_assert(conjuncts.hasData());
444+
445+
// Look at cardinality of the priorly joined streams. If it's known to be
446+
// not very small, give up a possible nested loop join in favor of a hash join.
447+
// Here we assume every equi-join condition having a default selectivity (0.1).
448+
// TODO: replace with a proper cost-based decision in the future.
449+
450+
double subSelectivity = MAXIMUM_SELECTIVITY;
451+
for (auto count = conjuncts.getCount(); count; count--)
452+
subSelectivity *= DEFAULT_SELECTIVITY;
453+
const auto thresholdCardinality = MINIMUM_CARDINALITY / subSelectivity;
454+
455+
return (cardinality && cardinality <= thresholdCardinality);
456+
}
457+
441458
static RecordSource* compile(thread_db* tdbb, CompilerScratch* csb, RseNode* rse)
442459
{
443460
bool firstRows = false;
@@ -452,7 +469,7 @@ class Optimizer : public Firebird::PermanentStorage
452469
firstRows = attachment->att_opt_first_rows.valueOr(defaultFirstRows);
453470
}
454471

455-
return Optimizer(tdbb, csb, rse, firstRows, 0).compile(nullptr);
472+
return Optimizer(tdbb, csb, rse, firstRows).compile(nullptr);
456473
}
457474

458475
~Optimizer();
@@ -537,7 +554,7 @@ class Optimizer : public Firebird::PermanentStorage
537554

538555
private:
539556
Optimizer(thread_db* aTdbb, CompilerScratch* aCsb, RseNode* aRse,
540-
bool parentFirstRows, double parentCardinality);
557+
bool parentFirstRows);
541558

542559
RecordSource* compile(BoolExprNodeStack* parentStack);
543560

0 commit comments

Comments
 (0)