duckdb 0.7.2-dev2366.0 → 0.7.2-dev2430.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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/src/common/enums/physical_operator_type.cpp +2 -0
  3. package/src/duckdb/src/common/file_buffer.cpp +8 -0
  4. package/src/duckdb/src/common/radix_partitioning.cpp +34 -0
  5. package/src/duckdb/src/common/sort/partition_state.cpp +44 -124
  6. package/src/duckdb/src/common/sort/sorted_block.cpp +1 -1
  7. package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +144 -31
  8. package/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +698 -0
  9. package/src/duckdb/src/execution/physical_plan/plan_asof_join.cpp +7 -1
  10. package/src/duckdb/src/function/scalar/list/list_sort.cpp +30 -56
  11. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  12. package/src/duckdb/src/include/duckdb/common/enums/debug_initialize.hpp +17 -0
  13. package/src/duckdb/src/include/duckdb/common/enums/order_type.hpp +8 -0
  14. package/src/duckdb/src/include/duckdb/common/enums/physical_operator_type.hpp +1 -0
  15. package/src/duckdb/src/include/duckdb/common/file_buffer.hpp +3 -0
  16. package/src/duckdb/src/include/duckdb/common/radix_partitioning.hpp +3 -0
  17. package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +11 -60
  18. package/src/duckdb/src/include/duckdb/execution/operator/join/outer_join_marker.hpp +6 -1
  19. package/src/duckdb/src/include/duckdb/execution/operator/join/physical_asof_join.hpp +93 -0
  20. package/src/duckdb/src/include/duckdb/execution/physical_operator.hpp +1 -1
  21. package/src/duckdb/src/include/duckdb/main/client_config.hpp +2 -0
  22. package/src/duckdb/src/include/duckdb/main/config.hpp +7 -2
  23. package/src/duckdb/src/include/duckdb/main/settings.hpp +13 -3
  24. package/src/duckdb/src/include/duckdb/parser/expression/window_expression.hpp +4 -2
  25. package/src/duckdb/src/include/duckdb/storage/block_manager.hpp +1 -0
  26. package/src/duckdb/src/include/duckdb/storage/in_memory_block_manager.hpp +3 -0
  27. package/src/duckdb/src/include/duckdb/storage/partial_block_manager.hpp +2 -1
  28. package/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp +11 -5
  29. package/src/duckdb/src/main/config.cpp +26 -0
  30. package/src/duckdb/src/main/settings/settings.cpp +31 -8
  31. package/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp +2 -5
  32. package/src/duckdb/src/planner/binder/expression/bind_window_expression.cpp +6 -14
  33. package/src/duckdb/src/planner/binder/query_node/bind_select_node.cpp +2 -5
  34. package/src/duckdb/src/storage/buffer/block_manager.cpp +1 -2
  35. package/src/duckdb/src/storage/meta_block_writer.cpp +4 -0
  36. package/src/duckdb/src/storage/partial_block_manager.cpp +11 -4
  37. package/src/duckdb/src/storage/single_file_block_manager.cpp +16 -9
  38. package/src/duckdb/src/storage/standard_buffer_manager.cpp +5 -2
  39. package/src/duckdb/src/storage/storage_manager.cpp +7 -2
  40. package/src/duckdb/src/storage/table/column_checkpoint_state.cpp +21 -1
  41. package/src/duckdb/ub_src_execution_operator_join.cpp +2 -0
@@ -142,7 +142,7 @@ Value DebugForceNoCrossProduct::GetSetting(ClientContext &context) {
142
142
  }
143
143
 
144
144
  //===--------------------------------------------------------------------===//
145
- // Debug Ordered Aggregate Threshold
145
+ // Ordered Aggregate Threshold
146
146
  //===--------------------------------------------------------------------===//
147
147
 
148
148
  void OrderedAggregateThreshold::ResetLocal(ClientContext &context) {
@@ -185,6 +185,21 @@ Value DebugWindowMode::GetSetting(ClientContext &context) {
185
185
  return Value();
186
186
  }
187
187
 
188
+ //===--------------------------------------------------------------------===//
189
+ // Debug AsOf Join
190
+ //===--------------------------------------------------------------------===//
191
+ void DebugAsOfIEJoin::ResetLocal(ClientContext &context) {
192
+ ClientConfig::GetConfig(context).force_no_cross_product = ClientConfig().force_asof_iejoin;
193
+ }
194
+
195
+ void DebugAsOfIEJoin::SetLocal(ClientContext &context, const Value &input) {
196
+ ClientConfig::GetConfig(context).force_asof_iejoin = input.GetValue<bool>();
197
+ }
198
+
199
+ Value DebugAsOfIEJoin::GetSetting(ClientContext &context) {
200
+ return Value::BOOLEAN(ClientConfig::GetConfig(context).force_asof_iejoin);
201
+ }
202
+
188
203
  //===--------------------------------------------------------------------===//
189
204
  // Default Collation
190
205
  //===--------------------------------------------------------------------===//
@@ -253,14 +268,18 @@ void DefaultNullOrderSetting::SetGlobal(DatabaseInstance *db, DBConfig &config,
253
268
  auto parameter = StringUtil::Lower(input.ToString());
254
269
 
255
270
  if (parameter == "nulls_first" || parameter == "nulls first" || parameter == "null first" || parameter == "first") {
256
- config.options.default_null_order = OrderByNullType::NULLS_FIRST;
271
+ config.options.default_null_order = DefaultOrderByNullType::NULLS_FIRST;
257
272
  } else if (parameter == "nulls_last" || parameter == "nulls last" || parameter == "null last" ||
258
273
  parameter == "last") {
259
- config.options.default_null_order = OrderByNullType::NULLS_LAST;
274
+ config.options.default_null_order = DefaultOrderByNullType::NULLS_LAST;
275
+ } else if (parameter == "nulls_first_on_asc_last_on_desc" || parameter == "sqlite" || parameter == "mysql") {
276
+ config.options.default_null_order = DefaultOrderByNullType::NULLS_FIRST_ON_ASC_LAST_ON_DESC;
277
+ } else if (parameter == "nulls_last_on_asc_first_on_desc" || parameter == "postgres") {
278
+ config.options.default_null_order = DefaultOrderByNullType::NULLS_LAST_ON_ASC_FIRST_ON_DESC;
260
279
  } else {
261
- throw ParserException(
262
- "Unrecognized parameter for option NULL_ORDER \"%s\", expected either NULLS FIRST or NULLS LAST",
263
- parameter);
280
+ throw ParserException("Unrecognized parameter for option NULL_ORDER \"%s\", expected either NULLS FIRST, NULLS "
281
+ "LAST, SQLite, MySQL or Postgres",
282
+ parameter);
264
283
  }
265
284
  }
266
285
 
@@ -271,10 +290,14 @@ void DefaultNullOrderSetting::ResetGlobal(DatabaseInstance *db, DBConfig &config
271
290
  Value DefaultNullOrderSetting::GetSetting(ClientContext &context) {
272
291
  auto &config = DBConfig::GetConfig(context);
273
292
  switch (config.options.default_null_order) {
274
- case OrderByNullType::NULLS_FIRST:
293
+ case DefaultOrderByNullType::NULLS_FIRST:
275
294
  return "nulls_first";
276
- case OrderByNullType::NULLS_LAST:
295
+ case DefaultOrderByNullType::NULLS_LAST:
277
296
  return "nulls_last";
297
+ case DefaultOrderByNullType::NULLS_FIRST_ON_ASC_LAST_ON_DESC:
298
+ return "nulls_first_on_asc_last_on_desc";
299
+ case DefaultOrderByNullType::NULLS_LAST_ON_ASC_FIRST_ON_DESC:
300
+ return "nulls_last_on_asc_first_on_desc";
278
301
  default:
279
302
  throw InternalException("Unknown null order setting");
280
303
  }
@@ -215,11 +215,8 @@ BindResult BaseSelectBinder::BindAggregate(FunctionExpression &aggr, optional_pt
215
215
  auto &config = DBConfig::GetConfig(context);
216
216
  for (auto &order : aggr.order_bys->orders) {
217
217
  auto &order_expr = (BoundExpression &)*order.expression;
218
- const auto sense =
219
- (order.type == OrderType::ORDER_DEFAULT) ? config.options.default_order_type : order.type;
220
- const auto null_order = (order.null_order == OrderByNullType::ORDER_DEFAULT)
221
- ? config.options.default_null_order
222
- : order.null_order;
218
+ const auto sense = config.ResolveOrder(order.type);
219
+ const auto null_order = config.ResolveNullOrder(sense, order.null_order);
223
220
  order_bys->orders.emplace_back(sense, null_order, std::move(order_expr.expr));
224
221
  }
225
222
  }
@@ -64,14 +64,6 @@ static LogicalType ResolveWindowExpressionType(ExpressionType window_type, const
64
64
  }
65
65
  }
66
66
 
67
- static inline OrderType ResolveOrderType(const DBConfig &config, OrderType type) {
68
- return (type == OrderType::ORDER_DEFAULT) ? config.options.default_order_type : type;
69
- }
70
-
71
- static inline OrderByNullType ResolveNullOrder(const DBConfig &config, OrderByNullType null_order) {
72
- return (null_order == OrderByNullType::ORDER_DEFAULT) ? config.options.default_null_order : null_order;
73
- }
74
-
75
67
  static unique_ptr<Expression> GetExpression(unique_ptr<ParsedExpression> &expr) {
76
68
  if (!expr) {
77
69
  return nullptr;
@@ -231,12 +223,12 @@ BindResult BaseSelectBinder::BindWindow(WindowExpression &window, idx_t depth) {
231
223
  LogicalType start_type = LogicalType::BIGINT;
232
224
  if (window.start == WindowBoundary::EXPR_PRECEDING_RANGE) {
233
225
  D_ASSERT(window.orders.size() == 1);
234
- range_sense = ResolveOrderType(config, window.orders[0].type);
226
+ range_sense = config.ResolveOrder(window.orders[0].type);
235
227
  const auto name = (range_sense == OrderType::ASCENDING) ? "-" : "+";
236
228
  start_type = BindRangeExpression(context, name, window.start_expr, window.orders[0].expression);
237
229
  } else if (window.start == WindowBoundary::EXPR_FOLLOWING_RANGE) {
238
230
  D_ASSERT(window.orders.size() == 1);
239
- range_sense = ResolveOrderType(config, window.orders[0].type);
231
+ range_sense = config.ResolveOrder(window.orders[0].type);
240
232
  const auto name = (range_sense == OrderType::ASCENDING) ? "+" : "-";
241
233
  start_type = BindRangeExpression(context, name, window.start_expr, window.orders[0].expression);
242
234
  }
@@ -244,12 +236,12 @@ BindResult BaseSelectBinder::BindWindow(WindowExpression &window, idx_t depth) {
244
236
  LogicalType end_type = LogicalType::BIGINT;
245
237
  if (window.end == WindowBoundary::EXPR_PRECEDING_RANGE) {
246
238
  D_ASSERT(window.orders.size() == 1);
247
- range_sense = ResolveOrderType(config, window.orders[0].type);
239
+ range_sense = config.ResolveOrder(window.orders[0].type);
248
240
  const auto name = (range_sense == OrderType::ASCENDING) ? "-" : "+";
249
241
  end_type = BindRangeExpression(context, name, window.end_expr, window.orders[0].expression);
250
242
  } else if (window.end == WindowBoundary::EXPR_FOLLOWING_RANGE) {
251
243
  D_ASSERT(window.orders.size() == 1);
252
- range_sense = ResolveOrderType(config, window.orders[0].type);
244
+ range_sense = config.ResolveOrder(window.orders[0].type);
253
245
  const auto name = (range_sense == OrderType::ASCENDING) ? "+" : "-";
254
246
  end_type = BindRangeExpression(context, name, window.end_expr, window.orders[0].expression);
255
247
  }
@@ -276,8 +268,8 @@ BindResult BaseSelectBinder::BindWindow(WindowExpression &window, idx_t depth) {
276
268
  }
277
269
 
278
270
  for (auto &order : window.orders) {
279
- auto type = ResolveOrderType(config, order.type);
280
- auto null_order = ResolveNullOrder(config, order.null_order);
271
+ auto type = config.ResolveOrder(order.type);
272
+ auto null_order = config.ResolveNullOrder(type, order.null_order);
281
273
  auto expression = GetExpression(order.expression);
282
274
  result->orders.emplace_back(type, null_order, std::move(expression));
283
275
  }
@@ -158,11 +158,8 @@ void Binder::BindModifiers(OrderBinder &order_binder, QueryNode &statement, Boun
158
158
  vector<unique_ptr<ParsedExpression>> order_list;
159
159
  order_binders[0]->ExpandStarExpression(std::move(order_node.expression), order_list);
160
160
 
161
- auto type =
162
- order_node.type == OrderType::ORDER_DEFAULT ? config.options.default_order_type : order_node.type;
163
- auto null_order = order_node.null_order == OrderByNullType::ORDER_DEFAULT
164
- ? config.options.default_null_order
165
- : order_node.null_order;
161
+ auto type = config.ResolveOrder(order_node.type);
162
+ auto null_order = config.ResolveNullOrder(type, order_node.null_order);
166
163
  for (auto &order_expr : order_list) {
167
164
  auto bound_expr = BindOrderExpression(order_binder, std::move(order_expr));
168
165
  if (!bound_expr) {
@@ -33,7 +33,6 @@ void BlockManager::ClearMetaBlockHandles() {
33
33
  }
34
34
 
35
35
  shared_ptr<BlockHandle> BlockManager::ConvertToPersistent(block_id_t block_id, shared_ptr<BlockHandle> old_block) {
36
-
37
36
  // pin the old block to ensure we have it loaded in memory
38
37
  auto old_handle = buffer_manager.Pin(old_block);
39
38
  D_ASSERT(old_block->state == BlockState::BLOCK_LOADED);
@@ -50,7 +49,7 @@ shared_ptr<BlockHandle> BlockManager::ConvertToPersistent(block_id_t block_id, s
50
49
 
51
50
  // move the data from the old block into data for the new block
52
51
  new_block->state = BlockState::BLOCK_LOADED;
53
- new_block->buffer = CreateBlock(block_id, old_block->buffer.get());
52
+ new_block->buffer = ConvertBlock(block_id, *old_block->buffer);
54
53
  new_block->memory_usage = old_block->memory_usage;
55
54
  new_block->memory_charge = std::move(old_block->memory_charge);
56
55
 
@@ -35,6 +35,10 @@ BlockPointer MetaBlockWriter::GetBlockPointer() {
35
35
  }
36
36
 
37
37
  void MetaBlockWriter::Flush() {
38
+ if (offset < block->size) {
39
+ // clear remaining bytes of block (if any)
40
+ memset(block->buffer + offset, 0, block->size - offset);
41
+ }
38
42
  AdvanceBlock();
39
43
  block = nullptr;
40
44
  }
@@ -54,9 +54,14 @@ bool PartialBlockManager::GetPartialBlock(idx_t segment_size, unique_ptr<Partial
54
54
  }
55
55
 
56
56
  void PartialBlockManager::RegisterPartialBlock(PartialBlockAllocation &&allocation) {
57
- auto &state(allocation.partial_block->state);
57
+ auto &state = allocation.partial_block->state;
58
58
  if (state.block_use_count < max_use_count) {
59
- auto new_size = AlignValue(allocation.allocation_size + state.offset_in_block);
59
+ auto unaligned_size = allocation.allocation_size + state.offset_in_block;
60
+ auto new_size = AlignValue(unaligned_size);
61
+ if (new_size != unaligned_size) {
62
+ // register the uninitialized region so we can correctly initialize it before writing to disk
63
+ allocation.partial_block->AddUninitializedRegion(unaligned_size, new_size);
64
+ }
60
65
  state.offset_in_block = new_size;
61
66
  auto new_space_left = state.block_size - new_size;
62
67
  // check if the block is STILL partially filled after adding the segment_size
@@ -65,22 +70,24 @@ void PartialBlockManager::RegisterPartialBlock(PartialBlockAllocation &&allocati
65
70
  partially_filled_blocks.insert(make_pair(new_space_left, std::move(allocation.partial_block)));
66
71
  }
67
72
  }
73
+ idx_t free_space = state.block_size - state.offset_in_block;
68
74
  auto block_to_free = std::move(allocation.partial_block);
69
75
  if (!block_to_free && partially_filled_blocks.size() > MAX_BLOCK_MAP_SIZE) {
70
76
  // Free the page with the least space free.
71
77
  auto itr = partially_filled_blocks.begin();
72
78
  block_to_free = std::move(itr->second);
79
+ free_space = state.block_size - itr->first;
73
80
  partially_filled_blocks.erase(itr);
74
81
  }
75
82
  // Flush any block that we're not going to reuse.
76
83
  if (block_to_free) {
77
- block_to_free->Flush();
84
+ block_to_free->Flush(free_space);
78
85
  }
79
86
  }
80
87
 
81
88
  void PartialBlockManager::FlushPartialBlocks() {
82
89
  for (auto &e : partially_filled_blocks) {
83
- e.second->Flush();
90
+ e.second->Flush(e.first);
84
91
  }
85
92
  partially_filled_blocks.clear();
86
93
  }
@@ -107,15 +107,15 @@ T DeserializeHeaderStructure(data_ptr_t ptr) {
107
107
  return T::Deserialize(source);
108
108
  }
109
109
 
110
- SingleFileBlockManager::SingleFileBlockManager(AttachedDatabase &db, string path_p, bool read_only, bool use_direct_io)
110
+ SingleFileBlockManager::SingleFileBlockManager(AttachedDatabase &db, string path_p, StorageManagerOptions options)
111
111
  : BlockManager(BufferManager::GetBufferManager(db)), db(db), path(std::move(path_p)),
112
112
  header_buffer(Allocator::Get(db), FileBufferType::MANAGED_BUFFER,
113
113
  Storage::FILE_HEADER_SIZE - Storage::BLOCK_HEADER_SIZE),
114
- iteration_count(0), read_only(read_only), use_direct_io(use_direct_io) {
114
+ iteration_count(0), options(options) {
115
115
  }
116
116
 
117
117
  void SingleFileBlockManager::GetFileFlags(uint8_t &flags, FileLockType &lock, bool create_new) {
118
- if (read_only) {
118
+ if (options.read_only) {
119
119
  D_ASSERT(!create_new);
120
120
  flags = FileFlags::FILE_FLAGS_READ;
121
121
  lock = FileLockType::READ_LOCK;
@@ -126,7 +126,7 @@ void SingleFileBlockManager::GetFileFlags(uint8_t &flags, FileLockType &lock, bo
126
126
  flags |= FileFlags::FILE_FLAGS_FILE_CREATE;
127
127
  }
128
128
  }
129
- if (use_direct_io) {
129
+ if (options.use_direct_io) {
130
130
  flags |= FileFlags::FILE_FLAGS_DIRECT_IO;
131
131
  }
132
132
  }
@@ -241,7 +241,7 @@ void SingleFileBlockManager::Initialize(DatabaseHeader &header) {
241
241
  }
242
242
 
243
243
  void SingleFileBlockManager::LoadFreeList() {
244
- if (read_only) {
244
+ if (options.read_only) {
245
245
  // no need to load free list for read only db
246
246
  return;
247
247
  }
@@ -343,13 +343,20 @@ idx_t SingleFileBlockManager::FreeBlocks() {
343
343
  return free_list.size();
344
344
  }
345
345
 
346
+ unique_ptr<Block> SingleFileBlockManager::ConvertBlock(block_id_t block_id, FileBuffer &source_buffer) {
347
+ D_ASSERT(source_buffer.AllocSize() == Storage::BLOCK_ALLOC_SIZE);
348
+ return make_uniq<Block>(source_buffer, block_id);
349
+ }
350
+
346
351
  unique_ptr<Block> SingleFileBlockManager::CreateBlock(block_id_t block_id, FileBuffer *source_buffer) {
352
+ unique_ptr<Block> result;
347
353
  if (source_buffer) {
348
- D_ASSERT(source_buffer->AllocSize() == Storage::BLOCK_ALLOC_SIZE);
349
- return make_uniq<Block>(*source_buffer, block_id);
354
+ result = ConvertBlock(block_id, *source_buffer);
350
355
  } else {
351
- return make_uniq<Block>(Allocator::Get(db), block_id);
356
+ result = make_uniq<Block>(Allocator::Get(db), block_id);
352
357
  }
358
+ result->Initialize(options.debug_initialize);
359
+ return result;
353
360
  }
354
361
 
355
362
  void SingleFileBlockManager::Read(Block &block) {
@@ -459,7 +466,7 @@ void SingleFileBlockManager::WriteHeader(DatabaseHeader header) {
459
466
  throw FatalException("Checkpoint aborted after free list write because of PRAGMA checkpoint_abort flag");
460
467
  }
461
468
 
462
- if (!use_direct_io) {
469
+ if (!options.use_direct_io) {
463
470
  // if we are not using Direct IO we need to fsync BEFORE we write the header to ensure that all the previous
464
471
  // blocks are written as well
465
472
  handle->Sync();
@@ -20,14 +20,17 @@ struct BufferAllocatorData : PrivateAllocatorData {
20
20
 
21
21
  unique_ptr<FileBuffer> StandardBufferManager::ConstructManagedBuffer(idx_t size, unique_ptr<FileBuffer> &&source,
22
22
  FileBufferType type) {
23
+ unique_ptr<FileBuffer> result;
23
24
  if (source) {
24
25
  auto tmp = std::move(source);
25
26
  D_ASSERT(tmp->AllocSize() == BufferManager::GetAllocSize(size));
26
- return make_uniq<FileBuffer>(*tmp, type);
27
+ result = make_uniq<FileBuffer>(*tmp, type);
27
28
  } else {
28
29
  // no re-usable buffer: allocate a new buffer
29
- return make_uniq<FileBuffer>(Allocator::Get(db), type, size);
30
+ result = make_uniq<FileBuffer>(Allocator::Get(db), type, size);
30
31
  }
32
+ result->Initialize(DBConfig::GetConfig(db).options.debug_initialize);
33
+ return result;
31
34
  }
32
35
 
33
36
  class TemporaryFileManager;
@@ -95,6 +95,11 @@ void SingleFileStorageManager::LoadDatabase() {
95
95
  auto &fs = FileSystem::Get(db);
96
96
  auto &config = DBConfig::Get(db);
97
97
  bool truncate_wal = false;
98
+
99
+ StorageManagerOptions options;
100
+ options.read_only = read_only;
101
+ options.use_direct_io = config.options.use_direct_io;
102
+ options.debug_initialize = config.options.debug_initialize;
98
103
  // first check if the database exists
99
104
  if (!fs.FileExists(path)) {
100
105
  if (read_only) {
@@ -107,13 +112,13 @@ void SingleFileStorageManager::LoadDatabase() {
107
112
  fs.RemoveFile(wal_path);
108
113
  }
109
114
  // initialize the block manager while creating a new db file
110
- auto sf_block_manager = make_uniq<SingleFileBlockManager>(db, path, read_only, config.options.use_direct_io);
115
+ auto sf_block_manager = make_uniq<SingleFileBlockManager>(db, path, options);
111
116
  sf_block_manager->CreateNewDatabase();
112
117
  block_manager = std::move(sf_block_manager);
113
118
  table_io_manager = make_uniq<SingleFileTableIOManager>(*block_manager);
114
119
  } else {
115
120
  // initialize the block manager while loading the current db file
116
- auto sf_block_manager = make_uniq<SingleFileBlockManager>(db, path, read_only, config.options.use_direct_io);
121
+ auto sf_block_manager = make_uniq<SingleFileBlockManager>(db, path, options);
117
122
  sf_block_manager->LoadExistingDatabase();
118
123
  block_manager = std::move(sf_block_manager);
119
124
  table_io_manager = make_uniq<SingleFileTableIOManager>(*block_manager);
@@ -50,16 +50,36 @@ public:
50
50
  BlockManager &block_manager;
51
51
  vector<PartialColumnSegment> tail_segments;
52
52
 
53
+ private:
54
+ struct UninitializedRegion {
55
+ idx_t start;
56
+ idx_t end;
57
+ };
58
+ vector<UninitializedRegion> uninitialized_regions;
59
+
53
60
  public:
54
61
  bool IsFlushed() {
55
62
  // first_segment is zeroed on Flush
56
63
  return !first_segment;
57
64
  }
58
65
 
59
- void Flush() override {
66
+ void AddUninitializedRegion(idx_t start, idx_t end) override {
67
+ uninitialized_regions.push_back({start, end});
68
+ }
69
+
70
+ void Flush(idx_t free_space_left) override {
60
71
  // At this point, we've already copied all data from tail_segments
61
72
  // into the page owned by first_segment. We flush all segment data to
62
73
  // disk with the following call.
74
+ if (free_space_left > 0 || !uninitialized_regions.empty()) {
75
+ auto handle = block_manager.buffer_manager.Pin(first_segment->block);
76
+ // memset any uninitialized regions
77
+ for (auto &uninitialized : uninitialized_regions) {
78
+ memset(handle.Ptr() + uninitialized.start, 0, uninitialized.end - uninitialized.start);
79
+ }
80
+ // memset any free space at the end of the block to 0 prior to writing to disk
81
+ memset(handle.Ptr() + Storage::BLOCK_SIZE - free_space_left, 0, free_space_left);
82
+ }
63
83
  first_data->IncrementVersion();
64
84
  first_segment->ConvertToPersistent(&block_manager, state.block_id);
65
85
  // Now that the page is persistent, update tail_segments to point to the
@@ -1,5 +1,7 @@
1
1
  #include "src/execution/operator/join/outer_join_marker.cpp"
2
2
 
3
+ #include "src/execution/operator/join/physical_asof_join.cpp"
4
+
3
5
  #include "src/execution/operator/join/physical_blockwise_nl_join.cpp"
4
6
 
5
7
  #include "src/execution/operator/join/physical_comparison_join.cpp"