duckdb 0.7.2-dev1901.0 → 0.7.2-dev2233.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.
Files changed (198) hide show
  1. package/binding.gyp +2 -0
  2. package/package.json +1 -1
  3. package/src/duckdb/extension/parquet/column_reader.cpp +3 -0
  4. package/src/duckdb/extension/parquet/include/parquet_writer.hpp +1 -1
  5. package/src/duckdb/extension/parquet/parquet_metadata.cpp +4 -2
  6. package/src/duckdb/src/catalog/catalog_entry/duck_index_entry.cpp +1 -1
  7. package/src/duckdb/src/common/arrow/arrow_appender.cpp +69 -44
  8. package/src/duckdb/src/common/arrow/arrow_converter.cpp +1 -1
  9. package/src/duckdb/src/common/arrow/arrow_wrapper.cpp +20 -2
  10. package/src/duckdb/src/common/box_renderer.cpp +4 -2
  11. package/src/duckdb/src/common/constants.cpp +10 -1
  12. package/src/duckdb/src/common/filename_pattern.cpp +41 -0
  13. package/src/duckdb/src/common/hive_partitioning.cpp +144 -15
  14. package/src/duckdb/src/common/radix_partitioning.cpp +101 -369
  15. package/src/duckdb/src/common/row_operations/row_aggregate.cpp +8 -9
  16. package/src/duckdb/src/common/row_operations/row_external.cpp +1 -1
  17. package/src/duckdb/src/common/row_operations/row_gather.cpp +5 -3
  18. package/src/duckdb/src/common/row_operations/row_match.cpp +117 -22
  19. package/src/duckdb/src/common/row_operations/row_scatter.cpp +2 -2
  20. package/src/duckdb/src/common/sort/partition_state.cpp +1 -1
  21. package/src/duckdb/src/common/sort/sort_state.cpp +2 -1
  22. package/src/duckdb/src/common/sort/sorted_block.cpp +1 -1
  23. package/src/duckdb/src/common/types/{column_data_allocator.cpp → column/column_data_allocator.cpp} +2 -2
  24. package/src/duckdb/src/common/types/{column_data_collection.cpp → column/column_data_collection.cpp} +29 -6
  25. package/src/duckdb/src/common/types/{column_data_collection_segment.cpp → column/column_data_collection_segment.cpp} +2 -1
  26. package/src/duckdb/src/common/types/{column_data_consumer.cpp → column/column_data_consumer.cpp} +1 -1
  27. package/src/duckdb/src/common/types/{partitioned_column_data.cpp → column/partitioned_column_data.cpp} +11 -9
  28. package/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp +316 -0
  29. package/src/duckdb/src/common/types/{row_data_collection.cpp → row/row_data_collection.cpp} +1 -1
  30. package/src/duckdb/src/common/types/{row_data_collection_scanner.cpp → row/row_data_collection_scanner.cpp} +2 -2
  31. package/src/duckdb/src/common/types/{row_layout.cpp → row/row_layout.cpp} +1 -1
  32. package/src/duckdb/src/common/types/row/tuple_data_allocator.cpp +465 -0
  33. package/src/duckdb/src/common/types/row/tuple_data_collection.cpp +511 -0
  34. package/src/duckdb/src/common/types/row/tuple_data_iterator.cpp +96 -0
  35. package/src/duckdb/src/common/types/row/tuple_data_layout.cpp +119 -0
  36. package/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp +1200 -0
  37. package/src/duckdb/src/common/types/row/tuple_data_segment.cpp +170 -0
  38. package/src/duckdb/src/common/types/vector.cpp +1 -1
  39. package/src/duckdb/src/execution/aggregate_hashtable.cpp +252 -290
  40. package/src/duckdb/src/execution/join_hashtable.cpp +192 -328
  41. package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +4 -4
  42. package/src/duckdb/src/execution/operator/helper/physical_execute.cpp +3 -3
  43. package/src/duckdb/src/execution/operator/helper/physical_limit_percent.cpp +2 -3
  44. package/src/duckdb/src/execution/operator/helper/physical_result_collector.cpp +2 -3
  45. package/src/duckdb/src/execution/operator/join/perfect_hash_join_executor.cpp +36 -21
  46. package/src/duckdb/src/execution/operator/join/physical_blockwise_nl_join.cpp +2 -2
  47. package/src/duckdb/src/execution/operator/join/physical_cross_product.cpp +1 -1
  48. package/src/duckdb/src/execution/operator/join/physical_delim_join.cpp +2 -2
  49. package/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +166 -144
  50. package/src/duckdb/src/execution/operator/join/physical_index_join.cpp +5 -5
  51. package/src/duckdb/src/execution/operator/join/physical_join.cpp +2 -10
  52. package/src/duckdb/src/execution/operator/join/physical_positional_join.cpp +0 -1
  53. package/src/duckdb/src/execution/operator/order/physical_top_n.cpp +2 -2
  54. package/src/duckdb/src/execution/operator/persistent/base_csv_reader.cpp +3 -0
  55. package/src/duckdb/src/execution/operator/persistent/buffered_csv_reader.cpp +71 -22
  56. package/src/duckdb/src/execution/operator/persistent/csv_buffer.cpp +17 -13
  57. package/src/duckdb/src/execution/operator/persistent/csv_reader_options.cpp +0 -7
  58. package/src/duckdb/src/execution/operator/persistent/parallel_csv_reader.cpp +124 -29
  59. package/src/duckdb/src/execution/operator/persistent/physical_copy_to_file.cpp +13 -11
  60. package/src/duckdb/src/execution/operator/persistent/physical_delete.cpp +3 -2
  61. package/src/duckdb/src/execution/operator/persistent/physical_export.cpp +25 -24
  62. package/src/duckdb/src/execution/operator/persistent/physical_insert.cpp +1 -1
  63. package/src/duckdb/src/execution/operator/persistent/physical_update.cpp +4 -3
  64. package/src/duckdb/src/execution/operator/scan/physical_table_scan.cpp +1 -1
  65. package/src/duckdb/src/execution/operator/schema/physical_create_type.cpp +1 -1
  66. package/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp +3 -3
  67. package/src/duckdb/src/execution/partitionable_hashtable.cpp +9 -37
  68. package/src/duckdb/src/execution/physical_operator.cpp +1 -1
  69. package/src/duckdb/src/execution/physical_plan/plan_comparison_join.cpp +19 -18
  70. package/src/duckdb/src/execution/physical_plan/plan_copy_to_file.cpp +2 -1
  71. package/src/duckdb/src/execution/physical_plan/plan_execute.cpp +2 -2
  72. package/src/duckdb/src/execution/physical_plan/plan_explain.cpp +5 -6
  73. package/src/duckdb/src/execution/physical_plan/plan_expression_get.cpp +2 -2
  74. package/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp +3 -3
  75. package/src/duckdb/src/execution/physical_plan_generator.cpp +1 -1
  76. package/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +39 -17
  77. package/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp +2 -2
  78. package/src/duckdb/src/function/table/pragma_detailed_profiling_output.cpp +5 -5
  79. package/src/duckdb/src/function/table/pragma_last_profiling_output.cpp +2 -2
  80. package/src/duckdb/src/function/table/read_csv.cpp +124 -58
  81. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  82. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/index_catalog_entry.hpp +1 -1
  83. package/src/duckdb/src/include/duckdb/common/arrow/arrow_appender.hpp +1 -1
  84. package/src/duckdb/src/include/duckdb/common/constants.hpp +2 -0
  85. package/src/duckdb/src/include/duckdb/common/exception.hpp +3 -0
  86. package/src/duckdb/src/include/duckdb/common/fast_mem.hpp +528 -0
  87. package/src/duckdb/src/include/duckdb/common/filename_pattern.hpp +34 -0
  88. package/src/duckdb/src/include/duckdb/common/helper.hpp +10 -0
  89. package/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp +13 -3
  90. package/src/duckdb/src/include/duckdb/common/optional_ptr.hpp +8 -0
  91. package/src/duckdb/src/include/duckdb/common/perfect_map_set.hpp +34 -0
  92. package/src/duckdb/src/include/duckdb/common/radix_partitioning.hpp +80 -27
  93. package/src/duckdb/src/include/duckdb/common/reference_map.hpp +38 -0
  94. package/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp +7 -6
  95. package/src/duckdb/src/include/duckdb/common/sort/comparators.hpp +1 -1
  96. package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +1 -1
  97. package/src/duckdb/src/include/duckdb/common/sort/sort.hpp +1 -1
  98. package/src/duckdb/src/include/duckdb/common/sort/sorted_block.hpp +2 -2
  99. package/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp +1 -1
  100. package/src/duckdb/src/include/duckdb/common/types/{column_data_allocator.hpp → column/column_data_allocator.hpp} +4 -4
  101. package/src/duckdb/src/include/duckdb/common/types/{column_data_collection.hpp → column/column_data_collection.hpp} +4 -4
  102. package/src/duckdb/src/include/duckdb/common/types/{column_data_collection_iterators.hpp → column/column_data_collection_iterators.hpp} +2 -2
  103. package/src/duckdb/src/include/duckdb/common/types/{column_data_collection_segment.hpp → column/column_data_collection_segment.hpp} +3 -3
  104. package/src/duckdb/src/include/duckdb/common/types/{column_data_consumer.hpp → column/column_data_consumer.hpp} +8 -4
  105. package/src/duckdb/src/include/duckdb/common/types/{column_data_scan_states.hpp → column/column_data_scan_states.hpp} +1 -1
  106. package/src/duckdb/src/include/duckdb/common/types/{partitioned_column_data.hpp → column/partitioned_column_data.hpp} +15 -7
  107. package/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp +140 -0
  108. package/src/duckdb/src/include/duckdb/common/types/{row_data_collection.hpp → row/row_data_collection.hpp} +1 -1
  109. package/src/duckdb/src/include/duckdb/common/types/{row_data_collection_scanner.hpp → row/row_data_collection_scanner.hpp} +2 -2
  110. package/src/duckdb/src/include/duckdb/common/types/{row_layout.hpp → row/row_layout.hpp} +3 -1
  111. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_allocator.hpp +116 -0
  112. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp +239 -0
  113. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_iterator.hpp +64 -0
  114. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_layout.hpp +113 -0
  115. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_segment.hpp +124 -0
  116. package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp +74 -0
  117. package/src/duckdb/src/include/duckdb/common/types/validity_mask.hpp +3 -0
  118. package/src/duckdb/src/include/duckdb/common/types/value.hpp +4 -12
  119. package/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp +34 -31
  120. package/src/duckdb/src/include/duckdb/execution/base_aggregate_hashtable.hpp +2 -2
  121. package/src/duckdb/src/include/duckdb/execution/execution_context.hpp +3 -2
  122. package/src/duckdb/src/include/duckdb/execution/expression_executor.hpp +1 -1
  123. package/src/duckdb/src/include/duckdb/execution/join_hashtable.hpp +41 -67
  124. package/src/duckdb/src/include/duckdb/execution/nested_loop_join.hpp +1 -1
  125. package/src/duckdb/src/include/duckdb/execution/operator/helper/physical_execute.hpp +2 -2
  126. package/src/duckdb/src/include/duckdb/execution/operator/helper/physical_result_collector.hpp +1 -1
  127. package/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp +2 -2
  128. package/src/duckdb/src/include/duckdb/execution/operator/join/perfect_hash_join_executor.hpp +1 -1
  129. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_cross_product.hpp +1 -1
  130. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_hash_join.hpp +0 -2
  131. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_index_join.hpp +2 -2
  132. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_positional_join.hpp +1 -1
  133. package/src/duckdb/src/include/duckdb/execution/operator/persistent/buffered_csv_reader.hpp +4 -1
  134. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_buffer.hpp +8 -3
  135. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_reader_options.hpp +5 -7
  136. package/src/duckdb/src/include/duckdb/execution/operator/persistent/parallel_csv_reader.hpp +5 -1
  137. package/src/duckdb/src/include/duckdb/execution/operator/persistent/physical_copy_to_file.hpp +4 -1
  138. package/src/duckdb/src/include/duckdb/execution/operator/scan/physical_column_data_scan.hpp +1 -1
  139. package/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp +1 -1
  140. package/src/duckdb/src/include/duckdb/execution/partitionable_hashtable.hpp +2 -2
  141. package/src/duckdb/src/include/duckdb/function/function.hpp +2 -0
  142. package/src/duckdb/src/include/duckdb/function/table/read_csv.hpp +25 -0
  143. package/src/duckdb/src/include/duckdb/main/client_data.hpp +3 -0
  144. package/src/duckdb/src/include/duckdb/main/config.hpp +0 -2
  145. package/src/duckdb/src/include/duckdb/main/materialized_query_result.hpp +1 -1
  146. package/src/duckdb/src/include/duckdb/main/query_result.hpp +14 -1
  147. package/src/duckdb/src/include/duckdb/optimizer/expression_rewriter.hpp +3 -3
  148. package/src/duckdb/src/include/duckdb/optimizer/join_order/cardinality_estimator.hpp +16 -16
  149. package/src/duckdb/src/include/duckdb/optimizer/join_order/join_node.hpp +8 -8
  150. package/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp +23 -15
  151. package/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation.hpp +9 -10
  152. package/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph.hpp +18 -11
  153. package/src/duckdb/src/include/duckdb/parallel/meta_pipeline.hpp +1 -1
  154. package/src/duckdb/src/include/duckdb/parser/parsed_data/exported_table_data.hpp +5 -1
  155. package/src/duckdb/src/include/duckdb/parser/parsed_data/vacuum_info.hpp +3 -2
  156. package/src/duckdb/src/include/duckdb/parser/query_error_context.hpp +4 -2
  157. package/src/duckdb/src/include/duckdb/parser/transformer.hpp +9 -35
  158. package/src/duckdb/src/include/duckdb/planner/binder.hpp +24 -23
  159. package/src/duckdb/src/include/duckdb/planner/expression_binder.hpp +3 -3
  160. package/src/duckdb/src/include/duckdb/planner/operator/logical_column_data_get.hpp +1 -1
  161. package/src/duckdb/src/include/duckdb/planner/operator/logical_copy_to_file.hpp +3 -1
  162. package/src/duckdb/src/include/duckdb/storage/table/table_index_list.hpp +1 -1
  163. package/src/duckdb/src/main/appender.cpp +6 -6
  164. package/src/duckdb/src/main/client_context.cpp +1 -1
  165. package/src/duckdb/src/main/connection.cpp +2 -2
  166. package/src/duckdb/src/main/query_result.cpp +13 -0
  167. package/src/duckdb/src/main/settings/settings.cpp +3 -4
  168. package/src/duckdb/src/optimizer/expression_rewriter.cpp +4 -4
  169. package/src/duckdb/src/optimizer/join_order/cardinality_estimator.cpp +91 -105
  170. package/src/duckdb/src/optimizer/join_order/join_node.cpp +5 -8
  171. package/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp +163 -160
  172. package/src/duckdb/src/optimizer/join_order/join_relation_set.cpp +30 -30
  173. package/src/duckdb/src/optimizer/join_order/query_graph.cpp +37 -38
  174. package/src/duckdb/src/parallel/executor.cpp +1 -1
  175. package/src/duckdb/src/parallel/meta_pipeline.cpp +2 -2
  176. package/src/duckdb/src/parser/transform/helpers/transform_cte.cpp +1 -1
  177. package/src/duckdb/src/parser/transform/tableref/transform_subquery.cpp +1 -1
  178. package/src/duckdb/src/parser/transformer.cpp +50 -9
  179. package/src/duckdb/src/planner/binder/expression/bind_operator_expression.cpp +13 -0
  180. package/src/duckdb/src/planner/binder/statement/bind_copy.cpp +15 -5
  181. package/src/duckdb/src/planner/binder/statement/bind_create.cpp +19 -17
  182. package/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +4 -4
  183. package/src/duckdb/src/planner/binder/statement/bind_export.cpp +20 -21
  184. package/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +24 -22
  185. package/src/duckdb/src/planner/binder/tableref/bind_subqueryref.cpp +2 -2
  186. package/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp +9 -0
  187. package/src/duckdb/src/planner/binder.cpp +16 -19
  188. package/src/duckdb/src/planner/expression_binder.cpp +8 -8
  189. package/src/duckdb/src/planner/operator/logical_copy_to_file.cpp +3 -3
  190. package/src/duckdb/src/storage/checkpoint_manager.cpp +23 -23
  191. package/src/duckdb/src/storage/standard_buffer_manager.cpp +1 -1
  192. package/src/duckdb/src/storage/table_index_list.cpp +3 -3
  193. package/src/duckdb/src/verification/statement_verifier.cpp +1 -1
  194. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +5552 -5598
  195. package/src/duckdb/ub_src_common.cpp +2 -0
  196. package/src/duckdb/ub_src_common_types.cpp +0 -16
  197. package/src/duckdb/ub_src_common_types_column.cpp +10 -0
  198. package/src/duckdb/ub_src_common_types_row.cpp +20 -0
@@ -1,8 +1,5 @@
1
1
  #include "duckdb/execution/operator/join/physical_hash_join.hpp"
2
2
 
3
- #include "duckdb/planner/expression/bound_aggregate_expression.hpp"
4
- #include "duckdb/planner/expression/bound_reference_expression.hpp"
5
- #include "duckdb/common/types/column_data_collection.hpp"
6
3
  #include "duckdb/common/vector_operations/vector_operations.hpp"
7
4
  #include "duckdb/execution/expression_executor.hpp"
8
5
  #include "duckdb/function/aggregate/distributive_functions.hpp"
@@ -12,6 +9,8 @@
12
9
  #include "duckdb/parallel/base_pipeline_event.hpp"
13
10
  #include "duckdb/parallel/pipeline.hpp"
14
11
  #include "duckdb/parallel/thread_context.hpp"
12
+ #include "duckdb/planner/expression/bound_aggregate_expression.hpp"
13
+ #include "duckdb/planner/expression/bound_reference_expression.hpp"
15
14
  #include "duckdb/storage/buffer_manager.hpp"
16
15
  #include "duckdb/storage/storage_manager.hpp"
17
16
 
@@ -52,20 +51,14 @@ PhysicalHashJoin::PhysicalHashJoin(LogicalOperator &op, unique_ptr<PhysicalOpera
52
51
  //===--------------------------------------------------------------------===//
53
52
  class HashJoinGlobalSinkState : public GlobalSinkState {
54
53
  public:
55
- HashJoinGlobalSinkState(const PhysicalHashJoin &op, ClientContext &context)
56
- : finalized(false), scanned_data(false) {
54
+ HashJoinGlobalSinkState(const PhysicalHashJoin &op, ClientContext &context_p)
55
+ : context(context_p), finalized(false), scanned_data(false) {
57
56
  hash_table = op.InitializeHashTable(context);
58
57
 
59
58
  // for perfect hash join
60
59
  perfect_join_executor = make_uniq<PerfectHashJoinExecutor>(op, *hash_table, op.perfect_join_statistics);
61
60
  // for external hash join
62
- external = op.can_go_external && ClientConfig::GetConfig(context).force_external;
63
- // memory usage per thread scales with max mem / num threads
64
- double max_memory = BufferManager::GetBufferManager(context).GetMaxMemory();
65
- double num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
66
- // HT may not exceed 60% of memory
67
- max_ht_size = max_memory * 0.6;
68
- sink_memory_per_thread = max_ht_size / num_threads;
61
+ external = ClientConfig::GetConfig(context).force_external;
69
62
  // Set probe types
70
63
  const auto &payload_types = op.children[0]->types;
71
64
  probe_types.insert(probe_types.end(), op.condition_types.begin(), op.condition_types.end());
@@ -74,9 +67,10 @@ public:
74
67
  }
75
68
 
76
69
  void ScheduleFinalize(Pipeline &pipeline, Event &event);
77
- void InitializeProbeSpill(ClientContext &context);
70
+ void InitializeProbeSpill();
78
71
 
79
72
  public:
73
+ ClientContext &context;
80
74
  //! Global HT used by the join
81
75
  unique_ptr<JoinHashTable> hash_table;
82
76
  //! The perfect hash join executor (if any)
@@ -86,9 +80,6 @@ public:
86
80
 
87
81
  //! Whether we are doing an external join
88
82
  bool external;
89
- //! Memory usage per thread during the Sink and Execute phases
90
- idx_t max_ht_size;
91
- idx_t sink_memory_per_thread;
92
83
 
93
84
  //! Hash tables built by each thread
94
85
  mutex lock;
@@ -115,9 +106,13 @@ public:
115
106
  join_keys.Initialize(allocator, op.condition_types);
116
107
 
117
108
  hash_table = op.InitializeHashTable(context);
109
+
110
+ hash_table->GetSinkCollection().InitializeAppendState(append_state);
118
111
  }
119
112
 
120
113
  public:
114
+ PartitionedTupleDataAppendState append_state;
115
+
121
116
  DataChunk build_chunk;
122
117
  DataChunk join_keys;
123
118
  ExpressionExecutor build_executor;
@@ -129,6 +124,7 @@ public:
129
124
  unique_ptr<JoinHashTable> PhysicalHashJoin::InitializeHashTable(ClientContext &context) const {
130
125
  auto result =
131
126
  make_uniq<JoinHashTable>(BufferManager::GetBufferManager(context), conditions, build_types, join_type);
127
+ result->max_ht_size = double(BufferManager::GetBufferManager(context).GetMaxMemory()) * 0.6;
132
128
  if (!delim_types.empty() && join_type == JoinType::MARK) {
133
129
  // correlated MARK join
134
130
  if (delim_types.size() + 1 == conditions.size()) {
@@ -187,12 +183,12 @@ unique_ptr<LocalSinkState> PhysicalHashJoin::GetLocalSinkState(ExecutionContext
187
183
 
188
184
  SinkResultType PhysicalHashJoin::Sink(ExecutionContext &context, GlobalSinkState &gstate_p, LocalSinkState &lstate_p,
189
185
  DataChunk &input) const {
190
- auto &gstate = gstate_p.Cast<HashJoinGlobalSinkState>();
191
186
  auto &lstate = lstate_p.Cast<HashJoinLocalSinkState>();
192
187
 
193
188
  // resolve the join keys for the right chunk
194
189
  lstate.join_keys.Reset();
195
190
  lstate.build_executor.Execute(input, lstate.join_keys);
191
+
196
192
  // build the HT
197
193
  auto &ht = *lstate.hash_table;
198
194
  if (!right_projection_map.empty()) {
@@ -202,21 +198,14 @@ SinkResultType PhysicalHashJoin::Sink(ExecutionContext &context, GlobalSinkState
202
198
  for (idx_t i = 0; i < right_projection_map.size(); i++) {
203
199
  lstate.build_chunk.data[i].Reference(input.data[right_projection_map[i]]);
204
200
  }
205
- ht.Build(lstate.join_keys, lstate.build_chunk);
201
+ ht.Build(lstate.append_state, lstate.join_keys, lstate.build_chunk);
206
202
  } else if (!build_types.empty()) {
207
203
  // there is not a projected map: place the entire right chunk in the HT
208
- ht.Build(lstate.join_keys, input);
204
+ ht.Build(lstate.append_state, lstate.join_keys, input);
209
205
  } else {
210
206
  // there are only keys: place an empty chunk in the payload
211
207
  lstate.build_chunk.SetCardinality(input.size());
212
- ht.Build(lstate.join_keys, lstate.build_chunk);
213
- }
214
-
215
- // swizzle if we reach memory limit
216
- auto approx_ptr_table_size = ht.Count() * 3 * sizeof(data_ptr_t);
217
- if (can_go_external && ht.SizeInBytes() + approx_ptr_table_size >= gstate.sink_memory_per_thread) {
218
- lstate.hash_table->SwizzleBlocks();
219
- gstate.external = true;
208
+ ht.Build(lstate.append_state, lstate.join_keys, lstate.build_chunk);
220
209
  }
221
210
 
222
211
  return SinkResultType::NEED_MORE_INPUT;
@@ -226,6 +215,7 @@ void PhysicalHashJoin::Combine(ExecutionContext &context, GlobalSinkState &gstat
226
215
  auto &gstate = gstate_p.Cast<HashJoinGlobalSinkState>();
227
216
  auto &lstate = lstate_p.Cast<HashJoinLocalSinkState>();
228
217
  if (lstate.hash_table) {
218
+ lstate.hash_table->GetSinkCollection().FlushAppendState(lstate.append_state);
229
219
  lock_guard<mutex> local_ht_lock(gstate.lock);
230
220
  gstate.local_hash_tables.push_back(std::move(lstate.hash_table));
231
221
  }
@@ -239,14 +229,14 @@ void PhysicalHashJoin::Combine(ExecutionContext &context, GlobalSinkState &gstat
239
229
  //===--------------------------------------------------------------------===//
240
230
  class HashJoinFinalizeTask : public ExecutorTask {
241
231
  public:
242
- HashJoinFinalizeTask(shared_ptr<Event> event_p, ClientContext &context, HashJoinGlobalSinkState &sink,
243
- idx_t block_idx_start, idx_t block_idx_end, bool parallel)
244
- : ExecutorTask(context), event(std::move(event_p)), sink(sink), block_idx_start(block_idx_start),
245
- block_idx_end(block_idx_end), parallel(parallel) {
232
+ HashJoinFinalizeTask(shared_ptr<Event> event_p, ClientContext &context, HashJoinGlobalSinkState &sink_p,
233
+ idx_t chunk_idx_from_p, idx_t chunk_idx_to_p, bool parallel_p)
234
+ : ExecutorTask(context), event(std::move(event_p)), sink(sink_p), chunk_idx_from(chunk_idx_from_p),
235
+ chunk_idx_to(chunk_idx_to_p), parallel(parallel_p) {
246
236
  }
247
237
 
248
238
  TaskExecutionResult ExecuteTask(TaskExecutionMode mode) override {
249
- sink.hash_table->Finalize(block_idx_start, block_idx_end, parallel);
239
+ sink.hash_table->Finalize(chunk_idx_from, chunk_idx_to, parallel);
250
240
  event->FinishTask();
251
241
  return TaskExecutionResult::TASK_FINISHED;
252
242
  }
@@ -254,8 +244,8 @@ public:
254
244
  private:
255
245
  shared_ptr<Event> event;
256
246
  HashJoinGlobalSinkState &sink;
257
- idx_t block_idx_start;
258
- idx_t block_idx_end;
247
+ idx_t chunk_idx_from;
248
+ idx_t chunk_idx_to;
259
249
  bool parallel;
260
250
  };
261
251
 
@@ -273,26 +263,24 @@ public:
273
263
 
274
264
  vector<unique_ptr<Task>> finalize_tasks;
275
265
  auto &ht = *sink.hash_table;
276
- const auto &block_collection = ht.GetBlockCollection();
277
- const auto &blocks = block_collection.blocks;
278
- const auto num_blocks = blocks.size();
279
- if (block_collection.count < PARALLEL_CONSTRUCT_THRESHOLD && !context.config.verify_parallelism) {
266
+ const auto chunk_count = ht.GetDataCollection().ChunkCount();
267
+ const idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
268
+ if (num_threads == 1 || (ht.Count() < PARALLEL_CONSTRUCT_THRESHOLD && !context.config.verify_parallelism)) {
280
269
  // Single-threaded finalize
281
270
  finalize_tasks.push_back(
282
- make_uniq<HashJoinFinalizeTask>(shared_from_this(), context, sink, 0, num_blocks, false));
271
+ make_uniq<HashJoinFinalizeTask>(shared_from_this(), context, sink, 0, chunk_count, false));
283
272
  } else {
284
273
  // Parallel finalize
285
- idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
286
- auto blocks_per_thread = MaxValue<idx_t>((num_blocks + num_threads - 1) / num_threads, 1);
274
+ auto chunks_per_thread = MaxValue<idx_t>((chunk_count + num_threads - 1) / num_threads, 1);
287
275
 
288
- idx_t block_idx = 0;
276
+ idx_t chunk_idx = 0;
289
277
  for (idx_t thread_idx = 0; thread_idx < num_threads; thread_idx++) {
290
- auto block_idx_start = block_idx;
291
- auto block_idx_end = MinValue<idx_t>(block_idx_start + blocks_per_thread, num_blocks);
278
+ auto chunk_idx_from = chunk_idx;
279
+ auto chunk_idx_to = MinValue<idx_t>(chunk_idx_from + chunks_per_thread, chunk_count);
292
280
  finalize_tasks.push_back(make_uniq<HashJoinFinalizeTask>(shared_from_this(), context, sink,
293
- block_idx_start, block_idx_end, true));
294
- block_idx = block_idx_end;
295
- if (block_idx == num_blocks) {
281
+ chunk_idx_from, chunk_idx_to, true));
282
+ chunk_idx = chunk_idx_to;
283
+ if (chunk_idx == chunk_count) {
296
284
  break;
297
285
  }
298
286
  }
@@ -301,6 +289,7 @@ public:
301
289
  }
302
290
 
303
291
  void FinishEvent() override {
292
+ sink.hash_table->GetDataCollection().VerifyEverythingPinned();
304
293
  sink.hash_table->finalized = true;
305
294
  }
306
295
 
@@ -317,7 +306,7 @@ void HashJoinGlobalSinkState::ScheduleFinalize(Pipeline &pipeline, Event &event)
317
306
  event.InsertEvent(std::move(new_event));
318
307
  }
319
308
 
320
- void HashJoinGlobalSinkState::InitializeProbeSpill(ClientContext &context) {
309
+ void HashJoinGlobalSinkState::InitializeProbeSpill() {
321
310
  lock_guard<mutex> guard(lock);
322
311
  if (!probe_spill) {
323
312
  probe_spill = make_uniq<JoinHashTable::ProbeSpill>(*hash_table, context, probe_types);
@@ -376,28 +365,37 @@ public:
376
365
  SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, ClientContext &context,
377
366
  GlobalSinkState &gstate) const {
378
367
  auto &sink = gstate.Cast<HashJoinGlobalSinkState>();
368
+ auto &ht = *sink.hash_table;
379
369
 
370
+ sink.external = ht.RequiresExternalJoin(context.config, sink.local_hash_tables);
380
371
  if (sink.external) {
381
- D_ASSERT(can_go_external);
382
- // External join - partition HT
383
372
  sink.perfect_join_executor.reset();
384
- sink.hash_table->ComputePartitionSizes(context.config, sink.local_hash_tables, sink.max_ht_size);
385
- auto new_event = make_shared<HashJoinPartitionEvent>(pipeline, sink, sink.local_hash_tables);
386
- event.InsertEvent(std::move(new_event));
373
+ if (ht.RequiresPartitioning(context.config, sink.local_hash_tables)) {
374
+ auto new_event = make_shared<HashJoinPartitionEvent>(pipeline, sink, sink.local_hash_tables);
375
+ event.InsertEvent(std::move(new_event));
376
+ } else {
377
+ for (auto &local_ht : sink.local_hash_tables) {
378
+ ht.Merge(*local_ht);
379
+ }
380
+ sink.local_hash_tables.clear();
381
+ sink.hash_table->PrepareExternalFinalize();
382
+ sink.ScheduleFinalize(pipeline, event);
383
+ }
387
384
  sink.finalized = true;
388
385
  return SinkFinalizeType::READY;
389
386
  } else {
390
387
  for (auto &local_ht : sink.local_hash_tables) {
391
- sink.hash_table->Merge(*local_ht);
388
+ ht.Merge(*local_ht);
392
389
  }
393
390
  sink.local_hash_tables.clear();
391
+ ht.Unpartition();
394
392
  }
395
393
 
396
394
  // check for possible perfect hash table
397
395
  auto use_perfect_hash = sink.perfect_join_executor->CanDoPerfectHashJoin();
398
396
  if (use_perfect_hash) {
399
- D_ASSERT(sink.hash_table->equality_types.size() == 1);
400
- auto key_type = sink.hash_table->equality_types[0];
397
+ D_ASSERT(ht.equality_types.size() == 1);
398
+ auto key_type = ht.equality_types[0];
401
399
  use_perfect_hash = sink.perfect_join_executor->BuildPerfectHashTable(key_type);
402
400
  }
403
401
  // In case of a large build side or duplicates, use regular hash join
@@ -406,7 +404,7 @@ SinkFinalizeType PhysicalHashJoin::Finalize(Pipeline &pipeline, Event &event, Cl
406
404
  sink.ScheduleFinalize(pipeline, event);
407
405
  }
408
406
  sink.finalized = true;
409
- if (sink.hash_table->Count() == 0 && EmptyResultIfRHSIsEmpty()) {
407
+ if (ht.Count() == 0 && EmptyResultIfRHSIsEmpty()) {
410
408
  return SinkFinalizeType::NO_OUTPUT_POSSIBLE;
411
409
  }
412
410
  return SinkFinalizeType::READY;
@@ -450,7 +448,7 @@ unique_ptr<OperatorState> PhysicalHashJoin::GetOperatorState(ExecutionContext &c
450
448
  }
451
449
  if (sink.external) {
452
450
  state->spill_chunk.Initialize(allocator, sink.probe_types);
453
- sink.InitializeProbeSpill(context.client);
451
+ sink.InitializeProbeSpill();
454
452
  }
455
453
 
456
454
  return std::move(state);
@@ -466,7 +464,7 @@ OperatorResultType PhysicalHashJoin::ExecuteInternal(ExecutionContext &context,
466
464
  // some initialization for external hash join
467
465
  if (sink.external && !state.initialized) {
468
466
  if (!sink.probe_spill) {
469
- sink.InitializeProbeSpill(context.client);
467
+ sink.InitializeProbeSpill();
470
468
  }
471
469
  state.spill_state = sink.probe_spill->RegisterThread();
472
470
  state.initialized = true;
@@ -524,12 +522,13 @@ public:
524
522
  HashJoinGlobalSourceState(const PhysicalHashJoin &op, ClientContext &context);
525
523
 
526
524
  //! Initialize this source state using the info in the sink
527
- void Initialize(ClientContext &context, HashJoinGlobalSinkState &sink);
525
+ void Initialize(HashJoinGlobalSinkState &sink);
528
526
  //! Try to prepare the next stage
529
527
  void TryPrepareNextStage(HashJoinGlobalSinkState &sink);
530
- //! Prepare the next build/probe stage for external hash join (must hold lock)
528
+ //! Prepare the next build/probe/scan_ht stage for external hash join (must hold lock)
531
529
  void PrepareBuild(HashJoinGlobalSinkState &sink);
532
530
  void PrepareProbe(HashJoinGlobalSinkState &sink);
531
+ void PrepareScanHT(HashJoinGlobalSinkState &sink);
533
532
  //! Assigns a task to a local source state
534
533
  bool AssignTask(HashJoinGlobalSinkState &sink, HashJoinLocalSourceState &lstate);
535
534
 
@@ -545,21 +544,24 @@ public:
545
544
  mutex lock;
546
545
 
547
546
  //! For HT build synchronization
548
- idx_t build_block_idx;
549
- idx_t build_block_count;
550
- idx_t build_block_done;
551
- idx_t build_blocks_per_thread;
547
+ idx_t build_chunk_idx;
548
+ idx_t build_chunk_count;
549
+ idx_t build_chunk_done;
550
+ idx_t build_chunks_per_thread;
552
551
 
553
552
  //! For probe synchronization
554
553
  idx_t probe_chunk_count;
555
554
  idx_t probe_chunk_done;
556
555
 
557
- //! For full/outer synchronization
558
- JoinHTScanState full_outer_scan;
559
-
560
556
  //! To determine the number of threads
561
557
  idx_t probe_count;
562
558
  idx_t parallel_scan_chunk_count;
559
+
560
+ //! For full/outer synchronization
561
+ idx_t full_outer_chunk_idx;
562
+ idx_t full_outer_chunk_count;
563
+ idx_t full_outer_chunk_done;
564
+ idx_t full_outer_chunks_per_thread;
563
565
  };
564
566
 
565
567
  class HashJoinLocalSourceState : public LocalSourceState {
@@ -575,18 +577,15 @@ public:
575
577
  void ExternalProbe(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate, DataChunk &chunk);
576
578
  void ExternalScanHT(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate, DataChunk &chunk);
577
579
 
578
- //! Scans the HT for full/outer join
579
- void ScanFullOuter(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate);
580
-
581
580
  public:
582
581
  //! The stage that this thread was assigned work for
583
582
  HashJoinSourceStage local_stage;
584
583
  //! Vector with pointers here so we don't have to re-initialize
585
584
  Vector addresses;
586
585
 
587
- //! Blocks assigned to this thread for building the pointer table
588
- idx_t build_block_idx_start;
589
- idx_t build_block_idx_end;
586
+ //! Chunks assigned to this thread for building the pointer table
587
+ idx_t build_chunk_idx_from;
588
+ idx_t build_chunk_idx_to;
590
589
 
591
590
  //! Local scan state for probe spill
592
591
  ColumnDataConsumerScanState probe_local_scan;
@@ -599,10 +598,12 @@ public:
599
598
  vector<idx_t> payload_indices;
600
599
  //! Scan structure for the external probe
601
600
  unique_ptr<JoinHashTable::ScanStructure> scan_structure;
601
+ bool empty_ht_probe_in_progress;
602
602
 
603
- //! Current number of tuples from a full/outer scan that are 'in-flight'
604
- idx_t full_outer_found_entries;
605
- idx_t full_outer_in_progress;
603
+ //! Chunks assigned to this thread for a full/outer scan
604
+ idx_t full_outer_chunk_idx_from;
605
+ idx_t full_outer_chunk_idx_to;
606
+ unique_ptr<JoinHTScanState> full_outer_scan_state;
606
607
  };
607
608
 
608
609
  unique_ptr<GlobalSourceState> PhysicalHashJoin::GetGlobalSourceState(ClientContext &context) const {
@@ -615,36 +616,32 @@ unique_ptr<LocalSourceState> PhysicalHashJoin::GetLocalSourceState(ExecutionCont
615
616
  }
616
617
 
617
618
  HashJoinGlobalSourceState::HashJoinGlobalSourceState(const PhysicalHashJoin &op, ClientContext &context)
618
- : op(op), global_stage(HashJoinSourceStage::INIT), probe_chunk_count(0), probe_chunk_done(0),
619
- probe_count(op.children[0]->estimated_cardinality),
619
+ : op(op), global_stage(HashJoinSourceStage::INIT), build_chunk_count(0), build_chunk_done(0), probe_chunk_count(0),
620
+ probe_chunk_done(0), probe_count(op.children[0]->estimated_cardinality),
620
621
  parallel_scan_chunk_count(context.config.verify_parallelism ? 1 : 120) {
621
622
  }
622
623
 
623
- void HashJoinGlobalSourceState::Initialize(ClientContext &context, HashJoinGlobalSinkState &sink) {
624
+ void HashJoinGlobalSourceState::Initialize(HashJoinGlobalSinkState &sink) {
624
625
  lock_guard<mutex> init_lock(lock);
625
626
  if (global_stage != HashJoinSourceStage::INIT) {
626
627
  // Another thread initialized
627
628
  return;
628
629
  }
629
- full_outer_scan.total = sink.hash_table->Count();
630
-
631
- idx_t num_blocks = sink.hash_table->GetBlockCollection().blocks.size();
632
- idx_t num_threads = TaskScheduler::GetScheduler(context).NumberOfThreads();
633
- build_blocks_per_thread = MaxValue<idx_t>((num_blocks + num_threads - 1) / num_threads, 1);
634
630
 
635
- // Finalize the probe spill too
631
+ // Finalize the probe spill
636
632
  if (sink.probe_spill) {
637
633
  sink.probe_spill->Finalize();
638
634
  }
639
635
 
640
636
  global_stage = HashJoinSourceStage::PROBE;
637
+ TryPrepareNextStage(sink);
641
638
  }
642
639
 
643
640
  void HashJoinGlobalSourceState::TryPrepareNextStage(HashJoinGlobalSinkState &sink) {
644
- lock_guard<mutex> guard(lock);
645
641
  switch (global_stage.load()) {
646
642
  case HashJoinSourceStage::BUILD:
647
- if (build_block_done == build_block_count) {
643
+ if (build_chunk_done == build_chunk_count) {
644
+ sink.hash_table->GetDataCollection().VerifyEverythingPinned();
648
645
  sink.hash_table->finalized = true;
649
646
  PrepareProbe(sink);
650
647
  }
@@ -652,14 +649,14 @@ void HashJoinGlobalSourceState::TryPrepareNextStage(HashJoinGlobalSinkState &sin
652
649
  case HashJoinSourceStage::PROBE:
653
650
  if (probe_chunk_done == probe_chunk_count) {
654
651
  if (IsRightOuterJoin(op.join_type)) {
655
- global_stage = HashJoinSourceStage::SCAN_HT;
652
+ PrepareScanHT(sink);
656
653
  } else {
657
654
  PrepareBuild(sink);
658
655
  }
659
656
  }
660
657
  break;
661
658
  case HashJoinSourceStage::SCAN_HT:
662
- if (full_outer_scan.scanned == full_outer_scan.total) {
659
+ if (full_outer_chunk_done == full_outer_chunk_count) {
663
660
  PrepareBuild(sink);
664
661
  }
665
662
  break;
@@ -673,15 +670,24 @@ void HashJoinGlobalSourceState::PrepareBuild(HashJoinGlobalSinkState &sink) {
673
670
  auto &ht = *sink.hash_table;
674
671
 
675
672
  // Try to put the next partitions in the block collection of the HT
676
- if (!ht.PrepareExternalFinalize()) {
673
+ if (!sink.external || !ht.PrepareExternalFinalize()) {
677
674
  global_stage = HashJoinSourceStage::DONE;
678
675
  return;
679
676
  }
680
677
 
681
- auto &block_collection = ht.GetBlockCollection();
682
- build_block_idx = 0;
683
- build_block_count = block_collection.blocks.size();
684
- build_block_done = 0;
678
+ auto &data_collection = ht.GetDataCollection();
679
+ if (data_collection.Count() == 0 && op.EmptyResultIfRHSIsEmpty()) {
680
+ PrepareBuild(sink);
681
+ return;
682
+ }
683
+
684
+ build_chunk_idx = 0;
685
+ build_chunk_count = data_collection.ChunkCount();
686
+ build_chunk_done = 0;
687
+
688
+ auto num_threads = TaskScheduler::GetScheduler(sink.context).NumberOfThreads();
689
+ build_chunks_per_thread = MaxValue<idx_t>((build_chunk_count + num_threads - 1) / num_threads, 1);
690
+
685
691
  ht.InitializePointerTable();
686
692
 
687
693
  global_stage = HashJoinSourceStage::BUILD;
@@ -689,16 +695,31 @@ void HashJoinGlobalSourceState::PrepareBuild(HashJoinGlobalSinkState &sink) {
689
695
 
690
696
  void HashJoinGlobalSourceState::PrepareProbe(HashJoinGlobalSinkState &sink) {
691
697
  sink.probe_spill->PrepareNextProbe();
698
+ const auto &consumer = *sink.probe_spill->consumer;
692
699
 
693
- probe_chunk_count = sink.probe_spill->consumer->ChunkCount();
700
+ probe_chunk_count = consumer.Count() == 0 ? 0 : consumer.ChunkCount();
694
701
  probe_chunk_done = 0;
695
702
 
696
- if (IsRightOuterJoin(op.join_type)) {
697
- full_outer_scan.Reset();
698
- full_outer_scan.total = sink.hash_table->Count();
703
+ global_stage = HashJoinSourceStage::PROBE;
704
+ if (probe_chunk_count == 0) {
705
+ TryPrepareNextStage(sink);
706
+ return;
699
707
  }
708
+ }
700
709
 
701
- global_stage = HashJoinSourceStage::PROBE;
710
+ void HashJoinGlobalSourceState::PrepareScanHT(HashJoinGlobalSinkState &sink) {
711
+ D_ASSERT(global_stage != HashJoinSourceStage::SCAN_HT);
712
+ auto &ht = *sink.hash_table;
713
+
714
+ auto &data_collection = ht.GetDataCollection();
715
+ full_outer_chunk_idx = 0;
716
+ full_outer_chunk_count = data_collection.ChunkCount();
717
+ full_outer_chunk_done = 0;
718
+
719
+ auto num_threads = TaskScheduler::GetScheduler(sink.context).NumberOfThreads();
720
+ full_outer_chunks_per_thread = MaxValue<idx_t>((full_outer_chunk_count + num_threads - 1) / num_threads, 1);
721
+
722
+ global_stage = HashJoinSourceStage::SCAN_HT;
702
723
  }
703
724
 
704
725
  bool HashJoinGlobalSourceState::AssignTask(HashJoinGlobalSinkState &sink, HashJoinLocalSourceState &lstate) {
@@ -707,24 +728,28 @@ bool HashJoinGlobalSourceState::AssignTask(HashJoinGlobalSinkState &sink, HashJo
707
728
  lock_guard<mutex> guard(lock);
708
729
  switch (global_stage.load()) {
709
730
  case HashJoinSourceStage::BUILD:
710
- if (build_block_idx != build_block_count) {
731
+ if (build_chunk_idx != build_chunk_count) {
711
732
  lstate.local_stage = global_stage;
712
- lstate.build_block_idx_start = build_block_idx;
713
- build_block_idx = MinValue<idx_t>(build_block_count, build_block_idx + build_blocks_per_thread);
714
- lstate.build_block_idx_end = build_block_idx;
733
+ lstate.build_chunk_idx_from = build_chunk_idx;
734
+ build_chunk_idx = MinValue<idx_t>(build_chunk_count, build_chunk_idx + build_chunks_per_thread);
735
+ lstate.build_chunk_idx_to = build_chunk_idx;
715
736
  return true;
716
737
  }
717
738
  break;
718
739
  case HashJoinSourceStage::PROBE:
719
740
  if (sink.probe_spill->consumer && sink.probe_spill->consumer->AssignChunk(lstate.probe_local_scan)) {
720
741
  lstate.local_stage = global_stage;
742
+ lstate.empty_ht_probe_in_progress = false;
721
743
  return true;
722
744
  }
723
745
  break;
724
746
  case HashJoinSourceStage::SCAN_HT:
725
- if (full_outer_scan.scan_index != full_outer_scan.total) {
747
+ if (full_outer_chunk_idx != full_outer_chunk_count) {
726
748
  lstate.local_stage = global_stage;
727
- lstate.ScanFullOuter(sink, *this);
749
+ lstate.full_outer_chunk_idx_from = full_outer_chunk_idx;
750
+ full_outer_chunk_idx =
751
+ MinValue<idx_t>(full_outer_chunk_count, full_outer_chunk_idx + full_outer_chunks_per_thread);
752
+ lstate.full_outer_chunk_idx_to = full_outer_chunk_idx;
728
753
  return true;
729
754
  }
730
755
  break;
@@ -779,9 +804,9 @@ bool HashJoinLocalSourceState::TaskFinished() {
779
804
  case HashJoinSourceStage::BUILD:
780
805
  return true;
781
806
  case HashJoinSourceStage::PROBE:
782
- return scan_structure == nullptr;
807
+ return scan_structure == nullptr && !empty_ht_probe_in_progress;
783
808
  case HashJoinSourceStage::SCAN_HT:
784
- return full_outer_in_progress == 0;
809
+ return full_outer_scan_state == nullptr;
785
810
  default:
786
811
  throw InternalException("Unexpected HashJoinSourceStage in TaskFinished!");
787
812
  }
@@ -791,10 +816,10 @@ void HashJoinLocalSourceState::ExternalBuild(HashJoinGlobalSinkState &sink, Hash
791
816
  D_ASSERT(local_stage == HashJoinSourceStage::BUILD);
792
817
 
793
818
  auto &ht = *sink.hash_table;
794
- ht.Finalize(build_block_idx_start, build_block_idx_end, true);
819
+ ht.Finalize(build_chunk_idx_from, build_chunk_idx_to, true);
795
820
 
796
821
  lock_guard<mutex> guard(gstate.lock);
797
- gstate.build_block_done += build_block_idx_end - build_block_idx_start;
822
+ gstate.build_chunk_done += build_chunk_idx_to - build_chunk_idx_from;
798
823
  }
799
824
 
800
825
  void HashJoinLocalSourceState::ExternalProbe(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate,
@@ -802,14 +827,20 @@ void HashJoinLocalSourceState::ExternalProbe(HashJoinGlobalSinkState &sink, Hash
802
827
  D_ASSERT(local_stage == HashJoinSourceStage::PROBE && sink.hash_table->finalized);
803
828
 
804
829
  if (scan_structure) {
805
- // still have elements remaining (i.e. we got >STANDARD_VECTOR_SIZE elements in the previous probe)
830
+ // Still have elements remaining (i.e. we got >STANDARD_VECTOR_SIZE elements in the previous probe)
806
831
  scan_structure->Next(join_keys, payload, chunk);
807
- if (chunk.size() == 0) {
808
- scan_structure = nullptr;
809
- sink.probe_spill->consumer->FinishChunk(probe_local_scan);
810
- lock_guard<mutex> lock(gstate.lock);
811
- gstate.probe_chunk_done++;
832
+ if (chunk.size() != 0) {
833
+ return;
812
834
  }
835
+ }
836
+
837
+ if (scan_structure || empty_ht_probe_in_progress) {
838
+ // Previous probe is done
839
+ scan_structure = nullptr;
840
+ empty_ht_probe_in_progress = false;
841
+ sink.probe_spill->consumer->FinishChunk(probe_local_scan);
842
+ lock_guard<mutex> lock(gstate.lock);
843
+ gstate.probe_chunk_done++;
813
844
  return;
814
845
  }
815
846
 
@@ -821,6 +852,12 @@ void HashJoinLocalSourceState::ExternalProbe(HashJoinGlobalSinkState &sink, Hash
821
852
  payload.ReferenceColumns(probe_chunk, payload_indices);
822
853
  auto precomputed_hashes = &probe_chunk.data.back();
823
854
 
855
+ if (sink.hash_table->Count() == 0 && !gstate.op.EmptyResultIfRHSIsEmpty()) {
856
+ gstate.op.ConstructEmptyJoinResult(sink.hash_table->join_type, sink.hash_table->has_null, payload, chunk);
857
+ empty_ht_probe_in_progress = true;
858
+ return;
859
+ }
860
+
824
861
  // Perform the probe
825
862
  scan_structure = sink.hash_table->Probe(join_keys, precomputed_hashes);
826
863
  scan_structure->Next(join_keys, payload, chunk);
@@ -828,27 +865,19 @@ void HashJoinLocalSourceState::ExternalProbe(HashJoinGlobalSinkState &sink, Hash
828
865
 
829
866
  void HashJoinLocalSourceState::ExternalScanHT(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate,
830
867
  DataChunk &chunk) {
831
- D_ASSERT(local_stage == HashJoinSourceStage::SCAN_HT && full_outer_in_progress != 0);
868
+ D_ASSERT(local_stage == HashJoinSourceStage::SCAN_HT);
832
869
 
833
- if (full_outer_found_entries != 0) {
834
- // Just did a scan, now gather
835
- sink.hash_table->GatherFullOuter(chunk, addresses, full_outer_found_entries);
836
- full_outer_found_entries = 0;
837
- return;
870
+ if (!full_outer_scan_state) {
871
+ full_outer_scan_state = make_uniq<JoinHTScanState>(sink.hash_table->GetDataCollection(),
872
+ full_outer_chunk_idx_from, full_outer_chunk_idx_to);
838
873
  }
874
+ sink.hash_table->ScanFullOuter(*full_outer_scan_state, addresses, chunk);
839
875
 
840
- lock_guard<mutex> guard(gstate.lock);
841
- auto &fo_ss = gstate.full_outer_scan;
842
- fo_ss.scanned += full_outer_in_progress;
843
- full_outer_in_progress = 0;
844
- }
845
-
846
- void HashJoinLocalSourceState::ScanFullOuter(HashJoinGlobalSinkState &sink, HashJoinGlobalSourceState &gstate) {
847
- auto &fo_ss = gstate.full_outer_scan;
848
- idx_t scan_index_before = fo_ss.scan_index;
849
- full_outer_found_entries = sink.hash_table->ScanFullOuter(fo_ss, addresses);
850
- idx_t scanned = fo_ss.scan_index - scan_index_before;
851
- full_outer_in_progress = scanned;
876
+ if (chunk.size() == 0) {
877
+ full_outer_scan_state = nullptr;
878
+ lock_guard<mutex> guard(gstate.lock);
879
+ gstate.full_outer_chunk_done += full_outer_chunk_idx_to - full_outer_chunk_idx_from;
880
+ }
852
881
  }
853
882
 
854
883
  void PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk &chunk, GlobalSourceState &gstate_p,
@@ -858,20 +887,12 @@ void PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk &chunk, Glob
858
887
  auto &lstate = lstate_p.Cast<HashJoinLocalSourceState>();
859
888
  sink.scanned_data = true;
860
889
 
861
- if (!sink.external) {
862
- if (IsRightOuterJoin(join_type)) {
863
- {
864
- lock_guard<mutex> guard(gstate.lock);
865
- lstate.ScanFullOuter(sink, gstate);
866
- }
867
- sink.hash_table->GatherFullOuter(chunk, lstate.addresses, lstate.full_outer_found_entries);
868
- }
890
+ if (!sink.external && !IsRightOuterJoin(join_type)) {
869
891
  return;
870
892
  }
871
893
 
872
- D_ASSERT(can_go_external);
873
894
  if (gstate.global_stage == HashJoinSourceStage::INIT) {
874
- gstate.Initialize(context.client, sink);
895
+ gstate.Initialize(sink);
875
896
  }
876
897
 
877
898
  // Any call to GetData must produce tuples, otherwise the pipeline executor thinks that we're done
@@ -880,6 +901,7 @@ void PhysicalHashJoin::GetData(ExecutionContext &context, DataChunk &chunk, Glob
880
901
  if (!lstate.TaskFinished() || gstate.AssignTask(sink, lstate)) {
881
902
  lstate.ExecuteTask(sink, gstate, chunk);
882
903
  } else {
904
+ lock_guard<mutex> guard(gstate.lock);
883
905
  gstate.TryPrepareNextStage(sink);
884
906
  }
885
907
  }