duckdb 0.6.2-dev1794.0 → 0.6.2-dev1830.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 (29) hide show
  1. package/package.json +1 -1
  2. package/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp +2 -2
  3. package/src/duckdb/src/execution/index/art/art.cpp +146 -85
  4. package/src/duckdb/src/execution/index/art/leaf.cpp +58 -22
  5. package/src/duckdb/src/execution/index/art/node.cpp +148 -62
  6. package/src/duckdb/src/execution/index/art/node16.cpp +49 -20
  7. package/src/duckdb/src/execution/index/art/node256.cpp +40 -15
  8. package/src/duckdb/src/execution/index/art/node4.cpp +49 -19
  9. package/src/duckdb/src/execution/index/art/node48.cpp +53 -21
  10. package/src/duckdb/src/execution/index/art/prefix.cpp +24 -10
  11. package/src/duckdb/src/execution/index/art/swizzleable_pointer.cpp +3 -0
  12. package/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +13 -11
  13. package/src/duckdb/src/execution/physical_plan/plan_create_index.cpp +3 -5
  14. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  15. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +10 -12
  16. package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +17 -8
  17. package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +16 -6
  18. package/src/duckdb/src/include/duckdb/execution/index/art/node16.hpp +13 -3
  19. package/src/duckdb/src/include/duckdb/execution/index/art/node256.hpp +12 -3
  20. package/src/duckdb/src/include/duckdb/execution/index/art/node4.hpp +14 -6
  21. package/src/duckdb/src/include/duckdb/execution/index/art/node48.hpp +12 -3
  22. package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +23 -19
  23. package/src/duckdb/src/include/duckdb/execution/index/art/swizzleable_pointer.hpp +4 -4
  24. package/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +7 -0
  25. package/src/duckdb/src/include/duckdb/storage/index.hpp +16 -5
  26. package/src/duckdb/src/storage/buffer_manager.cpp +12 -0
  27. package/src/duckdb/src/storage/checkpoint_manager.cpp +1 -1
  28. package/src/duckdb/src/storage/index.cpp +7 -3
  29. package/src/duckdb/src/storage/local_storage.cpp +1 -1
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.6.2-dev1794.0",
5
+ "version": "0.6.2-dev1830.0",
6
6
  "description": "DuckDB node.js API",
7
7
  "gypfile": true,
8
8
  "dependencies": {
@@ -65,10 +65,10 @@ void AddDataTableIndex(DataTable *storage, const ColumnList &columns, const vect
65
65
  // create an adaptive radix tree around the expressions
66
66
  if (index_block) {
67
67
  art = make_unique<ART>(column_ids, TableIOManager::Get(*storage), std::move(unbound_expressions),
68
- constraint_type, storage->db, index_block->block_id, index_block->offset);
68
+ constraint_type, storage->db, true, index_block->block_id, index_block->offset);
69
69
  } else {
70
70
  art = make_unique<ART>(column_ids, TableIOManager::Get(*storage), std::move(unbound_expressions),
71
- constraint_type, storage->db);
71
+ constraint_type, storage->db, true);
72
72
  if (!storage->IsRoot()) {
73
73
  throw TransactionException("Transaction conflict: cannot add an index to a table that has been altered!");
74
74
  }
@@ -15,66 +15,61 @@ namespace duckdb {
15
15
 
16
16
  ART::ART(const vector<column_t> &column_ids, TableIOManager &table_io_manager,
17
17
  const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type,
18
- AttachedDatabase &db, idx_t block_id, idx_t block_offset)
18
+ AttachedDatabase &db, bool track_memory, idx_t block_id, idx_t block_offset)
19
19
 
20
- : Index(IndexType::ART, table_io_manager, column_ids, unbound_expressions, constraint_type), db(db),
21
- estimated_art_size(0), estimated_key_size(16) {
20
+ : Index(db, IndexType::ART, table_io_manager, column_ids, unbound_expressions, constraint_type, track_memory) {
22
21
 
23
22
  if (!Radix::IsLittleEndian()) {
24
23
  throw NotImplementedException("ART indexes are not supported on big endian architectures");
25
24
  }
26
25
 
26
+ // set the root node of the tree
27
+ tree = nullptr;
27
28
  if (block_id != DConstants::INVALID_INDEX) {
28
29
  tree = Node::Deserialize(*this, block_id, block_offset);
29
- } else {
30
- tree = nullptr;
30
+ Verify();
31
31
  }
32
-
33
32
  serialized_data_pointer = BlockPointer(block_id, block_offset);
33
+
34
+ // validate the types of the key columns
34
35
  for (idx_t i = 0; i < types.size(); i++) {
35
36
  switch (types[i]) {
36
37
  case PhysicalType::BOOL:
37
38
  case PhysicalType::INT8:
38
- case PhysicalType::UINT8:
39
- estimated_key_size += sizeof(int8_t);
40
- break;
41
39
  case PhysicalType::INT16:
42
- case PhysicalType::UINT16:
43
- estimated_key_size += sizeof(int16_t);
44
- break;
45
40
  case PhysicalType::INT32:
46
- case PhysicalType::UINT32:
47
- case PhysicalType::FLOAT:
48
- estimated_key_size += sizeof(int32_t);
49
- break;
50
41
  case PhysicalType::INT64:
42
+ case PhysicalType::INT128:
43
+ case PhysicalType::UINT8:
44
+ case PhysicalType::UINT16:
45
+ case PhysicalType::UINT32:
51
46
  case PhysicalType::UINT64:
47
+ case PhysicalType::FLOAT:
52
48
  case PhysicalType::DOUBLE:
53
- estimated_key_size += sizeof(int64_t);
54
- break;
55
- case PhysicalType::INT128:
56
- estimated_key_size += sizeof(hugeint_t);
57
- break;
58
49
  case PhysicalType::VARCHAR:
59
- estimated_key_size += 16; // oh well
60
50
  break;
61
51
  default:
62
- throw InvalidTypeException(logical_types[i], "Invalid type for index");
52
+ throw InvalidTypeException(logical_types[i], "Invalid type for index key.");
63
53
  }
64
54
  }
65
55
  }
66
56
 
67
57
  ART::~ART() {
68
- if (estimated_art_size > 0) {
69
- BufferManager::GetBufferManager(db).FreeReservedMemory(estimated_art_size);
70
- estimated_art_size = 0;
58
+ if (!tree) {
59
+ return;
71
60
  }
72
- if (tree) {
73
- Node::Delete(tree);
74
- tree = nullptr;
61
+ Verify();
62
+ if (track_memory) {
63
+ buffer_manager.DecreaseUsedMemory(memory_size);
75
64
  }
65
+ Node::Delete(tree);
66
+ tree = nullptr;
76
67
  }
77
68
 
69
+ //===--------------------------------------------------------------------===//
70
+ // Initialize Predicate Scans
71
+ //===--------------------------------------------------------------------===//
72
+
78
73
  unique_ptr<IndexScanState> ART::InitializeScanSinglePredicate(Transaction &transaction, Value value,
79
74
  ExpressionType expression_type) {
80
75
  auto result = make_unique<ARTIndexScanState>();
@@ -230,7 +225,7 @@ void ART::GenerateKeys(ArenaAllocator &allocator, DataChunk &input, vector<Key>
230
225
  }
231
226
 
232
227
  //===--------------------------------------------------------------------===//
233
- // Insert
228
+ // Construct from sorted data
234
229
  //===--------------------------------------------------------------------===//
235
230
 
236
231
  struct KeySection {
@@ -256,7 +251,8 @@ void GetChildSections(vector<KeySection> &child_sections, vector<Key> &keys, Key
256
251
  child_sections.emplace_back(child_start_idx, key_section.end, keys, key_section);
257
252
  }
258
253
 
259
- void Construct(vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_section, bool &has_constraint) {
254
+ void Construct(ART &art, vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_section,
255
+ bool &has_constraint) {
260
256
 
261
257
  D_ASSERT(key_section.start < keys.size());
262
258
  D_ASSERT(key_section.end < keys.size());
@@ -284,9 +280,11 @@ void Construct(vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_s
284
280
 
285
281
  if (single_row_id) {
286
282
  node = Leaf::New(start_key, prefix_start, row_ids[key_section.start]);
283
+ art.memory_size += node->MemorySize(art, false);
287
284
  return;
288
285
  }
289
286
  node = Leaf::New(start_key, prefix_start, row_ids + key_section.start, num_row_ids);
287
+ art.memory_size += node->MemorySize(art, false);
290
288
 
291
289
  } else { // create a new node and recurse
292
290
 
@@ -299,12 +297,13 @@ void Construct(vector<Key> &keys, row_t *row_ids, Node *&node, KeySection &key_s
299
297
 
300
298
  auto prefix_length = key_section.depth - prefix_start;
301
299
  node->prefix = Prefix(start_key, prefix_start, prefix_length);
300
+ art.memory_size += node->MemorySize(art, false);
302
301
 
303
302
  // recurse on each child section
304
303
  for (auto &child_section : child_sections) {
305
304
  Node *new_child = nullptr;
306
- Construct(keys, row_ids, new_child, child_section, has_constraint);
307
- Node::InsertChild(node, child_section.key_byte, new_child);
305
+ Construct(art, keys, row_ids, new_child, child_section, has_constraint);
306
+ Node::InsertChild(art, node, child_section.key_byte, new_child);
308
307
  }
309
308
  }
310
309
  }
@@ -317,10 +316,15 @@ void ART::ConstructFromSorted(idx_t count, vector<Key> &keys, Vector &row_identi
317
316
 
318
317
  auto key_section = KeySection(0, count - 1, 0, 0);
319
318
  auto has_constraint = IsUnique();
320
- Construct(keys, row_ids, this->tree, key_section, has_constraint);
319
+ Construct(*this, keys, row_ids, this->tree, key_section, has_constraint);
321
320
  }
322
321
 
322
+ //===--------------------------------------------------------------------===//
323
+ // Insert
324
+ //===--------------------------------------------------------------------===//
325
+
323
326
  bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
327
+
324
328
  D_ASSERT(row_ids.GetType().InternalType() == ROW_TYPE);
325
329
  D_ASSERT(logical_types[0] == input.data[0].GetType());
326
330
 
@@ -329,13 +333,13 @@ bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
329
333
  vector<Key> keys(input.size());
330
334
  GenerateKeys(arena_allocator, input, keys);
331
335
 
332
- idx_t extra_memory = estimated_key_size * input.size();
333
- BufferManager::GetBufferManager(db).ReserveMemory(extra_memory);
334
- estimated_art_size += extra_memory;
336
+ auto old_memory_size = this->memory_size;
335
337
 
336
- // now insert the elements into the index
338
+ // get the corresponding row IDs
337
339
  row_ids.Flatten(input.size());
338
340
  auto row_identifiers = FlatVector::GetData<row_t>(row_ids);
341
+
342
+ // now insert the elements into the index
339
343
  idx_t failed_index = DConstants::INVALID_INDEX;
340
344
  for (idx_t i = 0; i < input.size(); i++) {
341
345
  if (keys[i].Empty()) {
@@ -359,8 +363,15 @@ bool ART::Insert(IndexLock &lock, DataChunk &input, Vector &row_ids) {
359
363
  row_t row_id = row_identifiers[i];
360
364
  Erase(tree, keys[i], 0, row_id);
361
365
  }
366
+ // nothing changed, no need to update the buffer memory size
362
367
  return false;
363
368
  }
369
+
370
+ D_ASSERT(old_memory_size <= memory_size);
371
+ Verify();
372
+ if (track_memory) {
373
+ buffer_manager.IncreaseUsedMemory(memory_size - old_memory_size);
374
+ }
364
375
  return true;
365
376
  }
366
377
 
@@ -407,7 +418,7 @@ bool ART::InsertToLeaf(Leaf &leaf, row_t row_id) {
407
418
  if (IsUnique() && leaf.count != 0) {
408
419
  return false;
409
420
  }
410
- leaf.Insert(row_id);
421
+ leaf.Insert(*this, row_id);
411
422
  return true;
412
423
  }
413
424
 
@@ -416,23 +427,24 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
416
427
  if (!node) {
417
428
  // node is currently empty, create a leaf here with the key
418
429
  node = Leaf::New(key, depth, row_id);
430
+ this->memory_size += node->MemorySize(*this, false);
419
431
  return true;
420
432
  }
421
433
 
422
434
  if (node->type == NodeType::NLeaf) {
423
- // Replace leaf with Node4 and store both leaves in it
435
+ // replace leaf with Node4 and store both leaves in it
436
+ // or add a row ID to a leaf, if they have the same key
424
437
  auto leaf = (Leaf *)node;
425
-
426
- auto &leaf_prefix = leaf->prefix;
427
438
  uint32_t new_prefix_length = 0;
428
439
 
429
- // Leaf node is already there (its key matches the current key), update row_id vector
440
+ // FIXME: this code (if and while) can be optimized, less branching, see Construct
441
+ // leaf node is already there (its key matches the current key), update row_id vector
430
442
  if (new_prefix_length == leaf->prefix.Size() && depth + leaf->prefix.Size() == key.len) {
431
443
  return InsertToLeaf(*leaf, row_id);
432
444
  }
433
- while (leaf_prefix[new_prefix_length] == key[depth + new_prefix_length]) {
445
+ while (leaf->prefix[new_prefix_length] == key[depth + new_prefix_length]) {
434
446
  new_prefix_length++;
435
- // Leaf node is already there (its key matches the current key), update row_id vector
447
+ // leaf node is already there (its key matches the current key), update row_id vector
436
448
  if (new_prefix_length == leaf->prefix.Size() && depth + leaf->prefix.Size() == key.len) {
437
449
  return InsertToLeaf(*leaf, row_id);
438
450
  }
@@ -440,34 +452,44 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
440
452
 
441
453
  Node *new_node = Node4::New();
442
454
  new_node->prefix = Prefix(key, depth, new_prefix_length);
443
- auto key_byte = node->prefix.Reduce(new_prefix_length);
444
- Node4::InsertChild(new_node, key_byte, node);
455
+ this->memory_size += new_node->MemorySize(*this, false);
456
+
457
+ auto key_byte = node->prefix.Reduce(*this, new_prefix_length);
458
+ Node4::InsertChild(*this, new_node, key_byte, node);
459
+
445
460
  Node *leaf_node = Leaf::New(key, depth + new_prefix_length + 1, row_id);
446
- Node4::InsertChild(new_node, key[depth + new_prefix_length], leaf_node);
461
+ Node4::InsertChild(*this, new_node, key[depth + new_prefix_length], leaf_node);
462
+ this->memory_size += leaf_node->MemorySize(*this, false);
463
+
447
464
  node = new_node;
448
465
  return true;
449
466
  }
450
467
 
451
- // Handle prefix of inner node
468
+ // handle prefix of inner node
452
469
  if (node->prefix.Size()) {
470
+
453
471
  uint32_t mismatch_pos = node->prefix.KeyMismatchPosition(key, depth);
454
472
  if (mismatch_pos != node->prefix.Size()) {
455
- // Prefix differs, create new node
473
+ // prefix differs, create new node
456
474
  Node *new_node = Node4::New();
457
475
  new_node->prefix = Prefix(key, depth, mismatch_pos);
458
- // Break up prefix
459
- auto key_byte = node->prefix.Reduce(mismatch_pos);
460
- Node4::InsertChild(new_node, key_byte, node);
476
+ this->memory_size += new_node->MemorySize(*this, false);
477
+
478
+ // break up prefix
479
+ auto key_byte = node->prefix.Reduce(*this, mismatch_pos);
480
+ Node4::InsertChild(*this, new_node, key_byte, node);
461
481
 
462
482
  Node *leaf_node = Leaf::New(key, depth + mismatch_pos + 1, row_id);
463
- Node4::InsertChild(new_node, key[depth + mismatch_pos], leaf_node);
483
+ Node4::InsertChild(*this, new_node, key[depth + mismatch_pos], leaf_node);
484
+ this->memory_size += leaf_node->MemorySize(*this, false);
485
+
464
486
  node = new_node;
465
487
  return true;
466
488
  }
467
489
  depth += node->prefix.Size();
468
490
  }
469
491
 
470
- // Recurse
492
+ // recurse
471
493
  D_ASSERT(depth < key.len);
472
494
  idx_t pos = node->GetChildPos(key[depth]);
473
495
  if (pos != DConstants::INVALID_INDEX) {
@@ -476,30 +498,32 @@ bool ART::Insert(Node *&node, Key &key, idx_t depth, row_t row_id) {
476
498
  node->ReplaceChildPointer(pos, child);
477
499
  return insertion_result;
478
500
  }
479
- Node *new_node = Leaf::New(key, depth + 1, row_id);
480
- Node::InsertChild(node, key[depth], new_node);
501
+
502
+ Node *leaf_node = Leaf::New(key, depth + 1, row_id);
503
+ Node::InsertChild(*this, node, key[depth], leaf_node);
504
+ this->memory_size += leaf_node->MemorySize(*this, false);
481
505
  return true;
482
506
  }
483
507
 
484
508
  //===--------------------------------------------------------------------===//
485
509
  // Delete
486
510
  //===--------------------------------------------------------------------===//
511
+
487
512
  void ART::Delete(IndexLock &state, DataChunk &input, Vector &row_ids) {
513
+
488
514
  DataChunk expression;
489
515
  expression.Initialize(Allocator::DefaultAllocator(), logical_types);
490
516
 
491
517
  // first resolve the expressions
492
518
  ExecuteExpressions(input, expression);
493
519
 
494
- idx_t released_memory = MinValue<idx_t>(estimated_art_size, estimated_key_size * input.size());
495
- BufferManager::GetBufferManager(db).FreeReservedMemory(released_memory);
496
- estimated_art_size -= released_memory;
497
-
498
520
  // then generate the keys for the given input
499
521
  ArenaAllocator arena_allocator(BufferAllocator::Get(db));
500
522
  vector<Key> keys(expression.size());
501
523
  GenerateKeys(arena_allocator, expression, keys);
502
524
 
525
+ auto old_memory_size = this->memory_size;
526
+
503
527
  // now erase the elements from the database
504
528
  row_ids.Flatten(input.size());
505
529
  auto row_identifiers = FlatVector::GetData<row_t>(row_ids);
@@ -512,54 +536,66 @@ void ART::Delete(IndexLock &state, DataChunk &input, Vector &row_ids) {
512
536
  #ifdef DEBUG
513
537
  auto node = Lookup(tree, keys[i], 0);
514
538
  if (node) {
515
- auto leaf = static_cast<Leaf *>(node);
539
+ auto leaf = (Leaf *)node;
516
540
  for (idx_t k = 0; k < leaf->count; k++) {
517
541
  D_ASSERT(leaf->GetRowId(k) != row_identifiers[i]);
518
542
  }
519
543
  }
520
544
  #endif
521
545
  }
546
+
547
+ D_ASSERT(old_memory_size >= memory_size);
548
+ Verify();
549
+ if (track_memory) {
550
+ buffer_manager.DecreaseUsedMemory(old_memory_size - memory_size);
551
+ }
522
552
  }
523
553
 
524
554
  void ART::Erase(Node *&node, Key &key, idx_t depth, row_t row_id) {
555
+
525
556
  if (!node) {
526
557
  return;
527
558
  }
528
- // Delete a leaf from a tree
559
+
560
+ // delete a leaf from a tree
529
561
  if (node->type == NodeType::NLeaf) {
530
- // Make sure we have the right leaf
531
- auto leaf = static_cast<Leaf *>(node);
532
- leaf->Remove(row_id);
562
+ auto leaf = (Leaf *)node;
563
+ leaf->Remove(*this, row_id);
564
+
533
565
  if (leaf->count == 0) {
566
+ D_ASSERT(this->memory_size >= leaf->MemorySize(*this, false));
567
+ this->memory_size -= leaf->MemorySize(*this, false);
534
568
  Node::Delete(node);
535
569
  node = nullptr;
536
570
  }
537
-
538
571
  return;
539
572
  }
540
573
 
541
- // Handle prefix
574
+ // handle prefix
542
575
  if (node->prefix.Size()) {
543
576
  if (node->prefix.KeyMismatchPosition(key, depth) != node->prefix.Size()) {
544
577
  return;
545
578
  }
546
579
  depth += node->prefix.Size();
547
580
  }
581
+
548
582
  idx_t pos = node->GetChildPos(key[depth]);
549
583
  if (pos != DConstants::INVALID_INDEX) {
550
584
  auto child = node->GetChild(*this, pos);
551
585
  D_ASSERT(child);
552
586
 
553
587
  if (child->type == NodeType::NLeaf) {
554
- // Leaf found, remove entry
588
+ // leaf found, remove entry
555
589
  auto leaf = (Leaf *)child;
556
- leaf->Remove(row_id);
590
+ leaf->Remove(*this, row_id);
591
+
557
592
  if (leaf->count == 0) {
558
- // Leaf is empty, delete leaf, decrement node counter and maybe shrink node
559
- Node::EraseChild(node, pos, *this);
593
+ // leaf is empty, delete leaf, decrement node counter and maybe shrink node
594
+ Node::EraseChild(*this, node, pos);
560
595
  }
596
+
561
597
  } else {
562
- // Recurse
598
+ // recurse
563
599
  Erase(child, key, depth + 1, row_id);
564
600
  node->ReplaceChildPointer(pos, child);
565
601
  }
@@ -569,6 +605,7 @@ void ART::Erase(Node *&node, Key &key, idx_t depth, row_t row_id) {
569
605
  //===--------------------------------------------------------------------===//
570
606
  // Point Query
571
607
  //===--------------------------------------------------------------------===//
608
+
572
609
  static Key CreateKey(ArenaAllocator &allocator, PhysicalType type, Value &value) {
573
610
  D_ASSERT(type == value.type().InternalType());
574
611
  switch (type) {
@@ -605,7 +642,7 @@ static Key CreateKey(ArenaAllocator &allocator, PhysicalType type, Value &value)
605
642
 
606
643
  bool ART::SearchEqual(Key &key, idx_t max_count, vector<row_t> &result_ids) {
607
644
 
608
- auto leaf = static_cast<Leaf *>(Lookup(tree, key, 0));
645
+ auto leaf = (Leaf *)(Lookup(tree, key, 0));
609
646
  if (!leaf) {
610
647
  return true;
611
648
  }
@@ -666,6 +703,7 @@ Leaf *ART::Lookup(Node *node, Key &key, idx_t depth) {
666
703
  // Returns: True (If found leaf >= key)
667
704
  // False (Otherwise)
668
705
  //===--------------------------------------------------------------------===//
706
+
669
707
  bool ART::SearchGreater(ARTIndexScanState *state, Key &key, bool inclusive, idx_t max_count,
670
708
  vector<row_t> &result_ids) {
671
709
 
@@ -689,6 +727,7 @@ bool ART::SearchGreater(ARTIndexScanState *state, Key &key, bool inclusive, idx_
689
727
  //===--------------------------------------------------------------------===//
690
728
  // Less Than
691
729
  //===--------------------------------------------------------------------===//
730
+
692
731
  bool ART::SearchLess(ARTIndexScanState *state, Key &upper_bound, bool inclusive, idx_t max_count,
693
732
  vector<row_t> &result_ids) {
694
733
 
@@ -714,6 +753,7 @@ bool ART::SearchLess(ARTIndexScanState *state, Key &upper_bound, bool inclusive,
714
753
  //===--------------------------------------------------------------------===//
715
754
  // Closed Range Query
716
755
  //===--------------------------------------------------------------------===//
756
+
717
757
  bool ART::SearchCloseRange(ARTIndexScanState *state, Key &lower_bound, Key &upper_bound, bool left_inclusive,
718
758
  bool right_inclusive, idx_t max_count, vector<row_t> &result_ids) {
719
759
 
@@ -820,18 +860,19 @@ string ART::GenerateErrorKeyName(DataChunk &input, idx_t row) {
820
860
  string ART::GenerateConstraintErrorMessage(VerifyExistenceType verify_type, const string &key_name) {
821
861
  switch (verify_type) {
822
862
  case VerifyExistenceType::APPEND: {
823
- // This node already exists in the tree
863
+ // APPEND to PK/UNIQUE table, but node/key already exists in PK/UNIQUE table
824
864
  string type = IsPrimary() ? "primary key" : "unique";
825
865
  return StringUtil::Format("Duplicate key \"%s\" violates %s constraint", key_name, type);
826
866
  }
827
867
  case VerifyExistenceType::APPEND_FK: {
828
- // The node we tried to insert does not exist in the foreign table
868
+ // APPEND_FK to FK table, node/key does not exist in PK/UNIQUE table
829
869
  return StringUtil::Format(
830
- "Violates foreign key constraint because key \"%s\" does not exist in referenced table", key_name);
870
+ "Violates foreign key constraint because key \"%s\" does not exist in the referenced table", key_name);
831
871
  }
832
872
  case VerifyExistenceType::DELETE_FK: {
833
- // The node we tried to delete still exists in the foreign table
834
- return StringUtil::Format("Violates foreign key constraint because key \"%s\" exists in table has foreign key",
873
+ // DELETE_FK that still exists in a FK table, i.e., not a valid delete
874
+ return StringUtil::Format("Violates foreign key constraint because key \"%s\" is still referenced by a foreign "
875
+ "key in a different table",
835
876
  key_name);
836
877
  }
837
878
  default:
@@ -840,13 +881,13 @@ string ART::GenerateConstraintErrorMessage(VerifyExistenceType verify_type, cons
840
881
  }
841
882
 
842
883
  void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
843
- DataChunk expression_chunk;
844
- expression_chunk.Initialize(Allocator::DefaultAllocator(), logical_types);
845
884
 
846
- // unique index, check
885
+ // don't alter the index during constraint checking
847
886
  lock_guard<mutex> l(lock);
848
887
 
849
888
  // first resolve the expressions for the index
889
+ DataChunk expression_chunk;
890
+ expression_chunk.Initialize(Allocator::DefaultAllocator(), logical_types);
850
891
  ExecuteExpressions(input, expression_chunk);
851
892
 
852
893
  // generate the keys for the given input
@@ -890,6 +931,7 @@ void ART::LookupValues(DataChunk &input, ConflictManager &conflict_manager) {
890
931
  //===--------------------------------------------------------------------===//
891
932
  // Serialization
892
933
  //===--------------------------------------------------------------------===//
934
+
893
935
  BlockPointer ART::Serialize(duckdb::MetaBlockWriter &writer) {
894
936
  lock_guard<mutex> l(lock);
895
937
  if (tree) {
@@ -901,13 +943,15 @@ BlockPointer ART::Serialize(duckdb::MetaBlockWriter &writer) {
901
943
  }
902
944
 
903
945
  //===--------------------------------------------------------------------===//
904
- // Merge ARTs
946
+ // Merging
905
947
  //===--------------------------------------------------------------------===//
948
+
906
949
  bool ART::MergeIndexes(IndexLock &state, Index *other_index) {
950
+
907
951
  auto other_art = (ART *)other_index;
908
- estimated_art_size += other_art->estimated_art_size;
909
- other_art->estimated_art_size = 0;
952
+
910
953
  if (!this->tree) {
954
+ this->memory_size += other_art->memory_size;
911
955
  this->tree = other_art->tree;
912
956
  other_art->tree = nullptr;
913
957
  return true;
@@ -916,6 +960,10 @@ bool ART::MergeIndexes(IndexLock &state, Index *other_index) {
916
960
  return Node::MergeARTs(this, other_art);
917
961
  }
918
962
 
963
+ //===--------------------------------------------------------------------===//
964
+ // Utility
965
+ //===--------------------------------------------------------------------===//
966
+
919
967
  string ART::ToString() {
920
968
  if (tree) {
921
969
  return tree->ToString(*this);
@@ -923,4 +971,17 @@ string ART::ToString() {
923
971
  return "[empty]";
924
972
  }
925
973
 
974
+ void ART::Verify() {
975
+ #ifdef DEBUG
976
+ idx_t current_mem_size = 0;
977
+ if (tree) {
978
+ current_mem_size = tree->MemorySize(*this, true);
979
+ }
980
+ if (memory_size != current_mem_size) {
981
+ throw InternalException("Memory_size value (%d) does not match actual memory size (%d).", memory_size,
982
+ current_mem_size);
983
+ }
984
+ #endif
985
+ }
986
+
926
987
  } // namespace duckdb