TestSqlHeap.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/ScopeGuard.h>
  7. #include <AK/StringBuilder.h>
  8. #include <LibCore/System.h>
  9. #include <LibSQL/Heap.h>
  10. #include <LibTest/TestCase.h>
  11. static constexpr auto db_path = "/tmp/test.db"sv;
  12. static NonnullRefPtr<SQL::Heap> create_heap()
  13. {
  14. auto heap = MUST(SQL::Heap::try_create(db_path));
  15. MUST(heap->open());
  16. return heap;
  17. }
  18. TEST_CASE(heap_write_large_storage_without_flush)
  19. {
  20. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  21. auto heap = create_heap();
  22. auto storage_block_id = heap->request_new_block_index();
  23. // Write large storage spanning multiple blocks
  24. StringBuilder builder;
  25. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  26. auto long_string = builder.string_view();
  27. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  28. // Read back
  29. auto stored_long_string = TRY_OR_FAIL(heap->read_storage(storage_block_id));
  30. EXPECT_EQ(long_string.bytes(), stored_long_string.bytes());
  31. }
  32. TEST_CASE(heap_write_large_storage_with_flush)
  33. {
  34. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  35. auto heap = create_heap();
  36. auto storage_block_id = heap->request_new_block_index();
  37. // Write large storage spanning multiple blocks
  38. StringBuilder builder;
  39. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  40. auto long_string = builder.string_view();
  41. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  42. MUST(heap->flush());
  43. // Read back
  44. auto stored_long_string = TRY_OR_FAIL(heap->read_storage(storage_block_id));
  45. EXPECT_EQ(long_string.bytes(), stored_long_string.bytes());
  46. }
  47. TEST_CASE(heap_overwrite_large_storage)
  48. {
  49. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  50. auto heap = create_heap();
  51. auto storage_block_id = heap->request_new_block_index();
  52. // Write large storage spanning multiple blocks
  53. StringBuilder builder;
  54. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  55. auto long_string = builder.string_view();
  56. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  57. MUST(heap->flush());
  58. auto heap_size = MUST(heap->file_size_in_bytes());
  59. // Let's write it again and check whether the Heap reused the same extended blocks
  60. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  61. MUST(heap->flush());
  62. auto new_heap_size = MUST(heap->file_size_in_bytes());
  63. EXPECT_EQ(heap_size, new_heap_size);
  64. // Write a smaller string and read back - heap size should be at most the previous size
  65. builder.clear();
  66. MUST(builder.try_append_repeated('y', SQL::Block::DATA_SIZE * 2));
  67. auto shorter_string = builder.string_view();
  68. TRY_OR_FAIL(heap->write_storage(storage_block_id, shorter_string.bytes()));
  69. MUST(heap->flush());
  70. new_heap_size = MUST(heap->file_size_in_bytes());
  71. EXPECT(new_heap_size <= heap_size);
  72. auto stored_shorter_string = TRY_OR_FAIL(heap->read_storage(storage_block_id));
  73. EXPECT_EQ(shorter_string.bytes(), stored_shorter_string.bytes());
  74. // Write a longer string and read back - heap size is expected to grow
  75. builder.clear();
  76. MUST(builder.try_append_repeated('z', SQL::Block::DATA_SIZE * 6));
  77. auto longest_string = builder.string_view();
  78. TRY_OR_FAIL(heap->write_storage(storage_block_id, longest_string.bytes()));
  79. MUST(heap->flush());
  80. new_heap_size = MUST(heap->file_size_in_bytes());
  81. EXPECT(new_heap_size > heap_size);
  82. auto stored_longest_string = TRY_OR_FAIL(heap->read_storage(storage_block_id));
  83. EXPECT_EQ(longest_string.bytes(), stored_longest_string.bytes());
  84. }
  85. TEST_CASE(heap_reuse_freed_blocks_after_storage_trim)
  86. {
  87. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  88. auto heap = create_heap();
  89. // First, write storage spanning 4 blocks
  90. auto first_index = heap->request_new_block_index();
  91. StringBuilder builder;
  92. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  93. auto long_string = builder.string_view();
  94. TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes()));
  95. MUST(heap->flush());
  96. auto original_heap_size = MUST(heap->file_size_in_bytes());
  97. // Then, overwrite the first storage and reduce it to 2 blocks
  98. builder.clear();
  99. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 2));
  100. long_string = builder.string_view();
  101. TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes()));
  102. MUST(heap->flush());
  103. auto heap_size_after_reduction = MUST(heap->file_size_in_bytes());
  104. EXPECT(heap_size_after_reduction <= original_heap_size);
  105. // Now add the second storage spanning 2 blocks - heap should not have grown compared to the original storage
  106. auto second_index = heap->request_new_block_index();
  107. TRY_OR_FAIL(heap->write_storage(second_index, long_string.bytes()));
  108. MUST(heap->flush());
  109. auto heap_size_after_second_storage = MUST(heap->file_size_in_bytes());
  110. EXPECT(heap_size_after_second_storage <= original_heap_size);
  111. }
  112. TEST_CASE(heap_reuse_freed_blocks_after_reopening_file)
  113. {
  114. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  115. size_t original_heap_size = 0;
  116. StringBuilder builder;
  117. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  118. auto long_string = builder.string_view();
  119. {
  120. auto heap = create_heap();
  121. // First, write storage spanning 4 blocks
  122. auto first_index = heap->request_new_block_index();
  123. TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes()));
  124. MUST(heap->flush());
  125. original_heap_size = MUST(heap->file_size_in_bytes());
  126. // Then, overwrite the first storage and reduce it to 2 blocks
  127. builder.clear();
  128. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 2));
  129. long_string = builder.string_view();
  130. TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes()));
  131. MUST(heap->flush());
  132. auto heap_size_after_reduction = MUST(heap->file_size_in_bytes());
  133. EXPECT(heap_size_after_reduction <= original_heap_size);
  134. }
  135. // Reopen the database file; we expect the heap to support reading back free blocks somehow.
  136. // Add the second storage spanning 2 blocks - heap should not have grown compared to the original storage.
  137. {
  138. auto heap = create_heap();
  139. auto second_index = heap->request_new_block_index();
  140. TRY_OR_FAIL(heap->write_storage(second_index, long_string.bytes()));
  141. MUST(heap->flush());
  142. auto heap_size_after_second_storage = MUST(heap->file_size_in_bytes());
  143. EXPECT(heap_size_after_second_storage <= original_heap_size);
  144. }
  145. }
  146. TEST_CASE(heap_free_storage)
  147. {
  148. ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); });
  149. auto heap = create_heap();
  150. auto storage_block_id = heap->request_new_block_index();
  151. // Write large storage spanning multiple blocks
  152. StringBuilder builder;
  153. MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4));
  154. auto long_string = builder.string_view();
  155. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  156. MUST(heap->flush());
  157. auto heap_size = MUST(heap->file_size_in_bytes());
  158. // Free the storage
  159. TRY_OR_FAIL(heap->free_storage(storage_block_id));
  160. // Again, write some large storage spanning multiple blocks
  161. storage_block_id = heap->request_new_block_index();
  162. TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes()));
  163. MUST(heap->flush());
  164. auto new_heap_size = MUST(heap->file_size_in_bytes());
  165. EXPECT(new_heap_size <= heap_size);
  166. }