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/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
- void CompleteMergeRound();
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 &order;
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 MergeJoinLocalState : public LocalSinkState {
54777
+ class MergeJoinGlobalState : public GlobalSinkState {
55107
54778
  public:
55108
- explicit MergeJoinLocalState(const vector<JoinCondition> &conditions) {
55109
- vector<LogicalType> condition_types;
55110
- for (auto &cond : conditions) {
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
- //! The chunk holding the right condition
55118
- DataChunk join_keys;
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
- mutex mj_lock;
55129
- //! The materialized data of the RHS
55130
- ChunkCollection right_chunks;
55131
- //! The materialized join keys of the RHS
55132
- ChunkCollection right_conditions;
55133
- //! The join orders of the RHS
55134
- vector<MergeOrder> right_orders;
55135
- //! Whether or not the RHS of the nested loop join has NULL values
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[]> right_found_match;
55139
- //! The position in the RHS in the final scan of the FULL OUTER JOIN
55140
- idx_t right_outer_position;
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
- return make_unique<MergeJoinGlobalState>();
55145
- }
55146
-
55147
- unique_ptr<LocalSinkState> PhysicalPiecewiseMergeJoin::GetLocalSinkState(ExecutionContext &context) const {
55148
- return make_unique<MergeJoinLocalState>(conditions);
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
- SinkResultType PhysicalPiecewiseMergeJoin::Sink(ExecutionContext &context, GlobalSinkState &state,
55152
- LocalSinkState &lstate, DataChunk &input) const {
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
- mj_state.join_keys.Reset();
55160
- mj_state.join_keys.SetCardinality(input);
55161
- for (idx_t k = 0; k < conditions.size(); k++) {
55162
- // resolve the join key
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
- // append the join keys and the chunk to the chunk collection
55166
- lock_guard<mutex> mj_guard(gstate.mj_lock);
55167
- gstate.right_chunks.Append(input);
55168
- gstate.right_conditions.Append(mj_state.join_keys);
55169
- return SinkResultType::NEED_MORE_INPUT;
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
- void PhysicalPiecewiseMergeJoin::Combine(ExecutionContext &context, GlobalSinkState &gstate,
55173
- LocalSinkState &lstate) const {
55174
- auto &state = (MergeJoinLocalState &)lstate;
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, &state.rhs_executor, "rhs_executor", 1);
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
- static void OrderVector(Vector &vector, idx_t count, MergeOrder &order);
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
- if (gstate.right_conditions.ChunkCount() > 0) {
55190
- // now order all the chunks
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.right_found_match = unique_ptr<bool[]>(new bool[gstate.right_chunks.Count()]);
55209
- memset(gstate.right_found_match.get(), 0, sizeof(bool) * gstate.right_chunks.Count());
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 (gstate.right_chunks.Count() == 0 && EmptyResultIfRHSIsEmpty()) {
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
- : op(op), first_fetch(true), finished(true), left_position(0), right_position(0), right_chunk_index(0) {
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
- join_keys.Initialize(condition_types);
55030
+ lhs_keys.Initialize(condition_types);
55230
55031
  if (IsLeftOuterJoin(op.join_type)) {
55231
- left_found_match = unique_ptr<bool[]>(new bool[STANDARD_VECTOR_SIZE]);
55232
- memset(left_found_match.get(), 0, sizeof(bool) * STANDARD_VECTOR_SIZE);
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
- DataChunk left_chunk;
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
- join_keys.Reset();
55253
- lhs_executor.SetChunk(input);
55254
- join_keys.SetCardinality(input);
55255
- for (idx_t k = 0; k < op.conditions.size(); k++) {
55256
- lhs_executor.ExecuteExpression(k, join_keys.data[k]);
55257
- // sort by join key
55258
- OrderVector(join_keys.data[k], join_keys.size(), left_orders);
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
- return make_unique<PiecewiseMergeJoinState>(*this);
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
- MergeJoinSimple::Perform(left_info, right_info, conditions[0].comparison);
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
- // now construct the result based ont he join result
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
- PhysicalJoin::ConstructMarkJoinResult(state.join_keys, input, chunk, right_info.found_match, gstate.has_null);
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(input, chunk, right_info.found_match);
55315
+ PhysicalJoin::ConstructSemiJoinResult(payload, chunk, found_match);
55291
55316
  break;
55292
55317
  case JoinType::ANTI:
55293
- PhysicalJoin::ConstructAntiJoinResult(input, chunk, right_info.found_match);
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.left_found_match.get());
55319
- memset(state.left_found_match.get(), 0, sizeof(bool) * STANDARD_VECTOR_SIZE);
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 &right_chunk = gstate.right_chunks.GetChunk(state.right_chunk_index);
55327
- auto &right_condition_chunk = gstate.right_conditions.GetChunk(state.right_chunk_index);
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
- ScalarMergeInfo left_info(state.left_orders, state.join_keys.data[0].GetType(), state.left_position);
55331
- ScalarMergeInfo right_info(right_orders, right_condition_chunk.data[0].GetType(), state.right_position);
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 = MergeJoinComplex::Perform(left_info, right_info, conditions[0].comparison);
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 >= gstate.right_chunks.ChunkCount()) {
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.left_found_match) {
55449
+ if (state.lhs_found_match) {
55346
55450
  for (idx_t i = 0; i < result_count; i++) {
55347
- state.left_found_match[left_info.result.get_index(i)] = true;
55451
+ state.lhs_found_match[left_info.result[i]] = true;
55348
55452
  }
55349
55453
  }
55350
- if (gstate.right_found_match) {
55351
- idx_t base_index = state.right_chunk_index * STANDARD_VECTOR_SIZE;
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.right_found_match[base_index + right_info.result.get_index(i)] = true;
55458
+ gstate.rhs_found_match[base_index + right_info.result[i]] = true;
55354
55459
  }
55355
55460
  }
55356
55461
  // found matches: output them
55357
- chunk.Slice(input, left_info.result, result_count);
55358
- chunk.Slice(right_chunk, right_info.result, result_count, input.ColumnCount());
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.right_chunks.Count() == 0) {
55475
+ if (gstate.Count() == 0) {
55369
55476
  // empty RHS
55370
55477
  if (!EmptyResultIfRHSIsEmpty()) {
55371
- ConstructEmptyJoinResult(join_type, gstate.has_null, input, chunk);
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 &not_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 &not_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 &not_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.right_chunks.Count() / (STANDARD_VECTOR_SIZE * 10);
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 &chunk, GlobalSourceState &gstate,
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
- lock_guard<mutex> l(state.lock);
55591
- ConstructFullOuterJoinResult(sink.right_found_match.get(), sink.right_chunks, chunk, state.right_outer_position);
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