mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 15:23:03 +01:00
Frontport the reworked implementation of the full outer join
This commit is contained in:
parent
e4c56776c0
commit
fe178a1404
@ -922,7 +922,7 @@ public:
|
|||||||
RecordSource* generate();
|
RecordSource* generate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RecordSource* process(const JoinType joinType);
|
RecordSource* process(StreamList* outerStreams = nullptr);
|
||||||
|
|
||||||
thread_db* const tdbb;
|
thread_db* const tdbb;
|
||||||
Optimizer* const optimizer;
|
Optimizer* const optimizer;
|
||||||
|
@ -94,15 +94,25 @@ OuterJoin::OuterJoin(thread_db* aTdbb, Optimizer* opt,
|
|||||||
|
|
||||||
RecordSource* OuterJoin::generate()
|
RecordSource* OuterJoin::generate()
|
||||||
{
|
{
|
||||||
const auto outerJoinRsb = process(OUTER_JOIN);
|
|
||||||
|
|
||||||
if (!optimizer->isFullJoin())
|
if (!optimizer->isFullJoin())
|
||||||
return outerJoinRsb;
|
{
|
||||||
|
fb_assert(optimizer->isLeftJoin());
|
||||||
|
return process();
|
||||||
|
}
|
||||||
|
|
||||||
// A FULL JOIN B is currently implemented similar to (A LEFT JOIN B) UNION ALL (B ANTI-JOIN A).
|
StreamList outerStreams;
|
||||||
|
const auto outerJoinRsb = process(&outerStreams);
|
||||||
|
|
||||||
|
// A FULL JOIN B is currently implemented similar to:
|
||||||
|
//
|
||||||
|
// (A LEFT JOIN B)
|
||||||
|
// UNION ALL
|
||||||
|
// (B LEFT JOIN A WHERE A.* IS NULL)
|
||||||
|
//
|
||||||
|
// See also FullOuterJoin class implementation.
|
||||||
//
|
//
|
||||||
// At this point we already have the first part -- (A LEFT JOIN B) -- ready,
|
// At this point we already have the first part -- (A LEFT JOIN B) -- ready,
|
||||||
// so just swap the sides and make an anti-join.
|
// so just swap the sides and make the second (inverted) join.
|
||||||
|
|
||||||
auto& outerStream = joinStreams[0];
|
auto& outerStream = joinStreams[0];
|
||||||
auto& innerStream = joinStreams[1];
|
auto& innerStream = joinStreams[1];
|
||||||
@ -131,15 +141,15 @@ RecordSource* OuterJoin::generate()
|
|||||||
iter.reset(CMP_clone_node_opt(tdbb, csb, iter));
|
iter.reset(CMP_clone_node_opt(tdbb, csb, iter));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto antiJoinRsb = process(ANTI_JOIN);
|
const auto antiJoinRsb = process();
|
||||||
|
|
||||||
// Allocate and return the final join record source
|
// Allocate and return the final join record source
|
||||||
|
|
||||||
return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb);
|
return FB_NEW_POOL(getPool()) FullOuterJoin(csb, outerJoinRsb, antiJoinRsb, outerStreams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RecordSource* OuterJoin::process(const JoinType joinType)
|
RecordSource* OuterJoin::process(StreamList* outerStreams)
|
||||||
{
|
{
|
||||||
BoolExprNode* boolean = nullptr;
|
BoolExprNode* boolean = nullptr;
|
||||||
|
|
||||||
@ -153,8 +163,7 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
|||||||
{
|
{
|
||||||
fb_assert(!outerStream.rsb);
|
fb_assert(!outerStream.rsb);
|
||||||
outerStream.rsb = optimizer->generateRetrieval(outerStream.number,
|
outerStream.rsb = optimizer->generateRetrieval(outerStream.number,
|
||||||
optimizer->isFullJoin() ? nullptr : sortPtr,
|
optimizer->isFullJoin() ? nullptr : sortPtr, true, false, &boolean);
|
||||||
true, false, &boolean);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -173,13 +182,15 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
|||||||
boolean = optimizer->composeBoolean();
|
boolean = optimizer->composeBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outerStreams)
|
||||||
|
outerStream.rsb->findUsedStreams(*outerStreams);
|
||||||
|
|
||||||
if (innerStream.number != INVALID_STREAM)
|
if (innerStream.number != INVALID_STREAM)
|
||||||
{
|
{
|
||||||
fb_assert(!innerStream.rsb);
|
fb_assert(!innerStream.rsb);
|
||||||
// AB: the sort clause for the inner stream of an OUTER JOIN
|
// AB: the sort clause for the inner stream of an OUTER JOIN
|
||||||
// should never be used for the index retrieval
|
// should never be used for the index retrieval
|
||||||
innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr,
|
innerStream.rsb = optimizer->generateRetrieval(innerStream.number, nullptr, false, true);
|
||||||
false, (joinType == OUTER_JOIN) ? true : false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a parent filter record source for any remaining booleans that
|
// Generate a parent filter record source for any remaining booleans that
|
||||||
@ -189,8 +200,7 @@ RecordSource* OuterJoin::process(const JoinType joinType)
|
|||||||
|
|
||||||
// Allocate and return the join record source
|
// Allocate and return the join record source
|
||||||
|
|
||||||
return FB_NEW_POOL(getPool())
|
return FB_NEW_POOL(getPool()) NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean);
|
||||||
NestedLoopJoin(csb, outerStream.rsb, innerRsb, boolean, joinType);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +37,13 @@ using namespace Jrd;
|
|||||||
// Data access: full outer join
|
// Data access: full outer join
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
|
|
||||||
FullOuterJoin::FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2)
|
FullOuterJoin::FullOuterJoin(CompilerScratch* csb,
|
||||||
|
RecordSource* arg1, RecordSource* arg2,
|
||||||
|
const StreamList& checkStreams)
|
||||||
: RecordSource(csb),
|
: RecordSource(csb),
|
||||||
m_arg1(arg1),
|
m_arg1(arg1),
|
||||||
m_arg2(arg2)
|
m_arg2(arg2),
|
||||||
|
m_checkStreams(csb->csb_pool, checkStreams)
|
||||||
{
|
{
|
||||||
fb_assert(m_arg1 && m_arg2);
|
fb_assert(m_arg1 && m_arg2);
|
||||||
|
|
||||||
@ -97,7 +100,27 @@ bool FullOuterJoin::internalGetRecord(thread_db* tdbb) const
|
|||||||
m_arg2->open(tdbb);
|
m_arg2->open(tdbb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_arg2->getRecord(tdbb);
|
// We should exclude matching records from the right-joined (second) record source,
|
||||||
|
// as they're already returned from the left-joined (first) record source
|
||||||
|
|
||||||
|
while (m_arg2->getRecord(tdbb))
|
||||||
|
{
|
||||||
|
bool matched = false;
|
||||||
|
|
||||||
|
for (const auto stream : m_checkStreams)
|
||||||
|
{
|
||||||
|
if (request->req_rpb[stream].rpb_number.isValid())
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FullOuterJoin::refetchRecord(thread_db* /*tdbb*/) const
|
bool FullOuterJoin::refetchRecord(thread_db* /*tdbb*/) const
|
||||||
|
@ -53,10 +53,11 @@ NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner,
|
NestedLoopJoin::NestedLoopJoin(CompilerScratch* csb,
|
||||||
BoolExprNode* boolean, JoinType joinType)
|
RecordSource* outer, RecordSource* inner,
|
||||||
|
BoolExprNode* boolean)
|
||||||
: RecordSource(csb),
|
: RecordSource(csb),
|
||||||
m_joinType(joinType),
|
m_joinType(OUTER_JOIN),
|
||||||
m_args(csb->csb_pool),
|
m_args(csb->csb_pool),
|
||||||
m_boolean(boolean)
|
m_boolean(boolean)
|
||||||
{
|
{
|
||||||
|
@ -1137,7 +1137,7 @@ namespace Jrd
|
|||||||
public:
|
public:
|
||||||
NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSource* const* args);
|
NestedLoopJoin(CompilerScratch* csb, FB_SIZE_T count, RecordSource* const* args);
|
||||||
NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner,
|
NestedLoopJoin(CompilerScratch* csb, RecordSource* outer, RecordSource* inner,
|
||||||
BoolExprNode* boolean, JoinType joinType);
|
BoolExprNode* boolean);
|
||||||
|
|
||||||
void close(thread_db* tdbb) const override;
|
void close(thread_db* tdbb) const override;
|
||||||
|
|
||||||
@ -1168,7 +1168,8 @@ namespace Jrd
|
|||||||
class FullOuterJoin : public RecordSource
|
class FullOuterJoin : public RecordSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2);
|
FullOuterJoin(CompilerScratch* csb, RecordSource* arg1, RecordSource* arg2,
|
||||||
|
const StreamList& checkStreams);
|
||||||
|
|
||||||
void close(thread_db* tdbb) const override;
|
void close(thread_db* tdbb) const override;
|
||||||
|
|
||||||
@ -1191,6 +1192,7 @@ namespace Jrd
|
|||||||
private:
|
private:
|
||||||
NestConst<RecordSource> m_arg1;
|
NestConst<RecordSource> m_arg1;
|
||||||
NestConst<RecordSource> m_arg2;
|
NestConst<RecordSource> m_arg2;
|
||||||
|
const StreamList m_checkStreams;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HashJoin : public RecordSource
|
class HashJoin : public RecordSource
|
||||||
|
Loading…
Reference in New Issue
Block a user