duckdb 0.7.1-dev37.0 → 0.7.1-dev407.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 (166) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +7 -7
  3. package/package.json +3 -3
  4. package/src/duckdb/extension/json/buffered_json_reader.cpp +50 -9
  5. package/src/duckdb/extension/json/include/buffered_json_reader.hpp +7 -2
  6. package/src/duckdb/extension/json/include/json_scan.hpp +45 -10
  7. package/src/duckdb/extension/json/json_functions/copy_json.cpp +35 -22
  8. package/src/duckdb/extension/json/json_functions/json_create.cpp +8 -8
  9. package/src/duckdb/extension/json/json_functions/json_structure.cpp +8 -3
  10. package/src/duckdb/extension/json/json_functions/json_transform.cpp +54 -10
  11. package/src/duckdb/extension/json/json_functions/read_json.cpp +104 -49
  12. package/src/duckdb/extension/json/json_functions/read_json_objects.cpp +5 -3
  13. package/src/duckdb/extension/json/json_functions.cpp +7 -0
  14. package/src/duckdb/extension/json/json_scan.cpp +144 -37
  15. package/src/duckdb/extension/parquet/column_reader.cpp +7 -0
  16. package/src/duckdb/extension/parquet/include/column_reader.hpp +1 -0
  17. package/src/duckdb/extension/parquet/parquet-extension.cpp +2 -9
  18. package/src/duckdb/src/catalog/catalog.cpp +62 -13
  19. package/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +8 -7
  20. package/src/duckdb/src/catalog/default/default_views.cpp +1 -1
  21. package/src/duckdb/src/common/bind_helpers.cpp +55 -0
  22. package/src/duckdb/src/common/enums/logical_operator_type.cpp +2 -0
  23. package/src/duckdb/src/common/enums/physical_operator_type.cpp +2 -0
  24. package/src/duckdb/src/common/enums/statement_type.cpp +2 -0
  25. package/src/duckdb/src/common/file_system.cpp +28 -0
  26. package/src/duckdb/src/common/hive_partitioning.cpp +1 -0
  27. package/src/duckdb/src/common/local_file_system.cpp +4 -4
  28. package/src/duckdb/src/common/operator/cast_operators.cpp +10 -4
  29. package/src/duckdb/src/common/types/partitioned_column_data.cpp +1 -0
  30. package/src/duckdb/src/common/types/time.cpp +1 -1
  31. package/src/duckdb/src/common/types/timestamp.cpp +35 -4
  32. package/src/duckdb/src/common/types.cpp +37 -11
  33. package/src/duckdb/src/execution/column_binding_resolver.cpp +5 -2
  34. package/src/duckdb/src/execution/index/art/art.cpp +117 -67
  35. package/src/duckdb/src/execution/index/art/art_key.cpp +24 -12
  36. package/src/duckdb/src/execution/index/art/leaf.cpp +7 -8
  37. package/src/duckdb/src/execution/index/art/node.cpp +13 -27
  38. package/src/duckdb/src/execution/index/art/node16.cpp +5 -8
  39. package/src/duckdb/src/execution/index/art/node256.cpp +3 -5
  40. package/src/duckdb/src/execution/index/art/node4.cpp +4 -7
  41. package/src/duckdb/src/execution/index/art/node48.cpp +5 -8
  42. package/src/duckdb/src/execution/index/art/prefix.cpp +2 -3
  43. package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +6 -27
  44. package/src/duckdb/src/execution/operator/helper/physical_reset.cpp +1 -9
  45. package/src/duckdb/src/execution/operator/helper/physical_set.cpp +1 -9
  46. package/src/duckdb/src/execution/operator/join/physical_iejoin.cpp +7 -9
  47. package/src/duckdb/src/execution/operator/persistent/base_csv_reader.cpp +6 -11
  48. package/src/duckdb/src/execution/operator/persistent/buffered_csv_reader.cpp +13 -13
  49. package/src/duckdb/src/execution/operator/schema/physical_detach.cpp +37 -0
  50. package/src/duckdb/src/execution/operator/schema/physical_drop.cpp +0 -5
  51. package/src/duckdb/src/execution/physical_operator.cpp +6 -6
  52. package/src/duckdb/src/execution/physical_plan/plan_simple.cpp +4 -0
  53. package/src/duckdb/src/execution/physical_plan_generator.cpp +1 -0
  54. package/src/duckdb/src/function/pragma/pragma_queries.cpp +38 -11
  55. package/src/duckdb/src/function/scalar/generic/current_setting.cpp +2 -2
  56. package/src/duckdb/src/function/scalar/map/map.cpp +69 -21
  57. package/src/duckdb/src/function/table/read_csv.cpp +17 -5
  58. package/src/duckdb/src/function/table/system/duckdb_temporary_files.cpp +59 -0
  59. package/src/duckdb/src/function/table/system_functions.cpp +1 -0
  60. package/src/duckdb/src/function/table/table_scan.cpp +3 -0
  61. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  62. package/src/duckdb/src/include/duckdb/catalog/catalog.hpp +7 -1
  63. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_index_entry.hpp +1 -1
  64. package/src/duckdb/src/include/duckdb/catalog/catalog_entry/index_catalog_entry.hpp +1 -1
  65. package/src/duckdb/src/include/duckdb/common/bind_helpers.hpp +2 -0
  66. package/src/duckdb/src/include/duckdb/common/enums/logical_operator_type.hpp +1 -0
  67. package/src/duckdb/src/include/duckdb/common/enums/physical_operator_type.hpp +1 -0
  68. package/src/duckdb/src/include/duckdb/common/enums/statement_type.hpp +3 -2
  69. package/src/duckdb/src/include/duckdb/common/enums/wal_type.hpp +3 -0
  70. package/src/duckdb/src/include/duckdb/common/exception.hpp +10 -0
  71. package/src/duckdb/src/include/duckdb/common/file_system.hpp +1 -0
  72. package/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp +9 -1
  73. package/src/duckdb/src/include/duckdb/common/radix_partitioning.hpp +4 -4
  74. package/src/duckdb/src/include/duckdb/common/types/timestamp.hpp +5 -1
  75. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +37 -41
  76. package/src/duckdb/src/include/duckdb/execution/index/art/art_key.hpp +8 -11
  77. package/src/duckdb/src/include/duckdb/execution/operator/persistent/base_csv_reader.hpp +1 -3
  78. package/src/duckdb/src/include/duckdb/execution/operator/persistent/buffered_csv_reader.hpp +0 -2
  79. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_reader_options.hpp +2 -0
  80. package/src/duckdb/src/include/duckdb/execution/operator/schema/physical_detach.hpp +32 -0
  81. package/src/duckdb/src/include/duckdb/function/table/system_functions.hpp +4 -0
  82. package/src/duckdb/src/include/duckdb/main/client_data.hpp +2 -2
  83. package/src/duckdb/src/include/duckdb/main/config.hpp +2 -3
  84. package/src/duckdb/src/include/duckdb/main/{extension_functions.hpp → extension_entries.hpp} +26 -5
  85. package/src/duckdb/src/include/duckdb/main/extension_helper.hpp +3 -0
  86. package/src/duckdb/src/include/duckdb/main/settings.hpp +9 -0
  87. package/src/duckdb/src/include/duckdb/parallel/pipeline_executor.hpp +0 -7
  88. package/src/duckdb/src/include/duckdb/parser/parsed_data/create_database_info.hpp +0 -4
  89. package/src/duckdb/src/include/duckdb/parser/parsed_data/detach_info.hpp +32 -0
  90. package/src/duckdb/src/include/duckdb/parser/query_node/select_node.hpp +1 -1
  91. package/src/duckdb/src/include/duckdb/parser/sql_statement.hpp +2 -2
  92. package/src/duckdb/src/include/duckdb/parser/statement/copy_statement.hpp +1 -1
  93. package/src/duckdb/src/include/duckdb/parser/statement/detach_statement.hpp +29 -0
  94. package/src/duckdb/src/include/duckdb/parser/statement/list.hpp +1 -0
  95. package/src/duckdb/src/include/duckdb/parser/statement/select_statement.hpp +3 -3
  96. package/src/duckdb/src/include/duckdb/parser/tableref/subqueryref.hpp +1 -1
  97. package/src/duckdb/src/include/duckdb/parser/tokens.hpp +1 -0
  98. package/src/duckdb/src/include/duckdb/parser/transformer.hpp +1 -0
  99. package/src/duckdb/src/include/duckdb/planner/binder.hpp +4 -0
  100. package/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp +10 -3
  101. package/src/duckdb/src/include/duckdb/planner/operator/logical_execute.hpp +1 -5
  102. package/src/duckdb/src/include/duckdb/planner/operator/logical_show.hpp +1 -2
  103. package/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +8 -0
  104. package/src/duckdb/src/include/duckdb/storage/data_table.hpp +7 -1
  105. package/src/duckdb/src/include/duckdb/storage/index.hpp +47 -38
  106. package/src/duckdb/src/include/duckdb/storage/storage_extension.hpp +7 -0
  107. package/src/duckdb/src/include/duckdb/storage/table/update_segment.hpp +2 -0
  108. package/src/duckdb/src/include/duckdb/storage/write_ahead_log.hpp +7 -0
  109. package/src/duckdb/src/main/client_context.cpp +2 -0
  110. package/src/duckdb/src/main/config.cpp +1 -0
  111. package/src/duckdb/src/main/database.cpp +14 -5
  112. package/src/duckdb/src/main/extension/extension_alias.cpp +2 -1
  113. package/src/duckdb/src/main/extension/extension_install.cpp +43 -9
  114. package/src/duckdb/src/main/extension/extension_load.cpp +29 -5
  115. package/src/duckdb/src/main/settings/settings.cpp +16 -0
  116. package/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp +2 -6
  117. package/src/duckdb/src/parallel/pipeline_executor.cpp +1 -55
  118. package/src/duckdb/src/parser/parsed_data/create_index_info.cpp +3 -0
  119. package/src/duckdb/src/parser/statement/copy_statement.cpp +2 -13
  120. package/src/duckdb/src/parser/statement/delete_statement.cpp +3 -0
  121. package/src/duckdb/src/parser/statement/detach_statement.cpp +15 -0
  122. package/src/duckdb/src/parser/statement/insert_statement.cpp +9 -0
  123. package/src/duckdb/src/parser/statement/update_statement.cpp +3 -0
  124. package/src/duckdb/src/parser/transform/expression/transform_case.cpp +3 -3
  125. package/src/duckdb/src/parser/transform/statement/transform_create_database.cpp +0 -1
  126. package/src/duckdb/src/parser/transform/statement/transform_detach.cpp +19 -0
  127. package/src/duckdb/src/parser/transformer.cpp +2 -0
  128. package/src/duckdb/src/planner/binder/expression/bind_aggregate_expression.cpp +3 -0
  129. package/src/duckdb/src/planner/binder/statement/bind_copy.cpp +7 -14
  130. package/src/duckdb/src/planner/binder/statement/bind_create.cpp +16 -14
  131. package/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +13 -0
  132. package/src/duckdb/src/planner/binder/statement/bind_detach.cpp +19 -0
  133. package/src/duckdb/src/planner/binder/statement/bind_drop.cpp +29 -4
  134. package/src/duckdb/src/planner/binder/statement/bind_insert.cpp +22 -1
  135. package/src/duckdb/src/planner/binder.cpp +2 -0
  136. package/src/duckdb/src/planner/expression_binder/index_binder.cpp +32 -1
  137. package/src/duckdb/src/planner/logical_operator.cpp +4 -0
  138. package/src/duckdb/src/planner/planner.cpp +1 -0
  139. package/src/duckdb/src/storage/buffer_manager.cpp +105 -26
  140. package/src/duckdb/src/storage/compression/bitpacking.cpp +16 -7
  141. package/src/duckdb/src/storage/data_table.cpp +66 -3
  142. package/src/duckdb/src/storage/index.cpp +1 -1
  143. package/src/duckdb/src/storage/local_storage.cpp +1 -1
  144. package/src/duckdb/src/storage/table/column_data.cpp +4 -2
  145. package/src/duckdb/src/storage/table/update_segment.cpp +15 -0
  146. package/src/duckdb/src/storage/table_index_list.cpp +1 -2
  147. package/src/duckdb/src/storage/wal_replay.cpp +68 -0
  148. package/src/duckdb/src/storage/write_ahead_log.cpp +21 -1
  149. package/src/duckdb/src/transaction/commit_state.cpp +5 -2
  150. package/src/duckdb/third_party/concurrentqueue/blockingconcurrentqueue.h +2 -2
  151. package/src/duckdb/third_party/fmt/include/fmt/core.h +1 -2
  152. package/src/duckdb/third_party/libpg_query/include/nodes/nodes.hpp +1 -0
  153. package/src/duckdb/third_party/libpg_query/include/nodes/parsenodes.hpp +14 -0
  154. package/src/duckdb/third_party/libpg_query/include/parser/gram.hpp +530 -1006
  155. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +17659 -17626
  156. package/src/duckdb/ub_extension_icu_third_party_icu_i18n.cpp +4 -4
  157. package/src/duckdb/ub_src_execution_operator_schema.cpp +2 -0
  158. package/src/duckdb/ub_src_function_table_system.cpp +2 -0
  159. package/src/duckdb/ub_src_parser_statement.cpp +2 -0
  160. package/src/duckdb/ub_src_parser_transform_statement.cpp +2 -0
  161. package/src/duckdb/ub_src_planner_binder_statement.cpp +2 -0
  162. package/src/statement.cpp +46 -12
  163. package/test/arrow.test.ts +3 -3
  164. package/test/prepare.test.ts +39 -1
  165. package/test/typescript_decls.test.ts +1 -1
  166. package/src/duckdb/src/include/duckdb/function/create_database_extension.hpp +0 -37
@@ -27,7 +27,10 @@ ART::ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager,
27
27
  tree = nullptr;
28
28
  if (block_id != DConstants::INVALID_INDEX) {
29
29
  tree = Node::Deserialize(*this, block_id, block_offset);
30
- ART::Verify();
30
+ Verify();
31
+ if (track_memory) {
32
+ buffer_manager.IncreaseUsedMemory(memory_size);
33
+ }
31
34
  }
32
35
  serialized_data_pointer = BlockPointer(block_id, block_offset);
33
36
 
@@ -58,7 +61,7 @@ ART::~ART() {
58
61
  if (!tree) {
59
62
  return;
60
63
  }
61
- ART::Verify();
64
+ Verify();
62
65
  if (track_memory) {
63
66
  buffer_manager.DecreaseUsedMemory(memory_size);
64
67
  }
@@ -72,6 +75,7 @@ ART::~ART() {
72
75
 
73
76
  unique_ptr<IndexScanState> ART::InitializeScanSinglePredicate(const Transaction &transaction, const Value &value,
74
77
  ExpressionType expression_type) {
78
+ // initialize point lookup
75
79
  auto result = make_unique<ARTIndexScanState>();
76
80
  result->values[0] = value;
77
81
  result->expressions[0] = expression_type;
@@ -81,6 +85,7 @@ unique_ptr<IndexScanState> ART::InitializeScanSinglePredicate(const Transaction
81
85
  unique_ptr<IndexScanState> ART::InitializeScanTwoPredicates(Transaction &transaction, const Value &low_value,
82
86
  ExpressionType low_expression_type, const Value &high_value,
83
87
  ExpressionType high_expression_type) {
88
+ // initialize range lookup
84
89
  auto result = make_unique<ARTIndexScanState>();
85
90
  result->values[0] = low_value;
86
91
  result->expressions[0] = low_expression_type;
@@ -103,7 +108,7 @@ static void TemplatedGenerateKeys(ArenaAllocator &allocator, Vector &input, idx_
103
108
  for (idx_t i = 0; i < count; i++) {
104
109
  auto idx = idata.sel->get_index(i);
105
110
  if (idata.validity.RowIsValid(idx)) {
106
- Key::CreateKey<T>(allocator, keys[i], input_data[idx]);
111
+ Key::CreateKey<T>(allocator, input.GetType(), keys[i], input_data[idx]);
107
112
  }
108
113
  }
109
114
  }
@@ -123,7 +128,7 @@ static void ConcatenateKeys(ArenaAllocator &allocator, Vector &input, idx_t coun
123
128
  // this column entry is NULL, set whole key to NULL
124
129
  keys[i] = Key();
125
130
  } else {
126
- auto other_key = Key::CreateKey<T>(allocator, input_data[idx]);
131
+ auto other_key = Key::CreateKey<T>(allocator, input.GetType(), input_data[idx]);
127
132
  keys[i].ConcatenateKey(allocator, other_key);
128
133
  }
129
134
  }
@@ -225,7 +230,7 @@ void ART::GenerateKeys(ArenaAllocator &allocator, DataChunk &input, vector<Key>
225
230
  }
226
231
 
227
232
  //===--------------------------------------------------------------------===//
228
- // Construct from sorted data
233
+ // Construct from sorted data (only during CREATE (UNIQUE) INDEX statements)
229
234
  //===--------------------------------------------------------------------===//
230
235
 
231
236
  struct KeySection {
@@ -283,7 +288,7 @@ bool Construct(ART &art, vector<Key> &keys, row_t *row_ids, Node *&node, KeySect
283
288
  } else {
284
289
  node = Leaf::New(start_key, prefix_start, row_ids + key_section.start, num_row_ids);
285
290
  }
286
- art.memory_size += node->MemorySize(art, false);
291
+ art.IncreaseMemorySize(node->MemorySize(art, false));
287
292
  return true;
288
293
  }
289
294
  // create a new node and recurse
@@ -297,7 +302,7 @@ bool Construct(ART &art, vector<Key> &keys, row_t *row_ids, Node *&node, KeySect
297
302
 
298
303
  auto prefix_length = key_section.depth - prefix_start;
299
304
  node->prefix = Prefix(start_key, prefix_start, prefix_length);
300
- art.memory_size += node->MemorySize(art, false);
305
+ art.IncreaseMemorySize(node->MemorySize(art, false));
301
306
 
302
307
  // recurse on each child section
303
308
  for (auto &child_section : child_sections) {
@@ -323,7 +328,7 @@ bool ART::ConstructFromSorted(idx_t count, vector<Key> &keys, Vector &row_identi
323
328
  }
324
329
 
325
330
  //===--------------------------------------------------------------------===//
326
- // Insert
331
+ // Insert / Verification / Constraint Checking
327
332
  //===--------------------------------------------------------------------===//
328
333
 
329
334
  bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
@@ -331,13 +336,13 @@ bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
331
336
  D_ASSERT(row_ids.GetType().InternalType() == ROW_TYPE);
332
337
  D_ASSERT(logical_types[0] == input.data[0].GetType());
333
338
 
339
+ auto old_memory_size = memory_size;
340
+
334
341
  // generate the keys for the given input
335
342
  ArenaAllocator arena_allocator(BufferAllocator::Get(db));
336
343
  vector<Key> keys(input.size());
337
344
  GenerateKeys(arena_allocator, input, keys);
338
345
 
339
- auto old_memory_size = this->memory_size;
340
-
341
346
  // get the corresponding row IDs
342
347
  row_ids.Flatten(input.size());
343
348
  auto row_identifiers = FlatVector::GetData<row_t>(row_ids);
@@ -356,9 +361,9 @@ bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
356
361
  break;
357
362
  }
358
363
  }
359
- if (failed_index != DConstants::INVALID_INDEX) {
360
364
 
361
- // failed to insert because of constraint violation: remove previously inserted entries
365
+ // failed to insert because of constraint violation: remove previously inserted entries
366
+ if (failed_index != DConstants::INVALID_INDEX) {
362
367
  for (idx_t i = 0; i < failed_index; i++) {
363
368
  if (keys[i].Empty()) {
364
369
  continue;
@@ -366,14 +371,11 @@ bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
366
371
  row_t row_id = row_identifiers[i];
367
372
  Erase(tree, keys[i], 0, row_id);
368
373
  }
369
- // nothing changed, no need to update the buffer memory size
370
- return false;
371
374
  }
372
375
 
373
- D_ASSERT(old_memory_size <= memory_size);
374
- Verify();
375
- if (track_memory) {
376
- buffer_manager.IncreaseUsedMemory(memory_size - old_memory_size);
376
+ IncreaseAndVerifyMemorySize(old_memory_size);
377
+ if (failed_index != DConstants::INVALID_INDEX) {
378
+ return false;
377
379
  }
378
380
  return true;
379
381
  }
@@ -391,25 +393,12 @@ bool ART::Append(IndexLock &lock, DataChunk &appended_data, Vector &row_identifi
391
393
 
392
394
  void ART::VerifyAppend(DataChunk &chunk) {
393
395
  ConflictManager conflict_manager(VerifyExistenceType::APPEND, chunk.size());
394
- LookupValues(chunk, conflict_manager);
396
+ CheckConstraintsForChunk(chunk, conflict_manager);
395
397
  }
396
398
 
397
399
  void ART::VerifyAppend(DataChunk &chunk, ConflictManager &conflict_manager) {
398
400
  D_ASSERT(conflict_manager.LookupType() == VerifyExistenceType::APPEND);
399
- LookupValues(chunk, conflict_manager);
400
- }
401
-
402
- void ART::VerifyAppendForeignKey(DataChunk &chunk) {
403
- ConflictManager conflict_manager(VerifyExistenceType::APPEND_FK, chunk.size());
404
- LookupValues(chunk, conflict_manager);
405
- }
406
-
407
- void ART::VerifyDeleteForeignKey(DataChunk &chunk) {
408
- if (!IsUnique()) {
409
- return;
410
- }
411
- ConflictManager conflict_manager(VerifyExistenceType::DELETE_FK, chunk.size());
412
- LookupValues(chunk, conflict_manager);
401
+ CheckConstraintsForChunk(chunk, conflict_manager);
413
402
  }
414
403
 
415
404
  bool ART::InsertToLeaf(Leaf &leaf, row_t row_id) {
@@ -430,7 +419,7 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
430
419
  if (!node) {
431
420
  // node is currently empty, create a leaf here with the key
432
421
  node = Leaf::New(key, depth, row_id);
433
- this->memory_size += node->MemorySize(*this, false);
422
+ IncreaseMemorySize(node->MemorySize(*this, false));
434
423
  return true;
435
424
  }
436
425
 
@@ -455,14 +444,14 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
455
444
 
456
445
  Node *new_node = Node4::New();
457
446
  new_node->prefix = Prefix(key, depth, new_prefix_length);
458
- this->memory_size += new_node->MemorySize(*this, false);
447
+ IncreaseMemorySize(new_node->MemorySize(*this, false));
459
448
 
460
449
  auto key_byte = node->prefix.Reduce(*this, new_prefix_length);
461
450
  Node4::InsertChild(*this, new_node, key_byte, node);
462
451
 
463
452
  Node *leaf_node = Leaf::New(key, depth + new_prefix_length + 1, row_id);
464
453
  Node4::InsertChild(*this, new_node, key[depth + new_prefix_length], leaf_node);
465
- this->memory_size += leaf_node->MemorySize(*this, false);
454
+ IncreaseMemorySize(leaf_node->MemorySize(*this, false));
466
455
 
467
456
  node = new_node;
468
457
  return true;
@@ -476,7 +465,7 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
476
465
  // prefix differs, create new node
477
466
  Node *new_node = Node4::New();
478
467
  new_node->prefix = Prefix(key, depth, mismatch_pos);
479
- this->memory_size += new_node->MemorySize(*this, false);
468
+ IncreaseMemorySize(new_node->MemorySize(*this, false));
480
469
 
481
470
  // break up prefix
482
471
  auto key_byte = node->prefix.Reduce(*this, mismatch_pos);
@@ -484,7 +473,7 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
484
473
 
485
474
  Node *leaf_node = Leaf::New(key, depth + mismatch_pos + 1, row_id);
486
475
  Node4::InsertChild(*this, new_node, key[depth + mismatch_pos], leaf_node);
487
- this->memory_size += leaf_node->MemorySize(*this, false);
476
+ IncreaseMemorySize(leaf_node->MemorySize(*this, false));
488
477
 
489
478
  node = new_node;
490
479
  return true;
@@ -504,7 +493,7 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
504
493
 
505
494
  Node *leaf_node = Leaf::New(key, depth + 1, row_id);
506
495
  Node::InsertChild(*this, node, key[depth], leaf_node);
507
- this->memory_size += leaf_node->MemorySize(*this, false);
496
+ IncreaseMemorySize(leaf_node->MemorySize(*this, false));
508
497
  return true;
509
498
  }
510
499
 
@@ -525,7 +514,7 @@ void ART::Delete(IndexLock &state, DataChunk &input, Vector &row_ids) {
525
514
  vector<Key> keys(expression.size());
526
515
  GenerateKeys(arena_allocator, expression, keys);
527
516
 
528
- auto old_memory_size = this->memory_size;
517
+ auto old_memory_size = memory_size;
529
518
 
530
519
  // now erase the elements from the database
531
520
  row_ids.Flatten(input.size());
@@ -547,10 +536,13 @@ void ART::Delete(IndexLock &state, DataChunk &input, Vector &row_ids) {
547
536
  #endif
548
537
  }
549
538
 
550
- D_ASSERT(old_memory_size >= memory_size);
539
+ // if we deserialize nodes while erasing, then we might end up with more
540
+ // memory afterwards, so we have to either increase or decrease the used memory
551
541
  Verify();
552
- if (track_memory) {
542
+ if (track_memory && old_memory_size >= memory_size) {
553
543
  buffer_manager.DecreaseUsedMemory(old_memory_size - memory_size);
544
+ } else if (track_memory) {
545
+ buffer_manager.IncreaseUsedMemory(memory_size - old_memory_size);
554
546
  }
555
547
  }
556
548
 
@@ -566,8 +558,7 @@ void ART::Erase(Node *&node, Key &key, idx_t depth, row_t row_id) {
566
558
  leaf->Remove(*this, row_id);
567
559
 
568
560
  if (leaf->count == 0) {
569
- D_ASSERT(this->memory_size >= leaf->MemorySize(*this, false));
570
- this->memory_size -= leaf->MemorySize(*this, false);
561
+ DecreaseMemorySize(leaf->MemorySize(*this, false));
571
562
  Node::Delete(node);
572
563
  node = nullptr;
573
564
  }
@@ -606,38 +597,38 @@ void ART::Erase(Node *&node, Key &key, idx_t depth, row_t row_id) {
606
597
  }
607
598
 
608
599
  //===--------------------------------------------------------------------===//
609
- // Point Query
600
+ // Point Query (Equal)
610
601
  //===--------------------------------------------------------------------===//
611
602
 
612
603
  static Key CreateKey(ArenaAllocator &allocator, PhysicalType type, Value &value) {
613
604
  D_ASSERT(type == value.type().InternalType());
614
605
  switch (type) {
615
606
  case PhysicalType::BOOL:
616
- return Key::CreateKey<bool>(allocator, value);
607
+ return Key::CreateKey<bool>(allocator, value.type(), value);
617
608
  case PhysicalType::INT8:
618
- return Key::CreateKey<int8_t>(allocator, value);
609
+ return Key::CreateKey<int8_t>(allocator, value.type(), value);
619
610
  case PhysicalType::INT16:
620
- return Key::CreateKey<int16_t>(allocator, value);
611
+ return Key::CreateKey<int16_t>(allocator, value.type(), value);
621
612
  case PhysicalType::INT32:
622
- return Key::CreateKey<int32_t>(allocator, value);
613
+ return Key::CreateKey<int32_t>(allocator, value.type(), value);
623
614
  case PhysicalType::INT64:
624
- return Key::CreateKey<int64_t>(allocator, value);
615
+ return Key::CreateKey<int64_t>(allocator, value.type(), value);
625
616
  case PhysicalType::UINT8:
626
- return Key::CreateKey<uint8_t>(allocator, value);
617
+ return Key::CreateKey<uint8_t>(allocator, value.type(), value);
627
618
  case PhysicalType::UINT16:
628
- return Key::CreateKey<uint16_t>(allocator, value);
619
+ return Key::CreateKey<uint16_t>(allocator, value.type(), value);
629
620
  case PhysicalType::UINT32:
630
- return Key::CreateKey<uint32_t>(allocator, value);
621
+ return Key::CreateKey<uint32_t>(allocator, value.type(), value);
631
622
  case PhysicalType::UINT64:
632
- return Key::CreateKey<uint64_t>(allocator, value);
623
+ return Key::CreateKey<uint64_t>(allocator, value.type(), value);
633
624
  case PhysicalType::INT128:
634
- return Key::CreateKey<hugeint_t>(allocator, value);
625
+ return Key::CreateKey<hugeint_t>(allocator, value.type(), value);
635
626
  case PhysicalType::FLOAT:
636
- return Key::CreateKey<float>(allocator, value);
627
+ return Key::CreateKey<float>(allocator, value.type(), value);
637
628
  case PhysicalType::DOUBLE:
638
- return Key::CreateKey<double>(allocator, value);
629
+ return Key::CreateKey<double>(allocator, value.type(), value);
639
630
  case PhysicalType::VARCHAR:
640
- return Key::CreateKey<string_t>(allocator, value);
631
+ return Key::CreateKey<string_t>(allocator, value.type(), value);
641
632
  default:
642
633
  throw InternalException("Invalid type for index");
643
634
  }
@@ -645,7 +636,10 @@ static Key CreateKey(ArenaAllocator &allocator, PhysicalType type, Value &value)
645
636
 
646
637
  bool ART::SearchEqual(Key &key, idx_t max_count, vector<row_t> &result_ids) {
647
638
 
639
+ auto old_memory_size = memory_size;
648
640
  auto leaf = (Leaf *)(Lookup(tree, key, 0));
641
+ IncreaseAndVerifyMemorySize(old_memory_size);
642
+
649
643
  if (!leaf) {
650
644
  return true;
651
645
  }
@@ -662,19 +656,28 @@ bool ART::SearchEqual(Key &key, idx_t max_count, vector<row_t> &result_ids) {
662
656
  void ART::SearchEqualJoinNoFetch(Key &key, idx_t &result_size) {
663
657
 
664
658
  // we need to look for a leaf
659
+ auto old_memory_size = memory_size;
665
660
  auto leaf = Lookup(tree, key, 0);
661
+ IncreaseAndVerifyMemorySize(old_memory_size);
662
+
666
663
  if (!leaf) {
667
664
  return;
668
665
  }
669
666
  result_size = leaf->count;
670
667
  }
671
668
 
669
+ //===--------------------------------------------------------------------===//
670
+ // Lookup
671
+ //===--------------------------------------------------------------------===//
672
+
672
673
  Leaf *ART::Lookup(Node *node, Key &key, idx_t depth) {
674
+
673
675
  while (node) {
674
676
  if (node->type == NodeType::NLeaf) {
675
677
  auto leaf = (Leaf *)node;
676
678
  auto &leaf_prefix = leaf->prefix;
677
- //! Check leaf
679
+
680
+ // check if leaf contains key
678
681
  for (idx_t i = 0; i < leaf->prefix.Size(); i++) {
679
682
  if (leaf_prefix[i] != key[i + depth]) {
680
683
  return nullptr;
@@ -682,22 +685,29 @@ Leaf *ART::Lookup(Node *node, Key &key, idx_t depth) {
682
685
  }
683
686
  return (Leaf *)node;
684
687
  }
688
+
685
689
  if (node->prefix.Size()) {
686
690
  for (idx_t pos = 0; pos < node->prefix.Size(); pos++) {
687
691
  if (key[depth + pos] != node->prefix[pos]) {
692
+ // prefix mismatch, does not contain key
688
693
  return nullptr;
689
694
  }
690
695
  }
691
696
  depth += node->prefix.Size();
692
697
  }
698
+
699
+ // prefix matches key, but no child at byte, does not contain key
693
700
  idx_t pos = node->GetChildPos(key[depth]);
694
701
  if (pos == DConstants::INVALID_INDEX) {
695
702
  return nullptr;
696
703
  }
704
+
705
+ // recurse into child
697
706
  node = node->GetChild(*this, pos);
698
707
  D_ASSERT(node);
699
708
  depth++;
700
709
  }
710
+
701
711
  return nullptr;
702
712
  }
703
713
 
@@ -710,6 +720,7 @@ Leaf *ART::Lookup(Node *node, Key &key, idx_t depth) {
710
720
  bool ART::SearchGreater(ARTIndexScanState *state, Key &key, bool inclusive, idx_t max_count,
711
721
  vector<row_t> &result_ids) {
712
722
 
723
+ auto old_memory_size = memory_size;
713
724
  Iterator *it = &state->iterator;
714
725
 
715
726
  // greater than scan: first set the iterator to the node at which we will start our scan by finding the lowest node
@@ -718,13 +729,16 @@ bool ART::SearchGreater(ARTIndexScanState *state, Key &key, bool inclusive, idx_
718
729
  it->art = this;
719
730
  bool found = it->LowerBound(tree, key, inclusive);
720
731
  if (!found) {
732
+ IncreaseAndVerifyMemorySize(old_memory_size);
721
733
  return true;
722
734
  }
723
735
  }
724
736
  // after that we continue the scan; we don't need to check the bounds as any value following this value is
725
737
  // automatically bigger and hence satisfies our predicate
726
738
  Key empty_key = Key();
727
- return it->Scan(empty_key, max_count, result_ids, false);
739
+ auto success = it->Scan(empty_key, max_count, result_ids, false);
740
+ IncreaseAndVerifyMemorySize(old_memory_size);
741
+ return success;
728
742
  }
729
743
 
730
744
  //===--------------------------------------------------------------------===//
@@ -738,6 +752,7 @@ bool ART::SearchLess(ARTIndexScanState *state, Key &upper_bound, bool inclusive,
738
752
  return true;
739
753
  }
740
754
 
755
+ auto old_memory_size = memory_size;
741
756
  Iterator *it = &state->iterator;
742
757
 
743
758
  if (!it->art) {
@@ -746,11 +761,14 @@ bool ART::SearchLess(ARTIndexScanState *state, Key &upper_bound, bool inclusive,
746
761
  it->FindMinimum(*tree);
747
762
  // early out min value higher than upper bound query
748
763
  if (it->cur_key > upper_bound) {
764
+ IncreaseAndVerifyMemorySize(old_memory_size);
749
765
  return true;
750
766
  }
751
767
  }
752
768
  // now continue the scan until we reach the upper bound
753
- return it->Scan(upper_bound, max_count, result_ids, inclusive);
769
+ auto success = it->Scan(upper_bound, max_count, result_ids, inclusive);
770
+ IncreaseAndVerifyMemorySize(old_memory_size);
771
+ return success;
754
772
  }
755
773
 
756
774
  //===--------------------------------------------------------------------===//
@@ -760,6 +778,7 @@ bool ART::SearchLess(ARTIndexScanState *state, Key &upper_bound, bool inclusive,
760
778
  bool ART::SearchCloseRange(ARTIndexScanState *state, Key &lower_bound, Key &upper_bound, bool left_inclusive,
761
779
  bool right_inclusive, idx_t max_count, vector<row_t> &result_ids) {
762
780
 
781
+ auto old_memory_size = memory_size;
763
782
  Iterator *it = &state->iterator;
764
783
 
765
784
  // first find the first node that satisfies the left predicate
@@ -767,11 +786,14 @@ bool ART::SearchCloseRange(ARTIndexScanState *state, Key &lower_bound, Key &uppe
767
786
  it->art = this;
768
787
  bool found = it->LowerBound(tree, lower_bound, left_inclusive);
769
788
  if (!found) {
789
+ IncreaseAndVerifyMemorySize(old_memory_size);
770
790
  return true;
771
791
  }
772
792
  }
773
793
  // now continue the scan until we reach the upper bound
774
- return it->Scan(upper_bound, max_count, result_ids, right_inclusive);
794
+ auto success = it->Scan(upper_bound, max_count, result_ids, right_inclusive);
795
+ IncreaseAndVerifyMemorySize(old_memory_size);
796
+ return success;
775
797
  }
776
798
 
777
799
  bool ART::Scan(Transaction &transaction, DataTable &table, IndexScanState &table_state, idx_t max_count,
@@ -844,8 +866,15 @@ bool ART::Scan(Transaction &transaction, DataTable &table, IndexScanState &table
844
866
  return true;
845
867
  }
846
868
 
869
+ //===--------------------------------------------------------------------===//
870
+ // More Verification / Constraint Checking
871
+ //===--------------------------------------------------------------------===//
872
+
847
873
  string ART::GenerateErrorKeyName(DataChunk &input, idx_t row) {
848
- // re-executing the expressions is not very fast, but we're going to throw anyways, so we don't care
874
+
875
+ // FIXME: why exactly can we not pass the expression_chunk as an argument to this
876
+ // FIXME: function instead of re-executing?
877
+ // re-executing the expressions is not very fast, but we're going to throw, so we don't care
849
878
  DataChunk expression_chunk;
850
879
  expression_chunk.Initialize(Allocator::DefaultAllocator(), logical_types);
851
880
  ExecuteExpressions(input, expression_chunk);
@@ -883,11 +912,13 @@ string ART::GenerateConstraintErrorMessage(VerifyExistenceType verify_type, cons
883
912
  }
884
913
  }
885
914
 
886
- void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
915
+ void ART::CheckConstraintsForChunk(DataChunk &input, ConflictManager &conflict_manager) {
887
916
 
888
917
  // don't alter the index during constraint checking
889
918
  lock_guard<mutex> l(lock);
890
919
 
920
+ auto old_memory_size = memory_size;
921
+
891
922
  // first resolve the expressions for the index
892
923
  DataChunk expression_chunk;
893
924
  expression_chunk.Initialize(Allocator::DefaultAllocator(), logical_types);
@@ -900,12 +931,14 @@ void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
900
931
 
901
932
  idx_t found_conflict = DConstants::INVALID_INDEX;
902
933
  for (idx_t i = 0; found_conflict == DConstants::INVALID_INDEX && i < input.size(); i++) {
934
+
903
935
  if (keys[i].Empty()) {
904
936
  if (conflict_manager.AddNull(i)) {
905
937
  found_conflict = i;
906
938
  }
907
939
  continue;
908
940
  }
941
+
909
942
  Leaf *leaf_ptr = Lookup(tree, keys[i], 0);
910
943
  if (leaf_ptr == nullptr) {
911
944
  if (conflict_manager.AddMiss(i)) {
@@ -913,6 +946,7 @@ void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
913
946
  }
914
947
  continue;
915
948
  }
949
+
916
950
  // When we find a node, we need to update the 'matches' and 'row_ids'
917
951
  // NOTE: Leafs can have more than one row_id, but for UNIQUE/PRIMARY KEY they will only have one
918
952
  D_ASSERT(leaf_ptr->count == 1);
@@ -921,11 +955,15 @@ void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
921
955
  found_conflict = i;
922
956
  }
923
957
  }
958
+
924
959
  conflict_manager.FinishLookup();
960
+ IncreaseAndVerifyMemorySize(old_memory_size);
961
+
925
962
  if (found_conflict == DConstants::INVALID_INDEX) {
926
963
  // No conflicts detected
927
964
  return;
928
965
  }
966
+
929
967
  auto key_name = GenerateErrorKeyName(input, found_conflict);
930
968
  auto exception_msg = GenerateConstraintErrorMessage(conflict_manager.LookupType(), key_name);
931
969
  throw ConstraintException(exception_msg);
@@ -935,13 +973,15 @@ void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
935
973
  // Serialization
936
974
  //===--------------------------------------------------------------------===//
937
975
 
938
- BlockPointer ART::Serialize(duckdb::MetaBlockWriter &writer) {
976
+ BlockPointer ART::Serialize(MetaBlockWriter &writer) {
939
977
  lock_guard<mutex> l(lock);
978
+ auto old_memory_size = memory_size;
940
979
  if (tree) {
941
980
  serialized_data_pointer = tree->Serialize(*this, writer);
942
981
  } else {
943
982
  serialized_data_pointer = {(block_id_t)DConstants::INVALID_INDEX, (uint32_t)DConstants::INVALID_INDEX};
944
983
  }
984
+ IncreaseAndVerifyMemorySize(old_memory_size);
945
985
  return serialized_data_pointer;
946
986
  }
947
987
 
@@ -954,8 +994,8 @@ bool ART::MergeIndexes(IndexLock &state, Index *other_index) {
954
994
  auto other_art = (ART *)other_index;
955
995
 
956
996
  if (!this->tree) {
957
- this->memory_size += other_art->memory_size;
958
- this->tree = other_art->tree;
997
+ IncreaseMemorySize(other_art->memory_size);
998
+ tree = other_art->tree;
959
999
  other_art->tree = nullptr;
960
1000
  return true;
961
1001
  }
@@ -987,4 +1027,14 @@ void ART::Verify() {
987
1027
  #endif
988
1028
  }
989
1029
 
1030
+ void ART::IncreaseAndVerifyMemorySize(idx_t old_memory_size) {
1031
+ // since we lazily deserialize ART nodes, it is possible that its in-memory size
1032
+ // increased during lookups
1033
+ Verify();
1034
+ D_ASSERT(memory_size >= old_memory_size);
1035
+ if (track_memory) {
1036
+ buffer_manager.IncreaseUsedMemory(memory_size - old_memory_size);
1037
+ }
1038
+ }
1039
+
990
1040
  } // namespace duckdb
@@ -15,40 +15,52 @@ Key::Key(ArenaAllocator &allocator, idx_t len) : len(len) {
15
15
  }
16
16
 
17
17
  template <>
18
- Key Key::CreateKey(ArenaAllocator &allocator, string_t value) {
18
+ Key Key::CreateKey(ArenaAllocator &allocator, const LogicalType &type, string_t value) {
19
19
  idx_t len = value.GetSize() + 1;
20
20
  auto data = allocator.Allocate(len);
21
21
  memcpy(data, value.GetDataUnsafe(), len - 1);
22
22
 
23
- if (len > 1 && data[len - 2] == '\0') {
24
- // FIXME: rethink this
25
- throw NotImplementedException("Indexes cannot have contain null-terminated decoded BLOBs.");
23
+ // FIXME: rethink this
24
+ if (type == LogicalType::BLOB || type == LogicalType::VARCHAR) {
25
+ // indexes cannot contain BLOBs (or BLOBs cast to VARCHARs) that contain null-terminated bytes
26
+ for (idx_t i = 0; i < len - 1; i++) {
27
+ if (data[i] == '\0') {
28
+ throw NotImplementedException("Indexes cannot contain BLOBs that contain null-terminated bytes.");
29
+ }
30
+ }
26
31
  }
32
+
27
33
  data[len - 1] = '\0';
28
34
  return Key(data, len);
29
35
  }
30
36
 
31
37
  template <>
32
- Key Key::CreateKey(ArenaAllocator &allocator, const char *value) {
33
- return Key::CreateKey(allocator, string_t(value, strlen(value)));
38
+ Key Key::CreateKey(ArenaAllocator &allocator, const LogicalType &type, const char *value) {
39
+ return Key::CreateKey(allocator, type, string_t(value, strlen(value)));
34
40
  }
35
41
 
36
42
  template <>
37
- void Key::CreateKey(ArenaAllocator &allocator, Key &key, string_t value) {
43
+ void Key::CreateKey(ArenaAllocator &allocator, const LogicalType &type, Key &key, string_t value) {
38
44
  key.len = value.GetSize() + 1;
39
45
  key.data = allocator.Allocate(key.len);
40
46
  memcpy(key.data, value.GetDataUnsafe(), key.len - 1);
41
47
 
42
- if (key.len > 1 && key.data[key.len - 2] == '\0') {
43
- // FIXME: rethink this
44
- throw NotImplementedException("Indexes cannot have contain null-terminated decoded BLOBs.");
48
+ // FIXME: rethink this
49
+ if (type == LogicalType::BLOB || type == LogicalType::VARCHAR) {
50
+ // indexes cannot contain BLOBs (or BLOBs cast to VARCHARs) that contain null-terminated bytes
51
+ for (idx_t i = 0; i < key.len - 1; i++) {
52
+ if (key.data[i] == '\0') {
53
+ throw NotImplementedException("Indexes cannot contain BLOBs that contain null-terminated bytes.");
54
+ }
55
+ }
45
56
  }
57
+
46
58
  key.data[key.len - 1] = '\0';
47
59
  }
48
60
 
49
61
  template <>
50
- void Key::CreateKey(ArenaAllocator &allocator, Key &key, const char *value) {
51
- Key::CreateKey(allocator, key, string_t(value, strlen(value)));
62
+ void Key::CreateKey(ArenaAllocator &allocator, const LogicalType &type, Key &key, const char *value) {
63
+ Key::CreateKey(allocator, type, key, string_t(value, strlen(value)));
52
64
  }
53
65
 
54
66
  bool Key::operator>(const Key &k) const {
@@ -109,9 +109,9 @@ void Leaf::Insert(ART &art, row_t row_id) {
109
109
  if (count == capacity) {
110
110
  // grow array
111
111
  if (IsInlined()) {
112
- art.memory_size += (capacity + 1) * sizeof(row_t);
112
+ art.IncreaseMemorySize((capacity + 1) * sizeof(row_t));
113
113
  } else {
114
- art.memory_size += capacity * sizeof(row_t);
114
+ art.IncreaseMemorySize(capacity * sizeof(row_t));
115
115
  }
116
116
  row_ids = Resize(row_ids, count, capacity * 2);
117
117
  }
@@ -143,6 +143,7 @@ void Leaf::Remove(ART &art, row_t row_id) {
143
143
  return;
144
144
  }
145
145
 
146
+ auto capacity = GetCapacity();
146
147
  count--;
147
148
  if (count == 1) {
148
149
  // after erasing we can now inline the leaf
@@ -150,18 +151,16 @@ void Leaf::Remove(ART &art, row_t row_id) {
150
151
  auto remaining_row_id = row_ids[0] == row_id ? row_ids[1] : row_ids[0];
151
152
  DeleteArray<row_t>(rowids.ptr, rowids.ptr[0] + 1);
152
153
  rowids.inlined = remaining_row_id;
153
- D_ASSERT(art.memory_size >= sizeof(row_t));
154
- art.memory_size -= 2 * sizeof(row_t);
154
+ art.DecreaseMemorySize(capacity * sizeof(row_t));
155
155
  return;
156
156
  }
157
157
 
158
158
  // shrink array, if less than half full
159
- auto capacity = GetCapacity();
159
+ capacity = GetCapacity();
160
160
  if (capacity > 2 && count < capacity / 2) {
161
161
 
162
162
  auto new_capacity = capacity / 2;
163
- D_ASSERT(art.memory_size >= (capacity - new_capacity) * sizeof(row_t));
164
- art.memory_size -= (capacity - new_capacity) * sizeof(row_t);
163
+ art.DecreaseMemorySize((capacity - new_capacity) * sizeof(row_t));
165
164
 
166
165
  auto new_allocation = AllocateArray<row_t>(new_capacity + 1);
167
166
  new_allocation[0] = new_capacity;
@@ -200,7 +199,7 @@ void Leaf::Merge(ART &art, Node *&l_node, Node *&r_node) {
200
199
  if (l_n->count + r_n->count > l_capacity) {
201
200
  auto capacity = l_n->GetCapacity();
202
201
  auto new_capacity = NextPowerOfTwo(l_n->count + r_n->count);
203
- art.memory_size += sizeof(row_t) * (new_capacity - capacity);
202
+ art.IncreaseMemorySize(sizeof(row_t) * (new_capacity - capacity));
204
203
  l_row_ids = l_n->Resize(l_row_ids, l_n->count, new_capacity);
205
204
  }
206
205