Skip to content

Commit d282004

Browse files
committed
Refactor join types in RSE and RecordSource. Stricter processing of special joins. The optimizer is mostly ready for anti-joins.
1 parent a5b25c1 commit d282004

File tree

11 files changed

+264
-255
lines changed

11 files changed

+264
-255
lines changed

src/jrd/RecordSourceNodes.cpp

Lines changed: 56 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ static ValueExprNode* resolveUsingField(DsqlCompilerScratch* dsqlScratch, const
5555

5656
namespace
5757
{
58+
struct SpecialJoinItem
59+
{
60+
RseNode* rse;
61+
bool semiJoin;
62+
BoolExprNode* boolean;
63+
};
64+
65+
typedef HalfStaticArray<SpecialJoinItem, 4> SpecialJoinList;
66+
5867
// Search through the list of ANDed booleans to find comparisons
5968
// referring streams of parent select expressions.
6069
// Extract those booleans and return them to the caller.
@@ -112,18 +121,17 @@ namespace
112121
bool findPossibleJoins(CompilerScratch* csb,
113122
const StreamList& rseStreams,
114123
BoolExprNode** parentBoolean,
115-
RecordSourceNodeStack& rseStack,
116-
BoolExprNodeStack& booleanStack)
124+
SpecialJoinList& result)
117125
{
118126
auto boolNode = *parentBoolean;
119127

120128
const auto binaryNode = nodeAs<BinaryBoolNode>(boolNode);
121129
if (binaryNode && binaryNode->blrOp == blr_and)
122130
{
123131
const bool found1 = findPossibleJoins(csb, rseStreams,
124-
binaryNode->arg1.getAddress(), rseStack, booleanStack);
132+
binaryNode->arg1.getAddress(), result);
125133
const bool found2 = findPossibleJoins(csb, rseStreams,
126-
binaryNode->arg2.getAddress(), rseStack, booleanStack);
134+
binaryNode->arg2.getAddress(), result);
127135

128136
if (!binaryNode->arg1 && !binaryNode->arg2)
129137
*parentBoolean = nullptr;
@@ -142,7 +150,7 @@ namespace
142150
auto rse = rseNode->rse;
143151
fb_assert(rse && (rse->flags & RseNode::FLAG_SUB_QUERY));
144152

145-
if (rse->rse_boolean && rse->rse_jointype == blr_inner &&
153+
if (rse->rse_boolean && rse->isInnerJoin() &&
146154
!rse->rse_first && !rse->rse_skip && !rse->rse_plan)
147155
{
148156
// Find booleans convertable into semi-joins
@@ -187,9 +195,7 @@ namespace
187195
if (!dependent)
188196
{
189197
rse->flags &= ~RseNode::FLAG_SUB_QUERY;
190-
rse->flags |= RseNode::FLAG_SEMI_JOINED;
191-
rseStack.push(rse);
192-
booleanStack.push(boolean);
198+
result.push({rse, true, boolean});
193199
*parentBoolean = nullptr;
194200
return true;
195201
}
@@ -993,7 +999,7 @@ void RelationSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseN
993999
// 1) If the view has a projection, sort, first/skip or explicit plan.
9941000
// 2) If it's part of an outer join.
9951001

996-
if (rse->rse_jointype != blr_inner || // viewRse->rse_jointype != blr_inner || ???
1002+
if (!rse->isInnerJoin() || // !viewRse->isInnerJoin() || ???
9971003
viewRse->rse_sorted || viewRse->rse_projection || viewRse->rse_first ||
9981004
viewRse->rse_skip || viewRse->rse_plan)
9991005
{
@@ -2913,19 +2919,19 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
29132919

29142920
switch (rse_jointype)
29152921
{
2916-
case blr_inner:
2922+
case INNER_JOIN:
29172923
streamList->items[0] = doDsqlPass(dsqlScratch, fromList->items[0]);
29182924
streamList->items[1] = doDsqlPass(dsqlScratch, fromList->items[1]);
29192925
break;
29202926

2921-
case blr_left:
2927+
case LEFT_JOIN:
29222928
streamList->items[0] = doDsqlPass(dsqlScratch, fromList->items[0]);
29232929
++dsqlScratch->inOuterJoin;
29242930
streamList->items[1] = doDsqlPass(dsqlScratch, fromList->items[1]);
29252931
--dsqlScratch->inOuterJoin;
29262932
break;
29272933

2928-
case blr_right:
2934+
case RIGHT_JOIN:
29292935
++dsqlScratch->inOuterJoin;
29302936
streamList->items[0] = doDsqlPass(dsqlScratch, fromList->items[0]);
29312937
--dsqlScratch->inOuterJoin;
@@ -2936,7 +2942,7 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
29362942
streamList->items[1] = doDsqlPass(dsqlScratch, fromList->items[1]);
29372943
break;
29382944

2939-
case blr_full:
2945+
case FULL_JOIN:
29402946
++dsqlScratch->inOuterJoin;
29412947
streamList->items[0] = doDsqlPass(dsqlScratch, fromList->items[0]);
29422948
// Temporarily remove just created context(s) from the stack,
@@ -3008,7 +3014,7 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
30083014
if (matched->items.isEmpty())
30093015
{
30103016
// There is no match. Transform to CROSS JOIN.
3011-
node->rse_jointype = blr_inner;
3017+
node->rse_jointype = INNER_JOIN;
30123018
usingList = NULL;
30133019

30143020
delete matched;
@@ -3223,14 +3229,14 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb)
32233229
ValueExprNode* skip = rse_skip;
32243230
PlanNode* plan = rse_plan;
32253231

3226-
if (rse_jointype == blr_inner)
3232+
if (isInnerJoin())
32273233
csb->csb_inner_booleans.push(rse_boolean);
32283234

32293235
// zip thru RseNode expanding views and inner joins
32303236
for (auto sub : rse_relations)
32313237
processSource(tdbb, csb, this, sub, &boolean, stack);
32323238

3233-
if (rse_jointype == blr_inner)
3239+
if (isInnerJoin())
32343240
csb->csb_inner_booleans.pop();
32353241

32363242
// Now, rebuild the RseNode block.
@@ -3305,7 +3311,7 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
33053311
return;
33063312
}
33073313

3308-
if (rse_jointype != blr_inner)
3314+
if (isOuterJoin())
33093315
{
33103316
// Check whether any of the upper level booleans (those belonging to the WHERE clause)
33113317
// is able to filter out rows from the "inner" streams. If this is the case,
@@ -3320,15 +3326,15 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
33203326
StreamList streams;
33213327

33223328
// First check the left stream of the full outer join
3323-
if (rse_jointype == blr_full)
3329+
if (isFullJoin())
33243330
{
33253331
rse1->computeRseStreams(streams);
33263332

33273333
for (const auto boolean : csb->csb_inner_booleans)
33283334
{
33293335
if (boolean && boolean->ignoreNulls(streams))
33303336
{
3331-
rse_jointype = blr_left;
3337+
rse_jointype = LEFT_JOIN;
33323338
break;
33333339
}
33343340
}
@@ -3342,16 +3348,16 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
33423348
{
33433349
if (boolean && boolean->ignoreNulls(streams))
33443350
{
3345-
if (rse_jointype == blr_full)
3351+
if (isFullJoin())
33463352
{
33473353
// We should transform FULL join to RIGHT join,
33483354
// but as we don't allow them inside the engine
33493355
// just swap the sides and insist it's LEFT join
33503356
std::swap(rse_relations[0], rse_relations[1]);
3351-
rse_jointype = blr_left;
3357+
rse_jointype = LEFT_JOIN;
33523358
}
33533359
else
3354-
rse_jointype = blr_inner;
3360+
rse_jointype = INNER_JOIN;
33553361

33563362
break;
33573363
}
@@ -3366,11 +3372,9 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse,
33663372
// where we are just trying to inner join more than 2 streams. If possible,
33673373
// try to flatten the tree out before we go any further.
33683374

3369-
if (!isLateral() && !isSemiJoined() &&
3370-
rse->rse_jointype == blr_inner &&
3371-
rse_jointype == blr_inner &&
3372-
!rse_sorted && !rse_projection &&
3373-
!rse_first && !rse_skip && !rse_plan)
3375+
if (!isLateral() &&
3376+
rse->isInnerJoin() && isInnerJoin() &&
3377+
!rse_sorted && !rse_projection && !rse_first && !rse_skip && !rse_plan)
33743378
{
33753379
for (auto sub : rse_relations)
33763380
processSource(tdbb, csb, rse, sub, boolean, stack);
@@ -3461,8 +3465,9 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
34613465
computeRseStreams(rseStreams);
34623466

34633467
BoolExprNodeStack conjunctStack;
3468+
StreamStateHolder stateHolder(csb, opt->getOuterStreams());
34643469

3465-
// pass RseNode boolean only to inner substreams because join condition
3470+
// Pass RseNode boolean only to inner substreams because join condition
34663471
// should never exclude records from outer substreams
34673472
if (opt->isInnerJoin() || (opt->isLeftJoin() && innerSubStream))
34683473
{
@@ -3472,47 +3477,32 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr
34723477
//
34733478
// dimitr: the same for lateral derived tables in inner joins
34743479

3475-
StreamStateHolder stateHolder(csb, opt->getOuterStreams());
3476-
3477-
if (opt->isLeftJoin() || isLateral() || isSemiJoined())
3478-
{
3480+
if (opt->isLeftJoin() || isLateral())
34793481
stateHolder.activate();
34803482

3481-
if (opt->isLeftJoin() || isSemiJoined())
3482-
{
3483-
// Push all conjuncts except "missing" ones (e.g. IS NULL)
3484-
for (auto iter = opt->getConjuncts(false, true); iter.hasData(); ++iter)
3485-
{
3486-
if (iter->containsAnyStream(rseStreams))
3487-
conjunctStack.push(iter);
3488-
}
3489-
}
3490-
}
3491-
else
3483+
// For the LEFT JOIN, push all conjuncts except "missing" ones (e.g. IS NULL)
3484+
for (auto iter = opt->getConjuncts(false, opt->isLeftJoin()); iter.hasData(); ++iter)
34923485
{
3493-
for (auto iter = opt->getConjuncts(); iter.hasData(); ++iter)
3494-
{
3495-
if (iter->containsAnyStream(rseStreams))
3496-
conjunctStack.push(iter);
3497-
}
3486+
if (iter->containsAnyStream(rseStreams))
3487+
conjunctStack.push(iter);
34983488
}
3499-
3500-
return opt->compile(this, &conjunctStack);
35013489
}
3502-
3503-
// Push only parent conjuncts to the outer stream
3504-
for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter)
3490+
else
35053491
{
3506-
if (iter->containsAnyStream(rseStreams))
3507-
conjunctStack.push(iter);
3492+
// Push only parent conjuncts to the outer stream
3493+
for (auto iter = opt->getConjuncts(true, false); iter.hasData(); ++iter)
3494+
{
3495+
if (iter->containsAnyStream(rseStreams))
3496+
conjunctStack.push(iter);
3497+
}
35083498
}
35093499

35103500
return opt->compile(this, &conjunctStack);
35113501
}
35123502

35133503
RseNode* RseNode::processPossibleJoins(thread_db* tdbb, CompilerScratch* csb)
35143504
{
3515-
if (rse_jointype != blr_inner || !rse_boolean || rse_plan)
3505+
if (!isInnerJoin() || !rse_boolean || rse_plan)
35163506
return nullptr;
35173507

35183508
// If the sub-query is nested inside the other sub-query which wasn't converted into semi-join,
@@ -3532,19 +3522,16 @@ RseNode* RseNode::processPossibleJoins(thread_db* tdbb, CompilerScratch* csb)
35323522
}
35333523
}
35343524

3535-
RecordSourceNodeStack rseStack;
3536-
BoolExprNodeStack booleanStack;
3537-
35383525
// Find possibly joinable sub-queries
35393526

35403527
StreamList rseStreams;
35413528
computeRseStreams(rseStreams);
3529+
SpecialJoinList specialJoins;
35423530

3543-
if (!findPossibleJoins(csb, rseStreams, rse_boolean.getAddress(), rseStack, booleanStack))
3531+
if (!findPossibleJoins(csb, rseStreams, rse_boolean.getAddress(), specialJoins))
35443532
return nullptr;
35453533

3546-
fb_assert(rseStack.hasData() && booleanStack.hasData());
3547-
fb_assert(rseStack.getCount() == booleanStack.getCount());
3534+
fb_assert(specialJoins.hasData());
35483535

35493536
// Create joins between the original node and detected joinable nodes.
35503537
// Preserve FIRST/SKIP nodes at their original position, i.e. outside semi-joins.
@@ -3559,16 +3546,18 @@ RseNode* RseNode::processPossibleJoins(thread_db* tdbb, CompilerScratch* csb)
35593546
flags = 0;
35603547

35613548
auto rse = this;
3562-
while (rseStack.hasData())
3549+
while (specialJoins.hasData())
35633550
{
35643551
const auto newRse = FB_NEW_POOL(*tdbb->getDefaultPool())
35653552
RseNode(*tdbb->getDefaultPool());
35663553

3554+
const auto item = specialJoins.pop();
3555+
35673556
newRse->rse_relations.add(rse);
3568-
newRse->rse_relations.add(rseStack.pop());
3557+
newRse->rse_relations.add(item.rse);
35693558

3570-
newRse->rse_jointype = blr_inner;
3571-
newRse->rse_boolean = booleanStack.pop();
3559+
newRse->rse_jointype = item.semiJoin ? SEMI_JOIN : ANTI_JOIN;
3560+
newRse->rse_boolean = item.boolean;
35723561

35733562
rse = newRse;
35743563
}
@@ -3579,7 +3568,7 @@ RseNode* RseNode::processPossibleJoins(thread_db* tdbb, CompilerScratch* csb)
35793568
RseNode(*tdbb->getDefaultPool());
35803569

35813570
newRse->rse_relations.add(rse);
3582-
newRse->rse_jointype = blr_inner;
3571+
newRse->rse_jointype = INNER_JOIN;
35833572
newRse->rse_first = first;
35843573
newRse->rse_skip = skip;
35853574

0 commit comments

Comments
 (0)