1 | #include "duckdb/storage/meta_block_writer.hpp" |
2 | |
3 | #include <cstring> |
4 | |
5 | namespace duckdb { |
6 | |
7 | MetaBlockWriter::MetaBlockWriter(BlockManager &block_manager, block_id_t initial_block_id) |
8 | : block_manager(block_manager) { |
9 | if (initial_block_id == INVALID_BLOCK) { |
10 | initial_block_id = MetaBlockWriter::GetNextBlockId(); |
11 | } |
12 | block = block_manager.CreateBlock(block_id: initial_block_id, source_buffer: nullptr); |
13 | Store<block_id_t>(val: -1, ptr: block->buffer); |
14 | offset = sizeof(block_id_t); |
15 | } |
16 | |
17 | MetaBlockWriter::~MetaBlockWriter() { |
18 | // If there's an exception during checkpoint, this can get destroyed without |
19 | // flushing the data...which is fine, because none of the unwritten data |
20 | // will be referenced. |
21 | // |
22 | // Otherwise, we should have explicitly flushed (and thereby nulled the block). |
23 | D_ASSERT(!block || Exception::UncaughtException()); |
24 | } |
25 | |
26 | block_id_t MetaBlockWriter::GetNextBlockId() { |
27 | return block_manager.GetFreeBlockId(); |
28 | } |
29 | |
30 | BlockPointer MetaBlockWriter::GetBlockPointer() { |
31 | BlockPointer pointer; |
32 | pointer.block_id = block->id; |
33 | pointer.offset = offset; |
34 | return pointer; |
35 | } |
36 | |
37 | void MetaBlockWriter::Flush() { |
38 | if (offset < block->size) { |
39 | // clear remaining bytes of block (if any) |
40 | memset(s: block->buffer + offset, c: 0, n: block->size - offset); |
41 | } |
42 | AdvanceBlock(); |
43 | block = nullptr; |
44 | } |
45 | |
46 | void MetaBlockWriter::AdvanceBlock() { |
47 | written_blocks.insert(x: block->id); |
48 | if (offset > sizeof(block_id_t)) { |
49 | block_manager.Write(block&: *block); |
50 | offset = sizeof(block_id_t); |
51 | } |
52 | } |
53 | |
54 | void MetaBlockWriter::WriteData(const_data_ptr_t buffer, idx_t write_size) { |
55 | while (offset + write_size > block->size) { |
56 | // we need to make a new block |
57 | // first copy what we can |
58 | D_ASSERT(offset <= block->size); |
59 | idx_t copy_amount = block->size - offset; |
60 | if (copy_amount > 0) { |
61 | memcpy(dest: block->buffer + offset, src: buffer, n: copy_amount); |
62 | buffer += copy_amount; |
63 | offset += copy_amount; |
64 | write_size -= copy_amount; |
65 | } |
66 | // now we need to get a new block id |
67 | block_id_t new_block_id = GetNextBlockId(); |
68 | // write the block id of the new block to the start of the current block |
69 | Store<block_id_t>(val: new_block_id, ptr: block->buffer); |
70 | // first flush the old block |
71 | AdvanceBlock(); |
72 | // now update the block id of the block |
73 | block->id = new_block_id; |
74 | Store<block_id_t>(val: -1, ptr: block->buffer); |
75 | } |
76 | memcpy(dest: block->buffer + offset, src: buffer, n: write_size); |
77 | offset += write_size; |
78 | } |
79 | |
80 | } // namespace duckdb |
81 | |