Skip to content

Commit 38334d7

Browse files
zhouyuaninfvg
authored andcommitted
[OAP] [11771] Fix smj result mismatch issue in semi, anit and full outer join
Signed-off-by: Yuan <yuanzhou@apache.org>
1 parent 447addb commit 38334d7

3 files changed

Lines changed: 153 additions & 96 deletions

File tree

velox/exec/MergeJoin.cpp

Lines changed: 90 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void MergeJoin::initialize() {
123123
isSemiFilterJoin(joinType_)) {
124124
joinTracker_ = JoinTracker(preferredOutputBatchRows_, pool());
125125
}
126-
} else if (joinNode_->isAntiJoin()) {
126+
} else if (joinNode_->isAntiJoin() || joinNode_->isFullJoin()) {
127127
// Anti join needs to track the left side rows that have no match on the
128128
// right.
129129
joinTracker_ = JoinTracker(preferredOutputBatchRows_, pool());
@@ -411,7 +411,8 @@ bool MergeJoin::tryAddOutputRow(
411411
const RowVectorPtr& leftBatch,
412412
vector_size_t leftRow,
413413
const RowVectorPtr& rightBatch,
414-
vector_size_t rightRow) {
414+
vector_size_t rightRow,
415+
bool isRightJoinForFullOuter) {
415416
if (outputSize_ == outputBatchSize_) {
416417
return false;
417418
}
@@ -445,12 +446,15 @@ bool MergeJoin::tryAddOutputRow(
445446
filterRightInputProjections_);
446447

447448
if (joinTracker_) {
448-
if (isRightJoin(joinType_) || isRightSemiFilterJoin(joinType_)) {
449+
if (isRightJoin(joinType_) || isRightSemiFilterJoin(joinType_) ||
450+
(isFullJoin(joinType_) && isRightJoinForFullOuter)) {
449451
// Record right-side row with a match on the left-side.
450-
joinTracker_->addMatch(rightBatch, rightRow, outputSize_);
452+
joinTracker_->addMatch(
453+
rightBatch, rightRow, outputSize_, isRightJoinForFullOuter);
451454
} else {
452455
// Record left-side row with a match on the right-side.
453-
joinTracker_->addMatch(leftBatch, leftRow, outputSize_);
456+
joinTracker_->addMatch(
457+
leftBatch, leftRow, outputSize_, isRightJoinForFullOuter);
454458
}
455459
}
456460
}
@@ -460,7 +464,8 @@ bool MergeJoin::tryAddOutputRow(
460464
if (isAntiJoin(joinType_)) {
461465
VELOX_CHECK(joinTracker_.has_value());
462466
// Record left-side row with a match on the right-side.
463-
joinTracker_->addMatch(leftBatch, leftRow, outputSize_);
467+
joinTracker_->addMatch(
468+
leftBatch, leftRow, outputSize_, isRightJoinForFullOuter);
464469
}
465470

466471
++outputSize_;
@@ -478,14 +483,16 @@ bool MergeJoin::prepareOutput(
478483
return true;
479484
}
480485

481-
if (isRightJoin(joinType_) && right != currentRight_) {
482-
return true;
483-
}
484-
485486
// If there is a new right, we need to flatten the dictionary.
486487
if (!isRightFlattened_ && right && currentRight_ != right) {
487488
flattenRightProjections();
488489
}
490+
491+
if ((isRightJoin(joinType_) || isFullJoin(joinType_)) &&
492+
right != currentRight_) {
493+
return true;
494+
}
495+
489496
return false;
490497
}
491498

@@ -508,11 +515,15 @@ bool MergeJoin::prepareOutput(
508515
}
509516
} else {
510517
for (const auto& projection : leftProjections_) {
518+
auto column = left->childAt(projection.inputChannel);
519+
// Flatten the left column if the column already is DictionaryVector.
520+
if (column->wrappedVector()->encoding() ==
521+
VectorEncoding::Simple::DICTIONARY) {
522+
BaseVector::flattenVector(column);
523+
}
524+
column->clearContainingLazyAndWrapped();
511525
localColumns[projection.outputChannel] = BaseVector::wrapInDictionary(
512-
{},
513-
leftOutputIndices_,
514-
outputBatchSize_,
515-
left->childAt(projection.inputChannel));
526+
{}, leftOutputIndices_, outputBatchSize_, column);
516527
}
517528
}
518529
currentLeft_ = left;
@@ -528,11 +539,10 @@ bool MergeJoin::prepareOutput(
528539
isRightFlattened_ = true;
529540
} else {
530541
for (const auto& projection : rightProjections_) {
542+
auto column = right->childAt(projection.inputChannel);
543+
column->clearContainingLazyAndWrapped();
531544
localColumns[projection.outputChannel] = BaseVector::wrapInDictionary(
532-
{},
533-
rightOutputIndices_,
534-
outputBatchSize_,
535-
right->childAt(projection.inputChannel));
545+
{}, rightOutputIndices_, outputBatchSize_, column);
536546
}
537547
isRightFlattened_ = false;
538548
}
@@ -596,6 +606,39 @@ bool MergeJoin::prepareOutput(
596606
bool MergeJoin::addToOutput() {
597607
if (isRightJoin(joinType_) || isRightSemiFilterJoin(joinType_)) {
598608
return addToOutputForRightJoin();
609+
} else if (isFullJoin(joinType_) && filter_) {
610+
if (!leftForRightJoinMatch_) {
611+
leftForRightJoinMatch_ = leftMatch_;
612+
rightForRightJoinMatch_ = rightMatch_;
613+
}
614+
615+
if (leftMatch_ && rightMatch_ && !leftJoinForFullFinished_) {
616+
auto left = addToOutputForLeftJoin();
617+
if (!leftMatch_) {
618+
leftJoinForFullFinished_ = true;
619+
}
620+
if (left) {
621+
if (!leftMatch_) {
622+
leftMatch_ = leftForRightJoinMatch_;
623+
rightMatch_ = rightForRightJoinMatch_;
624+
}
625+
626+
return true;
627+
}
628+
}
629+
630+
if (!leftMatch_ && !rightJoinForFullFinished_) {
631+
leftMatch_ = leftForRightJoinMatch_;
632+
rightMatch_ = rightForRightJoinMatch_;
633+
rightJoinForFullFinished_ = true;
634+
}
635+
636+
auto right = addToOutputForRightJoin();
637+
638+
leftForRightJoinMatch_ = leftMatch_;
639+
rightForRightJoinMatch_ = rightMatch_;
640+
641+
return right;
599642
} else {
600643
return addToOutputForLeftJoin();
601644
}
@@ -688,7 +731,13 @@ bool MergeJoin::addToOutputImpl() {
688731
} else {
689732
for (auto innerRow = innerStartRow; innerRow < innerEndRow;
690733
++innerRow) {
691-
if (!tryAddOutputRow(leftBatch, innerRow, rightBatch, outerRow)) {
734+
const auto isRightJoinForFullOuter = isFullJoin(joinType_);
735+
if (!tryAddOutputRow(
736+
leftBatch,
737+
innerRow,
738+
rightBatch,
739+
outerRow,
740+
isRightJoinForFullOuter)) {
692741
outerMatch->setCursor(outerBatchIndex, outerRow);
693742
innerMatch->setCursor(innerBatchIndex, innerRow);
694743
return true;
@@ -960,7 +1009,7 @@ RowVectorPtr MergeJoin::doGetOutput() {
9601009
isFullJoin(joinType_)) {
9611010
// If output_ is currently wrapping a different buffer, return it
9621011
// first.
963-
if (prepareOutput(input_, nullptr)) {
1012+
if (prepareOutput(input_, rightInput_)) {
9641013
output_->resize(outputSize_);
9651014
return std::move(output_);
9661015
}
@@ -985,7 +1034,7 @@ RowVectorPtr MergeJoin::doGetOutput() {
9851034
if (isRightJoin(joinType_) || isFullJoin(joinType_)) {
9861035
// If output_ is currently wrapping a different buffer, return it
9871036
// first.
988-
if (prepareOutput(nullptr, rightInput_)) {
1037+
if (prepareOutput(input_, rightInput_)) {
9891038
output_->resize(outputSize_);
9901039
return std::move(output_);
9911040
}
@@ -1035,6 +1084,8 @@ RowVectorPtr MergeJoin::doGetOutput() {
10351084
matchedLeftRows_ += leftEndRow - leftMatch_->startRowIndex;
10361085
matchedRightRows_ += rightEndRow - rightMatch_->startRowIndex;
10371086

1087+
leftJoinForFullFinished_ = false;
1088+
rightJoinForFullFinished_ = false;
10381089
if (!leftMatch_->complete || !rightMatch_->complete) {
10391090
if (!leftMatch_->complete) {
10401091
// Need to continue looking for the end of match.
@@ -1324,8 +1375,6 @@ void MergeJoin::updateOutputBatchSize(const RowVectorPtr& output) {
13241375
RowVectorPtr MergeJoin::applyFilter(const RowVectorPtr& output) {
13251376
const auto numRows = output->size();
13261377

1327-
RowVectorPtr fullOuterOutput = nullptr;
1328-
13291378
BufferPtr indices = allocateIndices(numRows, pool());
13301379
auto* rawIndices = indices->asMutable<vector_size_t>();
13311380
vector_size_t numPassed = 0;
@@ -1342,84 +1391,41 @@ RowVectorPtr MergeJoin::applyFilter(const RowVectorPtr& output) {
13421391

13431392
// If all matches for a given left-side row fail the filter, add a row to
13441393
// the output with nulls for the right-side columns.
1345-
const auto onMiss = [&](auto row) {
1394+
const auto onMiss = [&](auto row, bool isRightJoinForFullOuter) {
13461395
if (isSemiFilterJoin(joinType_)) {
13471396
return;
13481397
}
13491398
rawIndices[numPassed++] = row;
13501399

1351-
if (isFullJoin(joinType_)) {
1352-
// For filtered rows, it is necessary to insert additional data
1353-
// to ensure the result set is complete. Specifically, we
1354-
// need to generate two records: one record containing the
1355-
// columns from the left table along with nulls for the
1356-
// right table, and another record containing the columns
1357-
// from the right table along with nulls for the left table.
1358-
// For instance, the current output is filtered based on the condition
1359-
// t > 1.
1360-
1361-
// 1, 1
1362-
// 2, 2
1363-
// 3, 3
1364-
1365-
// In this scenario, we need to additionally insert a record 1, 1.
1366-
// Subsequently, we will set the values of the columns on the left to
1367-
// null and the values of the columns on the right to null as well. By
1368-
// doing so, we will obtain the final result set.
1369-
1370-
// 1, null
1371-
// null, 1
1372-
// 2, 2
1373-
// 3, 3
1374-
fullOuterOutput = BaseVector::create<RowVector>(
1375-
output->type(), output->size() + 1, pool());
1376-
1377-
for (auto i = 0; i < row + 1; ++i) {
1378-
for (auto j = 0; j < output->type()->size(); ++j) {
1379-
fullOuterOutput->childAt(j)->copy(
1380-
output->childAt(j).get(), i, i, 1);
1400+
if (!isRightJoin(joinType_)) {
1401+
if (isFullJoin(joinType_) && isRightJoinForFullOuter) {
1402+
for (auto& projection : leftProjections_) {
1403+
auto target = output->childAt(projection.outputChannel);
1404+
target->setNull(row, true);
13811405
}
1382-
}
1383-
1384-
for (auto j = 0; j < output->type()->size(); ++j) {
1385-
fullOuterOutput->childAt(j)->copy(
1386-
output->childAt(j).get(), row + 1, row, 1);
1387-
}
1388-
1389-
for (auto i = row + 1; i < output->size(); ++i) {
1390-
for (auto j = 0; j < output->type()->size(); ++j) {
1391-
fullOuterOutput->childAt(j)->copy(
1392-
output->childAt(j).get(), i + 1, i, 1);
1406+
} else {
1407+
for (auto& projection : rightProjections_) {
1408+
auto target = output->childAt(projection.outputChannel);
1409+
target->setNull(row, true);
13931410
}
13941411
}
1395-
1396-
for (auto& projection : leftProjections_) {
1397-
auto& target = fullOuterOutput->childAt(projection.outputChannel);
1398-
target->setNull(row, true);
1399-
}
1400-
1401-
for (auto& projection : rightProjections_) {
1402-
auto& target = fullOuterOutput->childAt(projection.outputChannel);
1403-
target->setNull(row + 1, true);
1404-
}
1405-
} else if (!isRightJoin(joinType_)) {
1406-
for (auto& projection : rightProjections_) {
1407-
auto& target = output->childAt(projection.outputChannel);
1408-
target->setNull(row, true);
1409-
}
14101412
} else {
14111413
for (auto& projection : leftProjections_) {
1412-
auto& target = output->childAt(projection.outputChannel);
1414+
auto target = output->childAt(projection.outputChannel);
14131415
target->setNull(row, true);
14141416
}
14151417
}
14161418
};
14171419

14181420
auto onMatch = [&](auto row, bool firstMatch) {
1419-
const bool isNonSemiAntiJoin =
1420-
!isSemiFilterJoin(joinType_) && !isAntiJoin(joinType_);
1421+
const bool isFullLeftJoin =
1422+
isFullJoin(joinType_) && !joinTracker_->isRightJoinForFullOuter(row);
1423+
1424+
const bool isNonSemiAntiFullJoin = !isSemiFilterJoin(joinType_) &&
1425+
!isAntiJoin(joinType_) && !isFullJoin(joinType_);
14211426

1422-
if ((isSemiFilterJoin(joinType_) && firstMatch) || isNonSemiAntiJoin) {
1427+
if ((isSemiFilterJoin(joinType_) && firstMatch) ||
1428+
isNonSemiAntiFullJoin || isFullLeftJoin) {
14231429
rawIndices[numPassed++] = row;
14241430
}
14251431
};
@@ -1480,17 +1486,10 @@ RowVectorPtr MergeJoin::applyFilter(const RowVectorPtr& output) {
14801486

14811487
if (numPassed == numRows) {
14821488
// All rows passed.
1483-
if (fullOuterOutput) {
1484-
return fullOuterOutput;
1485-
}
14861489
return output;
14871490
}
14881491

14891492
// Some, but not all rows passed.
1490-
if (fullOuterOutput) {
1491-
return wrap(numPassed, indices, fullOuterOutput);
1492-
}
1493-
14941493
return wrap(numPassed, indices, output);
14951494
}
14961495

0 commit comments

Comments
 (0)