duckdb 0.8.2-dev2509.0 → 0.8.2-dev2669.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 (64) hide show
  1. package/binding.gyp +1 -0
  2. package/package.json +1 -1
  3. package/src/duckdb/extension/icu/icu-datepart.cpp +3 -3
  4. package/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +1 -1
  5. package/src/duckdb/src/catalog/default/default_functions.cpp +5 -0
  6. package/src/duckdb/src/common/enum_util.cpp +35 -1
  7. package/src/duckdb/src/core_functions/scalar/list/array_slice.cpp +6 -0
  8. package/src/duckdb/src/execution/expression_executor/execute_parameter.cpp +2 -2
  9. package/src/duckdb/src/execution/index/art/art.cpp +43 -31
  10. package/src/duckdb/src/execution/index/art/leaf.cpp +47 -33
  11. package/src/duckdb/src/execution/index/art/node.cpp +31 -24
  12. package/src/duckdb/src/execution/index/art/prefix.cpp +100 -16
  13. package/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +54 -31
  14. package/src/duckdb/src/execution/physical_plan/plan_create_index.cpp +32 -15
  15. package/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp +57 -0
  16. package/src/duckdb/src/function/table/arrow.cpp +95 -92
  17. package/src/duckdb/src/function/table/arrow_conversion.cpp +45 -68
  18. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  19. package/src/duckdb/src/include/duckdb/common/case_insensitive_map.hpp +1 -0
  20. package/src/duckdb/src/include/duckdb/common/enum_util.hpp +8 -0
  21. package/src/duckdb/src/include/duckdb/common/helper.hpp +8 -3
  22. package/src/duckdb/src/include/duckdb/common/types/value.hpp +4 -1
  23. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +7 -5
  24. package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +6 -6
  25. package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +6 -0
  26. package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +9 -11
  27. package/src/duckdb/src/include/duckdb/execution/operator/schema/physical_create_index.hpp +8 -1
  28. package/src/duckdb/src/include/duckdb/function/table/arrow/arrow_duck_schema.hpp +99 -0
  29. package/src/duckdb/src/include/duckdb/function/table/arrow.hpp +6 -36
  30. package/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp +3 -1
  31. package/src/duckdb/src/include/duckdb/main/client_context.hpp +15 -14
  32. package/src/duckdb/src/include/duckdb/main/prepared_statement.hpp +73 -5
  33. package/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp +6 -6
  34. package/src/duckdb/src/include/duckdb/parser/expression/parameter_expression.hpp +17 -1
  35. package/src/duckdb/src/include/duckdb/parser/statement/execute_statement.hpp +1 -1
  36. package/src/duckdb/src/include/duckdb/parser/transformer.hpp +5 -3
  37. package/src/duckdb/src/include/duckdb/planner/bound_parameter_map.hpp +2 -1
  38. package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_data.hpp +20 -5
  39. package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_expression.hpp +3 -3
  40. package/src/duckdb/src/include/duckdb/planner/planner.hpp +4 -3
  41. package/src/duckdb/src/include/duckdb/storage/object_cache.hpp +1 -1
  42. package/src/duckdb/src/include/duckdb/verification/prepared_statement_verifier.hpp +1 -1
  43. package/src/duckdb/src/include/duckdb.h +16 -0
  44. package/src/duckdb/src/main/capi/pending-c.cpp +6 -0
  45. package/src/duckdb/src/main/capi/prepared-c.cpp +52 -4
  46. package/src/duckdb/src/main/client_context.cpp +27 -17
  47. package/src/duckdb/src/main/client_verify.cpp +17 -0
  48. package/src/duckdb/src/main/prepared_statement.cpp +38 -11
  49. package/src/duckdb/src/main/prepared_statement_data.cpp +23 -18
  50. package/src/duckdb/src/parser/expression/parameter_expression.cpp +7 -7
  51. package/src/duckdb/src/parser/statement/execute_statement.cpp +2 -2
  52. package/src/duckdb/src/parser/transform/expression/transform_param_ref.cpp +45 -26
  53. package/src/duckdb/src/parser/transform/statement/transform_prepare.cpp +28 -6
  54. package/src/duckdb/src/parser/transformer.cpp +27 -9
  55. package/src/duckdb/src/planner/binder/expression/bind_parameter_expression.cpp +10 -10
  56. package/src/duckdb/src/planner/binder/statement/bind_execute.cpp +13 -7
  57. package/src/duckdb/src/planner/expression/bound_parameter_expression.cpp +13 -13
  58. package/src/duckdb/src/planner/planner.cpp +7 -6
  59. package/src/duckdb/src/storage/checkpoint_manager.cpp +1 -1
  60. package/src/duckdb/src/storage/serialization/serialize_expression.cpp +3 -3
  61. package/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp +2 -2
  62. package/src/duckdb/src/verification/prepared_statement_verifier.cpp +16 -11
  63. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +9652 -9482
  64. package/src/duckdb/ub_src_function_table_arrow.cpp +2 -0
package/binding.gyp CHANGED
@@ -87,6 +87,7 @@
87
87
  "src/duckdb/ub_src_function_scalar_string_regexp.cpp",
88
88
  "src/duckdb/ub_src_function_scalar_struct.cpp",
89
89
  "src/duckdb/ub_src_function_scalar_system.cpp",
90
+ "src/duckdb/ub_src_function_table_arrow.cpp",
90
91
  "src/duckdb/ub_src_function_table.cpp",
91
92
  "src/duckdb/ub_src_function_table_system.cpp",
92
93
  "src/duckdb/ub_src_function_table_version.cpp",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
4
  "types": "./lib/duckdb.d.ts",
5
- "version": "0.8.2-dev2509.0",
5
+ "version": "0.8.2-dev2669.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -54,13 +54,13 @@ struct ICUDatePart : public ICUDateFunc {
54
54
  }
55
55
 
56
56
  static int64_t ExtractDayOfWeek(icu::Calendar *calendar, const uint64_t micros) {
57
- calendar->setFirstDayOfWeek(UCAL_SUNDAY);
57
+ // [Sun(0), Sat(6)]
58
58
  return ExtractField(calendar, UCAL_DAY_OF_WEEK) - UCAL_SUNDAY;
59
59
  }
60
60
 
61
61
  static int64_t ExtractISODayOfWeek(icu::Calendar *calendar, const uint64_t micros) {
62
- calendar->setFirstDayOfWeek(UCAL_MONDAY);
63
- return ExtractField(calendar, UCAL_DAY_OF_WEEK);
62
+ // [Mon(1), Sun(7)]
63
+ return 1 + (ExtractField(calendar, UCAL_DAY_OF_WEEK) + 7 - UCAL_MONDAY) % 7;
64
64
  }
65
65
 
66
66
  static int64_t ExtractWeek(icu::Calendar *calendar, const uint64_t micros) {
@@ -43,7 +43,7 @@ void AddDataTableIndex(DataTable &storage, const ColumnList &columns, const vect
43
43
  // create an adaptive radix tree around the expressions
44
44
  if (index_block) {
45
45
  art = make_uniq<ART>(column_ids, TableIOManager::Get(storage), std::move(unbound_expressions), constraint_type,
46
- storage.db, index_block->block_id, index_block->offset);
46
+ storage.db, nullptr, index_block->block_id, index_block->offset);
47
47
  } else {
48
48
  art = make_uniq<ART>(column_ids, TableIOManager::Get(storage), std::move(unbound_expressions), constraint_type,
49
49
  storage.db);
@@ -98,6 +98,11 @@ static DefaultMacro internal_macros[] = {
98
98
  {DEFAULT_SCHEMA, "fmod", {"x", "y", nullptr}, "(x-y*floor(x/y))"},
99
99
  {DEFAULT_SCHEMA, "count_if", {"l", nullptr}, "sum(if(l, 1, 0))"},
100
100
  {DEFAULT_SCHEMA, "split_part", {"string", "delimiter", "position", nullptr}, "coalesce(string_split(string, delimiter)[position],'')"},
101
+ {DEFAULT_SCHEMA, "geomean", {"x", nullptr}, "exp(avg(ln(x)))"},
102
+ {DEFAULT_SCHEMA, "geometric_mean", {"x", nullptr}, "geomean(x)"},
103
+
104
+ {DEFAULT_SCHEMA, "list_reverse", {"l", nullptr}, "l[:-:-1]"},
105
+ {DEFAULT_SCHEMA, "array_reverse", {"l", nullptr}, "list_reverse(l)"},
101
106
 
102
107
  // FIXME implement as actual function if we encounter a lot of performance issues. Complexity now: n * m, with hashing possibly n + m
103
108
  {DEFAULT_SCHEMA, "list_intersect", {"l1", "l2", nullptr}, "list_filter(l1, (x) -> list_contains(l2, x))"},
@@ -71,7 +71,7 @@
71
71
  #include "duckdb/function/macro_function.hpp"
72
72
  #include "duckdb/function/scalar/compressed_materialization_functions.hpp"
73
73
  #include "duckdb/function/scalar/strftime_format.hpp"
74
- #include "duckdb/function/table/arrow.hpp"
74
+ #include "duckdb/function/table/arrow/arrow_duck_schema.hpp"
75
75
  #include "duckdb/main/appender.hpp"
76
76
  #include "duckdb/main/capi/capi_internal.hpp"
77
77
  #include "duckdb/main/config.hpp"
@@ -81,6 +81,7 @@
81
81
  #include "duckdb/parallel/interrupt.hpp"
82
82
  #include "duckdb/parallel/task.hpp"
83
83
  #include "duckdb/parser/constraint.hpp"
84
+ #include "duckdb/parser/expression/parameter_expression.hpp"
84
85
  #include "duckdb/parser/expression/window_expression.hpp"
85
86
  #include "duckdb/parser/parsed_data/alter_info.hpp"
86
87
  #include "duckdb/parser/parsed_data/alter_scalar_function_info.hpp"
@@ -4404,6 +4405,39 @@ PragmaType EnumUtil::FromString<PragmaType>(const char *value) {
4404
4405
  throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value));
4405
4406
  }
4406
4407
 
4408
+ template<>
4409
+ const char* EnumUtil::ToChars<PreparedParamType>(PreparedParamType value) {
4410
+ switch(value) {
4411
+ case PreparedParamType::AUTO_INCREMENT:
4412
+ return "AUTO_INCREMENT";
4413
+ case PreparedParamType::POSITIONAL:
4414
+ return "POSITIONAL";
4415
+ case PreparedParamType::NAMED:
4416
+ return "NAMED";
4417
+ case PreparedParamType::INVALID:
4418
+ return "INVALID";
4419
+ default:
4420
+ throw NotImplementedException(StringUtil::Format("Enum value: '%d' not implemented", value));
4421
+ }
4422
+ }
4423
+
4424
+ template<>
4425
+ PreparedParamType EnumUtil::FromString<PreparedParamType>(const char *value) {
4426
+ if (StringUtil::Equals(value, "AUTO_INCREMENT")) {
4427
+ return PreparedParamType::AUTO_INCREMENT;
4428
+ }
4429
+ if (StringUtil::Equals(value, "POSITIONAL")) {
4430
+ return PreparedParamType::POSITIONAL;
4431
+ }
4432
+ if (StringUtil::Equals(value, "NAMED")) {
4433
+ return PreparedParamType::NAMED;
4434
+ }
4435
+ if (StringUtil::Equals(value, "INVALID")) {
4436
+ return PreparedParamType::INVALID;
4437
+ }
4438
+ throw NotImplementedException(StringUtil::Format("Enum value: '%s' not implemented", value));
4439
+ }
4440
+
4407
4441
  template<>
4408
4442
  const char* EnumUtil::ToChars<ProfilerPrintFormat>(ProfilerPrintFormat value) {
4409
4443
  switch(value) {
@@ -308,6 +308,12 @@ static void ArraySliceFunction(DataChunk &args, ExpressionState &state, Vector &
308
308
  auto count = args.size();
309
309
 
310
310
  Vector &list_or_str_vector = args.data[0];
311
+ if (list_or_str_vector.GetType().id() == LogicalTypeId::SQLNULL) {
312
+ auto &result_validity = FlatVector::Validity(result);
313
+ result_validity.SetInvalid(0);
314
+ return;
315
+ }
316
+
311
317
  Vector &begin_vector = args.data[1];
312
318
  Vector &end_vector = args.data[2];
313
319
 
@@ -15,8 +15,8 @@ void ExpressionExecutor::Execute(const BoundParameterExpression &expr, Expressio
15
15
  const SelectionVector *sel, idx_t count, Vector &result) {
16
16
  D_ASSERT(expr.parameter_data);
17
17
  D_ASSERT(expr.parameter_data->return_type == expr.return_type);
18
- D_ASSERT(expr.parameter_data->value.type() == expr.return_type);
19
- result.Reference(expr.parameter_data->value);
18
+ D_ASSERT(expr.parameter_data->GetValue().type() == expr.return_type);
19
+ result.Reference(expr.parameter_data->GetValue());
20
20
  }
21
21
 
22
22
  } // namespace duckdb
@@ -33,21 +33,27 @@ struct ARTIndexScanState : public IndexScanState {
33
33
 
34
34
  ART::ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager,
35
35
  const vector<unique_ptr<Expression>> &unbound_expressions, const IndexConstraintType constraint_type,
36
- AttachedDatabase &db, const idx_t block_id, const idx_t block_offset)
36
+ AttachedDatabase &db, const shared_ptr<vector<FixedSizeAllocator>> &allocators_ptr, const idx_t block_id,
37
+ const idx_t block_offset)
37
38
 
38
- : Index(db, IndexType::ART, table_io_manager, column_ids, unbound_expressions, constraint_type) {
39
+ : Index(db, IndexType::ART, table_io_manager, column_ids, unbound_expressions, constraint_type),
40
+ allocators(allocators_ptr), owns_data(false) {
39
41
 
40
42
  if (!Radix::IsLittleEndian()) {
41
43
  throw NotImplementedException("ART indexes are not supported on big endian architectures");
42
44
  }
43
45
 
44
46
  // initialize all allocators
45
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Prefix), buffer_manager.GetBufferAllocator()));
46
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Leaf), buffer_manager.GetBufferAllocator()));
47
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Node4), buffer_manager.GetBufferAllocator()));
48
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Node16), buffer_manager.GetBufferAllocator()));
49
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Node48), buffer_manager.GetBufferAllocator()));
50
- allocators.emplace_back(make_uniq<FixedSizeAllocator>(sizeof(Node256), buffer_manager.GetBufferAllocator()));
47
+ if (!allocators) {
48
+ owns_data = true;
49
+ allocators = make_shared<vector<FixedSizeAllocator>>();
50
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Prefix), buffer_manager.GetBufferAllocator()));
51
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Leaf), buffer_manager.GetBufferAllocator()));
52
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Node4), buffer_manager.GetBufferAllocator()));
53
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Node16), buffer_manager.GetBufferAllocator()));
54
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Node48), buffer_manager.GetBufferAllocator()));
55
+ allocators->emplace_back(FixedSizeAllocator(sizeof(Node256), buffer_manager.GetBufferAllocator()));
56
+ }
51
57
 
52
58
  // set the root node of the tree
53
59
  tree = make_uniq<Node>();
@@ -986,26 +992,28 @@ BlockPointer ART::Serialize(MetaBlockWriter &writer) {
986
992
 
987
993
  void ART::InitializeVacuum(ARTFlags &flags) {
988
994
 
989
- flags.vacuum_flags.reserve(allocators.size());
990
- for (auto &allocator : allocators) {
991
- flags.vacuum_flags.push_back(allocator->InitializeVacuum());
995
+ flags.vacuum_flags.reserve(allocators->size());
996
+ for (auto &allocator : *allocators) {
997
+ flags.vacuum_flags.push_back(allocator.InitializeVacuum());
992
998
  }
993
999
  }
994
1000
 
995
1001
  void ART::FinalizeVacuum(const ARTFlags &flags) {
996
1002
 
997
- for (idx_t i = 0; i < allocators.size(); i++) {
1003
+ for (idx_t i = 0; i < allocators->size(); i++) {
998
1004
  if (flags.vacuum_flags[i]) {
999
- allocators[i]->FinalizeVacuum();
1005
+ (*allocators)[i].FinalizeVacuum();
1000
1006
  }
1001
1007
  }
1002
1008
  }
1003
1009
 
1004
1010
  void ART::Vacuum(IndexLock &state) {
1005
1011
 
1012
+ D_ASSERT(owns_data);
1013
+
1006
1014
  if (!tree->IsSet()) {
1007
- for (auto &allocator : allocators) {
1008
- allocator->Reset();
1015
+ for (auto &allocator : *allocators) {
1016
+ allocator.Reset();
1009
1017
  }
1010
1018
  return;
1011
1019
  }
@@ -1032,8 +1040,8 @@ void ART::Vacuum(IndexLock &state) {
1032
1040
  // finalize the vacuum operation
1033
1041
  FinalizeVacuum(flags);
1034
1042
 
1035
- for (auto &allocator : allocators) {
1036
- allocator->Verify();
1043
+ for (auto &allocator : *allocators) {
1044
+ allocator.Verify();
1037
1045
  }
1038
1046
  }
1039
1047
 
@@ -1043,9 +1051,11 @@ void ART::Vacuum(IndexLock &state) {
1043
1051
 
1044
1052
  void ART::InitializeMerge(ARTFlags &flags) {
1045
1053
 
1046
- flags.merge_buffer_counts.reserve(allocators.size());
1047
- for (auto &allocator : allocators) {
1048
- flags.merge_buffer_counts.emplace_back(allocator->buffers.size());
1054
+ D_ASSERT(owns_data);
1055
+
1056
+ flags.merge_buffer_counts.reserve(allocators->size());
1057
+ for (auto &allocator : *allocators) {
1058
+ flags.merge_buffer_counts.emplace_back(allocator.buffers.size());
1049
1059
  }
1050
1060
  }
1051
1061
 
@@ -1056,16 +1066,18 @@ bool ART::MergeIndexes(IndexLock &state, Index &other_index) {
1056
1066
  return true;
1057
1067
  }
1058
1068
 
1059
- if (tree->IsSet()) {
1060
- // fully deserialize other_index, and traverse it to increment its buffer IDs
1061
- ARTFlags flags;
1062
- InitializeMerge(flags);
1063
- other_art.tree->InitializeMerge(other_art, flags);
1064
- }
1069
+ if (other_art.owns_data) {
1070
+ if (tree->IsSet()) {
1071
+ // fully deserialize other_index, and traverse it to increment its buffer IDs
1072
+ ARTFlags flags;
1073
+ InitializeMerge(flags);
1074
+ other_art.tree->InitializeMerge(other_art, flags);
1075
+ }
1065
1076
 
1066
- // merge the node storage
1067
- for (idx_t i = 0; i < allocators.size(); i++) {
1068
- allocators[i]->Merge(*other_art.allocators[i]);
1077
+ // merge the node storage
1078
+ for (idx_t i = 0; i < allocators->size(); i++) {
1079
+ (*allocators)[i].Merge((*other_art.allocators)[i]);
1080
+ }
1069
1081
  }
1070
1082
 
1071
1083
  // merge the ARTs
@@ -1073,8 +1085,8 @@ bool ART::MergeIndexes(IndexLock &state, Index &other_index) {
1073
1085
  return false;
1074
1086
  }
1075
1087
 
1076
- for (auto &allocator : allocators) {
1077
- allocator->Verify();
1088
+ for (auto &allocator : *allocators) {
1089
+ allocator.Verify();
1078
1090
  }
1079
1091
  return true;
1080
1092
  }
@@ -42,19 +42,30 @@ void Leaf::New(ART &art, reference<Node> &node, const row_t *row_ids, idx_t coun
42
42
 
43
43
  void Leaf::Free(ART &art, Node &node) {
44
44
 
45
- D_ASSERT(node.IsSet() && !node.IsSerialized());
46
- auto &child = Leaf::Get(art, node).ptr;
47
- Node::Free(art, child);
45
+ Node current_node = node;
46
+ Node next_node;
47
+ while (current_node.IsSet() && !current_node.IsSerialized()) {
48
+ next_node = Leaf::Get(art, current_node).ptr;
49
+ Node::GetAllocator(art, NType::LEAF).Free(current_node);
50
+ current_node = next_node;
51
+ }
52
+
53
+ node.Reset();
48
54
  }
49
55
 
50
56
  void Leaf::InitializeMerge(ART &art, Node &node, const ARTFlags &flags) {
51
57
 
52
- D_ASSERT(node.IsSet() && !node.IsSerialized());
53
- D_ASSERT(node.GetType() == NType::LEAF);
58
+ auto merge_buffer_count = flags.merge_buffer_counts[(uint8_t)NType::LEAF - 1];
54
59
 
55
- auto &leaf = Leaf::Get(art, node);
56
- if (leaf.ptr.IsSet()) {
57
- leaf.ptr.InitializeMerge(art, flags);
60
+ Node next_node = node;
61
+ node.AddToBufferID(merge_buffer_count);
62
+
63
+ while (next_node.IsSet()) {
64
+ auto &leaf = Leaf::Get(art, next_node);
65
+ next_node = leaf.ptr;
66
+ if (leaf.ptr.IsSet()) {
67
+ leaf.ptr.AddToBufferID(merge_buffer_count);
68
+ }
58
69
  }
59
70
  }
60
71
 
@@ -290,7 +301,6 @@ string Leaf::VerifyAndToString(ART &art, Node &node) {
290
301
  return "Leaf [count: 1, row ID: " + to_string(node.GetRowId()) + "]";
291
302
  }
292
303
 
293
- // NOTE: we could do this recursively, but the function-call overhead can become kinda crazy
294
304
  string str = "";
295
305
 
296
306
  reference<Node> node_ref(node);
@@ -322,46 +332,51 @@ BlockPointer Leaf::Serialize(ART &art, Node &node, MetaBlockWriter &writer) {
322
332
  return block_pointer;
323
333
  }
324
334
 
325
- // recurse into the child and retrieve its block pointer
326
- auto &leaf = Leaf::Get(art, node);
327
- auto child_block_pointer = leaf.ptr.Serialize(art, writer);
328
-
329
- // get pointer and write fields
330
335
  auto block_pointer = writer.GetBlockPointer();
331
336
  writer.Write(NType::LEAF);
332
- writer.Write<uint8_t>(leaf.count);
337
+ idx_t total_count = Leaf::TotalCount(art, node);
338
+ writer.Write<idx_t>(total_count);
333
339
 
334
- // write row IDs
335
- for (idx_t i = 0; i < leaf.count; i++) {
336
- writer.Write(leaf.row_ids[i]);
337
- }
340
+ // iterate all leaves and write their row IDs
341
+ reference<Node> ref_node(node);
342
+ while (ref_node.get().IsSet()) {
343
+ D_ASSERT(!ref_node.get().IsSerialized());
344
+ auto &leaf = Leaf::Get(art, ref_node);
338
345
 
339
- // write child block pointer
340
- writer.Write(child_block_pointer.block_id);
341
- writer.Write(child_block_pointer.offset);
346
+ // write row IDs
347
+ for (idx_t i = 0; i < leaf.count; i++) {
348
+ writer.Write(leaf.row_ids[i]);
349
+ }
350
+ ref_node = leaf.ptr;
351
+ }
342
352
 
343
353
  return block_pointer;
344
354
  }
345
355
 
346
356
  void Leaf::Deserialize(ART &art, Node &node, MetaBlockReader &reader) {
347
357
 
348
- D_ASSERT(node.GetType() == NType::LEAF);
358
+ auto total_count = reader.Read<idx_t>();
359
+ reference<Node> ref_node(node);
349
360
 
350
- auto &leaf = Leaf::Get(art, node);
351
- leaf.count = reader.Read<uint8_t>();
361
+ while (total_count) {
362
+ ref_node.get() = Node::GetAllocator(art, NType::LEAF).New();
363
+ ref_node.get().SetType((uint8_t)NType::LEAF);
352
364
 
353
- // read row IDs
354
- for (idx_t i = 0; i < leaf.count; i++) {
355
- leaf.row_ids[i] = reader.Read<row_t>();
356
- }
365
+ auto &leaf = Leaf::Get(art, ref_node);
366
+
367
+ leaf.count = MinValue((idx_t)Node::LEAF_SIZE, total_count);
368
+ for (idx_t i = 0; i < leaf.count; i++) {
369
+ leaf.row_ids[i] = reader.Read<row_t>();
370
+ }
357
371
 
358
- // read child block pointer
359
- leaf.ptr = Node(reader);
372
+ total_count -= leaf.count;
373
+ ref_node = leaf.ptr;
374
+ leaf.ptr.Reset();
375
+ }
360
376
  }
361
377
 
362
378
  void Leaf::Vacuum(ART &art, Node &node) {
363
379
 
364
- // NOTE: we could do this recursively, but the function-call overhead can become kinda crazy
365
380
  auto &allocator = Node::GetAllocator(art, NType::LEAF);
366
381
 
367
382
  reference<Node> node_ref(node);
@@ -373,7 +388,6 @@ void Leaf::Vacuum(ART &art, Node &node) {
373
388
  auto &leaf = Leaf::Get(art, node_ref);
374
389
  node_ref = leaf.ptr;
375
390
  }
376
- return;
377
391
  }
378
392
 
379
393
  void Leaf::MoveInlinedToLeaf(ART &art, Node &node) {
@@ -61,7 +61,6 @@ void Node::New(ART &art, Node &node, const NType type) {
61
61
 
62
62
  void Node::Free(ART &art, Node &node) {
63
63
 
64
- // recursively free all nodes that are in-memory, and skip serialized and empty nodes
65
64
  if (!node.IsSet()) {
66
65
  return;
67
66
  }
@@ -72,11 +71,11 @@ void Node::Free(ART &art, Node &node) {
72
71
  auto type = node.GetType();
73
72
  switch (type) {
74
73
  case NType::PREFIX:
75
- Prefix::Free(art, node);
76
- break;
74
+ // iterative
75
+ return Prefix::Free(art, node);
77
76
  case NType::LEAF:
78
- Leaf::Free(art, node);
79
- break;
77
+ // iterative
78
+ return Leaf::Free(art, node);
80
79
  case NType::NODE_4:
81
80
  Node4::Free(art, node);
82
81
  break;
@@ -90,8 +89,7 @@ void Node::Free(ART &art, Node &node) {
90
89
  Node256::Free(art, node);
91
90
  break;
92
91
  case NType::LEAF_INLINED:
93
- node.Reset();
94
- return;
92
+ return node.Reset();
95
93
  }
96
94
 
97
95
  Node::GetAllocator(art, type).Free(node);
@@ -236,8 +234,10 @@ BlockPointer Node::Serialize(ART &art, MetaBlockWriter &writer) {
236
234
 
237
235
  switch (GetType()) {
238
236
  case NType::PREFIX:
239
- return Prefix::Get(art, *this).Serialize(art, writer);
237
+ // iterative
238
+ return Prefix::Serialize(art, *this, writer);
240
239
  case NType::LEAF:
240
+ // iterative
241
241
  return Leaf::Serialize(art, *this, writer);
242
242
  case NType::NODE_4:
243
243
  return Node4::Get(art, *this).Serialize(art, writer);
@@ -263,19 +263,23 @@ void Node::Deserialize(ART &art) {
263
263
  SetType(reader.Read<uint8_t>());
264
264
 
265
265
  auto decoded_type = GetType();
266
+
267
+ // iterative functions
268
+ if (decoded_type == NType::PREFIX) {
269
+ return Prefix::Deserialize(art, *this, reader);
270
+ }
266
271
  if (decoded_type == NType::LEAF_INLINED) {
267
- SetRowId(reader.Read<row_t>());
268
- return;
272
+ return SetRowId(reader.Read<row_t>());
273
+ }
274
+ if (decoded_type == NType::LEAF) {
275
+ return Leaf::Deserialize(art, *this, reader);
269
276
  }
270
277
 
271
278
  *this = Node::GetAllocator(art, decoded_type).New();
272
279
  SetType((uint8_t)decoded_type);
273
280
 
281
+ // recursive functions
274
282
  switch (decoded_type) {
275
- case NType::PREFIX:
276
- return Prefix::Get(art, *this).Deserialize(reader);
277
- case NType::LEAF:
278
- return Leaf::Deserialize(art, *this, reader);
279
283
  case NType::NODE_4:
280
284
  return Node4::Get(art, *this).Deserialize(reader);
281
285
  case NType::NODE_16:
@@ -363,7 +367,7 @@ NType Node::GetARTNodeTypeByCount(const idx_t count) {
363
367
  }
364
368
 
365
369
  FixedSizeAllocator &Node::GetAllocator(const ART &art, NType type) {
366
- return *art.allocators[(uint8_t)type - 1];
370
+ return (*art.allocators)[(uint8_t)type - 1];
367
371
  }
368
372
 
369
373
  //===--------------------------------------------------------------------===//
@@ -377,11 +381,11 @@ void Node::InitializeMerge(ART &art, const ARTFlags &flags) {
377
381
 
378
382
  switch (GetType()) {
379
383
  case NType::PREFIX:
380
- Prefix::Get(art, *this).InitializeMerge(art, flags);
381
- break;
384
+ // iterative
385
+ return Prefix::InitializeMerge(art, *this, flags);
382
386
  case NType::LEAF:
383
- Leaf::InitializeMerge(art, *this, flags);
384
- break;
387
+ // iterative
388
+ return Leaf::InitializeMerge(art, *this, flags);
385
389
  case NType::NODE_4:
386
390
  Node4::Get(art, *this).InitializeMerge(art, flags);
387
391
  break;
@@ -398,8 +402,7 @@ void Node::InitializeMerge(ART &art, const ARTFlags &flags) {
398
402
  return;
399
403
  }
400
404
 
401
- // NOTE: this works because the rightmost 32 bits contain the buffer ID
402
- data += flags.merge_buffer_counts[(uint8_t)GetType() - 1];
405
+ AddToBufferID(flags.merge_buffer_counts[(uint8_t)GetType() - 1]);
403
406
  }
404
407
 
405
408
  bool Node::Merge(ART &art, Node &other) {
@@ -572,11 +575,16 @@ void Node::Vacuum(ART &art, const ARTFlags &flags) {
572
575
  }
573
576
 
574
577
  auto node_type = GetType();
578
+
579
+ // iterative functions
580
+ if (node_type == NType::PREFIX) {
581
+ return Prefix::Vacuum(art, *this, flags);
582
+ }
575
583
  if (node_type == NType::LEAF_INLINED) {
576
584
  return;
577
585
  }
578
586
  if (node_type == NType::LEAF) {
579
- if (flags.vacuum_flags[(uint8_t)GetType() - 1]) {
587
+ if (flags.vacuum_flags[(uint8_t)node_type - 1]) {
580
588
  Leaf::Vacuum(art, *this);
581
589
  }
582
590
  return;
@@ -589,9 +597,8 @@ void Node::Vacuum(ART &art, const ARTFlags &flags) {
589
597
  SetType((uint8_t)node_type);
590
598
  }
591
599
 
600
+ // recursive functions
592
601
  switch (node_type) {
593
- case NType::PREFIX:
594
- return Prefix::Get(art, *this).Vacuum(art, flags);
595
602
  case NType::NODE_4:
596
603
  return Node4::Get(art, *this).Vacuum(art, flags);
597
604
  case NType::NODE_16: