duckdb 0.3.3-dev1096.0 → 0.3.3-dev1133.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
@@ -6393,6 +6393,8 @@ string PhysicalOperatorToString(PhysicalOperatorType type) {
6393
6393
  return "INDEX_JOIN";
6394
6394
  case PhysicalOperatorType::PIECEWISE_MERGE_JOIN:
6395
6395
  return "PIECEWISE_MERGE_JOIN";
6396
+ case PhysicalOperatorType::IE_JOIN:
6397
+ return "IE_JOIN";
6396
6398
  case PhysicalOperatorType::CROSS_PRODUCT:
6397
6399
  return "CROSS_PRODUCT";
6398
6400
  case PhysicalOperatorType::UNION:
@@ -27978,7 +27980,26 @@ public:
27978
27980
  //! Used to scan the data into DataChunks after sorting
27979
27981
  struct PayloadScanner {
27980
27982
  public:
27981
- PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state);
27983
+ PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state, bool flush = true);
27984
+ explicit PayloadScanner(GlobalSortState &global_sort_state, bool flush = true);
27985
+
27986
+ //! Scan a single block
27987
+ PayloadScanner(GlobalSortState &global_sort_state, idx_t block_idx);
27988
+
27989
+ //! The type layout of the payload
27990
+ inline const vector<LogicalType> &GetPayloadTypes() const {
27991
+ return sorted_data.layout.GetTypes();
27992
+ }
27993
+
27994
+ //! The number of rows scanned so far
27995
+ inline idx_t Scanned() const {
27996
+ return total_scanned;
27997
+ }
27998
+
27999
+ //! The number of remaining rows
28000
+ inline idx_t Remaining() const {
28001
+ return total_count - total_scanned;
28002
+ }
27982
28003
 
27983
28004
  //! Scans the next data chunk from the sorted data
27984
28005
  void Scan(DataChunk &chunk);
@@ -27996,6 +28017,8 @@ private:
27996
28017
  Vector addresses = Vector(LogicalType::POINTER);
27997
28018
  //! The number of rows scanned so far
27998
28019
  idx_t total_scanned;
28020
+ //! Whether to flush the blocks after scanning
28021
+ const bool flush;
27999
28022
  };
28000
28023
 
28001
28024
  } // namespace duckdb
@@ -30249,21 +30272,34 @@ void SBScanState::SetIndices(idx_t block_idx_to, idx_t entry_idx_to) {
30249
30272
  entry_idx = entry_idx_to;
30250
30273
  }
30251
30274
 
30252
- PayloadScanner::PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state)
30275
+ PayloadScanner::PayloadScanner(SortedData &sorted_data, GlobalSortState &global_sort_state, bool flush_p)
30253
30276
  : sorted_data(sorted_data), read_state(global_sort_state.buffer_manager, global_sort_state),
30254
- total_count(sorted_data.Count()), global_sort_state(global_sort_state), total_scanned(0) {
30277
+ total_count(sorted_data.Count()), global_sort_state(global_sort_state), total_scanned(0), flush(flush_p) {
30278
+ }
30279
+
30280
+ PayloadScanner::PayloadScanner(GlobalSortState &global_sort_state, bool flush_p)
30281
+ : PayloadScanner(*global_sort_state.sorted_blocks[0]->payload_data, global_sort_state, flush_p) {
30282
+ }
30283
+
30284
+ PayloadScanner::PayloadScanner(GlobalSortState &global_sort_state, idx_t block_idx)
30285
+ : sorted_data(*global_sort_state.sorted_blocks[0]->payload_data),
30286
+ read_state(global_sort_state.buffer_manager, global_sort_state),
30287
+ total_count(sorted_data.data_blocks[block_idx].count), global_sort_state(global_sort_state), total_scanned(0),
30288
+ flush(false) {
30289
+ read_state.SetIndices(block_idx, 0);
30255
30290
  }
30256
30291
 
30257
30292
  void PayloadScanner::Scan(DataChunk &chunk) {
30258
30293
  auto count = MinValue((idx_t)STANDARD_VECTOR_SIZE, total_count - total_scanned);
30259
30294
  if (count == 0) {
30260
- D_ASSERT(read_state.block_idx == sorted_data.data_blocks.size());
30261
30295
  chunk.SetCardinality(count);
30262
30296
  return;
30263
30297
  }
30264
30298
  // Eagerly delete references to blocks that we've passed
30265
- for (idx_t i = 0; i < read_state.block_idx; i++) {
30266
- sorted_data.data_blocks[i].block = nullptr;
30299
+ if (flush) {
30300
+ for (idx_t i = 0; i < read_state.block_idx; i++) {
30301
+ sorted_data.data_blocks[i].block = nullptr;
30302
+ }
30267
30303
  }
30268
30304
  const idx_t &row_width = sorted_data.layout.GetRowWidth();
30269
30305
  // Set up a batch of pointers to scan data from
@@ -33142,6 +33178,7 @@ void DataChunk::Split(DataChunk &other, idx_t split_idx) {
33142
33178
  vector_caches.pop_back();
33143
33179
  }
33144
33180
  other.SetCardinality(*this);
33181
+ other.SetCapacity(*this);
33145
33182
  }
33146
33183
 
33147
33184
  void DataChunk::Fuse(DataChunk &other) {
@@ -43887,6 +43924,32 @@ bool VectorOperations::HasNull(Vector &input, idx_t count) {
43887
43924
  }
43888
43925
  }
43889
43926
 
43927
+ idx_t VectorOperations::CountNotNull(Vector &input, const idx_t count) {
43928
+ idx_t valid = 0;
43929
+
43930
+ VectorData vdata;
43931
+ input.Orrify(count, vdata);
43932
+ if (vdata.validity.AllValid()) {
43933
+ return count;
43934
+ }
43935
+ switch (input.GetVectorType()) {
43936
+ case VectorType::FLAT_VECTOR:
43937
+ valid += vdata.validity.CountValid(count);
43938
+ break;
43939
+ case VectorType::CONSTANT_VECTOR:
43940
+ valid += vdata.validity.CountValid(1) * count;
43941
+ break;
43942
+ default:
43943
+ for (idx_t i = 0; i < count; ++i) {
43944
+ const auto row_idx = vdata.sel->get_index(i);
43945
+ valid += int(vdata.validity.RowIsValid(row_idx));
43946
+ }
43947
+ break;
43948
+ }
43949
+
43950
+ return valid;
43951
+ }
43952
+
43890
43953
  } // namespace duckdb
43891
43954
  //===--------------------------------------------------------------------===//
43892
43955
  // numeric_inplace_operators.cpp
@@ -57374,6 +57437,1481 @@ void PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk &chunk, Glob
57374
57437
  sink.hash_table->ScanFullOuter(chunk, state.ht_scan_state);
57375
57438
  }
57376
57439
 
57440
+ } // namespace duckdb
57441
+ //===----------------------------------------------------------------------===//
57442
+ // DuckDB
57443
+ //
57444
+ // duckdb/execution/operator/join/physical_piecewise_merge_join.hpp
57445
+ //
57446
+ //
57447
+ //===----------------------------------------------------------------------===//
57448
+
57449
+
57450
+
57451
+
57452
+
57453
+
57454
+ namespace duckdb {
57455
+
57456
+ class IEJoinSortedTable;
57457
+
57458
+ //! PhysicalIEJoin represents a two inequality range join between
57459
+ //! two tables
57460
+ class PhysicalIEJoin : public PhysicalComparisonJoin {
57461
+ public:
57462
+ PhysicalIEJoin(LogicalOperator &op, unique_ptr<PhysicalOperator> left, unique_ptr<PhysicalOperator> right,
57463
+ vector<JoinCondition> cond, JoinType join_type, idx_t estimated_cardinality);
57464
+
57465
+ vector<LogicalType> join_key_types;
57466
+ vector<vector<BoundOrderByNode>> lhs_orders;
57467
+ vector<vector<BoundOrderByNode>> rhs_orders;
57468
+
57469
+ public:
57470
+ // Operator Interface
57471
+ OperatorResultType Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk,
57472
+ OperatorState &state) const override;
57473
+
57474
+ public:
57475
+ // Source interface
57476
+ unique_ptr<LocalSourceState> GetLocalSourceState(ExecutionContext &context,
57477
+ GlobalSourceState &gstate) const override;
57478
+ unique_ptr<GlobalSourceState> GetGlobalSourceState(ClientContext &context) const override;
57479
+ void GetData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate,
57480
+ LocalSourceState &lstate) const override;
57481
+
57482
+ bool IsSource() const override {
57483
+ return true;
57484
+ }
57485
+ bool ParallelSource() const override {
57486
+ return true;
57487
+ }
57488
+
57489
+ public:
57490
+ // Sink Interface
57491
+ unique_ptr<GlobalSinkState> GetGlobalSinkState(ClientContext &context) const override;
57492
+ unique_ptr<LocalSinkState> GetLocalSinkState(ExecutionContext &context) const override;
57493
+ SinkResultType Sink(ExecutionContext &context, GlobalSinkState &state, LocalSinkState &lstate,
57494
+ DataChunk &input) const override;
57495
+ void Combine(ExecutionContext &context, GlobalSinkState &gstate, LocalSinkState &lstate) const override;
57496
+ SinkFinalizeType Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
57497
+ GlobalSinkState &gstate) const override;
57498
+
57499
+ //! Schedules tasks to merge sort the current child's data during a Finalize phase
57500
+ static void ScheduleMergeTasks(Pipeline &pipeline, Event &event, IEJoinSortedTable &table);
57501
+
57502
+ bool IsSink() const override {
57503
+ return true;
57504
+ }
57505
+ bool ParallelSink() const override {
57506
+ return true;
57507
+ }
57508
+
57509
+ private:
57510
+ // resolve joins that can potentially output N*M elements (INNER, LEFT, FULL)
57511
+ void ResolveComplexJoin(ExecutionContext &context, DataChunk &result, LocalSourceState &state) const;
57512
+ };
57513
+
57514
+ } // namespace duckdb
57515
+
57516
+
57517
+
57518
+
57519
+
57520
+
57521
+
57522
+
57523
+
57524
+
57525
+
57526
+
57527
+
57528
+ #include <thread>
57529
+
57530
+ namespace duckdb {
57531
+
57532
+ PhysicalIEJoin::PhysicalIEJoin(LogicalOperator &op, unique_ptr<PhysicalOperator> left,
57533
+ unique_ptr<PhysicalOperator> right, vector<JoinCondition> cond, JoinType join_type,
57534
+ idx_t estimated_cardinality)
57535
+ : PhysicalComparisonJoin(op, PhysicalOperatorType::IE_JOIN, move(cond), join_type, estimated_cardinality) {
57536
+ // Reorder the conditions so that ranges are at the front.
57537
+ // TODO: use stats to improve the choice?
57538
+ // TODO: Prefer fixed length types?
57539
+ auto conditions_p = std::move(conditions);
57540
+ conditions.resize(conditions_p.size());
57541
+ idx_t range_position = 0;
57542
+ idx_t other_position = conditions_p.size();
57543
+ for (idx_t i = 0; i < conditions_p.size(); ++i) {
57544
+ switch (conditions_p[i].comparison) {
57545
+ case ExpressionType::COMPARE_LESSTHAN:
57546
+ case ExpressionType::COMPARE_LESSTHANOREQUALTO:
57547
+ case ExpressionType::COMPARE_GREATERTHAN:
57548
+ case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
57549
+ conditions[range_position++] = std::move(conditions_p[i]);
57550
+ break;
57551
+ case ExpressionType::COMPARE_NOTEQUAL:
57552
+ case ExpressionType::COMPARE_DISTINCT_FROM:
57553
+ // Allowed in multi-predicate joins, but can't be first/sort.
57554
+ conditions[--other_position] = std::move(conditions_p[i]);
57555
+ break;
57556
+ default:
57557
+ // COMPARE EQUAL not supported with iejoin join
57558
+ throw NotImplementedException("Unimplemented join type for IEJoin");
57559
+ }
57560
+ }
57561
+
57562
+ // IEJoin requires at least two comparisons.
57563
+ D_ASSERT(range_position > 1);
57564
+
57565
+ // 1. let L1 (resp. L2) be the array of column X (resp. Y)
57566
+ D_ASSERT(conditions.size() >= 2);
57567
+ lhs_orders.resize(2);
57568
+ rhs_orders.resize(2);
57569
+ for (idx_t i = 0; i < 2; ++i) {
57570
+ auto &cond = conditions[i];
57571
+ D_ASSERT(cond.left->return_type == cond.right->return_type);
57572
+ join_key_types.push_back(cond.left->return_type);
57573
+
57574
+ // Convert the conditions to sort orders
57575
+ auto left = cond.left->Copy();
57576
+ auto right = cond.right->Copy();
57577
+ auto sense = OrderType::INVALID;
57578
+
57579
+ // 2. if (op1 ∈ {>, ≥}) sort L1 in descending order
57580
+ // 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order
57581
+ // 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order
57582
+ // 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order
57583
+ switch (cond.comparison) {
57584
+ case ExpressionType::COMPARE_GREATERTHAN:
57585
+ case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
57586
+ sense = i ? OrderType::ASCENDING : OrderType::DESCENDING;
57587
+ break;
57588
+ default:
57589
+ sense = i ? OrderType::DESCENDING : OrderType::ASCENDING;
57590
+ break;
57591
+ }
57592
+ lhs_orders[i].emplace_back(BoundOrderByNode(sense, OrderByNullType::NULLS_LAST, move(left)));
57593
+ rhs_orders[i].emplace_back(BoundOrderByNode(sense, OrderByNullType::NULLS_LAST, move(right)));
57594
+ }
57595
+
57596
+ for (idx_t i = 2; i < conditions.size(); ++i) {
57597
+ auto &cond = conditions[i];
57598
+ D_ASSERT(cond.left->return_type == cond.right->return_type);
57599
+ join_key_types.push_back(cond.left->return_type);
57600
+ }
57601
+
57602
+ children.push_back(move(left));
57603
+ children.push_back(move(right));
57604
+ }
57605
+
57606
+ //===--------------------------------------------------------------------===//
57607
+ // Sink
57608
+ //===--------------------------------------------------------------------===//
57609
+ class IEJoinLocalState : public LocalSinkState {
57610
+ public:
57611
+ explicit IEJoinLocalState(const vector<JoinCondition> &conditions, const idx_t child) : has_null(0), count(0) {
57612
+ // Initialize order clause expression executor and key DataChunk
57613
+ vector<LogicalType> types;
57614
+ for (const auto &cond : conditions) {
57615
+ comparisons.emplace_back(cond.comparison);
57616
+
57617
+ const auto &expr = child ? cond.right : cond.left;
57618
+ executor.AddExpression(*expr);
57619
+
57620
+ types.push_back(expr->return_type);
57621
+ }
57622
+ keys.Initialize(types);
57623
+ }
57624
+
57625
+ //! The local sort state
57626
+ LocalSortState local_sort_state;
57627
+ //! Local copy of the sorting expression executor
57628
+ ExpressionExecutor executor;
57629
+ //! Holds a vector of incoming sorting columns
57630
+ DataChunk keys;
57631
+ //! The comparison list (for null merging)
57632
+ vector<ExpressionType> comparisons;
57633
+ //! The number of NULL values
57634
+ idx_t has_null;
57635
+ //! The total number of rows
57636
+ idx_t count;
57637
+
57638
+ idx_t MergeKeyNulls();
57639
+
57640
+ void Sink(DataChunk &input, GlobalSortState &global_sort_state) {
57641
+ // Initialize local state (if necessary)
57642
+ if (!local_sort_state.initialized) {
57643
+ local_sort_state.Initialize(global_sort_state, global_sort_state.buffer_manager);
57644
+ }
57645
+
57646
+ // Obtain sorting columns
57647
+ keys.Reset();
57648
+ executor.Execute(input, keys);
57649
+
57650
+ // Count the NULLs so we can exclude them later
57651
+ has_null += MergeKeyNulls();
57652
+ count += keys.size();
57653
+
57654
+ // Sink the data into the local sort state
57655
+ D_ASSERT(keys.ColumnCount() > 1);
57656
+ // Only sort the primary key
57657
+ DataChunk join_head;
57658
+ join_head.data.emplace_back(Vector(keys.data[0]));
57659
+ join_head.SetCardinality(keys.size());
57660
+
57661
+ local_sort_state.SinkChunk(join_head, input);
57662
+ }
57663
+
57664
+ void Sort(GlobalSortState &gss) {
57665
+ local_sort_state.Sort(gss, true);
57666
+ }
57667
+ void Reset() {
57668
+ has_null = 0;
57669
+ count = 0;
57670
+ }
57671
+ };
57672
+
57673
+ idx_t IEJoinLocalState::MergeKeyNulls() {
57674
+ // Merge the validity masks of the comparison keys into the primary
57675
+ // Return the number of NULLs in the resulting chunk
57676
+ D_ASSERT(keys.ColumnCount() > 0);
57677
+ const auto count = keys.size();
57678
+
57679
+ size_t all_constant = 0;
57680
+ for (auto &v : keys.data) {
57681
+ all_constant += int(v.GetVectorType() == VectorType::CONSTANT_VECTOR);
57682
+ }
57683
+
57684
+ auto &primary = keys.data[0];
57685
+ if (all_constant == keys.data.size()) {
57686
+ // Either all NULL or no NULLs
57687
+ for (auto &v : keys.data) {
57688
+ if (ConstantVector::IsNull(v)) {
57689
+ ConstantVector::SetNull(primary, true);
57690
+ return count;
57691
+ }
57692
+ }
57693
+ return 0;
57694
+ } else if (keys.ColumnCount() > 1) {
57695
+ // Normalify the primary, as it will need to merge arbitrary validity masks
57696
+ primary.Normalify(count);
57697
+ auto &pvalidity = FlatVector::Validity(primary);
57698
+ D_ASSERT(keys.ColumnCount() == comparisons.size());
57699
+ for (size_t c = 1; c < keys.data.size(); ++c) {
57700
+ // Skip comparisons that accept NULLs
57701
+ if (comparisons[c] == ExpressionType::COMPARE_DISTINCT_FROM) {
57702
+ continue;
57703
+ }
57704
+ // Orrify the rest, as the sort code will do this anyway.
57705
+ auto &v = keys.data[c];
57706
+ VectorData vdata;
57707
+ v.Orrify(count, vdata);
57708
+ auto &vvalidity = vdata.validity;
57709
+ if (vvalidity.AllValid()) {
57710
+ continue;
57711
+ }
57712
+ pvalidity.EnsureWritable();
57713
+ auto pmask = pvalidity.GetData();
57714
+ if (v.GetVectorType() == VectorType::FLAT_VECTOR) {
57715
+ // Merge entire entries
57716
+ const auto entry_count = pvalidity.EntryCount(count);
57717
+ for (idx_t entry_idx = 0; entry_idx < entry_count; ++entry_idx) {
57718
+ pmask[entry_idx] &= vvalidity.GetValidityEntry(entry_idx);
57719
+ }
57720
+ }
57721
+ }
57722
+ return count - pvalidity.CountValid(count);
57723
+ } else {
57724
+ return count - VectorOperations::CountNotNull(primary, count);
57725
+ }
57726
+ }
57727
+
57728
+ class IEJoinSortedTable {
57729
+ public:
57730
+ IEJoinSortedTable(ClientContext &context, const vector<BoundOrderByNode> &orders, RowLayout &payload_layout)
57731
+ : global_sort_state(BufferManager::GetBufferManager(context), orders, payload_layout), has_null(0), count(0),
57732
+ memory_per_thread(0) {
57733
+ D_ASSERT(orders.size() == 1);
57734
+
57735
+ // Set external (can be force with the PRAGMA)
57736
+ auto &config = ClientConfig::GetConfig(context);
57737
+ global_sort_state.external = config.force_external;
57738
+ // Memory usage per thread should scale with max mem / num threads
57739
+ // We take 1/4th of this, to be conservative
57740
+ idx_t max_memory = global_sort_state.buffer_manager.GetMaxMemory();
57741
+ idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
57742
+ memory_per_thread = (max_memory / num_threads) / 4;
57743
+ }
57744
+
57745
+ inline idx_t Count() const {
57746
+ return count;
57747
+ }
57748
+
57749
+ inline idx_t BlockCount() const {
57750
+ if (global_sort_state.sorted_blocks.empty()) {
57751
+ return 0;
57752
+ }
57753
+ D_ASSERT(global_sort_state.sorted_blocks.size() == 1);
57754
+ return global_sort_state.sorted_blocks[0]->radix_sorting_data.size();
57755
+ }
57756
+
57757
+ inline idx_t BlockSize(idx_t i) const {
57758
+ return global_sort_state.sorted_blocks[0]->radix_sorting_data[i].count;
57759
+ }
57760
+
57761
+ inline void Combine(IEJoinLocalState &lstate) {
57762
+ global_sort_state.AddLocalState(lstate.local_sort_state);
57763
+ has_null += lstate.has_null;
57764
+ count += lstate.count;
57765
+ }
57766
+
57767
+ inline void IntializeMatches() {
57768
+ found_match = unique_ptr<bool[]>(new bool[Count()]);
57769
+ memset(found_match.get(), 0, sizeof(bool) * Count());
57770
+ }
57771
+
57772
+ void Print() {
57773
+ PayloadScanner scanner(global_sort_state, false);
57774
+ DataChunk chunk;
57775
+ chunk.Initialize(scanner.GetPayloadTypes());
57776
+ for (;;) {
57777
+ scanner.Scan(chunk);
57778
+ const auto count = chunk.size();
57779
+ if (!count) {
57780
+ break;
57781
+ }
57782
+ chunk.Print();
57783
+ }
57784
+ }
57785
+
57786
+ GlobalSortState global_sort_state;
57787
+ //! Whether or not the RHS has NULL values
57788
+ atomic<idx_t> has_null;
57789
+ //! The total number of rows in the RHS
57790
+ atomic<idx_t> count;
57791
+ //! A bool indicating for each tuple in the RHS if they found a match (only used in FULL OUTER JOIN)
57792
+ unique_ptr<bool[]> found_match;
57793
+ //! Memory usage per thread
57794
+ idx_t memory_per_thread;
57795
+ };
57796
+
57797
+ class IEJoinGlobalState : public GlobalSinkState {
57798
+ public:
57799
+ IEJoinGlobalState(ClientContext &context, const PhysicalIEJoin &op) : child(0) {
57800
+ tables.resize(2);
57801
+ RowLayout lhs_layout;
57802
+ lhs_layout.Initialize(op.children[0]->types);
57803
+ vector<BoundOrderByNode> lhs_order;
57804
+ lhs_order.emplace_back(op.lhs_orders[0][0].Copy());
57805
+ tables[0] = make_unique<IEJoinSortedTable>(context, lhs_order, lhs_layout);
57806
+
57807
+ RowLayout rhs_layout;
57808
+ rhs_layout.Initialize(op.children[1]->types);
57809
+ vector<BoundOrderByNode> rhs_order;
57810
+ rhs_order.emplace_back(op.rhs_orders[0][0].Copy());
57811
+ tables[1] = make_unique<IEJoinSortedTable>(context, rhs_order, rhs_layout);
57812
+ }
57813
+
57814
+ IEJoinGlobalState(IEJoinGlobalState &prev)
57815
+ : GlobalSinkState(prev), tables(move(prev.tables)), child(prev.child + 1) {
57816
+ }
57817
+
57818
+ void Sink(DataChunk &input, IEJoinLocalState &lstate) {
57819
+ auto &table = *tables[child];
57820
+ auto &global_sort_state = table.global_sort_state;
57821
+ auto &local_sort_state = lstate.local_sort_state;
57822
+
57823
+ // Sink the data into the local sort state
57824
+ lstate.Sink(input, global_sort_state);
57825
+
57826
+ // When sorting data reaches a certain size, we sort it
57827
+ if (local_sort_state.SizeInBytes() >= table.memory_per_thread) {
57828
+ local_sort_state.Sort(global_sort_state, true);
57829
+ }
57830
+ }
57831
+
57832
+ vector<unique_ptr<IEJoinSortedTable>> tables;
57833
+ size_t child;
57834
+ };
57835
+
57836
+ unique_ptr<GlobalSinkState> PhysicalIEJoin::GetGlobalSinkState(ClientContext &context) const {
57837
+ D_ASSERT(!sink_state);
57838
+ return make_unique<IEJoinGlobalState>(context, *this);
57839
+ }
57840
+
57841
+ unique_ptr<LocalSinkState> PhysicalIEJoin::GetLocalSinkState(ExecutionContext &context) const {
57842
+ idx_t sink_child = 0;
57843
+ if (sink_state) {
57844
+ const auto &ie_sink = (IEJoinGlobalState &)*sink_state;
57845
+ sink_child = ie_sink.child;
57846
+ }
57847
+ return make_unique<IEJoinLocalState>(conditions, sink_child);
57848
+ }
57849
+
57850
+ SinkResultType PhysicalIEJoin::Sink(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p,
57851
+ DataChunk &input) const {
57852
+ auto &gstate = (IEJoinGlobalState &)gstate_p;
57853
+ auto &lstate = (IEJoinLocalState &)lstate_p;
57854
+
57855
+ gstate.Sink(input, lstate);
57856
+
57857
+ return SinkResultType::NEED_MORE_INPUT;
57858
+ }
57859
+
57860
+ void PhysicalIEJoin::Combine(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p) const {
57861
+ auto &gstate = (IEJoinGlobalState &)gstate_p;
57862
+ auto &lstate = (IEJoinLocalState &)lstate_p;
57863
+ gstate.tables[gstate.child]->Combine(lstate);
57864
+ auto &client_profiler = QueryProfiler::Get(context.client);
57865
+
57866
+ context.thread.profiler.Flush(this, &lstate.executor, gstate.child ? "rhs_executor" : "lhs_executor", 1);
57867
+ client_profiler.Flush(context.thread.profiler);
57868
+ }
57869
+
57870
+ //===--------------------------------------------------------------------===//
57871
+ // Finalize
57872
+ //===--------------------------------------------------------------------===//
57873
+ class IEJoinFinalizeTask : public ExecutorTask {
57874
+ public:
57875
+ IEJoinFinalizeTask(shared_ptr<Event> event_p, ClientContext &context, IEJoinSortedTable &table)
57876
+ : ExecutorTask(context), event(move(event_p)), context(context), table(table) {
57877
+ }
57878
+
57879
+ TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override {
57880
+ // Initialize iejoin sorted and iterate until done
57881
+ auto &global_sort_state = table.global_sort_state;
57882
+ MergeSorter merge_sorter(global_sort_state, BufferManager::GetBufferManager(context));
57883
+ merge_sorter.PerformInMergeRound();
57884
+ event->FinishTask();
57885
+
57886
+ return TaskExecutionResult::TASK_FINISHED;
57887
+ }
57888
+
57889
+ private:
57890
+ shared_ptr<Event> event;
57891
+ ClientContext &context;
57892
+ IEJoinSortedTable &table;
57893
+ };
57894
+
57895
+ class IEJoinFinalizeEvent : public Event {
57896
+ public:
57897
+ IEJoinFinalizeEvent(IEJoinSortedTable &table_p, Pipeline &pipeline_p)
57898
+ : Event(pipeline_p.executor), table(table_p), pipeline(pipeline_p) {
57899
+ }
57900
+
57901
+ IEJoinSortedTable &table;
57902
+ Pipeline &pipeline;
57903
+
57904
+ public:
57905
+ void Schedule() override {
57906
+ auto &context = pipeline.GetClientContext();
57907
+
57908
+ // Schedule tasks equal to the number of threads, which will each iejoin multiple partitions
57909
+ auto &ts = TaskScheduler::GetScheduler(context);
57910
+ idx_t num_threads = ts.NumberOfThreads();
57911
+
57912
+ vector<unique_ptr<Task>> iejoin_tasks;
57913
+ for (idx_t tnum = 0; tnum < num_threads; tnum++) {
57914
+ iejoin_tasks.push_back(make_unique<IEJoinFinalizeTask>(shared_from_this(), context, table));
57915
+ }
57916
+ SetTasks(move(iejoin_tasks));
57917
+ }
57918
+
57919
+ void FinishEvent() override {
57920
+ auto &global_sort_state = table.global_sort_state;
57921
+
57922
+ global_sort_state.CompleteMergeRound(true);
57923
+ if (global_sort_state.sorted_blocks.size() > 1) {
57924
+ // Multiple blocks remaining: Schedule the next round
57925
+ PhysicalIEJoin::ScheduleMergeTasks(pipeline, *this, table);
57926
+ }
57927
+ }
57928
+ };
57929
+
57930
+ void PhysicalIEJoin::ScheduleMergeTasks(Pipeline &pipeline, Event &event, IEJoinSortedTable &table) {
57931
+ // Initialize global sort state for a round of merging
57932
+ table.global_sort_state.InitializeMergeRound();
57933
+ auto new_event = make_shared<IEJoinFinalizeEvent>(table, pipeline);
57934
+ event.InsertEvent(move(new_event));
57935
+ }
57936
+
57937
+ SinkFinalizeType PhysicalIEJoin::Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
57938
+ GlobalSinkState &gstate_p) const {
57939
+ auto &gstate = (IEJoinGlobalState &)gstate_p;
57940
+ auto &table = *gstate.tables[gstate.child];
57941
+ auto &global_sort_state = table.global_sort_state;
57942
+
57943
+ if ((gstate.child == 1 && IsRightOuterJoin(join_type)) || (gstate.child == 0 && IsLeftOuterJoin(join_type))) {
57944
+ // for FULL/LEFT/RIGHT OUTER JOIN, initialize found_match to false for every tuple
57945
+ table.IntializeMatches();
57946
+ }
57947
+ if (gstate.child == 1 && global_sort_state.sorted_blocks.empty() && EmptyResultIfRHSIsEmpty()) {
57948
+ // Empty input!
57949
+ return SinkFinalizeType::NO_OUTPUT_POSSIBLE;
57950
+ }
57951
+
57952
+ // Prepare for child sort phase
57953
+ global_sort_state.PrepareMergePhase();
57954
+
57955
+ // Start the iejoin phase or finish if a iejoin is not necessary
57956
+ if (global_sort_state.sorted_blocks.size() > 1) {
57957
+ PhysicalIEJoin::ScheduleMergeTasks(pipeline, event, table);
57958
+ }
57959
+
57960
+ ++gstate.child;
57961
+
57962
+ return SinkFinalizeType::READY;
57963
+ }
57964
+
57965
+ //===--------------------------------------------------------------------===//
57966
+ // Operator
57967
+ //===--------------------------------------------------------------------===//
57968
+ struct SBIterator {
57969
+ static int ComparisonValue(ExpressionType comparison) {
57970
+ switch (comparison) {
57971
+ case ExpressionType::COMPARE_LESSTHAN:
57972
+ case ExpressionType::COMPARE_GREATERTHAN:
57973
+ return -1;
57974
+ case ExpressionType::COMPARE_LESSTHANOREQUALTO:
57975
+ case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
57976
+ return 0;
57977
+ default:
57978
+ throw InternalException("Unimplemented comparison type for IEJoin!");
57979
+ }
57980
+ }
57981
+
57982
+ explicit SBIterator(GlobalSortState &gss, ExpressionType comparison, idx_t entry_idx_p = 0)
57983
+ : sort_layout(gss.sort_layout), block_count(gss.sorted_blocks[0]->radix_sorting_data.size()),
57984
+ block_capacity(gss.block_capacity), cmp_size(sort_layout.comparison_size), entry_size(sort_layout.entry_size),
57985
+ all_constant(sort_layout.all_constant), external(gss.external), cmp(ComparisonValue(comparison)),
57986
+ scan(gss.buffer_manager, gss), block_ptr(nullptr), entry_ptr(nullptr) {
57987
+
57988
+ scan.sb = gss.sorted_blocks[0].get();
57989
+ scan.block_idx = block_count;
57990
+ SetIndex(entry_idx_p);
57991
+ }
57992
+
57993
+ inline idx_t GetIndex() const {
57994
+ return entry_idx;
57995
+ }
57996
+
57997
+ inline void SetIndex(idx_t entry_idx_p) {
57998
+ const auto new_block_idx = entry_idx_p / block_capacity;
57999
+ if (new_block_idx != scan.block_idx) {
58000
+ scan.SetIndices(new_block_idx, 0);
58001
+ if (new_block_idx < block_count) {
58002
+ scan.PinRadix(scan.block_idx);
58003
+ block_ptr = scan.RadixPtr();
58004
+ if (!all_constant) {
58005
+ scan.PinData(*scan.sb->blob_sorting_data);
58006
+ }
58007
+ }
58008
+ }
58009
+
58010
+ scan.entry_idx = entry_idx_p % block_capacity;
58011
+ entry_ptr = block_ptr + scan.entry_idx * entry_size;
58012
+ entry_idx = entry_idx_p;
58013
+ }
58014
+
58015
+ inline SBIterator &operator++() {
58016
+ if (++scan.entry_idx < block_capacity) {
58017
+ entry_ptr += entry_size;
58018
+ ++entry_idx;
58019
+ } else {
58020
+ SetIndex(entry_idx + 1);
58021
+ }
58022
+
58023
+ return *this;
58024
+ }
58025
+
58026
+ inline SBIterator &operator--() {
58027
+ if (scan.entry_idx) {
58028
+ --scan.entry_idx;
58029
+ --entry_idx;
58030
+ entry_ptr -= entry_size;
58031
+ } else {
58032
+ SetIndex(entry_idx - 1);
58033
+ }
58034
+
58035
+ return *this;
58036
+ }
58037
+
58038
+ inline bool Compare(const SBIterator &other) const {
58039
+ int comp_res;
58040
+ if (all_constant) {
58041
+ comp_res = FastMemcmp(entry_ptr, other.entry_ptr, cmp_size);
58042
+ } else {
58043
+ comp_res = Comparators::CompareTuple(scan, other.scan, entry_ptr, other.entry_ptr, sort_layout, external);
58044
+ }
58045
+
58046
+ return comp_res <= cmp;
58047
+ }
58048
+
58049
+ // Fixed comparison parameters
58050
+ const SortLayout &sort_layout;
58051
+ const idx_t block_count;
58052
+ const idx_t block_capacity;
58053
+ const size_t cmp_size;
58054
+ const size_t entry_size;
58055
+ const bool all_constant;
58056
+ const bool external;
58057
+ const int cmp;
58058
+
58059
+ // Iteration state
58060
+ SBScanState scan;
58061
+ idx_t entry_idx;
58062
+ data_ptr_t block_ptr;
58063
+ data_ptr_t entry_ptr;
58064
+ };
58065
+
58066
+ struct IEJoinUnion {
58067
+ using SortedTable = IEJoinSortedTable;
58068
+
58069
+ static idx_t AppendKey(SortedTable &table, ExpressionExecutor &executor, SortedTable &marked, int64_t increment,
58070
+ int64_t base, const idx_t block_idx);
58071
+
58072
+ static void Sort(SortedTable &table) {
58073
+ auto &global_sort_state = table.global_sort_state;
58074
+ global_sort_state.PrepareMergePhase();
58075
+ while (global_sort_state.sorted_blocks.size() > 1) {
58076
+ global_sort_state.InitializeMergeRound();
58077
+ MergeSorter merge_sorter(global_sort_state, global_sort_state.buffer_manager);
58078
+ merge_sorter.PerformInMergeRound();
58079
+ global_sort_state.CompleteMergeRound(true);
58080
+ }
58081
+ }
58082
+
58083
+ template <typename T>
58084
+ static vector<T> ExtractColumn(SortedTable &table, idx_t col_idx) {
58085
+ vector<T> result;
58086
+ result.reserve(table.count);
58087
+
58088
+ auto &gstate = table.global_sort_state;
58089
+ auto &blocks = *gstate.sorted_blocks[0]->payload_data;
58090
+ PayloadScanner scanner(blocks, gstate, false);
58091
+
58092
+ DataChunk payload;
58093
+ payload.Initialize(gstate.payload_layout.GetTypes());
58094
+ for (;;) {
58095
+ scanner.Scan(payload);
58096
+ const auto count = payload.size();
58097
+ if (!count) {
58098
+ break;
58099
+ }
58100
+
58101
+ const auto data_ptr = FlatVector::GetData<T>(payload.data[col_idx]);
58102
+ result.insert(result.end(), data_ptr, data_ptr + count);
58103
+ }
58104
+
58105
+ return result;
58106
+ }
58107
+
58108
+ IEJoinUnion(ClientContext &context, const PhysicalIEJoin &op, SortedTable &t1, const idx_t b1, SortedTable &t2,
58109
+ const idx_t b2);
58110
+
58111
+ bool NextRow();
58112
+
58113
+ //! Inverted loop
58114
+ idx_t JoinComplexBlocks(SelectionVector &lsel, SelectionVector &rsel);
58115
+
58116
+ //! L1
58117
+ unique_ptr<SortedTable> l1;
58118
+ //! L2
58119
+ unique_ptr<SortedTable> l2;
58120
+
58121
+ //! Li
58122
+ vector<int64_t> li;
58123
+ //! P
58124
+ vector<idx_t> p;
58125
+
58126
+ //! B
58127
+ vector<validity_t> bit_array;
58128
+ ValidityMask bit_mask;
58129
+
58130
+ //! Bloom Filter
58131
+ static constexpr idx_t BLOOM_CHUNK_BITS = 1024;
58132
+ idx_t bloom_count;
58133
+ vector<validity_t> bloom_array;
58134
+ ValidityMask bloom_filter;
58135
+
58136
+ //! Iteration state
58137
+ idx_t n;
58138
+ idx_t i;
58139
+ unique_ptr<SBIterator> op1;
58140
+ unique_ptr<SBIterator> off1;
58141
+ unique_ptr<SBIterator> op2;
58142
+ unique_ptr<SBIterator> off2;
58143
+ int64_t lrid;
58144
+ };
58145
+
58146
+ idx_t IEJoinUnion::AppendKey(SortedTable &table, ExpressionExecutor &executor, SortedTable &marked, int64_t increment,
58147
+ int64_t base, const idx_t block_idx) {
58148
+ LocalSortState local_sort_state;
58149
+ local_sort_state.Initialize(marked.global_sort_state, marked.global_sort_state.buffer_manager);
58150
+
58151
+ // Reading
58152
+ const auto valid = table.count - table.has_null;
58153
+ auto &gstate = table.global_sort_state;
58154
+ PayloadScanner scanner(gstate, block_idx);
58155
+ auto table_idx = block_idx * gstate.block_capacity;
58156
+
58157
+ DataChunk scanned;
58158
+ scanned.Initialize(scanner.GetPayloadTypes());
58159
+
58160
+ // Writing
58161
+ auto types = local_sort_state.sort_layout->logical_types;
58162
+ const idx_t payload_idx = types.size();
58163
+
58164
+ const auto &payload_types = local_sort_state.payload_layout->GetTypes();
58165
+ types.insert(types.end(), payload_types.begin(), payload_types.end());
58166
+ const idx_t rid_idx = types.size() - 1;
58167
+
58168
+ DataChunk keys;
58169
+ DataChunk payload;
58170
+ keys.Initialize(types);
58171
+
58172
+ idx_t inserted = 0;
58173
+ for (auto rid = base; table_idx < valid;) {
58174
+ scanner.Scan(scanned);
58175
+
58176
+ // NULLs are at the end, so stop when we reach them
58177
+ auto scan_count = scanned.size();
58178
+ if (table_idx + scan_count > valid) {
58179
+ scan_count = valid - table_idx;
58180
+ scanned.SetCardinality(scan_count);
58181
+ }
58182
+ if (scan_count == 0) {
58183
+ break;
58184
+ }
58185
+ table_idx += scan_count;
58186
+
58187
+ // Compute the input columns from the payload
58188
+ keys.Reset();
58189
+ keys.Split(payload, rid_idx);
58190
+ executor.Execute(scanned, keys);
58191
+
58192
+ // Mark the rid column
58193
+ payload.data[0].Sequence(rid, increment);
58194
+ payload.SetCardinality(scan_count);
58195
+ keys.Fuse(payload);
58196
+ rid += increment * scan_count;
58197
+
58198
+ // Sort on the sort columns (which will no longer be needed)
58199
+ keys.Split(payload, payload_idx);
58200
+ local_sort_state.SinkChunk(keys, payload);
58201
+ inserted += scan_count;
58202
+ keys.Fuse(payload);
58203
+
58204
+ // Flush when we have enough data
58205
+ if (local_sort_state.SizeInBytes() >= marked.memory_per_thread) {
58206
+ local_sort_state.Sort(marked.global_sort_state, true);
58207
+ }
58208
+ }
58209
+ marked.global_sort_state.AddLocalState(local_sort_state);
58210
+ marked.count += inserted;
58211
+
58212
+ return inserted;
58213
+ }
58214
+
58215
+ IEJoinUnion::IEJoinUnion(ClientContext &context, const PhysicalIEJoin &op, SortedTable &t1, const idx_t b1,
58216
+ SortedTable &t2, const idx_t b2)
58217
+ : n(0), i(0) {
58218
+ // input : query Q with 2 join predicates t1.X op1 t2.X' and t1.Y op2 t2.Y', tables T, T' of sizes m and n resp.
58219
+ // output: a list of tuple pairs (ti , tj)
58220
+ // Note that T/T' are already sorted on X/X' and contain the payload data
58221
+ // We only join the two block numbers and use the sizes of the blocks as the counts
58222
+
58223
+ // 0. Filter out tables with no overlap
58224
+ if (!t1.BlockSize(b1) || !t2.BlockSize(b2)) {
58225
+ return;
58226
+ }
58227
+
58228
+ const auto &cmp1 = op.conditions[0].comparison;
58229
+ SBIterator bounds1(t1.global_sort_state, cmp1);
58230
+ SBIterator bounds2(t2.global_sort_state, cmp1);
58231
+
58232
+ // t1.X[0] op1 t2.X'[-1]
58233
+ bounds1.SetIndex(bounds1.block_capacity * b1);
58234
+ bounds2.SetIndex(bounds2.block_capacity * b2 + t2.BlockSize(b2) - 1);
58235
+ if (!bounds1.Compare(bounds2)) {
58236
+ return;
58237
+ }
58238
+
58239
+ // 1. let L1 (resp. L2) be the array of column X (resp. Y )
58240
+ const auto &order1 = op.lhs_orders[0][0];
58241
+ const auto &order2 = op.lhs_orders[1][0];
58242
+
58243
+ // 2. if (op1 ∈ {>, ≥}) sort L1 in descending order
58244
+ // 3. else if (op1 ∈ {<, ≤}) sort L1 in ascending order
58245
+
58246
+ // For the union algorithm, we make a unified table with the keys and the rids as the payload:
58247
+ // X/X', Y/Y', R/R'/Li
58248
+ // The first position is the sort key.
58249
+ vector<LogicalType> types;
58250
+ types.emplace_back(order2.expression->return_type);
58251
+ types.emplace_back(LogicalType::BIGINT);
58252
+ RowLayout payload_layout;
58253
+ payload_layout.Initialize(types);
58254
+
58255
+ // Sort on the first expression
58256
+ auto ref = make_unique<BoundReferenceExpression>(order1.expression->return_type, 0);
58257
+ vector<BoundOrderByNode> orders;
58258
+ orders.emplace_back(BoundOrderByNode(order1.type, order1.null_order, move(ref)));
58259
+
58260
+ l1 = make_unique<SortedTable>(context, orders, payload_layout);
58261
+
58262
+ // LHS has positive rids
58263
+ ExpressionExecutor l_executor;
58264
+ l_executor.AddExpression(*order1.expression);
58265
+ l_executor.AddExpression(*order2.expression);
58266
+ AppendKey(t1, l_executor, *l1, 1, 1, b1);
58267
+
58268
+ // RHS has negative rids
58269
+ ExpressionExecutor r_executor;
58270
+ r_executor.AddExpression(*op.rhs_orders[0][0].expression);
58271
+ r_executor.AddExpression(*op.rhs_orders[1][0].expression);
58272
+ AppendKey(t2, r_executor, *l1, -1, -1, b2);
58273
+
58274
+ Sort(*l1);
58275
+
58276
+ op1 = make_unique<SBIterator>(l1->global_sort_state, cmp1);
58277
+ off1 = make_unique<SBIterator>(l1->global_sort_state, cmp1);
58278
+
58279
+ // We don't actually need the L1 column, just its sort key, which is in the sort blocks
58280
+ li = ExtractColumn<int64_t>(*l1, types.size() - 1);
58281
+
58282
+ // 4. if (op2 ∈ {>, ≥}) sort L2 in ascending order
58283
+ // 5. else if (op2 ∈ {<, ≤}) sort L2 in descending order
58284
+
58285
+ // We sort on Y/Y' to obtain the sort keys and the permutation array.
58286
+ // For this we just need a two-column table of Y, P
58287
+ types.clear();
58288
+ types.emplace_back(LogicalType::BIGINT);
58289
+ payload_layout.Initialize(types);
58290
+
58291
+ // Sort on the first expression
58292
+ orders.clear();
58293
+ ref = make_unique<BoundReferenceExpression>(order2.expression->return_type, 0);
58294
+ orders.emplace_back(BoundOrderByNode(order2.type, order2.null_order, move(ref)));
58295
+
58296
+ ExpressionExecutor executor;
58297
+ executor.AddExpression(*orders[0].expression);
58298
+
58299
+ l2 = make_unique<SortedTable>(context, orders, payload_layout);
58300
+ for (idx_t base = 0, block_idx = 0; block_idx < l1->BlockCount(); ++block_idx) {
58301
+ base += AppendKey(*l1, executor, *l2, 1, base, block_idx);
58302
+ }
58303
+
58304
+ Sort(*l2);
58305
+
58306
+ // We don't actually need the L2 column, just its sort key, which is in the sort blocks
58307
+
58308
+ // 6. compute the permutation array P of L2 w.r.t. L1
58309
+ p = ExtractColumn<idx_t>(*l2, types.size() - 1);
58310
+
58311
+ // 7. initialize bit-array B (|B| = n), and set all bits to 0
58312
+ n = l2->count.load();
58313
+ bit_array.resize(ValidityMask::EntryCount(n), 0);
58314
+ bit_mask.Initialize(bit_array.data());
58315
+
58316
+ // Bloom filter
58317
+ bloom_count = (n + (BLOOM_CHUNK_BITS - 1)) / BLOOM_CHUNK_BITS;
58318
+ bloom_array.resize(ValidityMask::EntryCount(bloom_count), 0);
58319
+ bloom_filter.Initialize(bloom_array.data());
58320
+
58321
+ // 11. for(i←1 to n) do
58322
+ const auto &cmp2 = op.conditions[1].comparison;
58323
+ op2 = make_unique<SBIterator>(l2->global_sort_state, cmp2);
58324
+ off2 = make_unique<SBIterator>(l2->global_sort_state, cmp2);
58325
+ i = 0;
58326
+ (void)NextRow();
58327
+ }
58328
+
58329
+ bool IEJoinUnion::NextRow() {
58330
+ for (; i < n; ++i) {
58331
+ // 12. pos ← P[i]
58332
+ auto pos = p[i];
58333
+ lrid = li[pos];
58334
+ if (lrid < 0) {
58335
+ continue;
58336
+ }
58337
+
58338
+ // 16. B[pos] ← 1
58339
+ op2->SetIndex(i);
58340
+ for (; off2->GetIndex() < n; ++(*off2)) {
58341
+ if (!off2->Compare(*op2)) {
58342
+ break;
58343
+ }
58344
+ const auto p2 = p[off2->GetIndex()];
58345
+ bit_mask.SetValid(p2);
58346
+ bloom_filter.SetValid(p2 / BLOOM_CHUNK_BITS);
58347
+ }
58348
+
58349
+ // 9. if (op1 ∈ {≤,≥} and op2 ∈ {≤,≥}) eqOff = 0
58350
+ // 10. else eqOff = 1
58351
+ // No, because there could be more than one equal value.
58352
+ // Scan the neighborhood instead
58353
+ op1->SetIndex(pos);
58354
+ off1->SetIndex(pos);
58355
+ for (; off1->GetIndex() > 0 && op1->Compare(*off1); --(*off1)) {
58356
+ continue;
58357
+ }
58358
+ for (; off1->GetIndex() < n && !op1->Compare(*off1); ++(*off1)) {
58359
+ continue;
58360
+ }
58361
+
58362
+ return true;
58363
+ }
58364
+ return false;
58365
+ }
58366
+
58367
+ static idx_t NextValid(const ValidityMask &bits, idx_t j, const idx_t n) {
58368
+ if (j >= n) {
58369
+ return n;
58370
+ }
58371
+
58372
+ // We can do a first approximation by checking entries one at a time
58373
+ // which gives 64:1.
58374
+ idx_t entry_idx, idx_in_entry;
58375
+ bits.GetEntryIndex(j, entry_idx, idx_in_entry);
58376
+ auto entry = bits.GetValidityEntry(entry_idx++);
58377
+
58378
+ // Trim the bits before the start position
58379
+ entry &= (ValidityMask::ValidityBuffer::MAX_ENTRY << idx_in_entry);
58380
+
58381
+ // Check the non-ragged entries
58382
+ for (const auto entry_count = bits.EntryCount(n); entry_idx < entry_count; ++entry_idx) {
58383
+ if (entry) {
58384
+ for (; idx_in_entry < bits.BITS_PER_VALUE; ++idx_in_entry, ++j) {
58385
+ if (bits.RowIsValid(entry, idx_in_entry)) {
58386
+ return j;
58387
+ }
58388
+ }
58389
+ } else {
58390
+ j += bits.BITS_PER_VALUE - idx_in_entry;
58391
+ }
58392
+
58393
+ entry = bits.GetValidityEntry(entry_idx);
58394
+ idx_in_entry = 0;
58395
+ }
58396
+
58397
+ // Check the final entry
58398
+ for (; j < n; ++idx_in_entry, ++j) {
58399
+ if (bits.RowIsValid(entry, idx_in_entry)) {
58400
+ return j;
58401
+ }
58402
+ }
58403
+
58404
+ return j;
58405
+ }
58406
+
58407
+ idx_t IEJoinUnion::JoinComplexBlocks(SelectionVector &lsel, SelectionVector &rsel) {
58408
+ // 8. initialize join result as an empty list for tuple pairs
58409
+ idx_t result_count = 0;
58410
+
58411
+ // 11. for(i←1 to n) do
58412
+ while (i < n) {
58413
+ // 13. for (j ← pos+eqOff to n) do
58414
+ for (;;) {
58415
+ // 14. if B[j] = 1 then
58416
+ auto j = off1->GetIndex();
58417
+
58418
+ // Use the Bloom filter to find candidate blocks
58419
+ while (j < n) {
58420
+ auto bloom_begin = NextValid(bloom_filter, j / BLOOM_CHUNK_BITS, bloom_count) * BLOOM_CHUNK_BITS;
58421
+ auto bloom_end = MinValue<idx_t>(n, bloom_begin + BLOOM_CHUNK_BITS);
58422
+
58423
+ j = MaxValue<idx_t>(j, bloom_begin);
58424
+ j = NextValid(bit_mask, j, bloom_end);
58425
+ if (j < bloom_end) {
58426
+ break;
58427
+ }
58428
+ }
58429
+
58430
+ if (j >= n) {
58431
+ break;
58432
+ }
58433
+ off1->SetIndex(j + 1);
58434
+
58435
+ // Filter out tuples with the same sign (they come from the same table)
58436
+ const auto rrid = li[j];
58437
+
58438
+ // 15. add tuples w.r.t. (L1[j], L1[i]) to join result
58439
+ if (lrid > 0 && rrid < 0) {
58440
+ lsel.set_index(result_count, sel_t(+lrid - 1));
58441
+ rsel.set_index(result_count, sel_t(-rrid - 1));
58442
+ ++result_count;
58443
+ if (result_count == STANDARD_VECTOR_SIZE) {
58444
+ // out of space!
58445
+ return result_count;
58446
+ }
58447
+ }
58448
+ }
58449
+ ++i;
58450
+
58451
+ if (!NextRow()) {
58452
+ break;
58453
+ }
58454
+ }
58455
+
58456
+ return result_count;
58457
+ }
58458
+
58459
+ class IEJoinState : public OperatorState {
58460
+ public:
58461
+ explicit IEJoinState(const PhysicalIEJoin &op) : local_left(op.conditions, 0) {};
58462
+
58463
+ IEJoinLocalState local_left;
58464
+ };
58465
+
58466
+ static void SliceSortedPayload(DataChunk &payload, GlobalSortState &state, const idx_t block_idx,
58467
+ const SelectionVector &result, const idx_t result_count, const idx_t left_cols = 0) {
58468
+ // There should only be one sorted block if they have been sorted
58469
+ D_ASSERT(state.sorted_blocks.size() == 1);
58470
+ SBScanState read_state(state.buffer_manager, state);
58471
+ read_state.sb = state.sorted_blocks[0].get();
58472
+ auto &sorted_data = *read_state.sb->payload_data;
58473
+
58474
+ read_state.SetIndices(block_idx, 0);
58475
+ read_state.PinData(sorted_data);
58476
+ const auto data_ptr = read_state.DataPtr(sorted_data);
58477
+
58478
+ // Set up a batch of pointers to scan data from
58479
+ Vector addresses(LogicalType::POINTER, result_count);
58480
+ auto data_pointers = FlatVector::GetData<data_ptr_t>(addresses);
58481
+
58482
+ // Set up the data pointers for the values that are actually referenced
58483
+ const idx_t &row_width = sorted_data.layout.GetRowWidth();
58484
+
58485
+ auto prev_idx = result.get_index(0);
58486
+ SelectionVector gsel(result_count);
58487
+ idx_t addr_count = 0;
58488
+ gsel.set_index(0, addr_count);
58489
+ data_pointers[addr_count] = data_ptr + prev_idx * row_width;
58490
+ for (idx_t i = 1; i < result_count; ++i) {
58491
+ const auto row_idx = result.get_index(i);
58492
+ if (row_idx != prev_idx) {
58493
+ data_pointers[++addr_count] = data_ptr + row_idx * row_width;
58494
+ prev_idx = row_idx;
58495
+ }
58496
+ gsel.set_index(i, addr_count);
58497
+ }
58498
+ ++addr_count;
58499
+
58500
+ // Unswizzle the offsets back to pointers (if needed)
58501
+ if (!sorted_data.layout.AllConstant() && state.external) {
58502
+ RowOperations::UnswizzlePointers(sorted_data.layout, data_ptr, read_state.payload_heap_handle->Ptr(),
58503
+ addr_count);
58504
+ }
58505
+
58506
+ // Deserialize the payload data
58507
+ auto sel = FlatVector::IncrementalSelectionVector();
58508
+ for (idx_t col_idx = 0; col_idx < sorted_data.layout.ColumnCount(); col_idx++) {
58509
+ const auto col_offset = sorted_data.layout.GetOffsets()[col_idx];
58510
+ auto &col = payload.data[left_cols + col_idx];
58511
+ RowOperations::Gather(addresses, *sel, col, *sel, addr_count, col_offset, col_idx);
58512
+ col.Slice(gsel, result_count);
58513
+ }
58514
+ }
58515
+
58516
+ class IEJoinLocalSourceState : public LocalSourceState {
58517
+ public:
58518
+ explicit IEJoinLocalSourceState(const PhysicalIEJoin &op)
58519
+ : op(op), true_sel(STANDARD_VECTOR_SIZE), left_matches(nullptr), right_matches(nullptr) {
58520
+
58521
+ if (op.conditions.size() < 3) {
58522
+ return;
58523
+ }
58524
+
58525
+ vector<LogicalType> left_types;
58526
+ vector<LogicalType> right_types;
58527
+ for (idx_t i = 2; i < op.conditions.size(); ++i) {
58528
+ const auto &cond = op.conditions[i];
58529
+
58530
+ left_types.push_back(cond.left->return_type);
58531
+ left_executor.AddExpression(*cond.left);
58532
+
58533
+ right_types.push_back(cond.left->return_type);
58534
+ right_executor.AddExpression(*cond.right);
58535
+ }
58536
+
58537
+ left_keys.Initialize(left_types);
58538
+ right_keys.Initialize(right_types);
58539
+ }
58540
+
58541
+ idx_t SelectJoinTail(const ExpressionType &condition, Vector &left, Vector &right, const SelectionVector *sel,
58542
+ idx_t count);
58543
+
58544
+ idx_t SelectOuterRows(bool *matches) {
58545
+ idx_t count = 0;
58546
+ for (; outer_idx < outer_count; ++outer_idx) {
58547
+ if (!matches[outer_idx]) {
58548
+ true_sel.set_index(count++, outer_idx);
58549
+ if (count >= STANDARD_VECTOR_SIZE) {
58550
+ break;
58551
+ }
58552
+ }
58553
+ }
58554
+
58555
+ return count;
58556
+ }
58557
+
58558
+ const PhysicalIEJoin &op;
58559
+
58560
+ // Joining
58561
+ unique_ptr<IEJoinUnion> joiner;
58562
+
58563
+ idx_t left_base;
58564
+ idx_t left_block_index;
58565
+
58566
+ idx_t right_base;
58567
+ idx_t right_block_index;
58568
+
58569
+ // Trailing predicates
58570
+ SelectionVector true_sel;
58571
+
58572
+ ExpressionExecutor left_executor;
58573
+ DataChunk left_keys;
58574
+
58575
+ ExpressionExecutor right_executor;
58576
+ DataChunk right_keys;
58577
+
58578
+ // Outer joins
58579
+ idx_t outer_idx;
58580
+ idx_t outer_count;
58581
+ bool *left_matches;
58582
+ bool *right_matches;
58583
+ };
58584
+
58585
+ idx_t IEJoinLocalSourceState::SelectJoinTail(const ExpressionType &condition, Vector &left, Vector &right,
58586
+ const SelectionVector *sel, idx_t count) {
58587
+ switch (condition) {
58588
+ case ExpressionType::COMPARE_NOTEQUAL:
58589
+ return VectorOperations::NotEquals(left, right, sel, count, &true_sel, nullptr);
58590
+ case ExpressionType::COMPARE_LESSTHAN:
58591
+ return VectorOperations::LessThan(left, right, sel, count, &true_sel, nullptr);
58592
+ case ExpressionType::COMPARE_GREATERTHAN:
58593
+ return VectorOperations::GreaterThan(left, right, sel, count, &true_sel, nullptr);
58594
+ case ExpressionType::COMPARE_LESSTHANOREQUALTO:
58595
+ return VectorOperations::LessThanEquals(left, right, sel, count, &true_sel, nullptr);
58596
+ case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
58597
+ return VectorOperations::GreaterThanEquals(left, right, sel, count, &true_sel, nullptr);
58598
+ case ExpressionType::COMPARE_DISTINCT_FROM:
58599
+ return VectorOperations::DistinctFrom(left, right, sel, count, &true_sel, nullptr);
58600
+ case ExpressionType::COMPARE_NOT_DISTINCT_FROM:
58601
+ case ExpressionType::COMPARE_EQUAL:
58602
+ default:
58603
+ throw InternalException("Unsupported comparison type for PhysicalIEJoin");
58604
+ }
58605
+
58606
+ return count;
58607
+ }
58608
+
58609
+ void PhysicalIEJoin::ResolveComplexJoin(ExecutionContext &context, DataChunk &chunk, LocalSourceState &state_p) const {
58610
+ auto &state = (IEJoinLocalSourceState &)state_p;
58611
+ auto &ie_sink = (IEJoinGlobalState &)*sink_state;
58612
+ auto &left_table = *ie_sink.tables[0];
58613
+ auto &right_table = *ie_sink.tables[1];
58614
+
58615
+ const auto left_cols = children[0]->GetTypes().size();
58616
+ do {
58617
+ SelectionVector lsel(STANDARD_VECTOR_SIZE);
58618
+ SelectionVector rsel(STANDARD_VECTOR_SIZE);
58619
+ auto result_count = state.joiner->JoinComplexBlocks(lsel, rsel);
58620
+ if (result_count == 0) {
58621
+ // exhausted this pair
58622
+ return;
58623
+ }
58624
+
58625
+ // found matches: extract them
58626
+ chunk.Reset();
58627
+ SliceSortedPayload(chunk, left_table.global_sort_state, state.left_block_index, lsel, result_count, 0);
58628
+ SliceSortedPayload(chunk, right_table.global_sort_state, state.right_block_index, rsel, result_count,
58629
+ left_cols);
58630
+ chunk.SetCardinality(result_count);
58631
+
58632
+ auto sel = FlatVector::IncrementalSelectionVector();
58633
+ if (conditions.size() > 2) {
58634
+ // If there are more expressions to compute,
58635
+ // split the result chunk into the left and right halves
58636
+ // so we can compute the values for comparison.
58637
+ const auto tail_cols = conditions.size() - 2;
58638
+
58639
+ DataChunk right_chunk;
58640
+ chunk.Split(right_chunk, left_cols);
58641
+ state.left_executor.SetChunk(chunk);
58642
+ state.right_executor.SetChunk(right_chunk);
58643
+
58644
+ auto tail_count = result_count;
58645
+ for (size_t cmp_idx = 0; cmp_idx < tail_cols; ++cmp_idx) {
58646
+ auto &left = state.left_keys.data[cmp_idx];
58647
+ state.left_executor.ExecuteExpression(cmp_idx, left);
58648
+
58649
+ auto &right = state.right_keys.data[cmp_idx];
58650
+ state.right_executor.ExecuteExpression(cmp_idx, right);
58651
+
58652
+ if (tail_count < result_count) {
58653
+ left.Slice(*sel, tail_count);
58654
+ right.Slice(*sel, tail_count);
58655
+ }
58656
+ tail_count = state.SelectJoinTail(conditions[cmp_idx + 2].comparison, left, right, sel, tail_count);
58657
+ sel = &state.true_sel;
58658
+ }
58659
+ chunk.Fuse(right_chunk);
58660
+
58661
+ if (tail_count < result_count) {
58662
+ result_count = tail_count;
58663
+ chunk.Slice(*sel, result_count);
58664
+ }
58665
+ }
58666
+
58667
+ // found matches: mark the found matches if required
58668
+ if (left_table.found_match) {
58669
+ for (idx_t i = 0; i < result_count; i++) {
58670
+ left_table.found_match[state.left_base + lsel[sel->get_index(i)]] = true;
58671
+ }
58672
+ }
58673
+ if (right_table.found_match) {
58674
+ for (idx_t i = 0; i < result_count; i++) {
58675
+ right_table.found_match[state.right_base + rsel[sel->get_index(i)]] = true;
58676
+ }
58677
+ }
58678
+ chunk.Verify();
58679
+ } while (chunk.size() == 0);
58680
+ }
58681
+
58682
+ OperatorResultType PhysicalIEJoin::Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk,
58683
+ OperatorState &state) const {
58684
+ return OperatorResultType::FINISHED;
58685
+ }
58686
+
58687
+ //===--------------------------------------------------------------------===//
58688
+ // Source
58689
+ //===--------------------------------------------------------------------===//
58690
+ class IEJoinGlobalSourceState : public GlobalSourceState {
58691
+ public:
58692
+ explicit IEJoinGlobalSourceState(const PhysicalIEJoin &op)
58693
+ : op(op), initialized(false), next_pair(0), completed(0), left_outers(0), next_left(0), right_outers(0),
58694
+ next_right(0) {
58695
+ }
58696
+
58697
+ void Initialize(IEJoinGlobalState &sink_state) {
58698
+ lock_guard<mutex> initializing(lock);
58699
+ if (initialized) {
58700
+ return;
58701
+ }
58702
+
58703
+ // Compute the starting row for reach block
58704
+ // (In theory these are all the same size, but you never know...)
58705
+ auto &left_table = *sink_state.tables[0];
58706
+ const auto left_blocks = left_table.BlockCount();
58707
+ idx_t left_base = 0;
58708
+
58709
+ for (size_t lhs = 0; lhs < left_blocks; ++lhs) {
58710
+ left_bases.emplace_back(left_base);
58711
+ left_base += left_table.BlockSize(lhs);
58712
+ }
58713
+
58714
+ auto &right_table = *sink_state.tables[1];
58715
+ const auto right_blocks = right_table.BlockCount();
58716
+ idx_t right_base = 0;
58717
+ for (size_t rhs = 0; rhs < right_blocks; ++rhs) {
58718
+ right_bases.emplace_back(right_base);
58719
+ right_base += right_table.BlockSize(rhs);
58720
+ }
58721
+
58722
+ // Outer join block counts
58723
+ if (left_table.found_match) {
58724
+ left_outers = left_blocks;
58725
+ }
58726
+
58727
+ if (right_table.found_match) {
58728
+ right_outers = right_blocks;
58729
+ }
58730
+
58731
+ // Ready for action
58732
+ initialized = true;
58733
+ }
58734
+
58735
+ public:
58736
+ idx_t MaxThreads() override {
58737
+ // We can't leverage any more threads than block pairs.
58738
+ const auto &sink_state = ((IEJoinGlobalState &)*op.sink_state);
58739
+ return sink_state.tables[0]->BlockCount() * sink_state.tables[1]->BlockCount();
58740
+ }
58741
+
58742
+ void GetNextPair(ClientContext &client, IEJoinGlobalState &gstate, IEJoinLocalSourceState &lstate) {
58743
+ auto &left_table = *gstate.tables[0];
58744
+ auto &right_table = *gstate.tables[1];
58745
+
58746
+ const auto left_blocks = left_table.BlockCount();
58747
+ const auto right_blocks = right_table.BlockCount();
58748
+ const auto pair_count = left_blocks * right_blocks;
58749
+
58750
+ // Regular block
58751
+ const auto i = next_pair++;
58752
+ if (i < pair_count) {
58753
+ const auto b1 = i / right_blocks;
58754
+ const auto b2 = i % right_blocks;
58755
+
58756
+ lstate.left_block_index = b1;
58757
+ lstate.left_base = left_bases[b1];
58758
+
58759
+ lstate.right_block_index = b2;
58760
+ lstate.right_base = right_bases[b2];
58761
+
58762
+ lstate.joiner = make_unique<IEJoinUnion>(client, op, left_table, b1, right_table, b2);
58763
+ return;
58764
+ } else {
58765
+ --next_pair;
58766
+ }
58767
+
58768
+ // Outer joins
58769
+ if (!left_outers && !right_outers) {
58770
+ return;
58771
+ }
58772
+
58773
+ // Spin wait for regular blocks to finish(!)
58774
+ while (completed < pair_count) {
58775
+ std::this_thread::yield();
58776
+ }
58777
+
58778
+ // Left outer blocks
58779
+ const auto l = next_left++;
58780
+ if (l < left_outers) {
58781
+ lstate.left_block_index = l;
58782
+ lstate.left_base = left_bases[l];
58783
+
58784
+ lstate.left_matches = left_table.found_match.get() + lstate.left_base;
58785
+ lstate.outer_idx = 0;
58786
+ lstate.outer_count = left_table.BlockSize(l);
58787
+ return;
58788
+ } else {
58789
+ lstate.left_matches = nullptr;
58790
+ --next_left;
58791
+ }
58792
+
58793
+ // Right outer block
58794
+ const auto r = next_right++;
58795
+ if (r < right_outers) {
58796
+ lstate.right_block_index = r;
58797
+ lstate.right_base = right_bases[r];
58798
+
58799
+ lstate.right_matches = right_table.found_match.get() + lstate.right_base;
58800
+ lstate.outer_idx = 0;
58801
+ lstate.outer_count = right_table.BlockSize(r);
58802
+ return;
58803
+ } else {
58804
+ lstate.right_matches = nullptr;
58805
+ --next_right;
58806
+ }
58807
+ }
58808
+
58809
+ void PairCompleted(ClientContext &client, IEJoinGlobalState &gstate, IEJoinLocalSourceState &lstate) {
58810
+ lstate.joiner.reset();
58811
+ ++completed;
58812
+ GetNextPair(client, gstate, lstate);
58813
+ }
58814
+
58815
+ const PhysicalIEJoin &op;
58816
+
58817
+ mutex lock;
58818
+ bool initialized;
58819
+
58820
+ // Join queue state
58821
+ std::atomic<size_t> next_pair;
58822
+ std::atomic<size_t> completed;
58823
+
58824
+ // Block base row number
58825
+ vector<idx_t> left_bases;
58826
+ vector<idx_t> right_bases;
58827
+
58828
+ // Outer joins
58829
+ idx_t left_outers;
58830
+ std::atomic<idx_t> next_left;
58831
+
58832
+ idx_t right_outers;
58833
+ std::atomic<idx_t> next_right;
58834
+ };
58835
+
58836
+ unique_ptr<GlobalSourceState> PhysicalIEJoin::GetGlobalSourceState(ClientContext &context) const {
58837
+ return make_unique<IEJoinGlobalSourceState>(*this);
58838
+ }
58839
+
58840
+ unique_ptr<LocalSourceState> PhysicalIEJoin::GetLocalSourceState(ExecutionContext &context,
58841
+ GlobalSourceState &gstate) const {
58842
+ return make_unique<IEJoinLocalSourceState>(*this);
58843
+ }
58844
+
58845
+ void PhysicalIEJoin::GetData(ExecutionContext &context, DataChunk &result, GlobalSourceState &gstate,
58846
+ LocalSourceState &lstate) const {
58847
+ auto &ie_sink = (IEJoinGlobalState &)*sink_state;
58848
+ auto &ie_gstate = (IEJoinGlobalSourceState &)gstate;
58849
+ auto &ie_lstate = (IEJoinLocalSourceState &)lstate;
58850
+
58851
+ ie_gstate.Initialize(ie_sink);
58852
+
58853
+ if (!ie_lstate.joiner) {
58854
+ ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
58855
+ }
58856
+
58857
+ // Process INNER results
58858
+ while (ie_lstate.joiner) {
58859
+ ResolveComplexJoin(context, result, ie_lstate);
58860
+
58861
+ if (result.size()) {
58862
+ return;
58863
+ }
58864
+
58865
+ ie_gstate.PairCompleted(context.client, ie_sink, ie_lstate);
58866
+ }
58867
+
58868
+ // Process LEFT OUTER results
58869
+ const auto left_cols = children[0]->GetTypes().size();
58870
+ while (ie_lstate.left_matches) {
58871
+ const idx_t count = ie_lstate.SelectOuterRows(ie_lstate.left_matches);
58872
+ if (!count) {
58873
+ ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
58874
+ continue;
58875
+ }
58876
+
58877
+ SliceSortedPayload(result, ie_sink.tables[0]->global_sort_state, ie_lstate.left_base, ie_lstate.true_sel,
58878
+ count);
58879
+
58880
+ // Fill in NULLs to the right
58881
+ for (auto col_idx = left_cols; col_idx < result.ColumnCount(); ++col_idx) {
58882
+ result.data[col_idx].SetVectorType(VectorType::CONSTANT_VECTOR);
58883
+ ConstantVector::SetNull(result.data[col_idx], true);
58884
+ }
58885
+
58886
+ result.SetCardinality(count);
58887
+ result.Verify();
58888
+
58889
+ return;
58890
+ }
58891
+
58892
+ // Process RIGHT OUTER results
58893
+ while (ie_lstate.right_matches) {
58894
+ const idx_t count = ie_lstate.SelectOuterRows(ie_lstate.right_matches);
58895
+ if (!count) {
58896
+ ie_gstate.GetNextPair(context.client, ie_sink, ie_lstate);
58897
+ }
58898
+
58899
+ SliceSortedPayload(result, ie_sink.tables[1]->global_sort_state, ie_lstate.right_base, ie_lstate.true_sel,
58900
+ count, left_cols);
58901
+
58902
+ // Fill in NULLs to the left
58903
+ for (idx_t col_idx = 0; col_idx < left_cols; ++col_idx) {
58904
+ result.data[col_idx].SetVectorType(VectorType::CONSTANT_VECTOR);
58905
+ ConstantVector::SetNull(result.data[col_idx], true);
58906
+ }
58907
+
58908
+ result.SetCardinality(count);
58909
+ result.Verify();
58910
+
58911
+ return;
58912
+ }
58913
+ }
58914
+
57377
58915
  } // namespace duckdb
57378
58916
  //===----------------------------------------------------------------------===//
57379
58917
  // DuckDB
@@ -58478,32 +60016,6 @@ unique_ptr<GlobalSinkState> PhysicalPiecewiseMergeJoin::GetGlobalSinkState(Clien
58478
60016
  return move(state);
58479
60017
  }
58480
60018
 
58481
- static idx_t CountValid(Vector &v, const idx_t count) {
58482
- idx_t valid = 0;
58483
-
58484
- VectorData vdata;
58485
- v.Orrify(count, vdata);
58486
- if (vdata.validity.AllValid()) {
58487
- return count;
58488
- }
58489
- switch (v.GetVectorType()) {
58490
- case VectorType::FLAT_VECTOR:
58491
- valid += vdata.validity.CountValid(count);
58492
- break;
58493
- case VectorType::CONSTANT_VECTOR:
58494
- valid += vdata.validity.CountValid(1) * count;
58495
- break;
58496
- default:
58497
- for (idx_t i = 0; i < count; ++i) {
58498
- const auto row_idx = vdata.sel->get_index(i);
58499
- valid += int(vdata.validity.RowIsValid(row_idx));
58500
- }
58501
- break;
58502
- }
58503
-
58504
- return valid;
58505
- }
58506
-
58507
60019
  class MergeJoinLocalState : public LocalSinkState {
58508
60020
  public:
58509
60021
  explicit MergeJoinLocalState() : rhs_has_null(0), rhs_count(0) {
@@ -58604,7 +60116,7 @@ static idx_t PiecewiseMergeNulls(DataChunk &keys, const vector<JoinCondition> &c
58604
60116
  }
58605
60117
  return count - pvalidity.CountValid(count);
58606
60118
  } else {
58607
- return count - CountValid(primary, count);
60119
+ return count - VectorOperations::CountNotNull(primary, count);
58608
60120
  }
58609
60121
  }
58610
60122
 
@@ -66022,6 +67534,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalChunkGet &
66022
67534
 
66023
67535
 
66024
67536
 
67537
+
66025
67538
  namespace duckdb {
66026
67539
 
66027
67540
  static bool CanPlanIndexJoin(Transaction &transaction, TableScanBindData *bind_data, PhysicalTableScan &scan) {
@@ -66180,8 +67693,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
66180
67693
 
66181
67694
  bool has_equality = false;
66182
67695
  // bool has_inequality = false;
66183
- bool has_range = false;
66184
- bool has_null_equal_conditions = false;
67696
+ size_t has_range = 0;
66185
67697
  for (size_t c = 0; c < op.conditions.size(); ++c) {
66186
67698
  auto &cond = op.conditions[c];
66187
67699
  switch (cond.comparison) {
@@ -66193,7 +67705,7 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
66193
67705
  case ExpressionType::COMPARE_GREATERTHAN:
66194
67706
  case ExpressionType::COMPARE_LESSTHANOREQUALTO:
66195
67707
  case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
66196
- has_range = true;
67708
+ ++has_range;
66197
67709
  break;
66198
67710
  case ExpressionType::COMPARE_NOTEQUAL:
66199
67711
  case ExpressionType::COMPARE_DISTINCT_FROM:
@@ -66203,12 +67715,10 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
66203
67715
  throw NotImplementedException("Unimplemented comparison join");
66204
67716
  }
66205
67717
  if (cond.null_values_are_equal) {
66206
- has_null_equal_conditions = true;
66207
67718
  D_ASSERT(cond.comparison == ExpressionType::COMPARE_EQUAL ||
66208
67719
  cond.comparison == ExpressionType::COMPARE_DISTINCT_FROM);
66209
67720
  }
66210
67721
  }
66211
- (void)has_null_equal_conditions;
66212
67722
 
66213
67723
  unique_ptr<PhysicalOperator> plan;
66214
67724
  if (has_equality) {
@@ -66237,17 +67747,22 @@ unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalComparison
66237
67747
  op.estimated_cardinality, perfect_join_stats);
66238
67748
 
66239
67749
  } else {
66240
- bool can_merge = has_range;
67750
+ bool can_merge = has_range > 0;
67751
+ bool can_iejoin = has_range >= 2 && rec_ctes.empty();
66241
67752
  switch (op.join_type) {
66242
67753
  case JoinType::SEMI:
66243
67754
  case JoinType::ANTI:
66244
67755
  case JoinType::MARK:
66245
67756
  can_merge = can_merge && op.conditions.size() == 1;
67757
+ can_iejoin = false;
66246
67758
  break;
66247
67759
  default:
66248
67760
  break;
66249
67761
  }
66250
- if (can_merge) {
67762
+ if (can_iejoin) {
67763
+ plan = make_unique<PhysicalIEJoin>(op, move(left), move(right), move(op.conditions), op.join_type,
67764
+ op.estimated_cardinality);
67765
+ } else if (can_merge) {
66251
67766
  // range join: use piecewise merge join
66252
67767
  plan = make_unique<PhysicalPiecewiseMergeJoin>(op, move(left), move(right), move(op.conditions),
66253
67768
  op.join_type, op.estimated_cardinality);
@@ -115711,6 +117226,7 @@ bool QueryProfiler::OperatorRequiresProfiling(PhysicalOperatorType op_type) {
115711
117226
  case PhysicalOperatorType::HASH_JOIN:
115712
117227
  case PhysicalOperatorType::CROSS_PRODUCT:
115713
117228
  case PhysicalOperatorType::PIECEWISE_MERGE_JOIN:
117229
+ case PhysicalOperatorType::IE_JOIN:
115714
117230
  case PhysicalOperatorType::DELIM_JOIN:
115715
117231
  case PhysicalOperatorType::UNION:
115716
117232
  case PhysicalOperatorType::RECURSIVE_CTE:
@@ -128568,6 +130084,7 @@ void Event::SetTasks(vector<unique_ptr<Task>> tasks) {
128568
130084
 
128569
130085
 
128570
130086
 
130087
+
128571
130088
  //===----------------------------------------------------------------------===//
128572
130089
  // DuckDB
128573
130090
  //
@@ -129243,6 +130760,35 @@ void Executor::BuildPipelines(PhysicalOperator *op, Pipeline *current) {
129243
130760
  }
129244
130761
  BuildPipelines(op->children[0].get(), current);
129245
130762
  break;
130763
+ case PhysicalOperatorType::IE_JOIN: {
130764
+ D_ASSERT(op->children.size() == 2);
130765
+ if (recursive_cte) {
130766
+ throw NotImplementedException("IEJoins are not supported in recursive CTEs yet");
130767
+ }
130768
+
130769
+ // Build the LHS
130770
+ auto lhs_pipeline = make_shared<Pipeline>(*this);
130771
+ lhs_pipeline->sink = op;
130772
+ D_ASSERT(op->children[0].get());
130773
+ BuildPipelines(op->children[0].get(), lhs_pipeline.get());
130774
+
130775
+ // Build the RHS
130776
+ auto rhs_pipeline = make_shared<Pipeline>(*this);
130777
+ rhs_pipeline->sink = op;
130778
+ D_ASSERT(op->children[1].get());
130779
+ BuildPipelines(op->children[1].get(), rhs_pipeline.get());
130780
+
130781
+ // RHS => LHS => current
130782
+ current->AddDependency(rhs_pipeline);
130783
+ rhs_pipeline->AddDependency(lhs_pipeline);
130784
+
130785
+ pipelines.emplace_back(move(lhs_pipeline));
130786
+ pipelines.emplace_back(move(rhs_pipeline));
130787
+
130788
+ // Now build both and scan
130789
+ current->source = op;
130790
+ return;
130791
+ }
129246
130792
  case PhysicalOperatorType::DELIM_JOIN: {
129247
130793
  // duplicate eliminated join
129248
130794
  // for delim joins, recurse into the actual join