1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
use glaredb_error::{DbError, Result};
use super::block::{Block, FixedSizedBlockInitializer, ValidityInitializer};
use super::block_scan::BlockScanState;
use super::row_layout::RowLayout;
use crate::buffer::buffer_manager::{AsRawBufferManager, RawBufferManager};
/// State used during appending data to the row collection.
#[derive(Debug)]
pub struct BlockAppendState {
/// Pointers to the start of each row to write to.
pub row_pointers: Vec<*mut u8>,
/// Pointers to the start of each location in the heap for writing nested or
/// varlen data.
pub heap_pointers: Vec<*mut u8>,
}
// SAFETY: The `Vec<*mut u8>` is just a buffer for storing row pointers.
unsafe impl Send for BlockAppendState {}
unsafe impl Sync for BlockAppendState {}
impl BlockAppendState {
pub fn clear(&mut self) {
self.row_pointers.clear();
self.heap_pointers.clear();
}
}
#[derive(Debug)]
pub struct RowBlocks<I: FixedSizedBlockInitializer> {
pub manager: RawBufferManager,
/// Row capacity per row block. Does not impact size of heap blocks.
pub row_capacity: usize,
/// Size in bytes of a single row stored in a fixed-size block.
pub row_width: usize,
/// Fixed size blocks initializer.
pub initializer: I,
/// Blocks for encoded rows.
pub row_blocks: Vec<Block>,
/// Blocks for varlen and nested data.
pub heap_blocks: Vec<Block>,
/// Optional alignment requirement for fixed size blocks.
///
/// If set, blocks will be allocated aligned to some multiple of this.
pub fixed_block_alignment: Option<usize>,
}
impl RowBlocks<ValidityInitializer> {
pub fn new_using_row_layout(
manager: &impl AsRawBufferManager,
row_layout: &RowLayout,
row_capacity: usize,
) -> Self {
let row_width = row_layout.row_width;
let initializer = ValidityInitializer::from_row_layout(row_layout);
Self::new(manager, initializer, row_width, row_capacity, None)
}
}
impl<I> RowBlocks<I>
where
I: FixedSizedBlockInitializer,
{
const MAX_HEAP_SIZE: usize = 1024 * 1024 * 1024 * 2; // 2GB
pub fn new(
manager: &impl AsRawBufferManager,
initializer: I,
row_width: usize,
row_capacity: usize,
fixed_block_alignment: Option<usize>,
) -> Self {
RowBlocks {
manager: manager.as_raw_buffer_manager(),
row_capacity,
row_width,
initializer,
row_blocks: Vec::new(),
heap_blocks: Vec::new(),
fixed_block_alignment,
}
}
pub fn row_mut_ptr_iter(&self) -> RowMutPtrIter<I> {
RowMutPtrIter {
blocks: self,
block_idx: 0,
row_idx: 0,
}
}
pub fn reserved_row_count(&self) -> usize {
self.row_blocks
.iter()
.map(|b| b.num_rows(self.row_width))
.sum()
}
pub fn rows_in_row_block(&self, row_block_idx: usize) -> usize {
self.row_blocks[row_block_idx].num_rows(self.row_width)
}
pub fn total_rows(&self) -> usize {
self.row_blocks
.iter()
.map(|b| b.num_rows(self.row_width))
.sum()
}
pub fn num_row_blocks(&self) -> usize {
self.row_blocks.len()
}
pub fn num_heap_blocks(&self) -> usize {
self.heap_blocks.len()
}
/// Moves the blocks from other to self.
///
/// This does not verify or update any data inside the blocks.
pub fn merge_blocks_from(&mut self, other: &mut Self) {
self.row_blocks.append(&mut other.row_blocks);
self.heap_blocks.append(&mut other.heap_blocks);
}
/// Allocates a new fixed-sized block based on the configure row width and
/// capacity.
///
/// This will initialize the block before returning it.
fn allocate_and_init_fixed_size_block(&self) -> Result<Block> {
let buf_size = self.row_width * self.row_capacity;
let block =
Block::try_new_reserve_none(&self.manager, buf_size, self.fixed_block_alignment)?;
self.initializer.initialize(block)
}
/// Prepares the read state for a single row block.
///
/// `selection` selects which rows from the row block to read.
///
/// `clear_ptrs` determines if the the pointer buffer in `state` is cleared
/// prior to pushing pointers.
// TODO: Probably remove `clear_ptrs` bool, this seems to only be used for
// the hash join, and we manually clear the pointers.
pub fn prepare_read(
&self,
state: &mut BlockScanState,
row_block_idx: usize,
selection: impl IntoIterator<Item = usize>,
clear_ptrs: bool,
) -> Result<()> {
let block = &self.row_blocks[row_block_idx];
unsafe {
state.prepare_block_scan(block, self.row_width, selection, clear_ptrs);
}
Ok(())
}
/// Prepares an append to this set of row blocks.
///
/// This will allocate additional blocks to fit an additional `row` number
/// of rows.
///
/// `heap_sizes` indicates the number of bytes each row will need in the
/// heap. May be None if columns don't require heap blocks.
///
/// The pointers to the blocks will be placed in the append state. The row
/// chunk will also be updated to indicate the set of blocks that this
/// append will reference.
///
/// This will append the pointers to the current state.
pub fn prepare_append(
&mut self,
state: &mut BlockAppendState,
rows: usize,
heap_sizes: Option<&[usize]>,
) -> Result<()> {
// Ensure we have at least one row block to work with.
if self.row_blocks.is_empty() {
let block = self.allocate_and_init_fixed_size_block()?;
self.row_blocks.push(block);
}
// Start with last block.
let mut block_idx = self.row_blocks.len() - 1;
let mut remaining = rows;
// Handle generating pointers to the row blocks.
while remaining > 0 {
let block = self.row_blocks.get_mut(block_idx).expect("block to exist");
let copy_count = usize::min(block.remaing_row_capacity(self.row_width), remaining);
let block_offset = block.num_rows(self.row_width);
// Create pointers to row locations.
state
.row_pointers
.extend((block_offset..(block_offset + copy_count)).map(|offset| {
let ptr = block.as_mut_ptr();
// SAFETY: We checked that the block we're creating pointers
// for can hold `copy_count` number of rows.
//
// Assumes that we allocated the correct size for the buffer.
let ptr = unsafe { ptr.byte_add(self.row_width * offset) };
debug_assert!(block.data.contains_addr(ptr.addr()));
ptr
}));
remaining -= copy_count;
block.reserved_bytes += copy_count * self.row_width;
if remaining > 0 {
// Means we filled the block to max capacity. Allocate new block
// and update block idx we're pointing to.
let block = self.allocate_and_init_fixed_size_block()?;
self.row_blocks.push(block);
block_idx = self.row_blocks.len() - 1
}
}
// Generate pointers to heap chunks if we're inserting varlen data.
if let Some(heap_sizes) = heap_sizes {
let total_heap_size: usize = heap_sizes.iter().sum();
// TODO: Currently this just allocates a heap block for each set of
// rows. Not sure if we want to try to be smarter about that.
if total_heap_size > Self::MAX_HEAP_SIZE {
return Err(DbError::new("Required heap allocation exceeds max")
.with_field("wanted", total_heap_size)
.with_field("max", Self::MAX_HEAP_SIZE));
}
// Create new heap block, no initialization nor alignment needs to
// happen.
let block = Block::try_new_reserve_none(&self.manager, total_heap_size, None)?;
self.heap_blocks.push(block);
let block = self.heap_blocks.last_mut().expect("heap block to exist");
let block_ptr = block.as_mut_ptr();
// Create pointer locations.
let mut offset = 0;
for &heap_size in heap_sizes {
// SAFETEY: We should have allocated the exact size needed for
// the heap block. Everything should be contained within that
// block.
let heap_ptr = unsafe { block_ptr.byte_add(offset) };
state.heap_pointers.push(heap_ptr);
// Assert that this block contains the computed pointer. Note
// that for 0-sized heap requirements, the 'contains' check may
// fail since it may point to the end of the allocation (which
// is fine, we're not writing to it in that case). The 0 check
// just catches this.
debug_assert!(
heap_size == 0 || block.data.contains_addr(heap_ptr.addr()),
"ptr: {}, block: {}",
heap_ptr.addr(),
block_ptr.addr(),
);
block.reserved_bytes += heap_size;
offset += heap_size;
}
}
Ok(())
}
/// Takes both the row and heap blocks and returns them as (row_blocks,
/// heap_blocks).
///
/// This collection can continue to be used after taking the blocks.
pub fn take_blocks(&mut self) -> (Vec<Block>, Vec<Block>) {
let row_blocks = std::mem::take(&mut self.row_blocks);
let heap_blocks = std::mem::take(&mut self.heap_blocks);
(row_blocks, heap_blocks)
}
}
/// Iterator over row pointers in a row block.
///
/// Should only be used in tests. `RowBlocks::prepare_read` should be used
/// outside of tests.
#[derive(Debug)]
pub struct RowMutPtrIter<'a, I: FixedSizedBlockInitializer> {
blocks: &'a RowBlocks<I>,
block_idx: usize,
row_idx: usize,
}
impl<I> Iterator for RowMutPtrIter<'_, I>
where
I: FixedSizedBlockInitializer,
{
type Item = *mut u8;
fn next(&mut self) -> Option<Self::Item> {
loop {
let block = self.blocks.row_blocks.get(self.block_idx)?;
if self.row_idx >= block.num_rows(self.blocks.row_width) {
self.block_idx += 1;
self.row_idx = 0;
continue;
}
let ptr = block.as_ptr();
let ptr = unsafe { ptr.byte_add(self.blocks.row_width * self.row_idx) };
self.row_idx += 1;
return Some(ptr as _);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arrays::datatype::DataType;
use crate::buffer::buffer_manager::DefaultBufferManager;
#[test]
fn prepare_append_allocate_single_row_block() {
let layout = RowLayout::try_new([DataType::int32()]).unwrap();
let mut blocks = RowBlocks::new_using_row_layout(&DefaultBufferManager, &layout, 16);
let mut append_state = BlockAppendState {
row_pointers: Vec::new(),
heap_pointers: Vec::new(),
};
blocks.prepare_append(&mut append_state, 4, None).unwrap();
assert_eq!(4, append_state.row_pointers.len());
assert_eq!(1, blocks.num_row_blocks());
assert_eq!(0, blocks.num_heap_blocks());
assert_eq!(4, blocks.reserved_row_count());
let mut read_state = BlockScanState {
row_pointers: Vec::new(),
};
blocks.prepare_read(&mut read_state, 0, 0..4, true).unwrap();
assert_eq!(4, read_state.row_pointers.len());
}
#[test]
fn prepare_append_allocate_multiple_row_blocks() {
let layout = RowLayout::try_new([DataType::int32()]).unwrap();
let mut blocks = RowBlocks::new_using_row_layout(&DefaultBufferManager, &layout, 16);
let mut append_state = BlockAppendState {
row_pointers: Vec::new(),
heap_pointers: Vec::new(),
};
blocks.prepare_append(&mut append_state, 24, None).unwrap();
assert_eq!(24, append_state.row_pointers.len());
assert_eq!(2, blocks.num_row_blocks());
assert_eq!(0, blocks.num_heap_blocks());
assert_eq!(24, blocks.reserved_row_count());
let mut read_state = BlockScanState {
row_pointers: Vec::new(),
};
blocks
.prepare_read(&mut read_state, 0, 0..16, true)
.unwrap();
assert_eq!(16, read_state.row_pointers.len());
}
}