duckdb 0.3.2-dev1000.0 → 0.3.2-dev1028.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/duckdb.cpp +648 -657
- package/src/duckdb.hpp +37 -2
- package/src/parquet-amalgamation.cpp +36540 -36540
package/src/duckdb.cpp
CHANGED
|
@@ -24867,8 +24867,9 @@ public:
|
|
|
24867
24867
|
void PrepareMergePhase();
|
|
24868
24868
|
//! Initializes the global sort state for another round of merging
|
|
24869
24869
|
void InitializeMergeRound();
|
|
24870
|
-
//! Completes the cascaded merge sort round
|
|
24871
|
-
|
|
24870
|
+
//! Completes the cascaded merge sort round.
|
|
24871
|
+
//! Pass true if you wish to use the radix data for further comparisons.
|
|
24872
|
+
void CompleteMergeRound(bool keep_radix_data = false);
|
|
24872
24873
|
|
|
24873
24874
|
public:
|
|
24874
24875
|
//! The lock for updating the order global state
|
|
@@ -26764,7 +26765,7 @@ void GlobalSortState::InitializeMergeRound() {
|
|
|
26764
26765
|
}
|
|
26765
26766
|
}
|
|
26766
26767
|
|
|
26767
|
-
void GlobalSortState::CompleteMergeRound() {
|
|
26768
|
+
void GlobalSortState::CompleteMergeRound(bool keep_radix_data) {
|
|
26768
26769
|
sorted_blocks.clear();
|
|
26769
26770
|
for (auto &sorted_block_vector : sorted_blocks_temp) {
|
|
26770
26771
|
sorted_blocks.push_back(make_unique<SortedBlock>(buffer_manager, *this));
|
|
@@ -26776,7 +26777,7 @@ void GlobalSortState::CompleteMergeRound() {
|
|
|
26776
26777
|
odd_one_out = nullptr;
|
|
26777
26778
|
}
|
|
26778
26779
|
// Only one block left: Done!
|
|
26779
|
-
if (sorted_blocks.size() == 1) {
|
|
26780
|
+
if (sorted_blocks.size() == 1 && !keep_radix_data) {
|
|
26780
26781
|
sorted_blocks[0]->radix_sorting_data.clear();
|
|
26781
26782
|
sorted_blocks[0]->blob_sorting_data = nullptr;
|
|
26782
26783
|
}
|
|
@@ -27075,6 +27076,7 @@ void PayloadScanner::Scan(DataChunk &chunk) {
|
|
|
27075
27076
|
auto count = MinValue((idx_t)STANDARD_VECTOR_SIZE, total_count - total_scanned);
|
|
27076
27077
|
if (count == 0) {
|
|
27077
27078
|
D_ASSERT(read_state.block_idx == sorted_data.data_blocks.size());
|
|
27079
|
+
chunk.SetCardinality(count);
|
|
27078
27080
|
return;
|
|
27079
27081
|
}
|
|
27080
27082
|
// Eagerly delete references to blocks that we've passed
|
|
@@ -47663,366 +47665,6 @@ idx_t JoinHashTable::FillWithHTOffsets(data_ptr_t *key_locations, JoinHTScanStat
|
|
|
47663
47665
|
}
|
|
47664
47666
|
return key_count;
|
|
47665
47667
|
}
|
|
47666
|
-
} // namespace duckdb
|
|
47667
|
-
//===----------------------------------------------------------------------===//
|
|
47668
|
-
// DuckDB
|
|
47669
|
-
//
|
|
47670
|
-
// duckdb/execution/merge_join.hpp
|
|
47671
|
-
//
|
|
47672
|
-
//
|
|
47673
|
-
//===----------------------------------------------------------------------===//
|
|
47674
|
-
|
|
47675
|
-
|
|
47676
|
-
|
|
47677
|
-
|
|
47678
|
-
|
|
47679
|
-
|
|
47680
|
-
|
|
47681
|
-
|
|
47682
|
-
namespace duckdb {
|
|
47683
|
-
|
|
47684
|
-
struct MergeOrder {
|
|
47685
|
-
SelectionVector order;
|
|
47686
|
-
idx_t count;
|
|
47687
|
-
VectorData vdata;
|
|
47688
|
-
};
|
|
47689
|
-
|
|
47690
|
-
enum MergeInfoType : uint8_t { SCALAR_MERGE_INFO = 1, CHUNK_MERGE_INFO = 2 };
|
|
47691
|
-
|
|
47692
|
-
struct MergeInfo {
|
|
47693
|
-
MergeInfo(MergeInfoType info_type, LogicalType type) : info_type(info_type), type(type) {
|
|
47694
|
-
}
|
|
47695
|
-
MergeInfoType info_type;
|
|
47696
|
-
LogicalType type;
|
|
47697
|
-
};
|
|
47698
|
-
|
|
47699
|
-
struct ScalarMergeInfo : public MergeInfo {
|
|
47700
|
-
MergeOrder ℴ
|
|
47701
|
-
idx_t &pos;
|
|
47702
|
-
SelectionVector result;
|
|
47703
|
-
|
|
47704
|
-
ScalarMergeInfo(MergeOrder &order, LogicalType type, idx_t &pos)
|
|
47705
|
-
: MergeInfo(MergeInfoType::SCALAR_MERGE_INFO, type), order(order), pos(pos), result(STANDARD_VECTOR_SIZE) {
|
|
47706
|
-
}
|
|
47707
|
-
};
|
|
47708
|
-
|
|
47709
|
-
struct ChunkMergeInfo : public MergeInfo {
|
|
47710
|
-
ChunkCollection &data_chunks;
|
|
47711
|
-
vector<MergeOrder> &order_info;
|
|
47712
|
-
bool found_match[STANDARD_VECTOR_SIZE];
|
|
47713
|
-
|
|
47714
|
-
ChunkMergeInfo(ChunkCollection &data_chunks, vector<MergeOrder> &order_info)
|
|
47715
|
-
: MergeInfo(MergeInfoType::CHUNK_MERGE_INFO, data_chunks.Types()[0]), data_chunks(data_chunks),
|
|
47716
|
-
order_info(order_info) {
|
|
47717
|
-
memset(found_match, 0, sizeof(found_match));
|
|
47718
|
-
}
|
|
47719
|
-
};
|
|
47720
|
-
|
|
47721
|
-
struct MergeJoinComplex {
|
|
47722
|
-
struct LessThan {
|
|
47723
|
-
template <class T>
|
|
47724
|
-
static idx_t Operation(ScalarMergeInfo &l, ScalarMergeInfo &r);
|
|
47725
|
-
};
|
|
47726
|
-
struct LessThanEquals {
|
|
47727
|
-
template <class T>
|
|
47728
|
-
static idx_t Operation(ScalarMergeInfo &l, ScalarMergeInfo &r);
|
|
47729
|
-
};
|
|
47730
|
-
struct GreaterThan {
|
|
47731
|
-
template <class T>
|
|
47732
|
-
static idx_t Operation(ScalarMergeInfo &l, ScalarMergeInfo &r) {
|
|
47733
|
-
return LessThan::Operation<T>(r, l);
|
|
47734
|
-
}
|
|
47735
|
-
};
|
|
47736
|
-
struct GreaterThanEquals {
|
|
47737
|
-
template <class T>
|
|
47738
|
-
static idx_t Operation(ScalarMergeInfo &l, ScalarMergeInfo &r) {
|
|
47739
|
-
return LessThanEquals::Operation<T>(r, l);
|
|
47740
|
-
}
|
|
47741
|
-
};
|
|
47742
|
-
|
|
47743
|
-
static idx_t Perform(MergeInfo &l, MergeInfo &r, ExpressionType comparison_type);
|
|
47744
|
-
};
|
|
47745
|
-
|
|
47746
|
-
struct MergeJoinSimple {
|
|
47747
|
-
struct LessThan {
|
|
47748
|
-
template <class T>
|
|
47749
|
-
static idx_t Operation(ScalarMergeInfo &l, ChunkMergeInfo &r);
|
|
47750
|
-
};
|
|
47751
|
-
struct LessThanEquals {
|
|
47752
|
-
template <class T>
|
|
47753
|
-
static idx_t Operation(ScalarMergeInfo &l, ChunkMergeInfo &r);
|
|
47754
|
-
};
|
|
47755
|
-
struct GreaterThan {
|
|
47756
|
-
template <class T>
|
|
47757
|
-
static idx_t Operation(ScalarMergeInfo &l, ChunkMergeInfo &r);
|
|
47758
|
-
};
|
|
47759
|
-
struct GreaterThanEquals {
|
|
47760
|
-
template <class T>
|
|
47761
|
-
static idx_t Operation(ScalarMergeInfo &l, ChunkMergeInfo &r);
|
|
47762
|
-
};
|
|
47763
|
-
|
|
47764
|
-
static idx_t Perform(MergeInfo &l, MergeInfo &r, ExpressionType comparison);
|
|
47765
|
-
};
|
|
47766
|
-
|
|
47767
|
-
#define INSTANTIATE_MERGEJOIN_TEMPLATES(MJCLASS, OPNAME, L, R) \
|
|
47768
|
-
template idx_t MJCLASS::OPNAME::Operation<int8_t>(L & l, R & r); \
|
|
47769
|
-
template idx_t MJCLASS::OPNAME::Operation<int16_t>(L & l, R & r); \
|
|
47770
|
-
template idx_t MJCLASS::OPNAME::Operation<int32_t>(L & l, R & r); \
|
|
47771
|
-
template idx_t MJCLASS::OPNAME::Operation<int64_t>(L & l, R & r); \
|
|
47772
|
-
template idx_t MJCLASS::OPNAME::Operation<uint8_t>(L & l, R & r); \
|
|
47773
|
-
template idx_t MJCLASS::OPNAME::Operation<uint16_t>(L & l, R & r); \
|
|
47774
|
-
template idx_t MJCLASS::OPNAME::Operation<uint32_t>(L & l, R & r); \
|
|
47775
|
-
template idx_t MJCLASS::OPNAME::Operation<uint64_t>(L & l, R & r); \
|
|
47776
|
-
template idx_t MJCLASS::OPNAME::Operation<hugeint_t>(L & l, R & r); \
|
|
47777
|
-
template idx_t MJCLASS::OPNAME::Operation<float>(L & l, R & r); \
|
|
47778
|
-
template idx_t MJCLASS::OPNAME::Operation<double>(L & l, R & r); \
|
|
47779
|
-
template idx_t MJCLASS::OPNAME::Operation<interval_t>(L & l, R & r); \
|
|
47780
|
-
template idx_t MJCLASS::OPNAME::Operation<string_t>(L & l, R & r);
|
|
47781
|
-
|
|
47782
|
-
} // namespace duckdb
|
|
47783
|
-
|
|
47784
|
-
|
|
47785
|
-
|
|
47786
|
-
|
|
47787
|
-
namespace duckdb {
|
|
47788
|
-
|
|
47789
|
-
template <class MJ, class L_ARG, class R_ARG>
|
|
47790
|
-
static idx_t MergeJoinSwitch(L_ARG &l, R_ARG &r) {
|
|
47791
|
-
switch (l.type.InternalType()) {
|
|
47792
|
-
case PhysicalType::BOOL:
|
|
47793
|
-
case PhysicalType::INT8:
|
|
47794
|
-
return MJ::template Operation<int8_t>(l, r);
|
|
47795
|
-
case PhysicalType::INT16:
|
|
47796
|
-
return MJ::template Operation<int16_t>(l, r);
|
|
47797
|
-
case PhysicalType::INT32:
|
|
47798
|
-
return MJ::template Operation<int32_t>(l, r);
|
|
47799
|
-
case PhysicalType::INT64:
|
|
47800
|
-
return MJ::template Operation<int64_t>(l, r);
|
|
47801
|
-
case PhysicalType::UINT8:
|
|
47802
|
-
return MJ::template Operation<uint8_t>(l, r);
|
|
47803
|
-
case PhysicalType::UINT16:
|
|
47804
|
-
return MJ::template Operation<uint16_t>(l, r);
|
|
47805
|
-
case PhysicalType::UINT32:
|
|
47806
|
-
return MJ::template Operation<uint32_t>(l, r);
|
|
47807
|
-
case PhysicalType::UINT64:
|
|
47808
|
-
return MJ::template Operation<uint64_t>(l, r);
|
|
47809
|
-
case PhysicalType::INT128:
|
|
47810
|
-
return MJ::template Operation<hugeint_t>(l, r);
|
|
47811
|
-
case PhysicalType::FLOAT:
|
|
47812
|
-
return MJ::template Operation<float>(l, r);
|
|
47813
|
-
case PhysicalType::DOUBLE:
|
|
47814
|
-
return MJ::template Operation<double>(l, r);
|
|
47815
|
-
case PhysicalType::INTERVAL:
|
|
47816
|
-
return MJ::template Operation<interval_t>(l, r);
|
|
47817
|
-
case PhysicalType::VARCHAR:
|
|
47818
|
-
return MJ::template Operation<string_t>(l, r);
|
|
47819
|
-
default:
|
|
47820
|
-
throw InternalException("Type not implemented for merge join!");
|
|
47821
|
-
}
|
|
47822
|
-
}
|
|
47823
|
-
|
|
47824
|
-
template <class T, class L_ARG, class R_ARG>
|
|
47825
|
-
static idx_t MergeJoinComparisonSwitch(L_ARG &l, R_ARG &r, ExpressionType comparison_type) {
|
|
47826
|
-
switch (comparison_type) {
|
|
47827
|
-
case ExpressionType::COMPARE_LESSTHAN:
|
|
47828
|
-
return MergeJoinSwitch<typename T::LessThan, L_ARG, R_ARG>(l, r);
|
|
47829
|
-
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
47830
|
-
return MergeJoinSwitch<typename T::LessThanEquals, L_ARG, R_ARG>(l, r);
|
|
47831
|
-
case ExpressionType::COMPARE_GREATERTHAN:
|
|
47832
|
-
return MergeJoinSwitch<typename T::GreaterThan, L_ARG, R_ARG>(l, r);
|
|
47833
|
-
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
47834
|
-
return MergeJoinSwitch<typename T::GreaterThanEquals, L_ARG, R_ARG>(l, r);
|
|
47835
|
-
default:
|
|
47836
|
-
throw InternalException("Unimplemented comparison type for merge join!");
|
|
47837
|
-
}
|
|
47838
|
-
}
|
|
47839
|
-
|
|
47840
|
-
idx_t MergeJoinComplex::Perform(MergeInfo &l, MergeInfo &r, ExpressionType comparison_type) {
|
|
47841
|
-
D_ASSERT(l.info_type == MergeInfoType::SCALAR_MERGE_INFO && r.info_type == MergeInfoType::SCALAR_MERGE_INFO);
|
|
47842
|
-
auto &left = (ScalarMergeInfo &)l;
|
|
47843
|
-
auto &right = (ScalarMergeInfo &)r;
|
|
47844
|
-
D_ASSERT(left.type == right.type);
|
|
47845
|
-
if (left.order.count == 0 || right.order.count == 0) {
|
|
47846
|
-
return 0;
|
|
47847
|
-
}
|
|
47848
|
-
return MergeJoinComparisonSwitch<MergeJoinComplex, ScalarMergeInfo, ScalarMergeInfo>(left, right, comparison_type);
|
|
47849
|
-
}
|
|
47850
|
-
|
|
47851
|
-
idx_t MergeJoinSimple::Perform(MergeInfo &l, MergeInfo &r, ExpressionType comparison_type) {
|
|
47852
|
-
D_ASSERT(l.info_type == MergeInfoType::SCALAR_MERGE_INFO && r.info_type == MergeInfoType::CHUNK_MERGE_INFO);
|
|
47853
|
-
auto &left = (ScalarMergeInfo &)l;
|
|
47854
|
-
auto &right = (ChunkMergeInfo &)r;
|
|
47855
|
-
D_ASSERT(left.type == right.type);
|
|
47856
|
-
if (left.order.count == 0 || right.data_chunks.Count() == 0) {
|
|
47857
|
-
return 0;
|
|
47858
|
-
}
|
|
47859
|
-
return MergeJoinComparisonSwitch<MergeJoinSimple, ScalarMergeInfo, ChunkMergeInfo>(left, right, comparison_type);
|
|
47860
|
-
}
|
|
47861
|
-
|
|
47862
|
-
} // namespace duckdb
|
|
47863
|
-
|
|
47864
|
-
|
|
47865
|
-
|
|
47866
|
-
|
|
47867
|
-
|
|
47868
|
-
namespace duckdb {
|
|
47869
|
-
|
|
47870
|
-
template <class T, class OP>
|
|
47871
|
-
idx_t MergeJoinComplexLessThan(ScalarMergeInfo &l, ScalarMergeInfo &r) {
|
|
47872
|
-
if (r.pos >= r.order.count) {
|
|
47873
|
-
return 0;
|
|
47874
|
-
}
|
|
47875
|
-
auto ldata = (T *)l.order.vdata.data;
|
|
47876
|
-
auto rdata = (T *)r.order.vdata.data;
|
|
47877
|
-
auto &lorder = l.order.order;
|
|
47878
|
-
auto &rorder = r.order.order;
|
|
47879
|
-
idx_t result_count = 0;
|
|
47880
|
-
while (true) {
|
|
47881
|
-
if (l.pos < l.order.count) {
|
|
47882
|
-
auto lidx = lorder.get_index(l.pos);
|
|
47883
|
-
auto ridx = rorder.get_index(r.pos);
|
|
47884
|
-
auto dlidx = l.order.vdata.sel->get_index(lidx);
|
|
47885
|
-
auto dridx = r.order.vdata.sel->get_index(ridx);
|
|
47886
|
-
if (OP::Operation(ldata[dlidx], rdata[dridx])) {
|
|
47887
|
-
// left side smaller: found match
|
|
47888
|
-
l.result.set_index(result_count, lidx);
|
|
47889
|
-
r.result.set_index(result_count, ridx);
|
|
47890
|
-
result_count++;
|
|
47891
|
-
// move left side forward
|
|
47892
|
-
l.pos++;
|
|
47893
|
-
if (result_count == STANDARD_VECTOR_SIZE) {
|
|
47894
|
-
// out of space!
|
|
47895
|
-
break;
|
|
47896
|
-
}
|
|
47897
|
-
continue;
|
|
47898
|
-
}
|
|
47899
|
-
}
|
|
47900
|
-
// right side smaller or equal, or left side exhausted: move
|
|
47901
|
-
// right pointer forward reset left side to start
|
|
47902
|
-
l.pos = 0;
|
|
47903
|
-
r.pos++;
|
|
47904
|
-
if (r.pos == r.order.count) {
|
|
47905
|
-
break;
|
|
47906
|
-
}
|
|
47907
|
-
}
|
|
47908
|
-
return result_count;
|
|
47909
|
-
}
|
|
47910
|
-
|
|
47911
|
-
template <class T>
|
|
47912
|
-
idx_t MergeJoinComplex::LessThan::Operation(ScalarMergeInfo &l, ScalarMergeInfo &r) {
|
|
47913
|
-
return MergeJoinComplexLessThan<T, duckdb::LessThan>(l, r);
|
|
47914
|
-
}
|
|
47915
|
-
|
|
47916
|
-
template <class T>
|
|
47917
|
-
idx_t MergeJoinComplex::LessThanEquals::Operation(ScalarMergeInfo &l, ScalarMergeInfo &r) {
|
|
47918
|
-
return MergeJoinComplexLessThan<T, duckdb::LessThanEquals>(l, r);
|
|
47919
|
-
}
|
|
47920
|
-
|
|
47921
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinComplex, LessThan, ScalarMergeInfo, ScalarMergeInfo)
|
|
47922
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinComplex, LessThanEquals, ScalarMergeInfo, ScalarMergeInfo)
|
|
47923
|
-
|
|
47924
|
-
} // namespace duckdb
|
|
47925
|
-
|
|
47926
|
-
|
|
47927
|
-
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
namespace duckdb {
|
|
47931
|
-
|
|
47932
|
-
template <class T, class OP>
|
|
47933
|
-
static idx_t MergeJoinSimpleGreaterThan(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
47934
|
-
auto ldata = (T *)l.order.vdata.data;
|
|
47935
|
-
auto &lorder = l.order.order;
|
|
47936
|
-
l.pos = l.order.count;
|
|
47937
|
-
for (idx_t chunk_idx = 0; chunk_idx < r.order_info.size(); chunk_idx++) {
|
|
47938
|
-
// we only care about the SMALLEST value in each of the RHS
|
|
47939
|
-
// because we want to figure out if they are greater than [or equal] to ANY value
|
|
47940
|
-
// get the smallest value from the RHS
|
|
47941
|
-
auto &rorder = r.order_info[chunk_idx];
|
|
47942
|
-
auto rdata = (T *)rorder.vdata.data;
|
|
47943
|
-
auto min_r_value = rdata[rorder.vdata.sel->get_index(rorder.order.get_index(0))];
|
|
47944
|
-
// now we start from the current lpos value and check if we found a new value that is [>= OR >] the min RHS
|
|
47945
|
-
// value
|
|
47946
|
-
while (true) {
|
|
47947
|
-
auto lidx = lorder.get_index(l.pos - 1);
|
|
47948
|
-
auto dlidx = l.order.vdata.sel->get_index(lidx);
|
|
47949
|
-
if (OP::Operation(ldata[dlidx], min_r_value)) {
|
|
47950
|
-
// found a match for lpos, set it in the found_match vector
|
|
47951
|
-
r.found_match[lidx] = true;
|
|
47952
|
-
l.pos--;
|
|
47953
|
-
if (l.pos == 0) {
|
|
47954
|
-
// early out: we exhausted the entire LHS and they all match
|
|
47955
|
-
return 0;
|
|
47956
|
-
}
|
|
47957
|
-
} else {
|
|
47958
|
-
// we found no match: any subsequent value from the LHS we scan now will be smaller and thus also not
|
|
47959
|
-
// match move to the next RHS chunk
|
|
47960
|
-
break;
|
|
47961
|
-
}
|
|
47962
|
-
}
|
|
47963
|
-
}
|
|
47964
|
-
return 0;
|
|
47965
|
-
}
|
|
47966
|
-
template <class T>
|
|
47967
|
-
idx_t MergeJoinSimple::GreaterThan::Operation(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
47968
|
-
return MergeJoinSimpleGreaterThan<T, duckdb::GreaterThan>(l, r);
|
|
47969
|
-
}
|
|
47970
|
-
|
|
47971
|
-
template <class T>
|
|
47972
|
-
idx_t MergeJoinSimple::GreaterThanEquals::Operation(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
47973
|
-
return MergeJoinSimpleGreaterThan<T, duckdb::GreaterThanEquals>(l, r);
|
|
47974
|
-
}
|
|
47975
|
-
|
|
47976
|
-
template <class T, class OP>
|
|
47977
|
-
static idx_t MergeJoinSimpleLessThan(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
47978
|
-
auto ldata = (T *)l.order.vdata.data;
|
|
47979
|
-
auto &lorder = l.order.order;
|
|
47980
|
-
l.pos = 0;
|
|
47981
|
-
for (idx_t chunk_idx = 0; chunk_idx < r.order_info.size(); chunk_idx++) {
|
|
47982
|
-
// we only care about the BIGGEST value in each of the RHS
|
|
47983
|
-
// because we want to figure out if they are less than [or equal] to ANY value
|
|
47984
|
-
// get the biggest value from the RHS
|
|
47985
|
-
auto &rorder = r.order_info[chunk_idx];
|
|
47986
|
-
auto rdata = (T *)rorder.vdata.data;
|
|
47987
|
-
auto max_r_value = rdata[rorder.vdata.sel->get_index(rorder.order.get_index(rorder.count - 1))];
|
|
47988
|
-
// now we start from the current lpos value and check if we found a new value that is [<= OR <] the max RHS
|
|
47989
|
-
// value
|
|
47990
|
-
while (true) {
|
|
47991
|
-
auto lidx = lorder.get_index(l.pos);
|
|
47992
|
-
auto dlidx = l.order.vdata.sel->get_index(lidx);
|
|
47993
|
-
if (OP::Operation(ldata[dlidx], max_r_value)) {
|
|
47994
|
-
// found a match for lpos, set it in the found_match vector
|
|
47995
|
-
r.found_match[lidx] = true;
|
|
47996
|
-
l.pos++;
|
|
47997
|
-
if (l.pos == l.order.count) {
|
|
47998
|
-
// early out: we exhausted the entire LHS and they all match
|
|
47999
|
-
return 0;
|
|
48000
|
-
}
|
|
48001
|
-
} else {
|
|
48002
|
-
// we found no match: any subsequent value from the LHS we scan now will be bigger and thus also not
|
|
48003
|
-
// match move to the next RHS chunk
|
|
48004
|
-
break;
|
|
48005
|
-
}
|
|
48006
|
-
}
|
|
48007
|
-
}
|
|
48008
|
-
return 0;
|
|
48009
|
-
}
|
|
48010
|
-
|
|
48011
|
-
template <class T>
|
|
48012
|
-
idx_t MergeJoinSimple::LessThan::Operation(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
48013
|
-
return MergeJoinSimpleLessThan<T, duckdb::LessThan>(l, r);
|
|
48014
|
-
}
|
|
48015
|
-
|
|
48016
|
-
template <class T>
|
|
48017
|
-
idx_t MergeJoinSimple::LessThanEquals::Operation(ScalarMergeInfo &l, ChunkMergeInfo &r) {
|
|
48018
|
-
return MergeJoinSimpleLessThan<T, duckdb::LessThanEquals>(l, r);
|
|
48019
|
-
}
|
|
48020
|
-
|
|
48021
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinSimple, LessThan, ScalarMergeInfo, ChunkMergeInfo)
|
|
48022
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinSimple, LessThanEquals, ScalarMergeInfo, ChunkMergeInfo)
|
|
48023
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinSimple, GreaterThan, ScalarMergeInfo, ChunkMergeInfo)
|
|
48024
|
-
INSTANTIATE_MERGEJOIN_TEMPLATES(MergeJoinSimple, GreaterThanEquals, ScalarMergeInfo, ChunkMergeInfo)
|
|
48025
|
-
|
|
48026
47668
|
} // namespace duckdb
|
|
48027
47669
|
|
|
48028
47670
|
//===----------------------------------------------------------------------===//
|
|
@@ -55010,6 +54652,8 @@ void PhysicalNestedLoopJoin::GetData(ExecutionContext &context, DataChunk &chunk
|
|
|
55010
54652
|
|
|
55011
54653
|
namespace duckdb {
|
|
55012
54654
|
|
|
54655
|
+
class MergeJoinGlobalState;
|
|
54656
|
+
|
|
55013
54657
|
//! PhysicalPiecewiseMergeJoin represents a piecewise merge loop join between
|
|
55014
54658
|
//! two tables
|
|
55015
54659
|
class PhysicalPiecewiseMergeJoin : public PhysicalComparisonJoin {
|
|
@@ -55019,6 +54663,8 @@ public:
|
|
|
55019
54663
|
idx_t estimated_cardinality);
|
|
55020
54664
|
|
|
55021
54665
|
vector<LogicalType> join_key_types;
|
|
54666
|
+
vector<BoundOrderByNode> lhs_orders;
|
|
54667
|
+
vector<BoundOrderByNode> rhs_orders;
|
|
55022
54668
|
|
|
55023
54669
|
public:
|
|
55024
54670
|
// Operator Interface
|
|
@@ -55057,6 +54703,9 @@ public:
|
|
|
55057
54703
|
SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
|
|
55058
54704
|
GlobalSinkState &gstate) const override;
|
|
55059
54705
|
|
|
54706
|
+
//! Schedules tasks to merge sort the RHS data during the Finalize phase
|
|
54707
|
+
static void ScheduleMergeTasks(Pipeline &pipeline, Event &event, MergeJoinGlobalState &state);
|
|
54708
|
+
|
|
55060
54709
|
bool IsSink() const override {
|
|
55061
54710
|
return true;
|
|
55062
54711
|
}
|
|
@@ -55081,6 +54730,11 @@ private:
|
|
|
55081
54730
|
|
|
55082
54731
|
|
|
55083
54732
|
|
|
54733
|
+
|
|
54734
|
+
|
|
54735
|
+
|
|
54736
|
+
|
|
54737
|
+
|
|
55084
54738
|
namespace duckdb {
|
|
55085
54739
|
|
|
55086
54740
|
PhysicalPiecewiseMergeJoin::PhysicalPiecewiseMergeJoin(LogicalOperator &op, unique_ptr<PhysicalOperator> left,
|
|
@@ -55091,10 +54745,27 @@ PhysicalPiecewiseMergeJoin::PhysicalPiecewiseMergeJoin(LogicalOperator &op, uniq
|
|
|
55091
54745
|
// for now we only support one condition!
|
|
55092
54746
|
D_ASSERT(conditions.size() == 1);
|
|
55093
54747
|
for (auto &cond : conditions) {
|
|
55094
|
-
// COMPARE NOT EQUAL not supported with merge join
|
|
55095
|
-
D_ASSERT(cond.comparison != ExpressionType::COMPARE_NOTEQUAL);
|
|
55096
54748
|
D_ASSERT(cond.left->return_type == cond.right->return_type);
|
|
55097
54749
|
join_key_types.push_back(cond.left->return_type);
|
|
54750
|
+
|
|
54751
|
+
// Convert the conditions to sort orders
|
|
54752
|
+
auto left = cond.left->Copy();
|
|
54753
|
+
auto right = cond.right->Copy();
|
|
54754
|
+
switch (cond.comparison) {
|
|
54755
|
+
case ExpressionType::COMPARE_LESSTHAN:
|
|
54756
|
+
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
54757
|
+
lhs_orders.emplace_back(BoundOrderByNode(OrderType::ASCENDING, OrderByNullType::NULLS_LAST, move(left)));
|
|
54758
|
+
rhs_orders.emplace_back(BoundOrderByNode(OrderType::ASCENDING, OrderByNullType::NULLS_LAST, move(right)));
|
|
54759
|
+
break;
|
|
54760
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
54761
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
54762
|
+
lhs_orders.emplace_back(BoundOrderByNode(OrderType::DESCENDING, OrderByNullType::NULLS_LAST, move(left)));
|
|
54763
|
+
rhs_orders.emplace_back(BoundOrderByNode(OrderType::DESCENDING, OrderByNullType::NULLS_LAST, move(right)));
|
|
54764
|
+
break;
|
|
54765
|
+
default:
|
|
54766
|
+
// COMPARE NOT EQUAL not supported with merge join
|
|
54767
|
+
throw NotImplementedException("Unimplemented join type for merge join");
|
|
54768
|
+
}
|
|
55098
54769
|
}
|
|
55099
54770
|
children.push_back(move(left));
|
|
55100
54771
|
children.push_back(move(right));
|
|
@@ -55103,114 +54774,242 @@ PhysicalPiecewiseMergeJoin::PhysicalPiecewiseMergeJoin(LogicalOperator &op, uniq
|
|
|
55103
54774
|
//===--------------------------------------------------------------------===//
|
|
55104
54775
|
// Sink
|
|
55105
54776
|
//===--------------------------------------------------------------------===//
|
|
55106
|
-
class
|
|
54777
|
+
class MergeJoinGlobalState : public GlobalSinkState {
|
|
55107
54778
|
public:
|
|
55108
|
-
|
|
55109
|
-
|
|
55110
|
-
|
|
55111
|
-
rhs_executor.AddExpression(*cond.right);
|
|
55112
|
-
condition_types.push_back(cond.right->return_type);
|
|
55113
|
-
}
|
|
55114
|
-
join_keys.Initialize(condition_types);
|
|
54779
|
+
MergeJoinGlobalState(BufferManager &buffer_manager, const vector<BoundOrderByNode> &orders, RowLayout &rhs_layout)
|
|
54780
|
+
: rhs_global_sort_state(buffer_manager, orders, rhs_layout), rhs_has_null(0), rhs_count(0),
|
|
54781
|
+
memory_per_thread(0) {
|
|
55115
54782
|
}
|
|
55116
54783
|
|
|
55117
|
-
|
|
55118
|
-
|
|
55119
|
-
//! The executor of the RHS condition
|
|
55120
|
-
ExpressionExecutor rhs_executor;
|
|
55121
|
-
};
|
|
55122
|
-
|
|
55123
|
-
class MergeJoinGlobalState : public GlobalSinkState {
|
|
55124
|
-
public:
|
|
55125
|
-
MergeJoinGlobalState() : has_null(false), right_outer_position(0) {
|
|
54784
|
+
inline idx_t Count() const {
|
|
54785
|
+
return rhs_count;
|
|
55126
54786
|
}
|
|
55127
54787
|
|
|
55128
|
-
|
|
55129
|
-
|
|
55130
|
-
|
|
55131
|
-
|
|
55132
|
-
|
|
55133
|
-
|
|
55134
|
-
|
|
55135
|
-
|
|
55136
|
-
bool has_null;
|
|
54788
|
+
//! The lock for updating the global state
|
|
54789
|
+
mutex lock;
|
|
54790
|
+
//! Global sort state
|
|
54791
|
+
GlobalSortState rhs_global_sort_state;
|
|
54792
|
+
//! Whether or not the RHS has NULL values
|
|
54793
|
+
idx_t rhs_has_null;
|
|
54794
|
+
//! The total number of rows in the RHS
|
|
54795
|
+
idx_t rhs_count;
|
|
55137
54796
|
//! A bool indicating for each tuple in the RHS if they found a match (only used in FULL OUTER JOIN)
|
|
55138
|
-
unique_ptr<bool[]>
|
|
55139
|
-
//!
|
|
55140
|
-
idx_t
|
|
54797
|
+
unique_ptr<bool[]> rhs_found_match;
|
|
54798
|
+
//! Memory usage per thread
|
|
54799
|
+
idx_t memory_per_thread;
|
|
55141
54800
|
};
|
|
55142
54801
|
|
|
55143
54802
|
unique_ptr<GlobalSinkState> PhysicalPiecewiseMergeJoin::GetGlobalSinkState(ClientContext &context) const {
|
|
55144
|
-
|
|
55145
|
-
|
|
55146
|
-
|
|
55147
|
-
|
|
55148
|
-
|
|
54803
|
+
// Get the payload layout from the rhs types
|
|
54804
|
+
RowLayout rhs_layout;
|
|
54805
|
+
auto types = children[1]->types;
|
|
54806
|
+
rhs_layout.Initialize(types);
|
|
54807
|
+
auto state = make_unique<MergeJoinGlobalState>(BufferManager::GetBufferManager(context), rhs_orders, rhs_layout);
|
|
54808
|
+
// Set external (can be force with the PRAGMA)
|
|
54809
|
+
auto &config = ClientConfig::GetConfig(context);
|
|
54810
|
+
state->rhs_global_sort_state.external = config.force_external;
|
|
54811
|
+
// Memory usage per thread should scale with max mem / num threads
|
|
54812
|
+
// We take 1/4th of this, to be conservative
|
|
54813
|
+
idx_t max_memory = BufferManager::GetBufferManager(context).GetMaxMemory();
|
|
54814
|
+
idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
|
|
54815
|
+
state->memory_per_thread = (max_memory / num_threads) / 4;
|
|
54816
|
+
return move(state);
|
|
55149
54817
|
}
|
|
55150
54818
|
|
|
55151
|
-
|
|
55152
|
-
|
|
55153
|
-
auto &gstate = (MergeJoinGlobalState &)state;
|
|
55154
|
-
auto &mj_state = (MergeJoinLocalState &)lstate;
|
|
55155
|
-
|
|
55156
|
-
// resolve the join keys for this chunk
|
|
55157
|
-
mj_state.rhs_executor.SetChunk(input);
|
|
54819
|
+
static idx_t CountValid(Vector &v, const idx_t count) {
|
|
54820
|
+
idx_t valid = 0;
|
|
55158
54821
|
|
|
55159
|
-
|
|
55160
|
-
|
|
55161
|
-
|
|
55162
|
-
|
|
55163
|
-
mj_state.rhs_executor.ExecuteExpression(k, mj_state.join_keys.data[k]);
|
|
54822
|
+
VectorData vdata;
|
|
54823
|
+
v.Orrify(count, vdata);
|
|
54824
|
+
if (vdata.validity.AllValid()) {
|
|
54825
|
+
return count;
|
|
55164
54826
|
}
|
|
55165
|
-
|
|
55166
|
-
|
|
55167
|
-
|
|
55168
|
-
|
|
55169
|
-
|
|
55170
|
-
|
|
54827
|
+
switch (v.GetVectorType()) {
|
|
54828
|
+
case VectorType::FLAT_VECTOR:
|
|
54829
|
+
valid += vdata.validity.CountValid(count);
|
|
54830
|
+
break;
|
|
54831
|
+
case VectorType::CONSTANT_VECTOR:
|
|
54832
|
+
valid += vdata.validity.CountValid(1) * count;
|
|
54833
|
+
break;
|
|
54834
|
+
default:
|
|
54835
|
+
for (idx_t i = 0; i < count; ++i) {
|
|
54836
|
+
const auto row_idx = vdata.sel->get_index(i);
|
|
54837
|
+
valid += int(vdata.validity.RowIsValid(row_idx));
|
|
54838
|
+
}
|
|
54839
|
+
break;
|
|
54840
|
+
}
|
|
54841
|
+
|
|
54842
|
+
return valid;
|
|
54843
|
+
}
|
|
55171
54844
|
|
|
55172
|
-
|
|
55173
|
-
|
|
55174
|
-
|
|
54845
|
+
class MergeJoinLocalState : public LocalSinkState {
|
|
54846
|
+
public:
|
|
54847
|
+
explicit MergeJoinLocalState() : rhs_has_null(0), rhs_count(0) {
|
|
54848
|
+
}
|
|
54849
|
+
|
|
54850
|
+
//! The local sort state
|
|
54851
|
+
LocalSortState rhs_local_sort_state;
|
|
54852
|
+
//! Local copy of the sorting expression executor
|
|
54853
|
+
ExpressionExecutor rhs_executor;
|
|
54854
|
+
//! Holds a vector of incoming sorting columns
|
|
54855
|
+
DataChunk rhs_keys;
|
|
54856
|
+
//! Whether or not the RHS has NULL values
|
|
54857
|
+
idx_t rhs_has_null;
|
|
54858
|
+
//! The total number of rows in the RHS
|
|
54859
|
+
idx_t rhs_count;
|
|
54860
|
+
};
|
|
54861
|
+
|
|
54862
|
+
unique_ptr<LocalSinkState> PhysicalPiecewiseMergeJoin::GetLocalSinkState(ExecutionContext &context) const {
|
|
54863
|
+
auto result = make_unique<MergeJoinLocalState>();
|
|
54864
|
+
// Initialize order clause expression executor and DataChunk
|
|
54865
|
+
vector<LogicalType> types;
|
|
54866
|
+
for (auto &order : rhs_orders) {
|
|
54867
|
+
types.push_back(order.expression->return_type);
|
|
54868
|
+
result->rhs_executor.AddExpression(*order.expression);
|
|
54869
|
+
}
|
|
54870
|
+
result->rhs_keys.Initialize(types);
|
|
54871
|
+
return move(result);
|
|
54872
|
+
}
|
|
54873
|
+
|
|
54874
|
+
SinkResultType PhysicalPiecewiseMergeJoin::Sink(ExecutionContext &context, GlobalSinkState &gstate_p,
|
|
54875
|
+
LocalSinkState &lstate_p, DataChunk &input) const {
|
|
54876
|
+
auto &gstate = (MergeJoinGlobalState &)gstate_p;
|
|
54877
|
+
auto &lstate = (MergeJoinLocalState &)lstate_p;
|
|
54878
|
+
|
|
54879
|
+
auto &global_sort_state = gstate.rhs_global_sort_state;
|
|
54880
|
+
auto &local_sort_state = lstate.rhs_local_sort_state;
|
|
54881
|
+
|
|
54882
|
+
// Initialize local state (if necessary)
|
|
54883
|
+
if (!local_sort_state.initialized) {
|
|
54884
|
+
local_sort_state.Initialize(global_sort_state, BufferManager::GetBufferManager(context.client));
|
|
54885
|
+
}
|
|
54886
|
+
|
|
54887
|
+
// Obtain sorting columns
|
|
54888
|
+
auto &join_keys = lstate.rhs_keys;
|
|
54889
|
+
join_keys.Reset();
|
|
54890
|
+
lstate.rhs_executor.Execute(input, join_keys);
|
|
54891
|
+
|
|
54892
|
+
// Count the NULLs so we can exclude them later
|
|
54893
|
+
// TODO: Sort any comparison NULLs to the end using an initial sort column
|
|
54894
|
+
const auto count = join_keys.size();
|
|
54895
|
+
for (auto &key : join_keys.data) {
|
|
54896
|
+
lstate.rhs_has_null += (count - CountValid(key, count));
|
|
54897
|
+
}
|
|
54898
|
+
lstate.rhs_count += count;
|
|
54899
|
+
|
|
54900
|
+
// Sink the data into the local sort state
|
|
54901
|
+
local_sort_state.SinkChunk(join_keys, input);
|
|
54902
|
+
|
|
54903
|
+
// When sorting data reaches a certain size, we sort it
|
|
54904
|
+
if (local_sort_state.SizeInBytes() >= gstate.memory_per_thread) {
|
|
54905
|
+
local_sort_state.Sort(global_sort_state, true);
|
|
54906
|
+
}
|
|
54907
|
+
return SinkResultType::NEED_MORE_INPUT;
|
|
54908
|
+
}
|
|
54909
|
+
|
|
54910
|
+
void PhysicalPiecewiseMergeJoin::Combine(ExecutionContext &context, GlobalSinkState &gstate_p,
|
|
54911
|
+
LocalSinkState &lstate_p) const {
|
|
54912
|
+
auto &gstate = (MergeJoinGlobalState &)gstate_p;
|
|
54913
|
+
auto &lstate = (MergeJoinLocalState &)lstate_p;
|
|
54914
|
+
gstate.rhs_global_sort_state.AddLocalState(lstate.rhs_local_sort_state);
|
|
54915
|
+
lock_guard<mutex> locked(gstate.lock);
|
|
54916
|
+
gstate.rhs_has_null += lstate.rhs_has_null;
|
|
54917
|
+
gstate.rhs_count += lstate.rhs_count;
|
|
55175
54918
|
auto &client_profiler = QueryProfiler::Get(context.client);
|
|
55176
54919
|
|
|
55177
|
-
context.thread.profiler.Flush(this, &
|
|
54920
|
+
context.thread.profiler.Flush(this, &lstate.rhs_executor, "rhs_executor", 1);
|
|
55178
54921
|
client_profiler.Flush(context.thread.profiler);
|
|
55179
54922
|
}
|
|
55180
54923
|
|
|
55181
54924
|
//===--------------------------------------------------------------------===//
|
|
55182
54925
|
// Finalize
|
|
55183
54926
|
//===--------------------------------------------------------------------===//
|
|
55184
|
-
|
|
54927
|
+
class MergeJoinFinalizeTask : public ExecutorTask {
|
|
54928
|
+
public:
|
|
54929
|
+
MergeJoinFinalizeTask(shared_ptr<Event> event_p, ClientContext &context, MergeJoinGlobalState &state)
|
|
54930
|
+
: ExecutorTask(context), event(move(event_p)), context(context), state(state) {
|
|
54931
|
+
}
|
|
54932
|
+
|
|
54933
|
+
TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override {
|
|
54934
|
+
// Initialize merge sorted and iterate until done
|
|
54935
|
+
auto &global_sort_state = state.rhs_global_sort_state;
|
|
54936
|
+
MergeSorter merge_sorter(global_sort_state, BufferManager::GetBufferManager(context));
|
|
54937
|
+
merge_sorter.PerformInMergeRound();
|
|
54938
|
+
event->FinishTask();
|
|
54939
|
+
|
|
54940
|
+
return TaskExecutionResult::TASK_FINISHED;
|
|
54941
|
+
}
|
|
54942
|
+
|
|
54943
|
+
private:
|
|
54944
|
+
shared_ptr<Event> event;
|
|
54945
|
+
ClientContext &context;
|
|
54946
|
+
MergeJoinGlobalState &state;
|
|
54947
|
+
};
|
|
54948
|
+
|
|
54949
|
+
class MergeJoinFinalizeEvent : public Event {
|
|
54950
|
+
public:
|
|
54951
|
+
MergeJoinFinalizeEvent(MergeJoinGlobalState &gstate_p, Pipeline &pipeline_p)
|
|
54952
|
+
: Event(pipeline_p.executor), gstate(gstate_p), pipeline(pipeline_p) {
|
|
54953
|
+
}
|
|
54954
|
+
|
|
54955
|
+
MergeJoinGlobalState &gstate;
|
|
54956
|
+
Pipeline &pipeline;
|
|
54957
|
+
|
|
54958
|
+
public:
|
|
54959
|
+
void Schedule() override {
|
|
54960
|
+
auto &context = pipeline.GetClientContext();
|
|
54961
|
+
|
|
54962
|
+
// Schedule tasks equal to the number of threads, which will each merge multiple partitions
|
|
54963
|
+
auto &ts = TaskScheduler::GetScheduler(context);
|
|
54964
|
+
idx_t num_threads = ts.NumberOfThreads();
|
|
54965
|
+
|
|
54966
|
+
vector<unique_ptr<Task>> merge_tasks;
|
|
54967
|
+
for (idx_t tnum = 0; tnum < num_threads; tnum++) {
|
|
54968
|
+
merge_tasks.push_back(make_unique<MergeJoinFinalizeTask>(shared_from_this(), context, gstate));
|
|
54969
|
+
}
|
|
54970
|
+
SetTasks(move(merge_tasks));
|
|
54971
|
+
}
|
|
54972
|
+
|
|
54973
|
+
void FinishEvent() override {
|
|
54974
|
+
auto &global_sort_state = gstate.rhs_global_sort_state;
|
|
54975
|
+
|
|
54976
|
+
global_sort_state.CompleteMergeRound(true);
|
|
54977
|
+
if (global_sort_state.sorted_blocks.size() > 1) {
|
|
54978
|
+
// Multiple blocks remaining: Schedule the next round
|
|
54979
|
+
PhysicalPiecewiseMergeJoin::ScheduleMergeTasks(pipeline, *this, gstate);
|
|
54980
|
+
}
|
|
54981
|
+
}
|
|
54982
|
+
};
|
|
54983
|
+
|
|
54984
|
+
void PhysicalPiecewiseMergeJoin::ScheduleMergeTasks(Pipeline &pipeline, Event &event, MergeJoinGlobalState &gstate) {
|
|
54985
|
+
// Initialize global sort state for a round of merging
|
|
54986
|
+
gstate.rhs_global_sort_state.InitializeMergeRound();
|
|
54987
|
+
auto new_event = make_shared<MergeJoinFinalizeEvent>(gstate, pipeline);
|
|
54988
|
+
event.InsertEvent(move(new_event));
|
|
54989
|
+
}
|
|
55185
54990
|
|
|
55186
54991
|
SinkFinalizeType PhysicalPiecewiseMergeJoin::Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
|
|
55187
54992
|
GlobalSinkState &gstate_p) const {
|
|
55188
54993
|
auto &gstate = (MergeJoinGlobalState &)gstate_p;
|
|
55189
|
-
|
|
55190
|
-
|
|
55191
|
-
gstate.right_orders.resize(gstate.right_conditions.ChunkCount());
|
|
55192
|
-
for (idx_t i = 0; i < gstate.right_conditions.ChunkCount(); i++) {
|
|
55193
|
-
auto &chunk_to_order = gstate.right_conditions.GetChunk(i);
|
|
55194
|
-
D_ASSERT(chunk_to_order.ColumnCount() == 1);
|
|
55195
|
-
for (idx_t col_idx = 0; col_idx < chunk_to_order.ColumnCount(); col_idx++) {
|
|
55196
|
-
OrderVector(chunk_to_order.data[col_idx], chunk_to_order.size(), gstate.right_orders[i]);
|
|
55197
|
-
if (gstate.right_orders[i].count < chunk_to_order.size()) {
|
|
55198
|
-
// the amount of entries in the order vector is smaller than the amount of entries in the vector
|
|
55199
|
-
// this only happens if there are NULL values in the right-hand side
|
|
55200
|
-
// hence we set the has_null to true (this is required for the MARK join)
|
|
55201
|
-
gstate.has_null = true;
|
|
55202
|
-
}
|
|
55203
|
-
}
|
|
55204
|
-
}
|
|
55205
|
-
}
|
|
54994
|
+
auto &global_sort_state = gstate.rhs_global_sort_state;
|
|
54995
|
+
|
|
55206
54996
|
if (IsRightOuterJoin(join_type)) {
|
|
55207
54997
|
// for FULL/RIGHT OUTER JOIN, initialize found_match to false for every tuple
|
|
55208
|
-
gstate.
|
|
55209
|
-
memset(gstate.
|
|
54998
|
+
gstate.rhs_found_match = unique_ptr<bool[]>(new bool[gstate.Count()]);
|
|
54999
|
+
memset(gstate.rhs_found_match.get(), 0, sizeof(bool) * gstate.Count());
|
|
55210
55000
|
}
|
|
55211
|
-
if (
|
|
55001
|
+
if (global_sort_state.sorted_blocks.empty() && EmptyResultIfRHSIsEmpty()) {
|
|
55002
|
+
// Empty input!
|
|
55212
55003
|
return SinkFinalizeType::NO_OUTPUT_POSSIBLE;
|
|
55213
55004
|
}
|
|
55005
|
+
|
|
55006
|
+
// Prepare for merge sort phase
|
|
55007
|
+
global_sort_state.PrepareMergePhase();
|
|
55008
|
+
|
|
55009
|
+
// Start the merge phase or finish if a merge is not necessary
|
|
55010
|
+
if (global_sort_state.sorted_blocks.size() > 1) {
|
|
55011
|
+
PhysicalPiecewiseMergeJoin::ScheduleMergeTasks(pipeline, event, gstate);
|
|
55012
|
+
}
|
|
55214
55013
|
return SinkFinalizeType::READY;
|
|
55215
55014
|
}
|
|
55216
55015
|
|
|
@@ -55219,43 +55018,75 @@ SinkFinalizeType PhysicalPiecewiseMergeJoin::Finalize(Pipeline &pipeline, Event
|
|
|
55219
55018
|
//===--------------------------------------------------------------------===//
|
|
55220
55019
|
class PiecewiseMergeJoinState : public OperatorState {
|
|
55221
55020
|
public:
|
|
55222
|
-
explicit PiecewiseMergeJoinState(const PhysicalPiecewiseMergeJoin &op
|
|
55223
|
-
|
|
55021
|
+
explicit PiecewiseMergeJoinState(const PhysicalPiecewiseMergeJoin &op, BufferManager &buffer_manager,
|
|
55022
|
+
bool force_external)
|
|
55023
|
+
: op(op), buffer_manager(buffer_manager), force_external(force_external), left_position(0), first_fetch(true),
|
|
55024
|
+
finished(true), right_position(0), right_chunk_index(0) {
|
|
55224
55025
|
vector<LogicalType> condition_types;
|
|
55225
55026
|
for (auto &cond : op.conditions) {
|
|
55226
55027
|
lhs_executor.AddExpression(*cond.left);
|
|
55227
55028
|
condition_types.push_back(cond.left->return_type);
|
|
55228
55029
|
}
|
|
55229
|
-
|
|
55030
|
+
lhs_keys.Initialize(condition_types);
|
|
55230
55031
|
if (IsLeftOuterJoin(op.join_type)) {
|
|
55231
|
-
|
|
55232
|
-
memset(
|
|
55032
|
+
lhs_found_match = unique_ptr<bool[]>(new bool[STANDARD_VECTOR_SIZE]);
|
|
55033
|
+
memset(lhs_found_match.get(), 0, sizeof(bool) * STANDARD_VECTOR_SIZE);
|
|
55233
55034
|
}
|
|
55035
|
+
lhs_layout.Initialize(op.children[0]->types);
|
|
55234
55036
|
}
|
|
55235
55037
|
|
|
55236
55038
|
const PhysicalPiecewiseMergeJoin &op;
|
|
55039
|
+
BufferManager &buffer_manager;
|
|
55040
|
+
bool force_external;
|
|
55041
|
+
|
|
55042
|
+
// Block sorting
|
|
55043
|
+
DataChunk lhs_keys;
|
|
55044
|
+
ExpressionExecutor lhs_executor;
|
|
55045
|
+
unique_ptr<bool[]> lhs_found_match;
|
|
55046
|
+
RowLayout lhs_layout;
|
|
55047
|
+
unique_ptr<LocalSortState> lhs_local_state;
|
|
55048
|
+
unique_ptr<GlobalSortState> lhs_global_state;
|
|
55049
|
+
idx_t lhs_count;
|
|
55050
|
+
idx_t lhs_has_null;
|
|
55051
|
+
|
|
55052
|
+
// Simple scans
|
|
55053
|
+
idx_t left_position;
|
|
55054
|
+
|
|
55055
|
+
// Complex scans
|
|
55237
55056
|
bool first_fetch;
|
|
55238
55057
|
bool finished;
|
|
55239
|
-
idx_t left_position;
|
|
55240
55058
|
idx_t right_position;
|
|
55241
55059
|
idx_t right_chunk_index;
|
|
55242
|
-
|
|
55243
|
-
DataChunk join_keys;
|
|
55244
|
-
MergeOrder left_orders;
|
|
55245
|
-
//! The executor of the RHS condition
|
|
55246
|
-
ExpressionExecutor lhs_executor;
|
|
55247
|
-
unique_ptr<bool[]> left_found_match;
|
|
55060
|
+
idx_t right_base;
|
|
55248
55061
|
|
|
55249
55062
|
public:
|
|
55250
55063
|
void ResolveJoinKeys(DataChunk &input) {
|
|
55251
55064
|
// resolve the join keys for the input
|
|
55252
|
-
|
|
55253
|
-
lhs_executor.
|
|
55254
|
-
|
|
55255
|
-
|
|
55256
|
-
|
|
55257
|
-
|
|
55258
|
-
|
|
55065
|
+
lhs_keys.Reset();
|
|
55066
|
+
lhs_executor.Execute(input, lhs_keys);
|
|
55067
|
+
|
|
55068
|
+
// Count the NULLs so we can exclude them later
|
|
55069
|
+
// TODO: Sort any multi-comparison NULLs to the end using an initial sort column
|
|
55070
|
+
lhs_count = lhs_keys.size();
|
|
55071
|
+
for (auto &key : lhs_keys.data) {
|
|
55072
|
+
lhs_has_null = lhs_count - CountValid(key, lhs_count);
|
|
55073
|
+
break;
|
|
55074
|
+
}
|
|
55075
|
+
|
|
55076
|
+
// sort by join key
|
|
55077
|
+
lhs_global_state = make_unique<GlobalSortState>(buffer_manager, op.lhs_orders, lhs_layout);
|
|
55078
|
+
lhs_local_state = make_unique<LocalSortState>();
|
|
55079
|
+
lhs_local_state->Initialize(*lhs_global_state, buffer_manager);
|
|
55080
|
+
lhs_local_state->SinkChunk(lhs_keys, input);
|
|
55081
|
+
|
|
55082
|
+
// Set external (can be force with the PRAGMA)
|
|
55083
|
+
lhs_global_state->external = force_external;
|
|
55084
|
+
lhs_global_state->AddLocalState(*lhs_local_state);
|
|
55085
|
+
lhs_global_state->PrepareMergePhase();
|
|
55086
|
+
while (lhs_global_state->sorted_blocks.size() > 1) {
|
|
55087
|
+
MergeSorter merge_sorter(*lhs_global_state, buffer_manager);
|
|
55088
|
+
merge_sorter.PerformInMergeRound();
|
|
55089
|
+
lhs_global_state->CompleteMergeRound();
|
|
55259
55090
|
}
|
|
55260
55091
|
}
|
|
55261
55092
|
|
|
@@ -55265,7 +55096,176 @@ public:
|
|
|
55265
55096
|
};
|
|
55266
55097
|
|
|
55267
55098
|
unique_ptr<OperatorState> PhysicalPiecewiseMergeJoin::GetOperatorState(ClientContext &context) const {
|
|
55268
|
-
|
|
55099
|
+
auto &buffer_manager = BufferManager::GetBufferManager(context);
|
|
55100
|
+
auto &config = ClientConfig::GetConfig(context);
|
|
55101
|
+
return make_unique<PiecewiseMergeJoinState>(*this, buffer_manager, config.force_external);
|
|
55102
|
+
}
|
|
55103
|
+
|
|
55104
|
+
static inline idx_t SortedBlockNotNull(const idx_t base, const idx_t count, const idx_t not_null) {
|
|
55105
|
+
return MinValue(base + count, MaxValue(base, not_null)) - base;
|
|
55106
|
+
}
|
|
55107
|
+
|
|
55108
|
+
static int MergeJoinComparisonValue(ExpressionType comparison) {
|
|
55109
|
+
switch (comparison) {
|
|
55110
|
+
case ExpressionType::COMPARE_LESSTHAN:
|
|
55111
|
+
case ExpressionType::COMPARE_GREATERTHAN:
|
|
55112
|
+
return -1;
|
|
55113
|
+
case ExpressionType::COMPARE_LESSTHANOREQUALTO:
|
|
55114
|
+
case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
|
|
55115
|
+
return 0;
|
|
55116
|
+
default:
|
|
55117
|
+
throw InternalException("Unimplemented comparison type for merge join!");
|
|
55118
|
+
}
|
|
55119
|
+
}
|
|
55120
|
+
|
|
55121
|
+
struct BlockMergeInfo {
|
|
55122
|
+
GlobalSortState &state;
|
|
55123
|
+
//! The block being scanned
|
|
55124
|
+
const idx_t block_idx;
|
|
55125
|
+
//! The start position being read from the block
|
|
55126
|
+
const idx_t base_idx;
|
|
55127
|
+
//! The number of not-NULL values in the block (they are at the end)
|
|
55128
|
+
const idx_t not_null;
|
|
55129
|
+
//! The current offset in the block
|
|
55130
|
+
idx_t &entry_idx;
|
|
55131
|
+
SelectionVector result;
|
|
55132
|
+
|
|
55133
|
+
BlockMergeInfo(GlobalSortState &state, idx_t block_idx, idx_t base_idx, idx_t &entry_idx, idx_t not_null)
|
|
55134
|
+
: state(state), block_idx(block_idx), base_idx(base_idx), not_null(not_null), entry_idx(entry_idx),
|
|
55135
|
+
result(STANDARD_VECTOR_SIZE) {
|
|
55136
|
+
}
|
|
55137
|
+
};
|
|
55138
|
+
|
|
55139
|
+
static void SliceSortedPayload(DataChunk &payload, BlockMergeInfo &info, const idx_t result_count, const idx_t vcount,
|
|
55140
|
+
const idx_t left_cols = 0) {
|
|
55141
|
+
// There should only be one sorted block if they have been sorted
|
|
55142
|
+
D_ASSERT(info.state.sorted_blocks.size() == 1);
|
|
55143
|
+
SBScanState read_state(info.state.buffer_manager, info.state);
|
|
55144
|
+
read_state.sb = info.state.sorted_blocks[0].get();
|
|
55145
|
+
auto &sorted_data = *read_state.sb->payload_data;
|
|
55146
|
+
|
|
55147
|
+
// We have to create pointers for the entire block
|
|
55148
|
+
// because unswizzle works on ranges not selections.
|
|
55149
|
+
read_state.SetIndices(info.block_idx, info.base_idx);
|
|
55150
|
+
read_state.PinData(sorted_data);
|
|
55151
|
+
const auto data_ptr = read_state.DataPtr(sorted_data);
|
|
55152
|
+
const auto next = vcount - info.base_idx;
|
|
55153
|
+
|
|
55154
|
+
// Set up a batch of pointers to scan data from
|
|
55155
|
+
Vector addresses(LogicalType::POINTER, next);
|
|
55156
|
+
auto data_pointers = FlatVector::GetData<data_ptr_t>(addresses);
|
|
55157
|
+
|
|
55158
|
+
// Set up the data pointers
|
|
55159
|
+
data_ptr_t row_ptr = data_ptr;
|
|
55160
|
+
const idx_t &row_width = sorted_data.layout.GetRowWidth();
|
|
55161
|
+
for (idx_t i = 0; i < next; ++i) {
|
|
55162
|
+
data_pointers[i] = row_ptr;
|
|
55163
|
+
row_ptr += row_width;
|
|
55164
|
+
}
|
|
55165
|
+
// Unswizzle the offsets back to pointers (if needed)
|
|
55166
|
+
if (!sorted_data.layout.AllConstant() && info.state.external) {
|
|
55167
|
+
RowOperations::UnswizzlePointers(sorted_data.layout, data_ptr, read_state.payload_heap_handle->Ptr(), next);
|
|
55168
|
+
}
|
|
55169
|
+
|
|
55170
|
+
// Deserialize the payload data
|
|
55171
|
+
for (idx_t col_idx = 0; col_idx < sorted_data.layout.ColumnCount(); col_idx++) {
|
|
55172
|
+
const auto col_offset = sorted_data.layout.GetOffsets()[col_idx];
|
|
55173
|
+
RowOperations::Gather(addresses, info.result, payload.data[left_cols + col_idx],
|
|
55174
|
+
*FlatVector::IncrementalSelectionVector(), result_count, col_offset, col_idx);
|
|
55175
|
+
}
|
|
55176
|
+
}
|
|
55177
|
+
|
|
55178
|
+
static void MergeJoinPinSortingBlock(SBScanState &scan, const idx_t block_idx) {
|
|
55179
|
+
scan.SetIndices(block_idx, 0);
|
|
55180
|
+
scan.PinRadix(block_idx);
|
|
55181
|
+
|
|
55182
|
+
auto &sd = *scan.sb->blob_sorting_data;
|
|
55183
|
+
if (block_idx < sd.data_blocks.size()) {
|
|
55184
|
+
scan.PinData(sd);
|
|
55185
|
+
}
|
|
55186
|
+
}
|
|
55187
|
+
|
|
55188
|
+
static data_ptr_t MergeJoinRadixPtr(SBScanState &scan, const idx_t entry_idx) {
|
|
55189
|
+
scan.entry_idx = entry_idx;
|
|
55190
|
+
return scan.RadixPtr();
|
|
55191
|
+
}
|
|
55192
|
+
|
|
55193
|
+
static idx_t MergeJoinSimpleBlocks(PiecewiseMergeJoinState &lstate, MergeJoinGlobalState &rstate, bool *found_match,
|
|
55194
|
+
const ExpressionType comparison) {
|
|
55195
|
+
const auto cmp = MergeJoinComparisonValue(comparison);
|
|
55196
|
+
|
|
55197
|
+
// The sort parameters should all be the same
|
|
55198
|
+
auto &lsort = *lstate.lhs_global_state;
|
|
55199
|
+
auto &rsort = rstate.rhs_global_sort_state;
|
|
55200
|
+
D_ASSERT(lsort.sort_layout.all_constant == rsort.sort_layout.all_constant);
|
|
55201
|
+
const auto all_constant = lsort.sort_layout.all_constant;
|
|
55202
|
+
D_ASSERT(lsort.external == rsort.external);
|
|
55203
|
+
const auto external = lsort.external;
|
|
55204
|
+
|
|
55205
|
+
// There should only be one sorted block if they have been sorted
|
|
55206
|
+
D_ASSERT(lsort.sorted_blocks.size() == 1);
|
|
55207
|
+
SBScanState lread(lsort.buffer_manager, lsort);
|
|
55208
|
+
lread.sb = lsort.sorted_blocks[0].get();
|
|
55209
|
+
|
|
55210
|
+
const idx_t l_block_idx = 0;
|
|
55211
|
+
idx_t l_entry_idx = 0;
|
|
55212
|
+
const auto lhs_not_null = lstate.lhs_count - lstate.lhs_has_null;
|
|
55213
|
+
MergeJoinPinSortingBlock(lread, l_block_idx);
|
|
55214
|
+
auto l_ptr = MergeJoinRadixPtr(lread, l_entry_idx);
|
|
55215
|
+
|
|
55216
|
+
D_ASSERT(rsort.sorted_blocks.size() == 1);
|
|
55217
|
+
SBScanState rread(rsort.buffer_manager, rsort);
|
|
55218
|
+
rread.sb = rsort.sorted_blocks[0].get();
|
|
55219
|
+
|
|
55220
|
+
const auto cmp_size = lsort.sort_layout.comparison_size;
|
|
55221
|
+
const auto entry_size = lsort.sort_layout.entry_size;
|
|
55222
|
+
|
|
55223
|
+
idx_t right_base = 0;
|
|
55224
|
+
for (idx_t r_block_idx = 0; r_block_idx < rread.sb->radix_sorting_data.size(); r_block_idx++) {
|
|
55225
|
+
// we only care about the BIGGEST value in each of the RHS data blocks
|
|
55226
|
+
// because we want to figure out if the LHS values are less than [or equal] to ANY value
|
|
55227
|
+
// get the biggest value from the RHS chunk
|
|
55228
|
+
MergeJoinPinSortingBlock(rread, r_block_idx);
|
|
55229
|
+
|
|
55230
|
+
auto &rblock = rread.sb->radix_sorting_data[r_block_idx];
|
|
55231
|
+
const auto r_not_null = SortedBlockNotNull(right_base, rblock.count, rstate.rhs_count - rstate.rhs_has_null);
|
|
55232
|
+
if (r_not_null == 0) {
|
|
55233
|
+
break;
|
|
55234
|
+
}
|
|
55235
|
+
const auto r_entry_idx = r_not_null - 1;
|
|
55236
|
+
right_base += rblock.count;
|
|
55237
|
+
|
|
55238
|
+
auto r_ptr = MergeJoinRadixPtr(rread, r_entry_idx);
|
|
55239
|
+
|
|
55240
|
+
// now we start from the current lpos value and check if we found a new value that is [<= OR <] the max RHS
|
|
55241
|
+
// value
|
|
55242
|
+
while (true) {
|
|
55243
|
+
int comp_res;
|
|
55244
|
+
if (all_constant) {
|
|
55245
|
+
comp_res = FastMemcmp(l_ptr, r_ptr, cmp_size);
|
|
55246
|
+
} else {
|
|
55247
|
+
lread.entry_idx = l_entry_idx;
|
|
55248
|
+
rread.entry_idx = r_entry_idx;
|
|
55249
|
+
comp_res = Comparators::CompareTuple(lread, rread, l_ptr, r_ptr, lsort.sort_layout, external);
|
|
55250
|
+
}
|
|
55251
|
+
|
|
55252
|
+
if (comp_res <= cmp) {
|
|
55253
|
+
// found a match for lpos, set it in the found_match vector
|
|
55254
|
+
found_match[l_entry_idx] = true;
|
|
55255
|
+
l_entry_idx++;
|
|
55256
|
+
l_ptr += entry_size;
|
|
55257
|
+
if (l_entry_idx >= lhs_not_null) {
|
|
55258
|
+
// early out: we exhausted the entire LHS and they all match
|
|
55259
|
+
return 0;
|
|
55260
|
+
}
|
|
55261
|
+
} else {
|
|
55262
|
+
// we found no match: any subsequent value from the LHS we scan now will be bigger and thus also not
|
|
55263
|
+
// match move to the next RHS chunk
|
|
55264
|
+
break;
|
|
55265
|
+
}
|
|
55266
|
+
}
|
|
55267
|
+
}
|
|
55268
|
+
return 0;
|
|
55269
55269
|
}
|
|
55270
55270
|
|
|
55271
55271
|
void PhysicalPiecewiseMergeJoin::ResolveSimpleJoin(ExecutionContext &context, DataChunk &input, DataChunk &chunk,
|
|
@@ -55275,37 +55275,138 @@ void PhysicalPiecewiseMergeJoin::ResolveSimpleJoin(ExecutionContext &context, Da
|
|
|
55275
55275
|
|
|
55276
55276
|
state.ResolveJoinKeys(input);
|
|
55277
55277
|
|
|
55278
|
-
ScalarMergeInfo left_info(state.left_orders, state.join_keys.data[0].GetType(), state.left_position);
|
|
55279
|
-
ChunkMergeInfo right_info(gstate.right_conditions, gstate.right_orders);
|
|
55280
|
-
|
|
55281
55278
|
// perform the actual join
|
|
55282
|
-
|
|
55279
|
+
bool found_match[STANDARD_VECTOR_SIZE];
|
|
55280
|
+
memset(found_match, 0, sizeof(found_match));
|
|
55281
|
+
MergeJoinSimpleBlocks(state, gstate, found_match, conditions[0].comparison);
|
|
55283
55282
|
|
|
55284
|
-
//
|
|
55283
|
+
// extract the sorted payload
|
|
55284
|
+
idx_t left_position = 0;
|
|
55285
|
+
const auto lhs_not_null = state.lhs_count - state.lhs_has_null;
|
|
55286
|
+
BlockMergeInfo left_info(*state.lhs_global_state, 0, 0, left_position, lhs_not_null);
|
|
55287
|
+
left_info.result.Initialize(nullptr);
|
|
55288
|
+
|
|
55289
|
+
DataChunk payload;
|
|
55290
|
+
payload.Initialize(input.GetTypes());
|
|
55291
|
+
SliceSortedPayload(payload, left_info, state.lhs_count, state.lhs_count);
|
|
55292
|
+
payload.SetCardinality(state.lhs_count);
|
|
55293
|
+
|
|
55294
|
+
// now construct the result based on the join result
|
|
55285
55295
|
switch (join_type) {
|
|
55286
|
-
case JoinType::MARK:
|
|
55287
|
-
|
|
55296
|
+
case JoinType::MARK: {
|
|
55297
|
+
// The only part of the join keys that is actually used is the validity mask.
|
|
55298
|
+
// Since the payload is sorted, we can just set the tail end of the validity masks to invalid.
|
|
55299
|
+
for (auto &key : state.lhs_keys.data) {
|
|
55300
|
+
key.Normalify(state.lhs_keys.size());
|
|
55301
|
+
auto &mask = FlatVector::Validity(key);
|
|
55302
|
+
if (mask.AllValid()) {
|
|
55303
|
+
continue;
|
|
55304
|
+
}
|
|
55305
|
+
mask.SetAllValid(lhs_not_null);
|
|
55306
|
+
for (idx_t i = lhs_not_null; i < state.lhs_count; ++i) {
|
|
55307
|
+
mask.SetInvalid(i);
|
|
55308
|
+
}
|
|
55309
|
+
}
|
|
55310
|
+
// So we make a set of keys that have the validity mask set for the
|
|
55311
|
+
PhysicalJoin::ConstructMarkJoinResult(state.lhs_keys, payload, chunk, found_match, gstate.rhs_has_null);
|
|
55288
55312
|
break;
|
|
55313
|
+
}
|
|
55289
55314
|
case JoinType::SEMI:
|
|
55290
|
-
PhysicalJoin::ConstructSemiJoinResult(
|
|
55315
|
+
PhysicalJoin::ConstructSemiJoinResult(payload, chunk, found_match);
|
|
55291
55316
|
break;
|
|
55292
55317
|
case JoinType::ANTI:
|
|
55293
|
-
PhysicalJoin::ConstructAntiJoinResult(
|
|
55318
|
+
PhysicalJoin::ConstructAntiJoinResult(payload, chunk, found_match);
|
|
55294
55319
|
break;
|
|
55295
55320
|
default:
|
|
55296
55321
|
throw NotImplementedException("Unimplemented join type for merge join");
|
|
55297
55322
|
}
|
|
55298
55323
|
}
|
|
55299
55324
|
|
|
55325
|
+
static idx_t MergeJoinComplexBlocks(BlockMergeInfo &l, BlockMergeInfo &r, const ExpressionType comparison) {
|
|
55326
|
+
const auto cmp = MergeJoinComparisonValue(comparison);
|
|
55327
|
+
|
|
55328
|
+
// The sort parameters should all be the same
|
|
55329
|
+
D_ASSERT(l.state.sort_layout.all_constant == r.state.sort_layout.all_constant);
|
|
55330
|
+
const auto all_constant = r.state.sort_layout.all_constant;
|
|
55331
|
+
D_ASSERT(l.state.external == r.state.external);
|
|
55332
|
+
const auto external = l.state.external;
|
|
55333
|
+
|
|
55334
|
+
// There should only be one sorted block if they have been sorted
|
|
55335
|
+
D_ASSERT(l.state.sorted_blocks.size() == 1);
|
|
55336
|
+
SBScanState lread(l.state.buffer_manager, l.state);
|
|
55337
|
+
lread.sb = l.state.sorted_blocks[0].get();
|
|
55338
|
+
D_ASSERT(lread.sb->radix_sorting_data.size() == 1);
|
|
55339
|
+
MergeJoinPinSortingBlock(lread, l.block_idx);
|
|
55340
|
+
auto l_start = MergeJoinRadixPtr(lread, 0);
|
|
55341
|
+
auto l_ptr = MergeJoinRadixPtr(lread, l.entry_idx);
|
|
55342
|
+
|
|
55343
|
+
D_ASSERT(r.state.sorted_blocks.size() == 1);
|
|
55344
|
+
SBScanState rread(r.state.buffer_manager, r.state);
|
|
55345
|
+
rread.sb = r.state.sorted_blocks[0].get();
|
|
55346
|
+
|
|
55347
|
+
if (r.entry_idx >= r.not_null) {
|
|
55348
|
+
return 0;
|
|
55349
|
+
}
|
|
55350
|
+
|
|
55351
|
+
MergeJoinPinSortingBlock(rread, r.block_idx);
|
|
55352
|
+
auto r_ptr = MergeJoinRadixPtr(rread, r.entry_idx);
|
|
55353
|
+
|
|
55354
|
+
const auto cmp_size = l.state.sort_layout.comparison_size;
|
|
55355
|
+
const auto entry_size = l.state.sort_layout.entry_size;
|
|
55356
|
+
|
|
55357
|
+
idx_t result_count = 0;
|
|
55358
|
+
while (true) {
|
|
55359
|
+
if (l.entry_idx < l.not_null) {
|
|
55360
|
+
int comp_res;
|
|
55361
|
+
if (all_constant) {
|
|
55362
|
+
comp_res = FastMemcmp(l_ptr, r_ptr, cmp_size);
|
|
55363
|
+
} else {
|
|
55364
|
+
lread.entry_idx = l.entry_idx;
|
|
55365
|
+
rread.entry_idx = r.entry_idx;
|
|
55366
|
+
comp_res = Comparators::CompareTuple(lread, rread, l_ptr, r_ptr, l.state.sort_layout, external);
|
|
55367
|
+
}
|
|
55368
|
+
|
|
55369
|
+
if (comp_res <= cmp) {
|
|
55370
|
+
// left side smaller: found match
|
|
55371
|
+
l.result.set_index(result_count, sel_t(l.entry_idx - l.base_idx));
|
|
55372
|
+
r.result.set_index(result_count, sel_t(r.entry_idx - r.base_idx));
|
|
55373
|
+
result_count++;
|
|
55374
|
+
// move left side forward
|
|
55375
|
+
l.entry_idx++;
|
|
55376
|
+
l_ptr += entry_size;
|
|
55377
|
+
if (result_count == STANDARD_VECTOR_SIZE) {
|
|
55378
|
+
// out of space!
|
|
55379
|
+
break;
|
|
55380
|
+
}
|
|
55381
|
+
continue;
|
|
55382
|
+
}
|
|
55383
|
+
}
|
|
55384
|
+
// right side smaller or equal, or left side exhausted: move
|
|
55385
|
+
// right pointer forward reset left side to start
|
|
55386
|
+
r.entry_idx++;
|
|
55387
|
+
if (r.entry_idx >= r.not_null) {
|
|
55388
|
+
break;
|
|
55389
|
+
}
|
|
55390
|
+
r_ptr += entry_size;
|
|
55391
|
+
|
|
55392
|
+
l_ptr = l_start;
|
|
55393
|
+
l.entry_idx = 0;
|
|
55394
|
+
}
|
|
55395
|
+
|
|
55396
|
+
return result_count;
|
|
55397
|
+
}
|
|
55398
|
+
|
|
55300
55399
|
OperatorResultType PhysicalPiecewiseMergeJoin::ResolveComplexJoin(ExecutionContext &context, DataChunk &input,
|
|
55301
55400
|
DataChunk &chunk, OperatorState &state_p) const {
|
|
55302
55401
|
auto &state = (PiecewiseMergeJoinState &)state_p;
|
|
55303
55402
|
auto &gstate = (MergeJoinGlobalState &)*sink_state;
|
|
55403
|
+
auto &rsorted = *gstate.rhs_global_sort_state.sorted_blocks[0];
|
|
55304
55404
|
do {
|
|
55305
55405
|
if (state.first_fetch) {
|
|
55306
55406
|
state.ResolveJoinKeys(input);
|
|
55307
55407
|
|
|
55308
55408
|
state.right_chunk_index = 0;
|
|
55409
|
+
state.right_base = 0;
|
|
55309
55410
|
state.left_position = 0;
|
|
55310
55411
|
state.right_position = 0;
|
|
55311
55412
|
state.first_fetch = false;
|
|
@@ -55315,47 +55416,53 @@ OperatorResultType PhysicalPiecewiseMergeJoin::ResolveComplexJoin(ExecutionConte
|
|
|
55315
55416
|
if (IsLeftOuterJoin(join_type)) {
|
|
55316
55417
|
// left join: before we move to the next chunk, see if we need to output any vectors that didn't
|
|
55317
55418
|
// have a match found
|
|
55318
|
-
PhysicalJoin::ConstructLeftJoinResult(input, chunk, state.
|
|
55319
|
-
memset(state.
|
|
55419
|
+
PhysicalJoin::ConstructLeftJoinResult(input, chunk, state.lhs_found_match.get());
|
|
55420
|
+
memset(state.lhs_found_match.get(), 0, sizeof(bool) * STANDARD_VECTOR_SIZE);
|
|
55320
55421
|
}
|
|
55321
55422
|
state.first_fetch = true;
|
|
55322
55423
|
state.finished = false;
|
|
55323
55424
|
return OperatorResultType::NEED_MORE_INPUT;
|
|
55324
55425
|
}
|
|
55325
55426
|
|
|
55326
|
-
auto
|
|
55327
|
-
|
|
55328
|
-
auto &right_orders = gstate.right_orders[state.right_chunk_index];
|
|
55427
|
+
const auto lhs_not_null = state.lhs_count - state.lhs_has_null;
|
|
55428
|
+
BlockMergeInfo left_info(*state.lhs_global_state, 0, 0, state.left_position, lhs_not_null);
|
|
55329
55429
|
|
|
55330
|
-
|
|
55331
|
-
|
|
55430
|
+
const auto &rblock = rsorted.radix_sorting_data[state.right_chunk_index];
|
|
55431
|
+
const auto rhs_not_null =
|
|
55432
|
+
SortedBlockNotNull(state.right_base, rblock.count, gstate.rhs_count - gstate.rhs_has_null);
|
|
55433
|
+
BlockMergeInfo right_info(gstate.rhs_global_sort_state, state.right_chunk_index, state.right_position,
|
|
55434
|
+
state.right_position, rhs_not_null);
|
|
55332
55435
|
|
|
55333
|
-
idx_t result_count =
|
|
55436
|
+
idx_t result_count = MergeJoinComplexBlocks(left_info, right_info, conditions[0].comparison);
|
|
55334
55437
|
if (result_count == 0) {
|
|
55335
55438
|
// exhausted this chunk on the right side
|
|
55336
55439
|
// move to the next right chunk
|
|
55337
55440
|
state.left_position = 0;
|
|
55338
55441
|
state.right_position = 0;
|
|
55442
|
+
state.right_base += rsorted.radix_sorting_data[state.right_chunk_index].count;
|
|
55339
55443
|
state.right_chunk_index++;
|
|
55340
|
-
if (state.right_chunk_index >=
|
|
55444
|
+
if (state.right_chunk_index >= rsorted.radix_sorting_data.size()) {
|
|
55341
55445
|
state.finished = true;
|
|
55342
55446
|
}
|
|
55343
55447
|
} else {
|
|
55344
55448
|
// found matches: mark the found matches if required
|
|
55345
|
-
if (state.
|
|
55449
|
+
if (state.lhs_found_match) {
|
|
55346
55450
|
for (idx_t i = 0; i < result_count; i++) {
|
|
55347
|
-
state.
|
|
55451
|
+
state.lhs_found_match[left_info.result[i]] = true;
|
|
55348
55452
|
}
|
|
55349
55453
|
}
|
|
55350
|
-
if (gstate.
|
|
55351
|
-
|
|
55454
|
+
if (gstate.rhs_found_match) {
|
|
55455
|
+
// Absolute position of the block + start position inside that block
|
|
55456
|
+
const idx_t base_index = state.right_base + right_info.base_idx;
|
|
55352
55457
|
for (idx_t i = 0; i < result_count; i++) {
|
|
55353
|
-
gstate.
|
|
55458
|
+
gstate.rhs_found_match[base_index + right_info.result[i]] = true;
|
|
55354
55459
|
}
|
|
55355
55460
|
}
|
|
55356
55461
|
// found matches: output them
|
|
55357
|
-
chunk
|
|
55358
|
-
chunk
|
|
55462
|
+
SliceSortedPayload(chunk, left_info, result_count, input.size());
|
|
55463
|
+
SliceSortedPayload(chunk, right_info, result_count, right_info.entry_idx + 1, input.ColumnCount());
|
|
55464
|
+
chunk.SetCardinality(result_count);
|
|
55465
|
+
chunk.Verify();
|
|
55359
55466
|
}
|
|
55360
55467
|
} while (chunk.size() == 0);
|
|
55361
55468
|
return OperatorResultType::HAVE_MORE_OUTPUT;
|
|
@@ -55365,10 +55472,10 @@ OperatorResultType PhysicalPiecewiseMergeJoin::Execute(ExecutionContext &context
|
|
|
55365
55472
|
OperatorState &state) const {
|
|
55366
55473
|
auto &gstate = (MergeJoinGlobalState &)*sink_state;
|
|
55367
55474
|
|
|
55368
|
-
if (gstate.
|
|
55475
|
+
if (gstate.Count() == 0) {
|
|
55369
55476
|
// empty RHS
|
|
55370
55477
|
if (!EmptyResultIfRHSIsEmpty()) {
|
|
55371
|
-
ConstructEmptyJoinResult(join_type, gstate.
|
|
55478
|
+
ConstructEmptyJoinResult(join_type, gstate.rhs_has_null, input, chunk);
|
|
55372
55479
|
return OperatorResultType::NEED_MORE_INPUT;
|
|
55373
55480
|
} else {
|
|
55374
55481
|
return OperatorResultType::FINISHED;
|
|
@@ -55392,169 +55499,6 @@ OperatorResultType PhysicalPiecewiseMergeJoin::Execute(ExecutionContext &context
|
|
|
55392
55499
|
}
|
|
55393
55500
|
}
|
|
55394
55501
|
|
|
55395
|
-
//===--------------------------------------------------------------------===//
|
|
55396
|
-
// OrderVector
|
|
55397
|
-
//===--------------------------------------------------------------------===//
|
|
55398
|
-
template <class T, class OP>
|
|
55399
|
-
static sel_t TemplatedQuicksortInitial(T *data, const SelectionVector &sel, const SelectionVector ¬_null_sel,
|
|
55400
|
-
idx_t count, SelectionVector &result) {
|
|
55401
|
-
// select pivot
|
|
55402
|
-
auto pivot_idx = not_null_sel.get_index(0);
|
|
55403
|
-
auto dpivot_idx = sel.get_index(pivot_idx);
|
|
55404
|
-
sel_t low = 0, high = count - 1;
|
|
55405
|
-
// now insert elements
|
|
55406
|
-
for (idx_t i = 1; i < count; i++) {
|
|
55407
|
-
auto idx = not_null_sel.get_index(i);
|
|
55408
|
-
auto didx = sel.get_index(idx);
|
|
55409
|
-
if (OP::Operation(data[didx], data[dpivot_idx])) {
|
|
55410
|
-
result.set_index(low++, idx);
|
|
55411
|
-
} else {
|
|
55412
|
-
result.set_index(high--, idx);
|
|
55413
|
-
}
|
|
55414
|
-
}
|
|
55415
|
-
D_ASSERT(low == high);
|
|
55416
|
-
result.set_index(low, pivot_idx);
|
|
55417
|
-
return low;
|
|
55418
|
-
}
|
|
55419
|
-
|
|
55420
|
-
struct QuickSortPivot {
|
|
55421
|
-
QuickSortPivot(sel_t left_p, sel_t right_p) : left(left_p), right(right_p) {
|
|
55422
|
-
}
|
|
55423
|
-
|
|
55424
|
-
sel_t left;
|
|
55425
|
-
sel_t right;
|
|
55426
|
-
};
|
|
55427
|
-
|
|
55428
|
-
template <class T, class OP>
|
|
55429
|
-
static void TemplatedQuicksortRefine(T *data, const SelectionVector &sel, idx_t count, SelectionVector &result,
|
|
55430
|
-
sel_t left, sel_t right, vector<QuickSortPivot> &pivots) {
|
|
55431
|
-
if (left >= right) {
|
|
55432
|
-
return;
|
|
55433
|
-
}
|
|
55434
|
-
|
|
55435
|
-
sel_t middle = left + (right - left) / 2;
|
|
55436
|
-
sel_t dpivot_idx = sel.get_index(result.get_index(middle));
|
|
55437
|
-
|
|
55438
|
-
// move the mid point value to the front.
|
|
55439
|
-
sel_t i = left + 1;
|
|
55440
|
-
sel_t j = right;
|
|
55441
|
-
|
|
55442
|
-
result.swap(middle, left);
|
|
55443
|
-
while (i <= j) {
|
|
55444
|
-
while (i <= j && (OP::Operation(data[sel.get_index(result.get_index(i))], data[dpivot_idx]))) {
|
|
55445
|
-
i++;
|
|
55446
|
-
}
|
|
55447
|
-
|
|
55448
|
-
while (i <= j && !OP::Operation(data[sel.get_index(result.get_index(j))], data[dpivot_idx])) {
|
|
55449
|
-
j--;
|
|
55450
|
-
}
|
|
55451
|
-
|
|
55452
|
-
if (i < j) {
|
|
55453
|
-
result.swap(i, j);
|
|
55454
|
-
}
|
|
55455
|
-
}
|
|
55456
|
-
result.swap(i - 1, left);
|
|
55457
|
-
sel_t part = i - 1;
|
|
55458
|
-
|
|
55459
|
-
if (part > 0) {
|
|
55460
|
-
pivots.emplace_back(left, part - 1);
|
|
55461
|
-
}
|
|
55462
|
-
if (part + 1 < right) {
|
|
55463
|
-
pivots.emplace_back(part + 1, right);
|
|
55464
|
-
}
|
|
55465
|
-
}
|
|
55466
|
-
|
|
55467
|
-
template <class T, class OP>
|
|
55468
|
-
void TemplatedQuicksort(T *__restrict data, const SelectionVector &sel, const SelectionVector ¬_null_sel,
|
|
55469
|
-
idx_t count, SelectionVector &result) {
|
|
55470
|
-
auto part = TemplatedQuicksortInitial<T, OP>(data, sel, not_null_sel, count, result);
|
|
55471
|
-
if (part > count) {
|
|
55472
|
-
return;
|
|
55473
|
-
}
|
|
55474
|
-
vector<QuickSortPivot> pivots;
|
|
55475
|
-
pivots.emplace_back(0, part);
|
|
55476
|
-
pivots.emplace_back(part + 1, count - 1);
|
|
55477
|
-
for (idx_t i = 0; i < pivots.size(); i++) {
|
|
55478
|
-
auto pivot = pivots[i];
|
|
55479
|
-
TemplatedQuicksortRefine<T, OP>(data, sel, count, result, pivot.left, pivot.right, pivots);
|
|
55480
|
-
}
|
|
55481
|
-
}
|
|
55482
|
-
|
|
55483
|
-
template <class T>
|
|
55484
|
-
static void TemplatedQuicksort(VectorData &vdata, const SelectionVector ¬_null_sel, idx_t not_null_count,
|
|
55485
|
-
SelectionVector &result) {
|
|
55486
|
-
if (not_null_count == 0) {
|
|
55487
|
-
return;
|
|
55488
|
-
}
|
|
55489
|
-
TemplatedQuicksort<T, duckdb::LessThanEquals>((T *)vdata.data, *vdata.sel, not_null_sel, not_null_count, result);
|
|
55490
|
-
}
|
|
55491
|
-
|
|
55492
|
-
void OrderVector(Vector &vector, idx_t count, MergeOrder &order) {
|
|
55493
|
-
if (count == 0) {
|
|
55494
|
-
order.count = 0;
|
|
55495
|
-
return;
|
|
55496
|
-
}
|
|
55497
|
-
vector.Orrify(count, order.vdata);
|
|
55498
|
-
auto &vdata = order.vdata;
|
|
55499
|
-
|
|
55500
|
-
// first filter out all the non-null values
|
|
55501
|
-
SelectionVector not_null(STANDARD_VECTOR_SIZE);
|
|
55502
|
-
idx_t not_null_count = 0;
|
|
55503
|
-
for (idx_t i = 0; i < count; i++) {
|
|
55504
|
-
auto idx = vdata.sel->get_index(i);
|
|
55505
|
-
if (vdata.validity.RowIsValid(idx)) {
|
|
55506
|
-
not_null.set_index(not_null_count++, i);
|
|
55507
|
-
}
|
|
55508
|
-
}
|
|
55509
|
-
|
|
55510
|
-
order.count = not_null_count;
|
|
55511
|
-
order.order.Initialize(STANDARD_VECTOR_SIZE);
|
|
55512
|
-
switch (vector.GetType().InternalType()) {
|
|
55513
|
-
case PhysicalType::BOOL:
|
|
55514
|
-
case PhysicalType::INT8:
|
|
55515
|
-
TemplatedQuicksort<int8_t>(vdata, not_null, not_null_count, order.order);
|
|
55516
|
-
break;
|
|
55517
|
-
case PhysicalType::INT16:
|
|
55518
|
-
TemplatedQuicksort<int16_t>(vdata, not_null, not_null_count, order.order);
|
|
55519
|
-
break;
|
|
55520
|
-
case PhysicalType::INT32:
|
|
55521
|
-
TemplatedQuicksort<int32_t>(vdata, not_null, not_null_count, order.order);
|
|
55522
|
-
break;
|
|
55523
|
-
case PhysicalType::INT64:
|
|
55524
|
-
TemplatedQuicksort<int64_t>(vdata, not_null, not_null_count, order.order);
|
|
55525
|
-
break;
|
|
55526
|
-
case PhysicalType::UINT8:
|
|
55527
|
-
TemplatedQuicksort<uint8_t>(vdata, not_null, not_null_count, order.order);
|
|
55528
|
-
break;
|
|
55529
|
-
case PhysicalType::UINT16:
|
|
55530
|
-
TemplatedQuicksort<uint16_t>(vdata, not_null, not_null_count, order.order);
|
|
55531
|
-
break;
|
|
55532
|
-
case PhysicalType::UINT32:
|
|
55533
|
-
TemplatedQuicksort<uint32_t>(vdata, not_null, not_null_count, order.order);
|
|
55534
|
-
break;
|
|
55535
|
-
case PhysicalType::UINT64:
|
|
55536
|
-
TemplatedQuicksort<uint64_t>(vdata, not_null, not_null_count, order.order);
|
|
55537
|
-
break;
|
|
55538
|
-
case PhysicalType::INT128:
|
|
55539
|
-
TemplatedQuicksort<hugeint_t>(vdata, not_null, not_null_count, order.order);
|
|
55540
|
-
break;
|
|
55541
|
-
case PhysicalType::FLOAT:
|
|
55542
|
-
TemplatedQuicksort<float>(vdata, not_null, not_null_count, order.order);
|
|
55543
|
-
break;
|
|
55544
|
-
case PhysicalType::DOUBLE:
|
|
55545
|
-
TemplatedQuicksort<double>(vdata, not_null, not_null_count, order.order);
|
|
55546
|
-
break;
|
|
55547
|
-
case PhysicalType::INTERVAL:
|
|
55548
|
-
TemplatedQuicksort<interval_t>(vdata, not_null, not_null_count, order.order);
|
|
55549
|
-
break;
|
|
55550
|
-
case PhysicalType::VARCHAR:
|
|
55551
|
-
TemplatedQuicksort<string_t>(vdata, not_null, not_null_count, order.order);
|
|
55552
|
-
break;
|
|
55553
|
-
default:
|
|
55554
|
-
throw NotImplementedException("Unimplemented type for sort");
|
|
55555
|
-
}
|
|
55556
|
-
}
|
|
55557
|
-
|
|
55558
55502
|
//===--------------------------------------------------------------------===//
|
|
55559
55503
|
// Source
|
|
55560
55504
|
//===--------------------------------------------------------------------===//
|
|
@@ -55565,12 +55509,13 @@ public:
|
|
|
55565
55509
|
|
|
55566
55510
|
mutex lock;
|
|
55567
55511
|
const PhysicalPiecewiseMergeJoin &op;
|
|
55512
|
+
unique_ptr<PayloadScanner> scanner;
|
|
55568
55513
|
idx_t right_outer_position;
|
|
55569
55514
|
|
|
55570
55515
|
public:
|
|
55571
55516
|
idx_t MaxThreads() override {
|
|
55572
55517
|
auto &sink = (MergeJoinGlobalState &)*op.sink_state;
|
|
55573
|
-
return sink.
|
|
55518
|
+
return sink.Count() / (STANDARD_VECTOR_SIZE * idx_t(10));
|
|
55574
55519
|
}
|
|
55575
55520
|
};
|
|
55576
55521
|
|
|
@@ -55578,17 +55523,63 @@ unique_ptr<GlobalSourceState> PhysicalPiecewiseMergeJoin::GetGlobalSourceState(C
|
|
|
55578
55523
|
return make_unique<PiecewiseJoinScanState>(*this);
|
|
55579
55524
|
}
|
|
55580
55525
|
|
|
55581
|
-
void PhysicalPiecewiseMergeJoin::GetData(ExecutionContext &context, DataChunk &
|
|
55526
|
+
void PhysicalPiecewiseMergeJoin::GetData(ExecutionContext &context, DataChunk &result, GlobalSourceState &gstate,
|
|
55582
55527
|
LocalSourceState &lstate) const {
|
|
55583
55528
|
D_ASSERT(IsRightOuterJoin(join_type));
|
|
55584
55529
|
// check if we need to scan any unmatched tuples from the RHS for the full/right outer join
|
|
55585
55530
|
auto &sink = (MergeJoinGlobalState &)*sink_state;
|
|
55586
55531
|
auto &state = (PiecewiseJoinScanState &)gstate;
|
|
55587
55532
|
|
|
55533
|
+
lock_guard<mutex> l(state.lock);
|
|
55534
|
+
if (!state.scanner) {
|
|
55535
|
+
// Initialize scanner (if not yet initialized)
|
|
55536
|
+
auto &sort_state = sink.rhs_global_sort_state;
|
|
55537
|
+
if (sort_state.sorted_blocks.empty()) {
|
|
55538
|
+
return;
|
|
55539
|
+
}
|
|
55540
|
+
state.scanner = make_unique<PayloadScanner>(*sort_state.sorted_blocks[0]->payload_data, sort_state);
|
|
55541
|
+
}
|
|
55542
|
+
|
|
55588
55543
|
// if the LHS is exhausted in a FULL/RIGHT OUTER JOIN, we scan the found_match for any chunks we
|
|
55589
55544
|
// still need to output
|
|
55590
|
-
|
|
55591
|
-
|
|
55545
|
+
const auto found_match = sink.rhs_found_match.get();
|
|
55546
|
+
|
|
55547
|
+
// ConstructFullOuterJoinResult(sink.rhs_found_match.get(), sink.right_chunks, chunk, state.right_outer_position);
|
|
55548
|
+
DataChunk rhs_chunk;
|
|
55549
|
+
rhs_chunk.Initialize(children[1]->types);
|
|
55550
|
+
SelectionVector rsel(STANDARD_VECTOR_SIZE);
|
|
55551
|
+
for (;;) {
|
|
55552
|
+
// Read the next sorted chunk
|
|
55553
|
+
state.scanner->Scan(rhs_chunk);
|
|
55554
|
+
|
|
55555
|
+
const auto count = rhs_chunk.size();
|
|
55556
|
+
if (count == 0) {
|
|
55557
|
+
return;
|
|
55558
|
+
}
|
|
55559
|
+
|
|
55560
|
+
idx_t result_count = 0;
|
|
55561
|
+
// figure out which tuples didn't find a match in the RHS
|
|
55562
|
+
for (idx_t i = 0; i < count; i++) {
|
|
55563
|
+
if (!found_match[state.right_outer_position + i]) {
|
|
55564
|
+
rsel.set_index(result_count++, i);
|
|
55565
|
+
}
|
|
55566
|
+
}
|
|
55567
|
+
state.right_outer_position += count;
|
|
55568
|
+
|
|
55569
|
+
if (result_count > 0) {
|
|
55570
|
+
// if there were any tuples that didn't find a match, output them
|
|
55571
|
+
const idx_t left_column_count = result.ColumnCount() - rhs_chunk.ColumnCount();
|
|
55572
|
+
for (idx_t col_idx = 0; col_idx < left_column_count; ++col_idx) {
|
|
55573
|
+
result.data[col_idx].SetVectorType(VectorType::CONSTANT_VECTOR);
|
|
55574
|
+
ConstantVector::SetNull(result.data[col_idx], true);
|
|
55575
|
+
}
|
|
55576
|
+
for (idx_t col_idx = 0; col_idx < rhs_chunk.ColumnCount(); ++col_idx) {
|
|
55577
|
+
result.data[left_column_count + col_idx].Slice(rhs_chunk.data[col_idx], rsel, result_count);
|
|
55578
|
+
}
|
|
55579
|
+
result.SetCardinality(result_count);
|
|
55580
|
+
return;
|
|
55581
|
+
}
|
|
55582
|
+
}
|
|
55592
55583
|
}
|
|
55593
55584
|
|
|
55594
55585
|
} // namespace duckdb
|