rudb_vector/vector.rs
1//! The vector itself.
2//!
3//! `spec/07-execution.md` section 7.1 calls this the widest interface in the system, says every
4//! operator depends on it, and says changing it after twenty operators exist is expensive. So it
5//! is written before the first operator rather than after the fifth.
6//!
7//! A vector is a type, a length of at most [`VECTOR_SIZE`], a physical form, a validity
8//! representation and some data. Four of the forms are the ones in `spec/04-architecture.md`
9//! section 4.3: flat, constant, sequence and dictionary. Run length, bit packed and string view come
10//! after them, one at a time with the kernels that read them rather than all at once ahead of
11//! anything that can use them.
12//!
13//! Dictionary and run length are the pair worth understanding together, because they answer
14//! different questions about the same column. A dictionary says which distinct values there are, so
15//! it wins on low cardinality however the rows are ordered. Run length says where the values stop,
16//! so it wins on a clustered column however many distinct values it has. A column can want either
17//! one without wanting the other, and `hits` has columns of both kinds.
18//!
19//! String view is the odd one out, because it is not about making a column smaller. It is about who
20//! owns the bytes: the views are the vector's and the arena is shared, so cutting a chunk out of a
21//! page of strings moves sixteen bytes a row and copies none of the payload. Every other form here
22//! trades a little work per row for less memory, and that one trades nothing at all.
23//!
24//! **What is not here yet.** Buffers are owned. Section 7.1 says a vector borrowed from a buffer
25//! managed page carries a pin, and there is no buffer manager until M2, so there is nothing to pin
26//! and pretending otherwise would be an interface built against an imaginary caller. Nested types
27//! are not stored yet either, for the same reason: a `LIST(STRUCT(...))` is offsets plus child
28//! column chunks, and child column chunks are storage.
29
30use std::borrow::Cow;
31use std::sync::Arc;
32
33use rudb_common::{Cause, Error, LogicalType, Result, Value, slow};
34
35use crate::buffer::Buffer;
36use crate::fsst::SymbolTable;
37use crate::string::{StringColumn, StringView};
38use crate::validity::Validity;
39
40/// How many values are in a full vector.
41///
42/// 1024 rather than DuckDB's 2048, per `spec/04-architecture.md` section 4.3. It is the FastLanes
43/// unit, it makes a validity mask exactly 16 `u64` words, and it keeps a vector of 16 byte string
44/// views at 16 KiB, which is the size at which several of these fit in L1 together rather than
45/// evicting each other.
46pub const VECTOR_SIZE: usize = 1024;
47
48/// Which physical form a vector is in.
49///
50/// An operator asks this once per vector and then takes the path it wants, which is the one branch
51/// per vector that the whole design is willing to spend.
52///
53/// Not exhaustive, and that is a decision rather than an oversight. `Encoded` is the fifth form
54/// and it arrives at layer three with the specialization contract. If this enum were exhaustive,
55/// the day it lands is the day every kernel in the workspace stops compiling, and the pressure at
56/// that moment would be to add an arm to each of them in a hurry rather than to think about what
57/// each one should do with an encoded vector. A required fallback arm means each kernel already
58/// has a correct answer for a form it has never seen, and specializing it is then a change that
59/// can be made one kernel at a time with a benchmark next to it.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61#[non_exhaustive]
62pub enum Form {
63 /// One value per position.
64 Flat,
65 /// One value, repeated.
66 Constant,
67 /// A start and a step, computed rather than stored.
68 Sequence,
69 /// Codes into a smaller vector of distinct values.
70 Dictionary,
71 /// Integers stored in as many bits as the range of the column needs, offset from a base.
72 ///
73 /// The form a narrow integer column is in. A ClickBench `ResolutionWidth` is a `SMALLINT` whose
74 /// values live between 0 and 2560, which is twelve bits, so the column is three quarters of the
75 /// size it was and the pages behind it are three quarters of the reads. What it costs is a shift
76 /// and a mask per value, which is why this is worth it at storage and at rest and is not a form
77 /// anything should be building in the middle of a pipeline.
78 BitPacked,
79 /// Sixteen byte views over an arena the vector shares rather than owns.
80 ///
81 /// The form a varchar column is in once more than one vector is looking at the same page. A flat
82 /// varchar vector owns its arena, so cutting a chunk out of it copies every byte of every long
83 /// string in the range, and on ClickBench that is most of what reading `URL` costs. Sharing the
84 /// arena makes the cut the views and nothing else, the way a dictionary cut is the codes and
85 /// nothing else.
86 StringView,
87 /// Strings compressed against one symbol table, each row on its own.
88 ///
89 /// The form a text column is in at rest. FSST is about half the bytes on the ClickBench `URL`
90 /// and `Title` columns, and unlike a block compressor it keeps random access, so reading row
91 /// four million does not decompress the four million before it. What it costs is a decompression
92 /// per row read, which is why an equality filter over it is worth writing in code space: the
93 /// literal compresses once and the rows never decompress at all.
94 Fsst,
95 /// One value per run, with the row each run ends at.
96 ///
97 /// The form a clustered column is in. `hits` is written in time order, so `EventDate` is a few
98 /// hundred runs over a hundred million rows, and a sum over it is a few hundred multiplications
99 /// rather than a hundred million additions. Dictionary says which distinct values there are and
100 /// this says where they stop, and a column can want either one without wanting the other.
101 Rle,
102}
103
104/// The values of a flat vector, one Rust vector per physical type.
105///
106/// The variants are physical rather than logical, which is what lets `DATE` and `INTEGER` share
107/// storage and share a kernel. What a run of `i32` means is the vector's logical type's business.
108#[derive(Debug, Clone, PartialEq)]
109#[non_exhaustive]
110pub enum Data {
111 /// No values, for the type of an untyped `NULL`.
112 Empty,
113 /// One byte per value.
114 Bool(Buffer<bool>),
115 /// 8 bit signed.
116 Int8(Buffer<i8>),
117 /// 16 bit signed.
118 Int16(Buffer<i16>),
119 /// 32 bit signed.
120 Int32(Buffer<i32>),
121 /// 64 bit signed.
122 Int64(Buffer<i64>),
123 /// 128 bit signed.
124 Int128(Buffer<i128>),
125 /// 8 bit unsigned.
126 UInt8(Buffer<u8>),
127 /// 16 bit unsigned.
128 UInt16(Buffer<u16>),
129 /// 32 bit unsigned.
130 UInt32(Buffer<u32>),
131 /// 64 bit unsigned.
132 UInt64(Buffer<u64>),
133 /// 128 bit unsigned.
134 UInt128(Buffer<u128>),
135 /// IEEE 754 binary32.
136 Float32(Buffer<f32>),
137 /// IEEE 754 binary64.
138 Float64(Buffer<f64>),
139 /// The months, days and microseconds triple.
140 Interval(Buffer<(i32, i32, i64)>),
141 /// Strings, as 16 byte views plus the arena the long ones live in.
142 Varlen(StringColumn),
143}
144
145impl Data {
146 /// How many values are stored.
147 ///
148 /// The match below has no wildcard arm, and that is what makes this function the check that
149 /// keeps [`for_each_layout`](crate::for_each_layout) honest. A variant added to this enum
150 /// without being added to the `all` group fails to compile here, which is a line in a build log
151 /// rather than a layout quietly missing from six kernels.
152 #[must_use]
153 pub fn len(&self) -> usize {
154 macro_rules! lengths {
155 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
156 match self {
157 Self::Empty => 0,
158 $(Self::$variant(values) => values.len(),)+
159 }
160 };
161 }
162 crate::for_each_layout!(all, lengths)
163 }
164
165 /// Whether there are no values.
166 #[must_use]
167 pub fn is_empty(&self) -> bool {
168 self.len() == 0
169 }
170
171 /// How many bytes of memory these values are holding.
172 ///
173 /// One arm per layout through the same macro as [`Data::len`], for the same reason: a layout
174 /// added without a size here is a layout the memory limit would charge nothing for, and a
175 /// buffer that is free is a buffer that can be grown until the process dies.
176 #[must_use]
177 pub fn footprint(&self) -> usize {
178 macro_rules! sizes {
179 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
180 match self {
181 Self::Empty => 0,
182 $(Self::$variant(values) => values.footprint(),)+
183 }
184 };
185 }
186 crate::for_each_layout!(all, sizes)
187 }
188
189 /// An integer at `index`, widened, for any of the signed integer layouts.
190 ///
191 /// Used by the decimal path, which needs the unscaled value out of whichever width the width
192 /// and scale picked, and by anything else that would otherwise repeat the same five arms.
193 #[must_use]
194 pub fn signed_at(&self, index: usize) -> Option<i128> {
195 macro_rules! widened {
196 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
197 match self {
198 $(Self::$variant(v) => v.get(index).map(|&x| i128::from(x)),)+
199 _ => None,
200 }
201 };
202 }
203 crate::for_each_layout!(signed, widened)
204 }
205
206 /// An unsigned integer at `index`, widened.
207 #[must_use]
208 pub fn unsigned_at(&self, index: usize) -> Option<u128> {
209 macro_rules! widened {
210 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
211 match self {
212 $(Self::$variant(v) => v.get(index).map(|&x| u128::from(x)),)+
213 _ => None,
214 }
215 };
216 }
217 crate::for_each_layout!(unsigned, widened)
218 }
219
220 /// The string at `index`, for a `Varlen`.
221 #[must_use]
222 pub fn str_at(&self, index: usize) -> Option<&str> {
223 match self {
224 Self::Varlen(column) => column.get(index),
225 _ => None,
226 }
227 }
228
229 /// The bytes at `index`, for a `Varlen`, whatever they are.
230 ///
231 /// What a `BLOB` reads through, since the bytes of one are not required to be text and
232 /// [`Self::str_at`] answers `None` for the ones that are not.
233 #[must_use]
234 pub fn bytes_at(&self, index: usize) -> Option<&[u8]> {
235 match self {
236 Self::Varlen(column) => column.bytes(index),
237 _ => None,
238 }
239 }
240}
241
242/// A type, a length, a validity representation and some data.
243#[derive(Debug, Clone, PartialEq)]
244pub struct Vector {
245 ty: LogicalType,
246 len: usize,
247 validity: Validity,
248 body: Body,
249}
250
251/// What the vector holds, which is what its form is decided by.
252#[derive(Debug, Clone, PartialEq)]
253enum Body {
254 Flat(Data),
255 Constant(Box<Value>),
256 Sequence {
257 start: i64,
258 step: i64,
259 },
260 /// The values are behind an `Arc` rather than a `Box` because slicing shares them.
261 ///
262 /// A dictionary vector is cut once per chunk and the dictionary itself is the same dictionary
263 /// every time, so a `Box` meant a copy of every value in it per cut. On the ClickBench columns
264 /// that are dictionary encoded the dictionary is larger than the chunk of codes pointing into
265 /// it, and copying it was ten percent of the cycles of reading the file.
266 ///
267 /// Nothing here mutates a dictionary in place, so sharing one is only ever a read, and the one
268 /// place that wants an owned copy of the values is [`compose`], which asks for one.
269 Dictionary {
270 codes: Vec<u32>,
271 values: Arc<Vector>,
272 },
273 /// Integer codes of `width` bits each, packed end to end, each one an offset from `base`.
274 ///
275 /// Row `r` is the `width` bits starting at bit `(offset + r) * width`, read little end first, so
276 /// a code that straddles a word boundary has its low bits in the earlier word. `offset` is what
277 /// lets a cut of a packed column be free: the bits are not byte aligned, so a slice either
278 /// repacks or remembers where it starts, and remembering is one addition per read.
279 ///
280 /// The words are behind an `Arc` for the reason the dictionary's values are. A page is packed
281 /// once and cut into chunk sized pieces, and copying the words per cut would undo most of what
282 /// the packing saved.
283 Packed {
284 words: Arc<Vec<u64>>,
285 width: u32,
286 base: i128,
287 offset: usize,
288 },
289 /// The views of a string column, over an arena that other vectors are reading at the same time.
290 ///
291 /// The views are owned because a cut is a different run of views, and the arena is shared
292 /// because a cut is the same bytes. That split is the whole form: sixteen bytes a row move and
293 /// the payload does not, however many cuts a page is taken in.
294 ///
295 /// A row's bytes are found the same way [`StringColumn`] finds them, through
296 /// [`StringView::bytes_in`], so a short string never reads the arena at all and the two ways of
297 /// holding strings cannot answer a row differently.
298 Views {
299 views: Vec<StringView>,
300 arena: Arc<Buffer<u8>>,
301 },
302 /// The FSST codes of every row, end to end, with one symbol table over all of them.
303 ///
304 /// A span rather than a run of offsets, because a gather keeps this form and a gather puts the
305 /// rows in an order the codes are not in. Eight bytes a row either way, and the span is the one
306 /// that survives being permuted.
307 ///
308 /// The codes and the table are shared for the reason a dictionary's values are: one table is
309 /// trained per page and every chunk cut out of it points at the same one. A table is sixty five
310 /// thousand hash slots, so a table per chunk would cost more than the compression saves.
311 Coded {
312 codes: Arc<Vec<u8>>,
313 spans: Vec<(u32, u32)>,
314 table: Arc<SymbolTable>,
315 },
316 /// One value per run, with the row each run ends at, exclusive and increasing.
317 ///
318 /// Ends rather than lengths, because every reader of this wants to know which run holds a row
319 /// and ends answer that with a binary search while lengths answer it with a running total. The
320 /// two are the same information and only one of them is the one that gets asked for.
321 ///
322 /// The values are behind an `Arc` for the reason the dictionary's are: a page is cut into chunk
323 /// sized pieces and the values are the same values every time.
324 Runs {
325 ends: Vec<u32>,
326 values: Arc<Vector>,
327 },
328}
329
330impl Vector {
331 /// A flat vector of `data`, all valid.
332 ///
333 /// # Errors
334 ///
335 /// If the data's physical layout is not the one the type calls for. That check is here rather
336 /// than left to the caller because a vector whose type and layout disagree is a wrong answer
337 /// waiting to be read out, and it costs one comparison at construction to prevent.
338 pub fn flat(ty: LogicalType, data: Data) -> Result<Self> {
339 let len = data.len();
340 if !matches!(data, Data::Empty) && layout_of(&data) != ty.physical() {
341 return Err(Error::internal(format!(
342 "a {ty} vector cannot hold {:?} data",
343 layout_of(&data)
344 )));
345 }
346 Ok(Self { ty, len, validity: Validity::AllValid, body: Body::Flat(data) })
347 }
348
349 /// A flat vector built from single values, with the nulls among them turning into validity.
350 ///
351 /// The slow way in, and the only way in that anything outside this crate has. It is what an
352 /// `INSERT`, a `VALUES` clause and a test build a column with, all of which arrive holding
353 /// values rather than a run of `i32`. Nothing on a scan path calls it: a scan produces a run of
354 /// data directly and hands it to [`Self::flat`].
355 ///
356 /// # Errors
357 ///
358 /// If a value is not one the type can hold, or if the type is one that cannot be stored flat
359 /// yet, which today means the nested types.
360 pub fn from_values(ty: LogicalType, values: &[Value]) -> Result<Self> {
361 let mut data = empty_data_for(&ty)?;
362 for value in values {
363 push_value(&mut data, value)?;
364 }
365 let validity = Validity::from_iter(values.len(), |index| !values[index].is_null());
366 Ok(Self { ty, len: values.len(), validity, body: Body::Flat(data) })
367 }
368
369 /// A vector of `len` copies of one value.
370 ///
371 /// Costs one value regardless of the length, which is what makes a literal in a predicate free
372 /// and what makes a projection of a constant free.
373 #[must_use]
374 pub fn constant(ty: LogicalType, value: Value, len: usize) -> Self {
375 let validity = if value.is_null() { Validity::AllInvalid } else { Validity::AllValid };
376 Self { ty, len, validity, body: Body::Constant(Box::new(value)) }
377 }
378
379 /// A vector of `len` values starting at `start` and stepping by `step`.
380 ///
381 /// This is what a row identifier column is, and it costs sixteen bytes rather than eight
382 /// kilobytes. A scan that produces row ids for a later fetch produces one of these.
383 #[must_use]
384 pub fn sequence(start: i64, step: i64, len: usize) -> Self {
385 Self {
386 ty: LogicalType::BigInt,
387 len,
388 validity: Validity::AllValid,
389 body: Body::Sequence { start, step },
390 }
391 }
392
393 /// A vector of codes into a smaller vector of distinct values.
394 ///
395 /// The form the whole M3 thesis rests on. A dictionary vector handed to a group by is an
396 /// integer column, and an aggregate over one is an aggregate over integers no matter what the
397 /// logical type says.
398 ///
399 /// A dictionary over a dictionary is composed into one level here rather than left as two, so
400 /// the form has a depth of one always and a kernel that reads [`Self::dictionary_parts`] is
401 /// reading the values rather than another layer of codes. Two filters over the same chunk build
402 /// the second case and four conjuncts pushed down separately build four of it.
403 ///
404 /// The cost of leaving them stacked turned out to be a cliff rather than a slope. Every loop in
405 /// `rudb-kernels` reaches for the values behind the codes with [`Self::data`], a dictionary
406 /// pointing at a dictionary has no data to hand back, so the second level does not make the
407 /// kernels slower, it turns them off and drops the work onto the row at a time path that exists
408 /// to be correct rather than fast. Measured on server3 over a chunk of two numeric columns and a
409 /// consumer of two vectorized passes, one level reads at 3.5 nanoseconds a row and two levels at
410 /// 104, and the third and fourth levels cost almost nothing more because the first one had
411 /// already given up everything there was to give. Composing is one pass over the outer codes,
412 /// which the range check above is already making.
413 ///
414 /// The one dictionary that is not composed past is one carrying a validity of its own. A
415 /// dictionary is built all valid and only [`Self::with_validity`] can change that, so such a
416 /// vector is saying that its nulls are at this level rather than in the values it points at, and
417 /// composing past it would drop them.
418 ///
419 /// # Errors
420 ///
421 /// If any code is past the end of the value vector.
422 pub fn dictionary(codes: Vec<u32>, values: Vector) -> Result<Self> {
423 if let Some(&bad) = codes.iter().find(|&&code| code as usize >= values.len()) {
424 return Err(Error::internal(format!(
425 "dictionary code {bad} is past the end of a {} value dictionary",
426 values.len()
427 )));
428 }
429 let (codes, values) = compose(codes, values);
430 Ok(Self {
431 ty: values.ty.clone(),
432 len: codes.len(),
433 validity: Validity::AllValid,
434 body: Body::Dictionary { codes, values: Arc::new(values) },
435 })
436 }
437
438 /// A vector of runs, one value each, with the row each run ends at.
439 ///
440 /// `ends` is exclusive and strictly increasing, so run `i` covers the rows from `ends[i - 1]` to
441 /// `ends[i]` and run zero starts at nothing. The length of the vector is the last end.
442 ///
443 /// The depth is one, the same way a dictionary's is, and for a sharper reason. Every kernel that
444 /// wants runs wants the value of a run without another search, and a run length vector over a
445 /// run length vector turns one search into two and then into three. Rather than compose, this
446 /// refuses: nothing in the engine builds a stacked one, because [`Self::run_encoded`] only ever
447 /// reads a flat body, so a stacked one is a caller doing something by hand and the useful answer
448 /// is to say so rather than to quietly do a pass of work they did not ask for.
449 ///
450 /// A run over a dictionary is fine and is not that case. The two forms answer different
451 /// questions and a column that is both clustered and low cardinality genuinely wants both.
452 ///
453 /// # Errors
454 ///
455 /// If there is not exactly one value per run, if the ends do not increase, or if the values are
456 /// themselves run length encoded.
457 pub fn runs(ends: Vec<u32>, values: Vector) -> Result<Self> {
458 if matches!(values.body, Body::Runs { .. }) {
459 return Err(Error::internal("runs of runs, which is two searches to read one row"));
460 }
461 if ends.len() != values.len() {
462 return Err(Error::internal(format!(
463 "{} runs and {} values to put in them",
464 ends.len(),
465 values.len()
466 )));
467 }
468 if ends.windows(2).any(|pair| pair[0] >= pair[1]) || ends.first() == Some(&0) {
469 return Err(Error::internal("run ends that do not increase"));
470 }
471 let len = ends.last().copied().unwrap_or(0) as usize;
472 Ok(Self {
473 ty: values.ty.clone(),
474 len,
475 validity: Validity::AllValid,
476 body: Body::Runs { ends, values: Arc::new(values) },
477 })
478 }
479
480 /// The same values as runs, when there are few enough runs for that to be smaller.
481 ///
482 /// Costs one pass over the column to find out, which is why this is a call somebody makes rather
483 /// than something a constructor does. The decision is the same arithmetic every time: a row in
484 /// flat form costs one value, a run costs one value plus the four bytes of its end, so runs are
485 /// smaller once there are fewer than about half as many runs as rows, and the narrower the
486 /// column the more runs it takes. `RUNS_PAY_AT` is that ratio, written down rather than spelt
487 /// into an `if`, because it is the number a sweep will want to move.
488 ///
489 /// Only a flat body is looked at. A constant and a sequence are already one value and two
490 /// numbers, so there is nothing to win, and a dictionary that is also clustered is a real case
491 /// that wants its codes run length encoded rather than its values, which is a different function
492 /// and not this one.
493 ///
494 /// Two adjacent nulls are one run. Two adjacent equal values with a null between them are three,
495 /// because the null is a value of the column as far as anything reading it is concerned.
496 ///
497 /// # Errors
498 ///
499 /// If the type has no flat layout, which today means the nested types.
500 pub fn run_encoded(&self) -> Result<Self> {
501 let Body::Flat(data) = &self.body else {
502 return Ok(self.clone());
503 };
504 let ends = boundaries(data, &self.validity, self.len);
505 if ends.len().saturating_mul(RUNS_PAY_AT) >= self.len {
506 return Ok(self.clone());
507 }
508 let starts: Vec<u32> =
509 std::iter::once(0).chain(ends.iter().copied()).take(ends.len()).collect();
510 Self::runs(ends, self.gather(&starts)?)
511 }
512
513 /// A vector of `len` integers packed `width` bits each, every one an offset from `base`.
514 ///
515 /// The way in for a reader that already has the packed bits, which is what a column file holds
516 /// and what a network frame carries. Nothing unpacks on the way in, so a scan of a packed column
517 /// hands the bits straight to the chunk and the cost of the form is paid by whoever reads a
518 /// value rather than by the scan.
519 ///
520 /// The range check is on the two ends rather than on every code, which is the whole check. A
521 /// code is between zero and `2^width - 1` by construction, so if `base` and `base + 2^width - 1`
522 /// both fit the column's layout then every value does, and that is two comparisons instead of
523 /// one per row.
524 ///
525 /// # Errors
526 ///
527 /// If the type is not one of the integer layouts, if the width is not between one and
528 /// [`PACKED_WIDTH_MAX`], if there are not enough words for the length, or if either end of the
529 /// range would not fit the type.
530 pub fn packed(
531 ty: LogicalType,
532 words: Vec<u64>,
533 width: u32,
534 base: i128,
535 len: usize,
536 ) -> Result<Self> {
537 let Some((low, high)) = layout_range(&ty) else {
538 return Err(Error::internal(format!("a {ty} vector has no integer layout to pack")));
539 };
540 if width == 0 || width > PACKED_WIDTH_MAX {
541 return Err(Error::internal(format!(
542 "a packed width of {width}, which is outside 1 to {PACKED_WIDTH_MAX}"
543 )));
544 }
545 let needed = words_for(len, width);
546 if words.len() < needed {
547 return Err(Error::internal(format!(
548 "{} words for {len} values of {width} bits, which needs {needed}",
549 words.len()
550 )));
551 }
552 let top = base + i128::from(u64::MAX >> (64 - width));
553 if base < low || top > high {
554 return Err(Error::internal(format!(
555 "packed values from {base} to {top}, which a {ty} cannot hold"
556 )));
557 }
558 Ok(Self {
559 ty,
560 len,
561 validity: Validity::AllValid,
562 body: Body::Packed { words: Arc::new(words), width, base, offset: 0 },
563 })
564 }
565
566 /// The same values bit packed, when the range of the column makes that smaller.
567 ///
568 /// Costs one pass to find the range and one to write the bits, which is why this is a call
569 /// somebody makes rather than something a constructor does. It is the counterpart of
570 /// [`Self::run_encoded`] and the decision has the same shape: a row flat costs the width of its
571 /// layout, a row packed costs the bits the column's range needs, and the form is worth having
572 /// only when the second is a good deal smaller than the first. [`PACKING_PAYS_AT`] is that
573 /// ratio, written down rather than spelt into an `if`, because it is the number a sweep will
574 /// want to move.
575 ///
576 /// Only a flat integer body is looked at. A constant and a sequence are already smaller than any
577 /// packing of them, a dictionary's codes are the thing that would want packing rather than its
578 /// values, and a float has no range to pack into since the bits of an `f64` are not an integer
579 /// that arithmetic on the column agrees with.
580 ///
581 /// The range is taken over every slot including the null ones, which hold a zero. A column of
582 /// large values with one null in it therefore packs a range that reaches down to zero and comes
583 /// out wider than it needed to be. The alternative is a pass that consults the validity per slot
584 /// to find the range and a second rule for what to write into a null slot, and this form exists
585 /// to make reads cheap rather than to squeeze the last bit out of a sparse column.
586 ///
587 /// A column whose values are all the same packs to nothing at all, and rather than invent a zero
588 /// bit code this declines and leaves it to [`Self::run_encoded`], which turns that column into
589 /// one run and is smaller than any packing of it.
590 ///
591 /// # Errors
592 ///
593 /// If the packed bits and the length disagree, which would be a bug here rather than a caller
594 /// doing something wrong.
595 pub fn bit_packed(&self) -> Result<Self> {
596 let Body::Flat(data) = &self.body else {
597 return Ok(self.clone());
598 };
599 let Some((low, high)) = span_of(data, self.len) else {
600 return Ok(self.clone());
601 };
602 let Some(range) = high.checked_sub(low).and_then(|range| u64::try_from(range).ok()) else {
603 return Ok(self.clone());
604 };
605 let width = u64::BITS - range.leading_zeros();
606 if width == 0 || width > PACKED_WIDTH_MAX {
607 return Ok(self.clone());
608 }
609 if words_for(self.len, width) * size_of::<u64>() * PACKING_PAYS_AT > data.footprint() {
610 return Ok(self.clone());
611 }
612 let words = pack(data, self.len, low, width);
613 let packed = Self::packed(self.ty.clone(), words, width, low, self.len)?;
614 Ok(packed.with_validity(self.validity.clone()))
615 }
616
617 /// A vector of string views over an arena somebody else is holding too.
618 ///
619 /// The way in for a scan that has a page of strings and wants several chunks over it. Each chunk
620 /// gets its own run of views and they all share the one arena, so the bytes are read where the
621 /// page put them and nothing copies them.
622 ///
623 /// Every view is checked against the arena here rather than when a row is read. That is a pass
624 /// over the views at construction, which is the same pass the caller just did to build them, and
625 /// what it buys is that a row of this form cannot resolve to bytes that are not there. The check
626 /// is on the offsets and not on the bytes, so it says nothing about whether the payload is text,
627 /// which is the same promise a `BLOB` column makes.
628 ///
629 /// # Errors
630 ///
631 /// If the type is not one stored as views, or if a view points past the end of the arena.
632 pub fn string_views(
633 ty: LogicalType,
634 views: Vec<StringView>,
635 arena: Arc<Buffer<u8>>,
636 ) -> Result<Self> {
637 if ty.physical() != rudb_common::PhysicalType::Varlen {
638 return Err(Error::internal(format!("a {ty} vector cannot hold string views")));
639 }
640 if views.iter().any(|view| view.bytes_in(&arena).is_none()) {
641 return Err(Error::internal("a string view points past the end of its arena"));
642 }
643 let len = views.len();
644 Ok(Self { ty, len, validity: Validity::AllValid, body: Body::Views { views, arena } })
645 }
646
647 /// The same strings, in a form where a cut of them does not copy the bytes.
648 ///
649 /// The counterpart of [`Self::run_encoded`] and [`Self::bit_packed`] for a string column, and
650 /// the only one of the three that takes `self` by value. It has to: what it does is move the
651 /// arena into an `Arc` so nothing copies it again, and a version taking `&self` would start by
652 /// copying the arena once to have one to move.
653 ///
654 /// Anything that is not a flat string column comes back as it was, which includes a column that
655 /// is already in this form.
656 ///
657 /// # Errors
658 ///
659 /// Nothing here fails today. The result is a `Result` because the check inside
660 /// [`Self::string_views`] is worth running on the views this builds rather than trusting that
661 /// this function built them right.
662 pub fn shared_text(self) -> Result<Self> {
663 let Body::Flat(Data::Varlen(column)) = self.body else {
664 return Ok(self);
665 };
666 let (views, arena) = column.into_parts();
667 let shared = Self::string_views(self.ty, views, Arc::new(arena))?;
668 Ok(shared.with_validity(self.validity))
669 }
670
671 /// A vector of FSST codes against a table somebody else trained.
672 ///
673 /// The way in for a reader that has a page of compressed strings and the table that goes with
674 /// it. The codes are not copied and the table is not retrained, so laying several chunks over
675 /// one page costs the spans and nothing else.
676 ///
677 /// # Errors
678 ///
679 /// If the type is not one stored as text, or if a span runs past the end of the codes.
680 pub fn coded(
681 ty: LogicalType,
682 codes: Arc<Vec<u8>>,
683 spans: Vec<(u32, u32)>,
684 table: Arc<SymbolTable>,
685 ) -> Result<Self> {
686 if ty.physical() != rudb_common::PhysicalType::Varlen {
687 return Err(Error::internal(format!("a {ty} vector cannot hold FSST codes")));
688 }
689 let end = u32::try_from(codes.len()).unwrap_or(u32::MAX);
690 if spans.iter().any(|&(from, to)| from > to || to > end) {
691 return Err(Error::internal("an FSST span runs past the end of the codes"));
692 }
693 let len = spans.len();
694 Ok(Self {
695 ty,
696 len,
697 validity: Validity::AllValid,
698 body: Body::Coded { codes, spans, table },
699 })
700 }
701
702 /// The same strings, compressed against a table trained on them.
703 ///
704 /// The counterpart of [`Self::run_encoded`] and [`Self::bit_packed`] for a text column, and it
705 /// takes `self` by value for the reason [`Self::shared_text`] does.
706 ///
707 /// The table is trained on every row rather than on a sample. A vector is at most 1024 rows, so
708 /// the sample would be most of the column anyway, and the systematic sampling
709 /// `spec/06-compression.md` section 6.3 asks for is a decision about a page and belongs to
710 /// whoever is holding one.
711 ///
712 /// It declines unless the codes are at most half the bytes the strings are. FSST gets about that
713 /// on text and rather less on anything already short or already random, and below that the
714 /// decompression per row read is not bought back. A column it declines on comes back as it was.
715 ///
716 /// # Errors
717 ///
718 /// Nothing here fails today. The result is a `Result` because the checks inside [`Self::coded`]
719 /// are worth running on what this builds rather than trusting that this built it right.
720 pub fn compressed(self) -> Result<Self> {
721 let Body::Flat(Data::Varlen(column)) = &self.body else {
722 return Ok(self);
723 };
724 let rows: Vec<&[u8]> = (0..self.len).filter_map(|row| column.bytes(row)).collect();
725 if rows.len() != self.len {
726 return Ok(self);
727 }
728 let plain: usize = rows.iter().map(|row| row.len()).sum();
729 let table = SymbolTable::train(&rows);
730 let mut codes = Vec::with_capacity(plain);
731 let mut spans = Vec::with_capacity(self.len);
732 for row in &rows {
733 let from = u32::try_from(codes.len()).unwrap_or(u32::MAX);
734 table.compress(row, &mut codes);
735 spans.push((from, u32::try_from(codes.len()).unwrap_or(u32::MAX)));
736 }
737 if codes.len() * FSST_PAYS_AT > plain {
738 return Ok(self);
739 }
740 let coded = Self::coded(self.ty.clone(), Arc::new(codes), spans, Arc::new(table))?;
741 Ok(coded.with_validity(self.validity.clone()))
742 }
743
744 /// The same vector with a different validity.
745 #[must_use]
746 pub fn with_validity(mut self, validity: Validity) -> Self {
747 self.validity = validity;
748 self
749 }
750
751 /// What kind of values these are.
752 #[must_use]
753 pub fn logical_type(&self) -> &LogicalType {
754 &self.ty
755 }
756
757 /// How many values there are.
758 #[must_use]
759 pub fn len(&self) -> usize {
760 self.len
761 }
762
763 /// Whether there are no values.
764 #[must_use]
765 pub fn is_empty(&self) -> bool {
766 self.len == 0
767 }
768
769 /// How many bytes of memory this vector is holding.
770 ///
771 /// What the memory limit charges for it. A constant and a sequence hold one value and two
772 /// numbers however long they are, which is the point of both forms, so the number here is the
773 /// form's cost and not the column's width times its length.
774 ///
775 /// A dictionary counts its values in full, and two vectors sharing one dictionary each report
776 /// all of it. That over counts, deliberately: working out that two operators are looking at the
777 /// same `Arc` means threading identity through the accounting, and a limit that over counts
778 /// refuses a query that would have fit while a limit that under counts lets one through that
779 /// does not. The first is a worse answer to give and the second is a worse thing to be.
780 #[must_use]
781 pub fn footprint(&self) -> usize {
782 let body = match &self.body {
783 Body::Flat(data) => data.footprint(),
784 Body::Constant(value) => value.footprint(),
785 Body::Sequence { .. } => 0,
786 Body::Dictionary { codes, values } => {
787 codes.capacity() * size_of::<u32>() + values.footprint()
788 }
789 Body::Packed { words, .. } => words.capacity() * size_of::<u64>(),
790 // The arena counts in full in every vector sharing it, for the reason a shared
791 // dictionary does: over counting refuses a query that would have fit and under counting
792 // admits one that does not, and the first is the better way to be wrong.
793 Body::Views { views, arena } => {
794 views.capacity() * size_of::<StringView>() + arena.footprint()
795 }
796 // The table counts in full in every vector sharing it, the way a shared arena and a
797 // shared dictionary do. It is the largest of the three and the most shared of them, so
798 // this is the one place the over counting is worth saying out loud: a page of a hundred
799 // chunks reports its table a hundred times.
800 Body::Coded { codes, spans, table } => {
801 codes.capacity() + spans.capacity() * size_of::<(u32, u32)>() + table.footprint()
802 }
803 Body::Runs { ends, values } => ends.capacity() * size_of::<u32>() + values.footprint(),
804 };
805 size_of::<Self>() + self.validity.footprint() + body
806 }
807
808 /// Which of the values are not null.
809 #[must_use]
810 pub fn validity(&self) -> &Validity {
811 &self.validity
812 }
813
814 /// Which physical form this vector is in.
815 #[must_use]
816 pub fn form(&self) -> Form {
817 match self.body {
818 Body::Flat(_) => Form::Flat,
819 Body::Constant(_) => Form::Constant,
820 Body::Sequence { .. } => Form::Sequence,
821 Body::Dictionary { .. } => Form::Dictionary,
822 Body::Packed { .. } => Form::BitPacked,
823 Body::Views { .. } => Form::StringView,
824 Body::Coded { .. } => Form::Fsst,
825 Body::Runs { .. } => Form::Rle,
826 }
827 }
828
829 /// The data, for a flat vector, and `None` for any other form.
830 ///
831 /// A kernel that wants a slice asks for it and takes the flat path if it gets one. A kernel
832 /// that can do better on a constant or a dictionary checks [`Self::form`] first.
833 #[must_use]
834 pub fn data(&self) -> Option<&Data> {
835 match &self.body {
836 Body::Flat(data) => Some(data),
837 _ => None,
838 }
839 }
840
841 /// The one value, for a constant vector, and `None` for any other form.
842 ///
843 /// A kernel comparing a column against a literal wants the literal once rather than 1024
844 /// times, and [`Self::value_at`] on a constant clones it on every call because it has to be
845 /// able to hand back a `Value` for any form. This is the accessor that lets the specialized
846 /// path hoist the clone out of the loop.
847 #[must_use]
848 pub fn constant_value(&self) -> Option<&Value> {
849 match &self.body {
850 Body::Constant(value) => Some(value.as_ref()),
851 _ => None,
852 }
853 }
854
855 /// The codes and the values, for a dictionary vector, and `None` for any other form.
856 ///
857 /// The reason a kernel needs this rather than reading the dictionary through
858 /// [`Self::value_at`] is the entire argument for the form existing. A filter against a
859 /// dictionary column of 1024 rows and 40 distinct values is 40 comparisons and 1024 lookups,
860 /// not 1024 comparisons, and there is no way to write that loop without seeing the codes.
861 ///
862 /// Note what the validity of the returned vector means. A dictionary keeps its nulls in the
863 /// vector it points at, and the dictionary's own validity says nothing about them, so a caller
864 /// deciding whether row `i` is null has to ask the value vector about `codes[i]` rather than
865 /// asking this vector about `i`. [`Self::flatten`] has the same note on it for the same
866 /// reason, because getting this wrong is a null that survives being selected and comes out as
867 /// a zero.
868 #[must_use]
869 pub fn dictionary_parts(&self) -> Option<(&[u32], &Self)> {
870 match &self.body {
871 Body::Dictionary { codes, values } => Some((codes, values.as_ref())),
872 _ => None,
873 }
874 }
875
876 /// The run ends and the run values, for a run length vector, and `None` for any other form.
877 ///
878 /// The ends are exclusive and increasing, and there is exactly one value per run, so a kernel
879 /// that wants to walk this walks the pairs and never asks which run a row is in. That is the
880 /// whole argument for the form: an aggregate over a clustered column is one multiply per run
881 /// instead of one add per row, and there is no way to write that loop without seeing the ends.
882 ///
883 /// The nulls are in the values, the way a dictionary's are, so a caller deciding whether row `i`
884 /// is null asks the value vector about the run rather than asking this vector about `i`.
885 #[must_use]
886 pub fn run_parts(&self) -> Option<(&[u32], &Self)> {
887 match &self.body {
888 Body::Runs { ends, values } => Some((ends, values.as_ref())),
889 _ => None,
890 }
891 }
892
893 /// Where each row's value is, for the two forms that keep their values somewhere else.
894 ///
895 /// A dictionary and a run length vector are the same shape seen from a kernel: a run of
896 /// positions and a vector to read them out of. The difference is that a dictionary stores the
897 /// positions and a run length vector works them out, and a kernel writing `values[at[row]]` does
898 /// not care which. So every specialization written against [`Self::dictionary_parts`] covers
899 /// both forms by asking this instead, and the day a third form with an indirection arrives it
900 /// covers that one too without any of those kernels being reopened.
901 ///
902 /// The run length side costs an allocation of one position per row and a pass to fill it, which
903 /// is the same four bytes a row a dictionary was already carrying and is paid once per kernel
904 /// call rather than once per row. That is the price of this being one accessor rather than a
905 /// second arm in eighteen kernels, and it is not the last word: a kernel that wants a run at a
906 /// time reads [`Self::run_parts`] and pays nothing, which is the specialization this makes it
907 /// possible to skip writing until a sweep says it is worth it.
908 #[must_use]
909 pub fn positions(&self) -> Option<(Cow<'_, [u32]>, &Self)> {
910 match &self.body {
911 Body::Dictionary { codes, values } => Some((Cow::Borrowed(codes), values.as_ref())),
912 Body::Runs { ends, values } => {
913 let mut at = Vec::with_capacity(self.len);
914 for (run, &stop) in ends.iter().enumerate() {
915 let run = u32::try_from(run).unwrap_or(u32::MAX);
916 at.resize(stop as usize, run);
917 }
918 Some((Cow::Owned(at), values.as_ref()))
919 }
920 _ => None,
921 }
922 }
923
924 /// The bits and what they mean, for a bit packed vector, and `None` for any other form.
925 ///
926 /// What a kernel needs to stay in code space. A comparison against a literal is the case that
927 /// pays: `column > 900` over a column packed from a base of 40 is `code > 860`, which is the
928 /// same shift and mask the read was going to do anyway and no unpacking at all, and a literal
929 /// outside the packed range answers the whole vector without reading a bit of it. None of that
930 /// can be written without seeing the width and the base.
931 #[must_use]
932 pub fn packed_parts(&self) -> Option<Packed<'_>> {
933 match &self.body {
934 Body::Packed { words, width, base, offset } => {
935 Some(Packed { words, width: *width, base: *base, offset: *offset })
936 }
937 _ => None,
938 }
939 }
940
941 /// The views and the arena, for either form that stores strings, and `None` for the rest.
942 ///
943 /// This is to the two string forms what [`Self::positions`] is to the two forms that point
944 /// somewhere else. A flat varchar column owns its arena and a string view column shares one, and
945 /// a kernel reading a row wants the view and the bytes either way, so every specialization
946 /// written against this covers both forms and neither has to be reopened when a third way of
947 /// holding an arena arrives.
948 ///
949 /// The arena is whatever the long strings live in, which for a column over a page is the page,
950 /// including the parts of it no view points at. Only the views say which bytes are a row.
951 #[must_use]
952 pub fn text_parts(&self) -> Option<(&[StringView], &[u8])> {
953 match &self.body {
954 Body::Flat(Data::Varlen(column)) => Some((column.views(), column.arena())),
955 Body::Views { views, arena } => Some((views, arena)),
956 _ => None,
957 }
958 }
959
960 /// The codes and the table, for an FSST vector, and `None` for any other form.
961 ///
962 /// What a kernel needs to stay in code space. An equality filter is the case that pays, and it
963 /// pays completely: the literal is compressed once against the same table and after that a row
964 /// matches exactly when its code bytes match, because compressing is a function and so is
965 /// decompressing. No row is decompressed at all. An ordering comparison cannot do that, since a
966 /// symbol code says nothing about where its symbol sorts, so those decompress and say so.
967 #[must_use]
968 pub fn coded_parts(&self) -> Option<Coded<'_>> {
969 match &self.body {
970 Body::Coded { codes, spans, table } => Some(Coded { codes, spans, table }),
971 _ => None,
972 }
973 }
974
975 /// The start and the step, for a sequence vector, and `None` for any other form.
976 #[must_use]
977 pub fn sequence_parts(&self) -> Option<(i64, i64)> {
978 match self.body {
979 Body::Sequence { start, step } => Some((start, step)),
980 _ => None,
981 }
982 }
983
984 /// The value at `index`, as a single value.
985 ///
986 /// This is the slow path on purpose. It is what a result set is read out with and what a test
987 /// asserts on, and an operator that calls it per row is an operator that has already lost the
988 /// argument the vector interface exists to win.
989 #[must_use]
990 pub fn value_at(&self, index: usize) -> Value {
991 if index >= self.len || !self.validity.is_valid(index) {
992 return Value::Null;
993 }
994 match &self.body {
995 Body::Constant(value) => value.as_ref().clone(),
996 Body::Sequence { start, step } => Value::BigInt(start + step * index as i64),
997 Body::Dictionary { codes, values } => match codes.get(index) {
998 Some(&code) => values.value_at(code as usize),
999 None => Value::Null,
1000 },
1001 Body::Runs { ends, values } => match run_holding(ends, index) {
1002 Some(run) => values.value_at(run),
1003 None => Value::Null,
1004 },
1005 // One value unpacked into a run of one, so that what a packed value means is decided in
1006 // the same place a flat one is rather than in a second copy of the type mapping that
1007 // could drift from it. It allocates, which this path is allowed to do and the typed
1008 // unpack in `copied` is not, and it is the reason anything about to read a packed
1009 // column a row at a time should flatten it once instead.
1010 Body::Packed { words, width, base, offset } => {
1011 unpack(&self.ty, words, *offset, *width, *base, &[index])
1012 .map_or(Value::Null, |data| value_from(&self.ty, &data, 0))
1013 }
1014 // The bytes are where the arena has them, and what they are read as is the logical
1015 // type's business, so this hands the row to the same reader a flat column goes through
1016 // rather than deciding here that a `BLOB` is a string.
1017 Body::Views { views, arena } => {
1018 match views.get(index).and_then(|v| v.bytes_in(arena)) {
1019 Some(bytes) => bytes_as(&self.ty, bytes),
1020 None => Value::Null,
1021 }
1022 }
1023 // One row decompressed on its own, which is the property the form is chosen for. It
1024 // allocates, which this path is allowed to do, and it is the reason anything about to
1025 // read a compressed column a row at a time should flatten it once instead.
1026 Body::Coded { codes, spans, table } => {
1027 match spans.get(index).and_then(|&(from, to)| {
1028 let mut out = Vec::new();
1029 table.decompress(codes.get(from as usize..to as usize)?, &mut out).ok()?;
1030 Some(out)
1031 }) {
1032 Some(bytes) => bytes_as(&self.ty, &bytes),
1033 None => Value::Null,
1034 }
1035 }
1036 Body::Flat(data) => value_from(&self.ty, data, index),
1037 }
1038 }
1039
1040 /// The text at `index`, borrowed rather than copied.
1041 ///
1042 /// [`Self::value_at`] on a `VARCHAR` column allocates a `String` per call, and a group by that
1043 /// reads a string column keys on one string per input row. This hands back the bytes where they
1044 /// already are, so a caller with somewhere to put them does not go to the allocator at all.
1045 ///
1046 /// `None` for a null, for an index past the end, for a column that is not `VARCHAR`, and for the
1047 /// constant and sequence forms, whose values are not stored per position. A caller that gets
1048 /// `None` has to fall back to [`Self::value_at`], which is correct for all of those.
1049 #[must_use]
1050 pub fn text_at(&self, index: usize) -> Option<&str> {
1051 if self.ty != LogicalType::Varchar || index >= self.len || !self.validity.is_valid(index) {
1052 return None;
1053 }
1054 match &self.body {
1055 Body::Flat(data) => data.str_at(index),
1056 Body::Dictionary { codes, values } => {
1057 values.text_at(usize::try_from(*codes.get(index)?).ok()?)
1058 }
1059 Body::Runs { ends, values } => values.text_at(run_holding(ends, index)?),
1060 Body::Views { views, arena } => {
1061 std::str::from_utf8(views.get(index)?.bytes_in(arena)?).ok()
1062 }
1063 _ => None,
1064 }
1065 }
1066
1067 /// The variable length bytes at `index`, borrowed without validating or copying them.
1068 ///
1069 /// String data is validated when it enters a vector. Hashing and equality only need its bytes,
1070 /// so those kernels should not pay for UTF-8 validation again on every read.
1071 #[must_use]
1072 pub fn bytes_at(&self, index: usize) -> Option<&[u8]> {
1073 if index >= self.len || !self.validity.is_valid(index) {
1074 return None;
1075 }
1076 match &self.body {
1077 Body::Constant(value) => match value.as_ref() {
1078 Value::Varchar(text) => Some(text.as_bytes()),
1079 Value::Blob(bytes) => Some(bytes),
1080 _ => None,
1081 },
1082 Body::Dictionary { codes, values } => {
1083 values.bytes_at(usize::try_from(*codes.get(index)?).ok()?)
1084 }
1085 Body::Runs { ends, values } => values.bytes_at(run_holding(ends, index)?),
1086 Body::Views { views, arena } => views.get(index)?.bytes_in(arena),
1087 Body::Flat(data) => data.bytes_at(index),
1088 // The same `None` [`Self::text_at`] gives, for the same reason. A compressed row is not
1089 // anywhere in its plain bytes, so there is nothing here to hand back a borrow of, and a
1090 // caller that gets `None` goes to `value_at` and gets the row decompressed into a value.
1091 Body::Coded { .. } | Body::Sequence { .. } | Body::Packed { .. } => None,
1092 }
1093 }
1094
1095 /// Every value in order, as single values.
1096 pub fn iter(&self) -> impl Iterator<Item = Value> + '_ {
1097 (0..self.len).map(|index| self.value_at(index))
1098 }
1099
1100 /// A contiguous run of the values, in the form they are already in.
1101 ///
1102 /// This is the cut [`Self::gather`] cannot do. A gather walks a dictionary to its leaf and
1103 /// copies, so gathering a piece of a dictionary encoded column hands back a flat one, and a
1104 /// caller that only wanted the first thousand rows of a page has silently paid for a copy and
1105 /// thrown the dictionary away. A group by over a dictionary encoded column is the case that
1106 /// cares, and it is most of ClickBench.
1107 ///
1108 /// So each form is cut as itself. A dictionary keeps its dictionary and slices its codes, a
1109 /// sequence stays arithmetic with its start moved along, a constant stays a shorter constant,
1110 /// and a flat body is the one that genuinely has to copy its range.
1111 ///
1112 /// The dictionary itself is shared rather than copied, so a cut is the codes and nothing else.
1113 /// It used to be copied, and on a read of a ClickBench partition that copy was ten percent of
1114 /// the cycles: a page holds one dictionary and is cut into chunk sized pieces, so the whole
1115 /// dictionary was copied once per chunk to be read the same way each time.
1116 ///
1117 /// # Errors
1118 ///
1119 /// If the range runs past the end of the vector, or if the type has no flat layout and the
1120 /// body is one that has to be copied.
1121 pub fn slice(&self, at: usize, len: usize) -> Result<Self> {
1122 let end = at.checked_add(len).ok_or_else(|| Error::internal("a slice that wraps"))?;
1123 if end > self.len {
1124 return Err(Error::internal(format!("rows {at} to {end} of a vector of {}", self.len)));
1125 }
1126 if at == 0 && len == self.len {
1127 return Ok(self.clone());
1128 }
1129 let validity = Validity::from_iter(len, |row| self.validity.is_valid(at + row));
1130 let body = match &self.body {
1131 Body::Constant(value) => Body::Constant(value.clone()),
1132 Body::Sequence { start, step } => {
1133 Body::Sequence { start: start + step * at as i64, step: *step }
1134 }
1135 Body::Dictionary { codes, values } => {
1136 Body::Dictionary { codes: codes[at..end].to_vec(), values: Arc::clone(values) }
1137 }
1138 // The bits are not byte aligned, so a cut either repacks them or moves the row the
1139 // reading starts at. Moving it is one addition and repacking is a pass, and a page is
1140 // cut into chunk sized pieces often enough that the difference is the form.
1141 Body::Packed { words, width, base, offset } => Body::Packed {
1142 words: Arc::clone(words),
1143 width: *width,
1144 base: *base,
1145 offset: offset + at,
1146 },
1147 // The cut a flat string column cannot do. Sixteen bytes a row move and the payload stays
1148 // where the page put it, so taking a chunk out of a column of long strings costs the
1149 // same as taking one out of a column of integers. A flat varchar body copies every byte
1150 // of every long string in the range instead, which is the measurement written down in
1151 // `Chunk::compact`: compaction loses on a varchar column, and this is the half of the
1152 // reason that is about cutting rather than about selecting.
1153 Body::Views { views, arena } => {
1154 Body::Views { views: views[at..end].to_vec(), arena: Arc::clone(arena) }
1155 }
1156 // The spans are absolute positions in the shared codes, so a cut is a run of them and
1157 // nothing has to be rebased. One page of compressed strings, one table, and as many
1158 // chunks over it as the reader wants.
1159 Body::Coded { codes, spans, table } => Body::Coded {
1160 codes: Arc::clone(codes),
1161 spans: spans[at..end].to_vec(),
1162 table: Arc::clone(table),
1163 },
1164 // Only the runs the range touches survive, the first and last of them cut back to where
1165 // the range starts and stops, and every end moved to be relative to the new row zero. A
1166 // cut of a hundred rows out of a column of a hundred million is a handful of runs, which
1167 // is the reason this form is worth cutting as itself rather than copying out.
1168 Body::Runs { ends, values } if len > 0 => {
1169 let first = run_holding(ends, at).unwrap_or(0);
1170 let last = run_holding(ends, end - 1).unwrap_or(first);
1171 let cut: Vec<u32> = ends[first..=last]
1172 .iter()
1173 .map(|&stop| stop.min(end as u32) - at as u32)
1174 .collect();
1175 let values = values.slice(first, last - first + 1)?;
1176 Body::Runs { ends: cut, values: Arc::new(values) }
1177 }
1178 // An empty cut has no run to point at and an empty run length body would be a vector of
1179 // no runs claiming a length, so it comes back as the empty flat vector instead.
1180 Body::Runs { .. } => return self.gather(&[]),
1181 // The one form with nowhere to point, so its range is copied out. A gather is the
1182 // right tool here and does no more than this would: a flat body has no dictionary
1183 // under it for the gather to flatten.
1184 Body::Flat(_) => {
1185 let indices: Vec<u32> =
1186 (at..end).map(|row| u32::try_from(row).unwrap_or(u32::MAX)).collect();
1187 return self.gather(&indices);
1188 }
1189 };
1190 Ok(Self { ty: self.ty.clone(), len, validity, body })
1191 }
1192
1193 /// The same values in flat form.
1194 ///
1195 /// Flattening a vector that is already flat is free. Flattening any other form costs a copy,
1196 /// which is exactly why the other forms exist and why nothing on the hot path should call
1197 /// this. It is here for the operators that genuinely cannot do better and for the tests that
1198 /// check the other forms against it.
1199 ///
1200 /// A call that copies counts itself against [`Cause::Flatten`], because a flatten on a hot path
1201 /// is the most expensive thing in this crate and the only way to find one is to have the number.
1202 /// A call on a vector that is already flat does not count, since it neither copies nor gives
1203 /// anything up.
1204 ///
1205 /// # Errors
1206 ///
1207 /// If the type is one this crate cannot store flat yet, which today means the nested types.
1208 pub fn flatten(&self) -> Result<Self> {
1209 if let Body::Flat(_) = self.body {
1210 return Ok(self.clone());
1211 }
1212 slow::took(Cause::Flatten);
1213 self.copied((0..self.len).collect(), false)
1214 }
1215
1216 /// The values at the given positions, copied, in a form that does not point back at this vector.
1217 ///
1218 /// This is the copying counterpart to [`Self::dictionary`], and the two are the two halves of
1219 /// the decision `spec/07-execution.md` section 7.1 describes. Which half is right is measured
1220 /// rather than argued, and [`Chunk::compact`](crate::Chunk::compact) is where the measurement
1221 /// is written down.
1222 ///
1223 /// A dictionary chain is walked to its leaf first and the codes composed on the way down, so the
1224 /// copy runs once over the data rather than once per level, and a position that is null at any
1225 /// level comes out null here. The copy is a typed loop per physical layout rather than a `Value`
1226 /// per row, which is the whole point of it and is what [`Self::flatten`] now goes through too.
1227 ///
1228 /// # Errors
1229 ///
1230 /// If the type has no flat layout, which today means the nested types.
1231 pub fn gather(&self, indices: &[u32]) -> Result<Self> {
1232 self.copied(indices.iter().map(|&index| index as usize).collect(), true)
1233 }
1234
1235 /// The copy both [`Self::gather`] and [`Self::flatten`] are.
1236 ///
1237 /// `forms_stay` is the one thing the two want differently. A gather of a constant is a shorter
1238 /// constant and copying it out would be a thousand writes of the same value for nothing, and a
1239 /// gather of string views is a shorter run of views over the same arena rather than a copy of
1240 /// the bytes. Flattening promises flat form to a caller that is about to read the data slice, so
1241 /// for that one both of them have to be written out.
1242 fn copied(&self, at: Vec<usize>, forms_stay: bool) -> Result<Self> {
1243 let rows = at.len();
1244 let (at, leaf) = self.resolve(at);
1245 let live: Vec<bool> = at.iter().map(|&index| index != NOWHERE).collect();
1246 let validity = Validity::from_run(&live);
1247 let body = match &leaf.body {
1248 // Every position holds the same value, so the only thing the gather can change is the
1249 // length and which positions are null. A gather with no null in it is still a constant.
1250 Body::Constant(value) => {
1251 if forms_stay && matches!(validity, Validity::AllValid) {
1252 return Ok(Self::constant(self.ty.clone(), value.as_ref().clone(), rows));
1253 }
1254 let mut data = empty_data_for(&self.ty)?;
1255 for &index in &at {
1256 push_value(&mut data, if index == NOWHERE { &Value::Null } else { value })?;
1257 }
1258 Body::Flat(data)
1259 }
1260 // A sequence is arithmetic rather than storage, so the gather is the arithmetic done at
1261 // the positions asked for, and a null writes the zero every other layout writes.
1262 Body::Sequence { start, step } => Body::Flat(Data::Int64(
1263 at.iter()
1264 .map(|&index| if index == NOWHERE { 0 } else { start + step * index as i64 })
1265 .collect(),
1266 )),
1267 // A flat body with no values is the untyped null, so every position asked for is null
1268 // whatever was asked for. Going through the copy would build a run of no values and
1269 // call it `rows` long, which is a vector whose length and data disagree.
1270 Body::Flat(Data::Empty) => {
1271 return Ok(Self::constant(self.ty.clone(), Value::Null, rows));
1272 }
1273 Body::Flat(data) => Body::Flat(copy_of(data, &at)),
1274 // The one form whose copy is arithmetic rather than a move of bytes. It goes through a
1275 // typed loop per layout the way the flat copy does, because the alternative is a `Value`
1276 // per row and this is the path a flatten of a scanned column takes.
1277 Body::Packed { words, width, base, offset } => {
1278 Body::Flat(unpack(&self.ty, words, *offset, *width, *base, &at)?)
1279 }
1280 // A gather keeps the form, which is what makes selecting rows out of a string column
1281 // cost sixteen bytes a row instead of the bytes of the strings. The arena it shares is
1282 // the whole arena and not the part the kept rows point at, so a selection that throws
1283 // most of a page away goes on holding the page. That is the trade the form is: a cut and
1284 // a filter are cheap and the memory comes back when the last vector over the page goes,
1285 // and a caller that wants the bytes narrowed asks for a flatten.
1286 Body::Views { views, arena } if forms_stay => Body::Views {
1287 views: at
1288 .iter()
1289 .map(|&index| views.get(index).copied().unwrap_or_else(StringView::empty))
1290 .collect(),
1291 arena: Arc::clone(arena),
1292 },
1293 // Flattening promises a data slice, so the bytes are copied out into an arena of their
1294 // own and the shared one is let go of. The total is known before any of it is copied,
1295 // the way the flat copy works it out, so the new arena is one allocation.
1296 Body::Views { views, arena } => {
1297 let mut out = StringColumn::with_capacity(at.len());
1298 out.reserve_bytes(
1299 at.iter()
1300 .filter_map(|&index| views.get(index))
1301 .filter(|view| !view.is_inline())
1302 .map(StringView::len)
1303 .sum(),
1304 );
1305 for &index in &at {
1306 let bytes = views.get(index).and_then(|view| view.bytes_in(arena));
1307 out.push_bytes(bytes.unwrap_or_default());
1308 }
1309 Body::Flat(Data::Varlen(out))
1310 }
1311 // A gather keeps the form, because the codes do not move and a span survives being put
1312 // in an order the codes are not in. A position that resolved to nowhere gets the empty
1313 // span, which decompresses to no bytes, which is the zero every other layout writes.
1314 Body::Coded { codes, spans, table } if forms_stay => Body::Coded {
1315 codes: Arc::clone(codes),
1316 spans: at
1317 .iter()
1318 .map(|&index| spans.get(index).copied().unwrap_or((0, 0)))
1319 .collect(),
1320 table: Arc::clone(table),
1321 },
1322 // Flattening decompresses, which is the price of the data slice it promises. The scratch
1323 // buffer is reused across rows, so this is one allocation for the whole column rather
1324 // than one per row the way reading it a value at a time would be.
1325 Body::Coded { codes, spans, table } => {
1326 let mut out = StringColumn::with_capacity(at.len());
1327 let mut scratch = Vec::new();
1328 for &index in &at {
1329 scratch.clear();
1330 let span = spans
1331 .get(index)
1332 .and_then(|&(from, to)| codes.get(from as usize..to as usize));
1333 if let Some(span) = span {
1334 table.decompress(span, &mut scratch)?;
1335 }
1336 out.push_bytes(&scratch);
1337 }
1338 Body::Flat(Data::Varlen(out))
1339 }
1340 // Unreachable, because `resolve` walks past both of the forms that point at another
1341 // vector and stops at the first body that does not.
1342 Body::Dictionary { .. } | Body::Runs { .. } => {
1343 return Err(Error::internal(
1344 "a form that points somewhere survived being resolved",
1345 ));
1346 }
1347 };
1348 Ok(Self { ty: self.ty.clone(), len: rows, validity, body })
1349 }
1350
1351 /// Where each wanted position lives in the first body that is not a dictionary, and that body.
1352 ///
1353 /// A position that is null anywhere on the way down, or past the end of anything on the way
1354 /// down, comes back as [`NOWHERE`]. That single sentinel is what keeps the copy loop from
1355 /// carrying a validity mask alongside the positions it is already walking.
1356 fn resolve(&self, mut at: Vec<usize>) -> (Vec<usize>, &Self) {
1357 let mut source = self;
1358 loop {
1359 for slot in &mut at {
1360 if *slot >= source.len || !source.validity.is_valid(*slot) {
1361 *slot = NOWHERE;
1362 }
1363 }
1364 source = match &source.body {
1365 Body::Dictionary { codes, values } => {
1366 for slot in &mut at {
1367 *slot = match codes.get(*slot) {
1368 Some(&code) => code as usize,
1369 None => NOWHERE,
1370 };
1371 }
1372 values.as_ref()
1373 }
1374 // A run length body is a dictionary whose code is worked out from the position
1375 // rather than stored, so the walk down is the same walk with a search where the
1376 // lookup was. `NOWHERE` searches for nothing and stays `NOWHERE`.
1377 Body::Runs { ends, values } => {
1378 for slot in &mut at {
1379 *slot = run_holding(ends, *slot).unwrap_or(NOWHERE);
1380 }
1381 values.as_ref()
1382 }
1383 _ => return (at, source),
1384 };
1385 }
1386 }
1387}
1388
1389/// So that a kernel can take its operands as either a list of vectors or a list of references.
1390///
1391/// A caller that built a `Vec<Vector>` and a caller whose operands are already somewhere else, in a
1392/// chunk or in an evaluator's scratch, want the same kernel. Without this the second kind has to
1393/// clone every operand into a `Vec` to satisfy the signature, and a clone of a vector is a copy of
1394/// the whole column, so the type would be charging real memory traffic for nothing.
1395impl AsRef<Vector> for Vector {
1396 fn as_ref(&self) -> &Vector {
1397 self
1398 }
1399}
1400
1401/// The bits of a packed vector and what they mean, for a kernel that wants to stay in code space.
1402///
1403/// Borrowed from the vector rather than owning anything, so getting one costs nothing and a kernel
1404/// that finds it cannot use them has given up nothing by asking.
1405#[derive(Debug, Clone, Copy)]
1406pub struct Packed<'a> {
1407 words: &'a [u64],
1408 width: u32,
1409 base: i128,
1410 offset: usize,
1411}
1412
1413impl Packed<'_> {
1414 /// How many bits one code takes, between one and [`PACKED_WIDTH_MAX`].
1415 #[must_use]
1416 pub fn width(&self) -> u32 {
1417 self.width
1418 }
1419
1420 /// What zero means, so that the value of a row is the base plus its code.
1421 #[must_use]
1422 pub fn base(&self) -> i128 {
1423 self.base
1424 }
1425
1426 /// The largest value this vector can be holding, whatever it is actually holding.
1427 ///
1428 /// With [`Self::base`] this is the pair a comparison kernel wants first. A literal outside the
1429 /// two answers every row of the vector the same way, which is a whole chunk decided without a
1430 /// bit being read, and that is the case a zone map would have caught if there were one here.
1431 #[must_use]
1432 pub fn ceiling(&self) -> i128 {
1433 self.base + i128::from(u64::MAX >> (u64::BITS - self.width))
1434 }
1435
1436 /// The code of row `row`, which is its value minus [`Self::base`].
1437 ///
1438 /// Out of range rows read as zero rather than panicking, the way every other accessor in this
1439 /// file answers for a row that is not there.
1440 #[must_use]
1441 pub fn code(&self, row: usize) -> u64 {
1442 code_at(self.words, (self.offset + row) * self.width as usize, self.width)
1443 }
1444
1445 /// Which code a value would have, and `None` for a value this vector cannot be holding.
1446 ///
1447 /// The translation a comparison does once per vector so that it does not have to unpack once per
1448 /// row. `None` is the useful answer rather than a failure: it says the literal is outside the
1449 /// packed range, so every row compares against it the same way.
1450 #[must_use]
1451 pub fn code_of(&self, value: i128) -> Option<u64> {
1452 u64::try_from(value.checked_sub(self.base)?).ok().filter(|&code| code <= self.mask())
1453 }
1454
1455 /// The largest code the width allows.
1456 fn mask(&self) -> u64 {
1457 u64::MAX >> (u64::BITS - self.width)
1458 }
1459}
1460
1461/// The widest a packed code is allowed to be.
1462///
1463/// Sixty three rather than sixty four so that a mask is `u64::MAX >> (64 - width)` with no shift of
1464/// a whole word in it, and reading a code is one branch on whether it straddles rather than two. A
1465/// sixty four bit code saves nothing anyway, since it is the layout it came from.
1466pub const PACKED_WIDTH_MAX: u32 = 63;
1467
1468/// How much smaller packing has to be before it is worth the shift and the mask on every read.
1469///
1470/// Two, so a column packs when the bits come to half the flat size or less. A column that would save
1471/// a tenth stays flat, because a tenth of a column is not worth turning every read of it into
1472/// arithmetic, and the whole argument for the form is that a narrow column saves most of itself.
1473pub const PACKING_PAYS_AT: usize = 2;
1474
1475/// How much smaller compressing has to be before it is worth a decompression on every read.
1476///
1477/// Two, the same rule packing follows and for the same reason. FSST gets about that on text, so a
1478/// column of English or of URLs compresses and a column of short codes or of random bytes does not,
1479/// which is the right answer for both.
1480pub const FSST_PAYS_AT: usize = 2;
1481
1482/// The codes of a compressed column and the table they are against.
1483///
1484/// Handed out by [`Vector::coded_parts`] so a kernel can work in code space. Nothing here
1485/// decompresses, which is the point: [`Self::encode`] puts the literal into the same space the rows
1486/// are already in, and after that an equality test is a byte slice comparison.
1487#[derive(Debug, Clone, Copy)]
1488pub struct Coded<'a> {
1489 codes: &'a [u8],
1490 spans: &'a [(u32, u32)],
1491 table: &'a SymbolTable,
1492}
1493
1494impl Coded<'_> {
1495 /// The table every row in this vector is compressed against.
1496 #[must_use]
1497 pub fn table(&self) -> &SymbolTable {
1498 self.table
1499 }
1500
1501 /// The code bytes of one row, still compressed.
1502 #[must_use]
1503 pub fn row(&self, row: usize) -> Option<&[u8]> {
1504 let &(from, to) = self.spans.get(row)?;
1505 self.codes.get(from as usize..to as usize)
1506 }
1507
1508 /// Some bytes in the code space this vector is in.
1509 ///
1510 /// The literal side of an equality filter. Compressing is a function of the table and the bytes,
1511 /// so two strings compress to the same codes exactly when they are the same string, and an
1512 /// equality test on the codes is an equality test on the strings with no decompression in it.
1513 #[must_use]
1514 pub fn encode(&self, bytes: &[u8]) -> Vec<u8> {
1515 let mut out = Vec::with_capacity(bytes.len());
1516 self.table.compress(bytes, &mut out);
1517 out
1518 }
1519}
1520
1521/// How many words hold `len` codes of `width` bits.
1522fn words_for(len: usize, width: u32) -> usize {
1523 (len * width as usize).div_ceil(u64::BITS as usize)
1524}
1525
1526/// The lowest and highest value a type's layout can hold, and `None` for a type with no integer one.
1527///
1528/// This is also the test of whether a type can be packed at all, and it is the only one, so the
1529/// layouts listed here and the layouts [`pack`] and [`unpack`] know how to walk are the same list
1530/// from the same macro and cannot drift apart.
1531fn layout_range(ty: &LogicalType) -> Option<(i128, i128)> {
1532 use rudb_common::PhysicalType as P;
1533 macro_rules! ranges {
1534 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1535 match ty.physical() {
1536 $(P::$variant => Some((i128::from(<$native>::MIN), i128::from(<$native>::MAX))),)+
1537 _ => None,
1538 }
1539 };
1540 }
1541 crate::for_each_layout!(exact, ranges)
1542}
1543
1544/// The lowest and highest value in the first `len` slots of a run of integer data.
1545///
1546/// `None` for data that is not integers, which is what says a column cannot be packed. The null
1547/// slots are in the span, holding whatever zero was written into them, which
1548/// [`Vector::bit_packed`] says more about.
1549fn span_of(data: &Data, len: usize) -> Option<(i128, i128)> {
1550 macro_rules! spans {
1551 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1552 match data {
1553 $(Data::$variant(values) => {
1554 let mut low = i128::MAX;
1555 let mut high = i128::MIN;
1556 for &value in values.as_slice().iter().take(len) {
1557 let value = i128::from(value);
1558 low = low.min(value);
1559 high = high.max(value);
1560 }
1561 (low <= high).then_some((low, high))
1562 })+
1563 _ => None,
1564 }
1565 };
1566 }
1567 crate::for_each_layout!(exact, spans)
1568}
1569
1570/// The first `len` values of a run of integer data, written out as codes of `width` bits from `base`.
1571fn pack(data: &Data, len: usize, base: i128, width: u32) -> Vec<u64> {
1572 let mut words = vec![0u64; words_for(len, width)];
1573 macro_rules! packing {
1574 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1575 match data {
1576 $(Data::$variant(values) => {
1577 for (row, &value) in values.as_slice().iter().take(len).enumerate() {
1578 // In range because `base` and `width` came from the span of this same run.
1579 let code = u64::try_from(i128::from(value) - base).unwrap_or(0);
1580 write_code(&mut words, row * width as usize, width, code);
1581 }
1582 })+
1583 _ => {}
1584 }
1585 };
1586 }
1587 crate::for_each_layout!(exact, packing);
1588 words
1589}
1590
1591/// The codes at the given rows, unpacked into the flat layout the type calls for.
1592///
1593/// A row of [`NOWHERE`] writes the layout's zero, which is the rule [`copy_of`] follows for the same
1594/// reason: every layout here is a parallel array to a validity mask, so a null takes a slot.
1595///
1596/// # Errors
1597///
1598/// If the type has no flat layout, which a packed vector cannot have and which is checked when one
1599/// is built, so an error here is a bug rather than a caller mistake.
1600fn unpack(
1601 ty: &LogicalType,
1602 words: &[u64],
1603 offset: usize,
1604 width: u32,
1605 base: i128,
1606 at: &[usize],
1607) -> Result<Data> {
1608 let mut out = empty_data_for(ty)?;
1609 let value_of = |row: usize| {
1610 if row == NOWHERE {
1611 return None;
1612 }
1613 Some(base + i128::from(code_at(words, (offset + row) * width as usize, width)))
1614 };
1615 macro_rules! unpacking {
1616 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1617 match &mut out {
1618 $(Data::$variant(values) => {
1619 values.reserve(at.len());
1620 for &row in at {
1621 // In range because both ends of it were checked when the vector was built.
1622 let value = value_of(row)
1623 .and_then(|value| <$native>::try_from(value).ok())
1624 .unwrap_or($zero);
1625 values.push(value);
1626 }
1627 })+
1628 _ => {
1629 return Err(Error::internal(format!(
1630 "a {ty} vector was packed, which no integer layout allows"
1631 )));
1632 }
1633 }
1634 };
1635 }
1636 crate::for_each_layout!(exact, unpacking);
1637 Ok(out)
1638}
1639
1640/// The `width` bits starting at `bit`, low end first.
1641///
1642/// Zero for bits past the end of the words, which keeps a read of a row that is not there from
1643/// panicking and matches what every other accessor here does with one.
1644fn code_at(words: &[u64], bit: usize, width: u32) -> u64 {
1645 let word = bit / u64::BITS as usize;
1646 let shift = (bit % u64::BITS as usize) as u32;
1647 let mask = u64::MAX >> (u64::BITS - width);
1648 let low = words.get(word).copied().unwrap_or(0) >> shift;
1649 let taken = u64::BITS - shift;
1650 if taken >= width {
1651 return low & mask;
1652 }
1653 // The code straddles two words, and `taken` is under the width here so it is under sixty four,
1654 // which is what makes the shift below one the hardware will do rather than one it refuses.
1655 let high = words.get(word + 1).copied().unwrap_or(0) << taken;
1656 (low | high) & mask
1657}
1658
1659/// Writes `width` bits of `code` starting at `bit`, over words that started out zero.
1660fn write_code(words: &mut [u64], bit: usize, width: u32, code: u64) {
1661 let word = bit / u64::BITS as usize;
1662 let shift = (bit % u64::BITS as usize) as u32;
1663 words[word] |= code << shift;
1664 let taken = u64::BITS - shift;
1665 if taken < width {
1666 words[word + 1] |= code >> taken;
1667 }
1668}
1669
1670/// One level of dictionary out of however many levels were handed to [`Vector::dictionary`].
1671///
1672/// Every dictionary in the system is built through that constructor and every one of them comes
1673/// through here first, so the invariant this maintains is that the vector a dictionary points at is
1674/// never itself a dictionary that could have been composed away. That makes the work a single `if`
1675/// rather than a loop: the inner vector was already composed when it was built, so composing the
1676/// outer codes through it leaves the result no deeper than the inner vector already was.
1677///
1678/// The codes are indexed rather than fetched with `get`, because the caller has already walked the
1679/// whole outer array to check that every code is in range and the inner array is exactly as long as
1680/// the vector those codes were checked against.
1681fn compose(codes: Vec<u32>, values: Vector) -> (Vec<u32>, Vector) {
1682 // A dictionary carrying a validity of its own is one whose nulls live at this level rather than
1683 // in the values, which is the one thing composition cannot carry down with it.
1684 if !matches!(values.validity, Validity::AllValid) {
1685 return (codes, values);
1686 }
1687 let Vector { ty, len, validity, body } = values;
1688 match body {
1689 Body::Dictionary { codes: inner, values: leaf } => {
1690 debug_assert!(
1691 !matches!(leaf.body, Body::Dictionary { .. })
1692 || !matches!(leaf.validity, Validity::AllValid),
1693 "a dictionary was stacked on a dictionary without going through the constructor"
1694 );
1695 // The leaf is shared, so taking it out of the `Arc` copies it when something else is
1696 // still holding the same dictionary. That is the rare path: a dictionary over a
1697 // dictionary only arrives from a caller that built one that way, and the cut that made
1698 // sharing worth doing produces neither.
1699 (codes.iter().map(|&code| inner[code as usize]).collect(), Arc::unwrap_or_clone(leaf))
1700 }
1701 body => (codes, Vector { ty, len, validity, body }),
1702 }
1703}
1704
1705/// How many rows a run has to cover on average before run length encoding is smaller.
1706///
1707/// A run costs its value plus the four bytes of its end, so on a four byte column a run of two rows
1708/// breaks even and a run of three wins. Wider columns win sooner and narrower ones later, and this
1709/// is the one ratio for all of them because a threshold per width is a table that has to be right
1710/// nine times rather than once. It is a constant with a name so that the sweep that eventually moves
1711/// it has something to move.
1712const RUNS_PAY_AT: usize = 2;
1713
1714/// Which run holds `row`, given ends that are exclusive and increasing.
1715///
1716/// A binary search rather than a scan, because the callers that ask this are the ones that are not
1717/// walking the runs in order: a single value read out of a result set, or a gather at scattered
1718/// positions. Anything walking in order should be reading [`Vector::run_parts`] instead, which is
1719/// what the form is for.
1720fn run_holding(ends: &[u32], row: usize) -> Option<usize> {
1721 let row = u32::try_from(row).ok()?;
1722 let run = match ends.binary_search(&row) {
1723 // The ends are exclusive, so landing exactly on one means the row is the first of the next.
1724 Ok(at) => at + 1,
1725 Err(at) => at,
1726 };
1727 (run < ends.len()).then_some(run)
1728}
1729
1730/// The row each run ends at, for a flat body read alongside the validity that goes with it.
1731///
1732/// Two adjacent nulls are one run, because a reader of either gets a null and cannot tell them
1733/// apart. A null between two equal values is three runs for the same reason, since the null is a
1734/// value of the column as far as anything reading it is concerned.
1735///
1736/// The comparison is per layout rather than per `Value`, which is the whole reason this is a macro.
1737/// A `Value` a row would allocate a string per row on a `VARCHAR` column and would be the exact
1738/// defect `cargo xtask rowloop` exists to fail the build on.
1739fn boundaries(data: &Data, validity: &Validity, len: usize) -> Vec<u32> {
1740 if len == 0 {
1741 return Vec::new();
1742 }
1743 let breaks = |ends: &mut Vec<u32>, mut differs: Box<dyn FnMut(usize, usize) -> bool + '_>| {
1744 for row in 1..len {
1745 let same = match (validity.is_valid(row), validity.is_valid(row - 1)) {
1746 (false, false) => true,
1747 (true, true) => !differs(row, row - 1),
1748 _ => false,
1749 };
1750 if !same {
1751 ends.push(u32::try_from(row).unwrap_or(u32::MAX));
1752 }
1753 }
1754 ends.push(u32::try_from(len).unwrap_or(u32::MAX));
1755 };
1756 let mut ends = Vec::new();
1757 macro_rules! walked {
1758 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1759 match data {
1760 // No values at all, so every row is the same null and the column is one run.
1761 Data::Empty => ends.push(u32::try_from(len).unwrap_or(u32::MAX)),
1762 $(Data::$variant(values) => {
1763 breaks(&mut ends, Box::new(|a, b| values.get(a) != values.get(b)));
1764 })+
1765 Data::Varlen(values) => {
1766 breaks(&mut ends, Box::new(|a, b| values.bytes(a) != values.bytes(b)));
1767 }
1768 }
1769 };
1770 }
1771 crate::for_each_layout!(fixed, walked);
1772 ends
1773}
1774
1775/// The position of a value that is not anywhere, because it is null or out of range.
1776///
1777/// `usize::MAX` rather than an `Option<usize>`, because the copy loop's bounds check rejects it for
1778/// free and an `Option` would put a second branch next to the one already there.
1779const NOWHERE: usize = usize::MAX;
1780
1781/// A run of data copied at the given positions, with a zero wherever the position is [`NOWHERE`].
1782///
1783/// A zero and not a skip, because every layout here is a parallel array to a validity mask and a
1784/// short one would put every value after the first null at the wrong index. It is the same rule
1785/// [`push_value`] follows for a null.
1786fn copy_of(data: &Data, at: &[usize]) -> Data {
1787 macro_rules! copied {
1788 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1789 match data {
1790 Data::Empty => Data::Empty,
1791 $(Data::$variant(values) => {
1792 let mut out = Buffer::with_capacity(at.len());
1793 for &index in at {
1794 // One bounds check rather than a null test and a bounds check, because
1795 // `NOWHERE` is past the end of every slice there can be.
1796 out.push(values.get(index).copied().unwrap_or($zero));
1797 }
1798 Data::$variant(out)
1799 })+
1800 // The one layout where a gather is a copy of bytes rather than a copy of fixed
1801 // width slots, and the reason compaction is a decision rather than a default on a
1802 // string column.
1803 Data::Varlen(values) => {
1804 let mut out = StringColumn::with_capacity(at.len());
1805 // The bytes are known before any of them are copied, because a view carries its
1806 // length and the wanted positions are already in hand, so the arena is one
1807 // allocation rather than a run of doublings that each copy what the last one
1808 // copied.
1809 let views = values.views();
1810 out.reserve_bytes(
1811 at.iter()
1812 .filter_map(|&index| views.get(index))
1813 .filter(|view| !view.is_inline())
1814 .map(StringView::len)
1815 .sum(),
1816 );
1817 for &index in at {
1818 out.push_from(values, index);
1819 }
1820 Data::Varlen(out)
1821 }
1822 }
1823 };
1824 }
1825 crate::for_each_layout!(fixed, copied)
1826}
1827
1828/// The physical layout a run of data is in, for the check that it matches its type.
1829///
1830/// The two enums name their variants the same way on purpose, so this is one generated arm rather
1831/// than sixteen chances to pair the wrong two up.
1832fn layout_of(data: &Data) -> rudb_common::PhysicalType {
1833 use rudb_common::PhysicalType as P;
1834 macro_rules! layouts {
1835 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1836 match data {
1837 Data::Empty => P::Empty,
1838 $(Data::$variant(_) => P::$variant,)+
1839 }
1840 };
1841 }
1842 crate::for_each_layout!(all, layouts)
1843}
1844
1845/// One value out of a run of data, given what the run means.
1846///
1847/// The match is on the logical type rather than on the data, because the data cannot tell a `DATE`
1848/// from an `INTEGER` and that is the whole reason the two are kept apart.
1849fn value_from(ty: &LogicalType, data: &Data, index: usize) -> Value {
1850 let signed = || data.signed_at(index);
1851 let unsigned = || data.unsigned_at(index);
1852 let value = match ty {
1853 LogicalType::Boolean => match data {
1854 Data::Bool(v) => v.get(index).map(|&x| Value::Boolean(x)),
1855 _ => None,
1856 },
1857 LogicalType::TinyInt => signed().and_then(|x| i8::try_from(x).ok()).map(Value::TinyInt),
1858 LogicalType::SmallInt => signed().and_then(|x| i16::try_from(x).ok()).map(Value::SmallInt),
1859 LogicalType::Integer => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Integer),
1860 LogicalType::BigInt => signed().and_then(|x| i64::try_from(x).ok()).map(Value::BigInt),
1861 LogicalType::HugeInt => signed().map(Value::HugeInt),
1862 LogicalType::UTinyInt => unsigned().and_then(|x| u8::try_from(x).ok()).map(Value::UTinyInt),
1863 LogicalType::USmallInt => {
1864 unsigned().and_then(|x| u16::try_from(x).ok()).map(Value::USmallInt)
1865 }
1866 LogicalType::UInteger => {
1867 unsigned().and_then(|x| u32::try_from(x).ok()).map(Value::UInteger)
1868 }
1869 LogicalType::UBigInt => unsigned().and_then(|x| u64::try_from(x).ok()).map(Value::UBigInt),
1870 LogicalType::UHugeInt => unsigned().map(Value::UHugeInt),
1871 LogicalType::Float => match data {
1872 Data::Float32(v) => v.get(index).map(|&x| Value::Float(x)),
1873 _ => None,
1874 },
1875 LogicalType::Double => match data {
1876 Data::Float64(v) => v.get(index).map(|&x| Value::Double(x)),
1877 _ => None,
1878 },
1879 LogicalType::Decimal { width, scale } => {
1880 signed().map(|unscaled| Value::Decimal { unscaled, width: *width, scale: *scale })
1881 }
1882 LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit => {
1883 data.bytes_at(index).map(|bytes| bytes_as(ty, bytes))
1884 }
1885 LogicalType::Date => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Date),
1886 LogicalType::Time | LogicalType::TimeTz => {
1887 signed().and_then(|x| i64::try_from(x).ok()).map(Value::Time)
1888 }
1889 LogicalType::Timestamp
1890 | LogicalType::TimestampS
1891 | LogicalType::TimestampMs
1892 | LogicalType::TimestampNs
1893 | LogicalType::TimestampTz => {
1894 signed().and_then(|x| i64::try_from(x).ok()).map(Value::Timestamp)
1895 }
1896 LogicalType::Interval => match data {
1897 Data::Interval(v) => {
1898 v.get(index).map(|&(months, days, micros)| Value::Interval { months, days, micros })
1899 }
1900 _ => None,
1901 },
1902 _ => None,
1903 };
1904 value.unwrap_or(Value::Null)
1905}
1906
1907/// One row of a string column as a value, given what its bytes are meant to be read as.
1908///
1909/// Both forms that hold strings come through here, so a row that is a `BLOB` in a flat column is a
1910/// `BLOB` in a string view column too. Bytes that are not text in a `VARCHAR` column are a null
1911/// rather than a panic, since everything that got in went in as a string and a column that has
1912/// something else in it is a bug somewhere earlier that a read should not turn into a crash.
1913fn bytes_as(ty: &LogicalType, bytes: &[u8]) -> Value {
1914 match ty {
1915 LogicalType::Varchar => {
1916 std::str::from_utf8(bytes).map_or(Value::Null, |text| Value::Varchar(text.to_owned()))
1917 }
1918 LogicalType::Blob | LogicalType::Bit => Value::Blob(bytes.to_vec()),
1919 _ => Value::Null,
1920 }
1921}
1922
1923/// An empty run of data of the right layout for a type.
1924fn empty_data_for(ty: &LogicalType) -> Result<Data> {
1925 use rudb_common::PhysicalType as P;
1926 macro_rules! empties {
1927 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
1928 match ty.physical() {
1929 P::Empty => Data::Empty,
1930 $(P::$variant => Data::$variant(Buffer::new()),)+
1931 P::Varlen => Data::Varlen(StringColumn::new()),
1932 other => {
1933 return Err(Error::not_implemented(format!(
1934 "a flat vector of {other:?} data, which arrives with the storage layer"
1935 )));
1936 }
1937 }
1938 };
1939 }
1940 Ok(crate::for_each_layout!(fixed, empties))
1941}
1942
1943/// Appends one value to a run of data, or a zero of the right shape when it is null.
1944///
1945/// The zero matters. A null still occupies a position, the validity mask is what says it is null,
1946/// and a run of data with a hole in it would put every value after the hole in the wrong place.
1947fn push_value(data: &mut Data, value: &Value) -> Result<()> {
1948 macro_rules! push {
1949 ($vec:expr, $variant:path, $zero:expr) => {
1950 match value {
1951 Value::Null => $vec.push($zero),
1952 $variant(x) => $vec.push(*x),
1953 other => {
1954 return Err(Error::internal(format!(
1955 "{other:?} does not belong in this vector"
1956 )));
1957 }
1958 }
1959 };
1960 }
1961 // A decimal is stored as its unscaled integer in whatever width its precision needs, which
1962 // `LogicalType::physical` decides and which is why the same `Value::Decimal` is at home in four
1963 // different runs. The narrowing cannot fail for a value the binder produced, because the width
1964 // that chose the run is the width in the value, but it is checked rather than assumed because
1965 // an unchecked cast here would silently store a different number.
1966 macro_rules! decimal {
1967 ($vec:expr, $ty:ty, $unscaled:expr) => {
1968 match <$ty>::try_from(*$unscaled) {
1969 Ok(x) => $vec.push(x),
1970 Err(_) => {
1971 return Err(Error::internal(format!(
1972 "an unscaled decimal of {} does not fit the run its precision chose",
1973 $unscaled
1974 )));
1975 }
1976 }
1977 };
1978 }
1979 match data {
1980 Data::Empty => {}
1981 Data::Bool(v) => push!(v, Value::Boolean, false),
1982 Data::Int8(v) => push!(v, Value::TinyInt, 0),
1983 Data::Int16(v) => match value {
1984 Value::Null => v.push(0),
1985 Value::SmallInt(x) => v.push(*x),
1986 Value::Decimal { unscaled, .. } => decimal!(v, i16, unscaled),
1987 other => return Err(Error::internal(format!("{other:?} is not a 16 bit value"))),
1988 },
1989 Data::Int32(v) => match value {
1990 Value::Null => v.push(0),
1991 Value::Integer(x) | Value::Date(x) => v.push(*x),
1992 Value::Decimal { unscaled, .. } => decimal!(v, i32, unscaled),
1993 other => return Err(Error::internal(format!("{other:?} is not a 32 bit value"))),
1994 },
1995 Data::Int64(v) => match value {
1996 Value::Null => v.push(0),
1997 Value::BigInt(x) | Value::Time(x) | Value::Timestamp(x) => v.push(*x),
1998 Value::Decimal { unscaled, .. } => decimal!(v, i64, unscaled),
1999 other => return Err(Error::internal(format!("{other:?} is not a 64 bit value"))),
2000 },
2001 Data::Int128(v) => match value {
2002 Value::Null => v.push(0),
2003 Value::HugeInt(x) => v.push(*x),
2004 Value::Decimal { unscaled, .. } => v.push(*unscaled),
2005 other => return Err(Error::internal(format!("{other:?} is not a 128 bit value"))),
2006 },
2007 Data::UInt8(v) => push!(v, Value::UTinyInt, 0),
2008 Data::UInt16(v) => push!(v, Value::USmallInt, 0),
2009 Data::UInt32(v) => push!(v, Value::UInteger, 0),
2010 Data::UInt64(v) => push!(v, Value::UBigInt, 0),
2011 Data::UInt128(v) => push!(v, Value::UHugeInt, 0),
2012 Data::Float32(v) => push!(v, Value::Float, 0.0),
2013 Data::Float64(v) => push!(v, Value::Double, 0.0),
2014 Data::Interval(v) => match value {
2015 Value::Null => v.push((0, 0, 0)),
2016 Value::Interval { months, days, micros } => v.push((*months, *days, *micros)),
2017 other => return Err(Error::internal(format!("{other:?} is not an interval"))),
2018 },
2019 Data::Varlen(column) => match value {
2020 Value::Null => {
2021 column.push("");
2022 }
2023 Value::Varchar(text) => {
2024 column.push(text);
2025 }
2026 // A blob goes in as the bytes it is. The column stores a length and some bytes either
2027 // way, so text is the reading of one rather than a different column, and a blob that
2028 // is not UTF-8 is stored exactly like one that happens to be.
2029 Value::Blob(bytes) => {
2030 column.push_bytes(bytes);
2031 }
2032 other => return Err(Error::internal(format!("{other:?} is not a string"))),
2033 },
2034 }
2035 Ok(())
2036}
2037
2038#[cfg(test)]
2039mod tests {
2040 use std::sync::Arc;
2041
2042 use rudb_common::{LogicalType, Value};
2043
2044 use super::{Body, Data, FSST_PAYS_AT, Form, VECTOR_SIZE, Vector};
2045 use crate::buffer::Buffer;
2046 use crate::fsst::SymbolTable;
2047 use crate::string::{StringColumn, StringView};
2048 use crate::validity::Validity;
2049
2050 fn integers(values: &[i32]) -> Vector {
2051 Vector::flat(LogicalType::Integer, Data::Int32(values.to_vec().into())).unwrap()
2052 }
2053
2054 #[test]
2055 fn a_clustered_column_becomes_runs_and_reads_back_the_same() {
2056 let mut values = Vec::new();
2057 for (value, times) in [(7, 400), (8, 300), (7, 324)] {
2058 values.extend(std::iter::repeat_n(value, times));
2059 }
2060 let flat = integers(&values);
2061 let runs = flat.run_encoded().unwrap();
2062 assert_eq!(runs.form(), Form::Rle);
2063 assert_eq!(runs.run_parts().expect("runs").0, [400, 700, 1024]);
2064 assert_eq!(runs.len(), flat.len());
2065 assert_eq!(runs.iter().collect::<Vec<_>>(), flat.iter().collect::<Vec<_>>());
2066 assert!(
2067 runs.footprint() * 10 < flat.footprint(),
2068 "three runs against a thousand rows: {} against {}",
2069 runs.footprint(),
2070 flat.footprint()
2071 );
2072 }
2073
2074 /// The check is worth having in both directions. A form that is only ever bigger than what it
2075 /// replaced is a form that costs a pass over the column to decide not to use.
2076 #[test]
2077 fn a_column_that_does_not_repeat_is_left_flat() {
2078 let flat = integers(&(0..1024).collect::<Vec<i32>>());
2079 assert_eq!(flat.run_encoded().unwrap().form(), Form::Flat);
2080 // Two runs over four rows is exactly break even on a four byte column, and break even is
2081 // not a reason to change form.
2082 assert_eq!(integers(&[1, 1, 2, 2]).run_encoded().unwrap().form(), Form::Flat);
2083 assert_eq!(integers(&[1, 1, 1, 2, 2]).run_encoded().unwrap().form(), Form::Rle);
2084 }
2085
2086 #[test]
2087 fn two_nulls_beside_each_other_are_one_run_and_a_null_between_two_equals_is_a_break() {
2088 let mut values = vec![Value::Integer(4), Value::Integer(4)];
2089 values.extend([Value::Null, Value::Null, Value::Null]);
2090 values.extend(std::iter::repeat_n(Value::Integer(4), 5));
2091 let flat = Vector::from_values(LogicalType::Integer, &values).unwrap();
2092 let runs = flat.run_encoded().unwrap();
2093 assert_eq!(runs.run_parts().expect("runs").0, [2, 5, 10]);
2094 assert_eq!(runs.iter().collect::<Vec<_>>(), values);
2095 }
2096
2097 #[test]
2098 fn slicing_runs_keeps_them_runs_and_cuts_the_first_and_last_one_back() {
2099 let flat = integers(&[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]);
2100 let runs = flat.run_encoded().unwrap();
2101 let piece = runs.slice(3, 6).unwrap();
2102 assert_eq!(piece.form(), Form::Rle, "the form is the whole point");
2103 assert_eq!(piece.run_parts().expect("runs").0, [1, 5, 6]);
2104 assert_eq!(
2105 piece.iter().collect::<Vec<_>>(),
2106 flat.slice(3, 6).unwrap().iter().collect::<Vec<_>>()
2107 );
2108 assert_eq!(runs.slice(0, 0).unwrap().len(), 0);
2109 assert_eq!(runs.slice(0, 12).unwrap().form(), Form::Rle);
2110 }
2111
2112 #[test]
2113 fn gathering_out_of_runs_walks_to_the_values_the_way_it_walks_a_dictionary() {
2114 let mut values = vec![Value::Varchar("red".into()); 4];
2115 values.extend([Value::Null, Value::Null, Value::Null]);
2116 values.extend(vec![Value::Varchar("blue".into()); 4]);
2117 let runs =
2118 Vector::from_values(LogicalType::Varchar, &values).unwrap().run_encoded().unwrap();
2119 assert_eq!(runs.form(), Form::Rle);
2120 let picked = runs.gather(&[8, 0, 5, 2]).unwrap();
2121 assert_eq!(picked.form(), Form::Flat, "a gather copies, whatever it gathered from");
2122 assert_eq!(
2123 picked.iter().collect::<Vec<_>>(),
2124 [values[8].clone(), values[0].clone(), Value::Null, values[2].clone()]
2125 );
2126 assert_eq!(runs.text_at(1), Some("red"));
2127 assert_eq!(runs.text_at(5), None, "a null has no text");
2128 assert_eq!(runs.flatten().unwrap().iter().collect::<Vec<_>>(), values);
2129 }
2130
2131 /// A run length vector over a run length vector turns one search per row into two, and there is
2132 /// nothing in the engine that builds one, so it is refused rather than composed.
2133 #[test]
2134 fn runs_of_runs_are_refused_and_runs_of_a_dictionary_are_not() {
2135 let inner = integers(&[1, 1, 1, 1, 2]).run_encoded().unwrap();
2136 assert_eq!(inner.form(), Form::Rle);
2137 let error = Vector::runs(vec![2, 8], inner).unwrap_err();
2138 assert!(error.to_string().contains("runs of runs"), "{error}");
2139
2140 let words = Vector::from_values(
2141 LogicalType::Varchar,
2142 &[Value::Varchar("red".into()), Value::Varchar("blue".into())],
2143 )
2144 .unwrap();
2145 let dictionary = Vector::dictionary(vec![1, 0], words).unwrap();
2146 let stacked = Vector::runs(vec![4, 9], dictionary).unwrap();
2147 assert_eq!(stacked.len(), 9);
2148 assert_eq!(stacked.value_at(3), Value::Varchar("blue".into()));
2149 assert_eq!(stacked.value_at(4), Value::Varchar("red".into()));
2150 }
2151
2152 #[test]
2153 fn run_ends_have_to_increase_and_there_is_one_value_for_each_of_them() {
2154 let values = integers(&[1, 2]);
2155 assert!(Vector::runs(vec![4], values.clone()).is_err(), "two values and one run");
2156 assert!(Vector::runs(vec![4, 4], values.clone()).is_err(), "an end that repeats");
2157 assert!(Vector::runs(vec![4, 2], values.clone()).is_err(), "an end that goes backwards");
2158 assert!(Vector::runs(vec![0, 2], values.clone()).is_err(), "a first run holding no rows");
2159 assert_eq!(Vector::runs(vec![4, 9], values).unwrap().len(), 9);
2160 }
2161
2162 #[test]
2163 fn a_form_that_is_already_compact_is_left_where_it_is() {
2164 let constant = Vector::constant(LogicalType::Integer, Value::Integer(1), 1000);
2165 assert_eq!(constant.run_encoded().unwrap().form(), Form::Constant);
2166 assert_eq!(Vector::sequence(0, 1, 1000).run_encoded().unwrap().form(), Form::Sequence);
2167 }
2168
2169 /// What makes one accessor cover both forms. A dictionary hands back the codes it stores and a
2170 /// run length vector works the same numbers out, and a kernel writing `values[at[row]]` reads
2171 /// the same rows out of either.
2172 #[test]
2173 fn both_forms_that_point_somewhere_hand_back_a_position_per_row() {
2174 let words = Vector::from_values(
2175 LogicalType::Varchar,
2176 &[Value::Varchar("red".into()), Value::Varchar("blue".into())],
2177 )
2178 .unwrap();
2179 let runs = Vector::runs(vec![3, 5], words.clone()).unwrap();
2180 let (at, values) = runs.positions().expect("runs point somewhere");
2181 assert_eq!(at.as_ref(), [0, 0, 0, 1, 1]);
2182 assert_eq!(values.value_at(at[3] as usize), runs.value_at(3));
2183
2184 let dictionary = Vector::dictionary(vec![1, 0, 1], words).unwrap();
2185 let (at, values) = dictionary.positions().expect("a dictionary points somewhere");
2186 assert_eq!(at.as_ref(), [1, 0, 1]);
2187 assert_eq!(values.value_at(at[0] as usize), dictionary.value_at(0));
2188
2189 assert!(integers(&[1, 2, 3]).positions().is_none(), "a flat vector points at itself");
2190 assert!(Vector::sequence(0, 1, 4).positions().is_none(), "a sequence stores nothing");
2191 }
2192
2193 #[test]
2194 fn slicing_a_dictionary_keeps_it_a_dictionary_where_gathering_would_not() {
2195 let values = Vector::from_values(
2196 LogicalType::Varchar,
2197 &[Value::Varchar("red".into()), Value::Varchar("blue".into())],
2198 )
2199 .unwrap();
2200 let vector = Vector::dictionary(vec![0, 1, 1, 0, 1], values).unwrap();
2201
2202 let piece = vector.slice(1, 3).unwrap();
2203 assert_eq!(piece.form(), Form::Dictionary, "the form is the whole point");
2204 assert_eq!(piece.len(), 3);
2205 assert_eq!(
2206 piece.iter().collect::<Vec<_>>(),
2207 [
2208 Value::Varchar("blue".into()),
2209 Value::Varchar("blue".into()),
2210 Value::Varchar("red".into())
2211 ]
2212 );
2213 assert_eq!(vector.gather(&[1, 2, 3]).unwrap().form(), Form::Flat, "which a gather loses");
2214 }
2215
2216 #[test]
2217 fn slicing_a_dictionary_shares_the_dictionary_rather_than_copying_it() {
2218 // The assertion is about the address and not about the values, because the values were
2219 // right when the dictionary was copied too. A page holds one dictionary and is cut into a
2220 // chunk of codes at a time, so copying the dictionary here is a copy of every string in it
2221 // per chunk, and on a read of a ClickBench partition it was ten percent of the cycles.
2222 let values = Vector::from_values(
2223 LogicalType::Varchar,
2224 &[Value::Varchar("red".into()), Value::Varchar("blue".into())],
2225 )
2226 .unwrap();
2227 let vector = Vector::dictionary(vec![0, 1, 1, 0, 1], values).unwrap();
2228 let Body::Dictionary { values: whole, .. } = &vector.body else {
2229 panic!("a dictionary vector holds a dictionary");
2230 };
2231
2232 let piece = vector.slice(1, 3).unwrap();
2233 let Body::Dictionary { codes, values: cut } = &piece.body else {
2234 panic!("a slice of a dictionary is a dictionary");
2235 };
2236 assert!(Arc::ptr_eq(whole, cut), "the cut copied the dictionary");
2237 assert_eq!(codes, &[1, 1, 0], "the codes are the part that is cut");
2238
2239 // And a cut of a cut shares it too, since that is what a scan does to a page it reads twice.
2240 let again = piece.slice(1, 2).unwrap();
2241 let Body::Dictionary { values: cut, .. } = &again.body else {
2242 panic!("a slice of a slice of a dictionary is a dictionary");
2243 };
2244 assert!(Arc::ptr_eq(whole, cut), "the second cut copied the dictionary");
2245 assert_eq!(
2246 again.iter().collect::<Vec<_>>(),
2247 [Value::Varchar("blue".into()), Value::Varchar("red".into())]
2248 );
2249 }
2250
2251 #[test]
2252 fn a_slice_carries_the_nulls_that_were_in_its_range_and_not_the_others() {
2253 let vector =
2254 integers(&[1, 2, 3, 4]).with_validity(Validity::from_run(&[false, true, false, true]));
2255 let piece = vector.slice(1, 2).unwrap();
2256 assert!(piece.validity().is_valid(0));
2257 assert!(!piece.validity().is_valid(1));
2258 assert_eq!(piece.value_at(1), Value::Null);
2259 }
2260
2261 #[test]
2262 fn slicing_a_sequence_moves_its_start_rather_than_writing_the_values_out() {
2263 let vector = Vector::sequence(100, 5, 10);
2264 let piece = vector.slice(3, 4).unwrap();
2265 assert_eq!(piece.form(), Form::Sequence);
2266 assert_eq!(
2267 piece.iter().collect::<Vec<_>>(),
2268 [Value::BigInt(115), Value::BigInt(120), Value::BigInt(125), Value::BigInt(130)]
2269 );
2270 }
2271
2272 #[test]
2273 fn slicing_a_constant_is_a_shorter_constant() {
2274 let vector = Vector::constant(LogicalType::Integer, Value::Integer(9), 8);
2275 let piece = vector.slice(2, 3).unwrap();
2276 assert_eq!(piece.form(), Form::Constant);
2277 assert_eq!(piece.len(), 3);
2278 assert_eq!(piece.value_at(2), Value::Integer(9));
2279 }
2280
2281 #[test]
2282 fn slicing_the_whole_vector_hands_it_back_as_it_was() {
2283 let vector = integers(&[1, 2, 3]);
2284 assert_eq!(
2285 vector.slice(0, 3).unwrap().iter().collect::<Vec<_>>(),
2286 [Value::Integer(1), Value::Integer(2), Value::Integer(3)]
2287 );
2288 }
2289
2290 #[test]
2291 fn a_slice_past_the_end_is_an_error_rather_than_a_short_vector() {
2292 let error = integers(&[1, 2, 3]).slice(2, 2).unwrap_err();
2293 assert!(error.to_string().contains("of a vector of 3"), "{error}");
2294 }
2295
2296 #[test]
2297 fn the_vector_size_is_the_one_the_design_is_built_around() {
2298 // 1024 and not DuckDB's 2048. A validity mask is 16 u64 words and a vector of string views
2299 // is 16 KiB, both of which are consequences of this number rather than coincidences.
2300 assert_eq!(VECTOR_SIZE, 1024);
2301 assert_eq!(VECTOR_SIZE / 64, 16);
2302 }
2303
2304 #[test]
2305 fn a_flat_vector_reads_back_what_was_put_in_it() {
2306 let vector = integers(&[1, 2, 3]);
2307 assert_eq!(vector.form(), Form::Flat);
2308 assert_eq!(vector.len(), 3);
2309 assert_eq!(vector.value_at(1), Value::Integer(2));
2310 assert_eq!(
2311 vector.iter().collect::<Vec<_>>(),
2312 vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)]
2313 );
2314 }
2315
2316 #[test]
2317 fn a_vector_built_from_values_reads_the_same_values_back() {
2318 let vector = Vector::from_values(
2319 LogicalType::Varchar,
2320 &[
2321 Value::Varchar("a".to_string()),
2322 Value::Null,
2323 Value::Varchar("a string too long to sit inside a view".to_string()),
2324 ],
2325 )
2326 .expect("strings and a null");
2327 assert_eq!(vector.len(), 3);
2328 assert_eq!(vector.value_at(0), Value::Varchar("a".to_string()));
2329 assert_eq!(vector.value_at(1), Value::Null);
2330 assert_eq!(
2331 vector.value_at(2),
2332 Value::Varchar("a string too long to sit inside a view".to_string())
2333 );
2334 }
2335
2336 /// A null still occupies a position. If it did not then every value after it would read back
2337 /// one place to the left, which is the kind of bug that looks like a storage bug for a week.
2338 #[test]
2339 fn a_null_in_the_middle_does_not_move_the_values_after_it() {
2340 let vector = Vector::from_values(
2341 LogicalType::Integer,
2342 &[Value::Integer(1), Value::Null, Value::Integer(3)],
2343 )
2344 .expect("integers and a null");
2345 assert_eq!(vector.value_at(2), Value::Integer(3));
2346 assert!(vector.validity().has_nulls(3), "the middle one is null");
2347 }
2348
2349 #[test]
2350 fn a_value_the_type_cannot_hold_is_refused() {
2351 let wrong = Vector::from_values(LogicalType::Integer, &[Value::Varchar("x".to_string())]);
2352 assert!(wrong.is_err(), "a string is not an integer");
2353 }
2354
2355 #[test]
2356 fn a_type_that_does_not_match_its_layout_is_refused_at_construction() {
2357 // One comparison here against a wrong answer read out three layers later.
2358 let wrong = Vector::flat(LogicalType::Varchar, Data::Int32(vec![1].into()));
2359 assert!(wrong.is_err());
2360 let right = Vector::flat(LogicalType::Date, Data::Int32(vec![1].into()));
2361 assert!(right.is_ok(), "a date is stored in an i32 and that has to be allowed");
2362 }
2363
2364 #[test]
2365 fn a_constant_vector_costs_one_value_whatever_its_length() {
2366 let vector = Vector::constant(LogicalType::Integer, Value::Integer(7), VECTOR_SIZE);
2367 assert_eq!(vector.form(), Form::Constant);
2368 assert_eq!(vector.len(), VECTOR_SIZE);
2369 assert_eq!(vector.value_at(0), Value::Integer(7));
2370 assert_eq!(vector.value_at(VECTOR_SIZE - 1), Value::Integer(7));
2371 assert_eq!(vector.value_at(VECTOR_SIZE), Value::Null, "past the end is null, not a panic");
2372 }
2373
2374 #[test]
2375 fn a_constant_null_is_all_invalid_without_being_told() {
2376 let vector = Vector::constant(LogicalType::Integer, Value::Null, 8);
2377 assert_eq!(vector.validity(), &Validity::AllInvalid);
2378 assert_eq!(vector.value_at(3), Value::Null);
2379 }
2380
2381 #[test]
2382 fn a_sequence_vector_is_sixteen_bytes_of_row_identifiers() {
2383 let vector = Vector::sequence(100, 1, VECTOR_SIZE);
2384 assert_eq!(vector.form(), Form::Sequence);
2385 assert_eq!(vector.value_at(0), Value::BigInt(100));
2386 assert_eq!(vector.value_at(923), Value::BigInt(1023));
2387 let stepped = Vector::sequence(0, 5, 4);
2388 assert_eq!(
2389 stepped.iter().collect::<Vec<_>>(),
2390 vec![Value::BigInt(0), Value::BigInt(5), Value::BigInt(10), Value::BigInt(15)]
2391 );
2392 }
2393
2394 #[test]
2395 fn a_dictionary_vector_reads_through_its_codes() {
2396 let mut column = StringColumn::new();
2397 column.push("red");
2398 column.push("green");
2399 let values = Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap();
2400 let vector = Vector::dictionary(vec![0, 1, 1, 0], values).unwrap();
2401 assert_eq!(vector.form(), Form::Dictionary);
2402 assert_eq!(vector.logical_type(), &LogicalType::Varchar);
2403 assert_eq!(vector.value_at(2), Value::Varchar("green".into()));
2404 assert_eq!(vector.len(), 4);
2405 }
2406
2407 /// The accessor a group by keys a string column through, which has to agree with `value_at` on
2408 /// every position or two rows holding one string end up in two groups.
2409 #[test]
2410 fn text_is_read_where_it_already_is_for_the_forms_that_store_it() {
2411 let mut column = StringColumn::new();
2412 column.push("red");
2413 column.push("green");
2414 column.push("");
2415 let flat = Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap();
2416 for index in 0..flat.len() {
2417 assert_eq!(flat.text_at(index).map(str::to_string), text_of(&flat.value_at(index)));
2418 }
2419 let dictionary = Vector::dictionary(vec![1, 0, 1, 2], flat).unwrap();
2420 for index in 0..dictionary.len() {
2421 assert_eq!(
2422 dictionary.text_at(index).map(str::to_string),
2423 text_of(&dictionary.value_at(index))
2424 );
2425 }
2426 assert_eq!(dictionary.text_at(4), None, "past the end");
2427 }
2428
2429 /// The forms and types that have no text to hand back, which a caller answers by falling back
2430 /// to `value_at`. A blob is the one that would be a correctness bug rather than a slow path,
2431 /// since its bytes are not required to be text and it is not a `VARCHAR` either way.
2432 #[test]
2433 fn text_is_refused_where_it_is_not_stored_as_itself() {
2434 let nulls =
2435 Vector::from_values(LogicalType::Varchar, &[Value::Varchar("red".into()), Value::Null])
2436 .unwrap();
2437 assert_eq!(nulls.text_at(0), Some("red"));
2438 assert_eq!(nulls.text_at(1), None, "a null has no text");
2439 let constant = Vector::constant(LogicalType::Varchar, Value::Varchar("red".into()), 3);
2440 assert_eq!(constant.text_at(0), None, "a constant is not stored per position");
2441 assert_eq!(integers(&[1, 2]).text_at(0), None, "an integer is not text");
2442 let mut bytes = StringColumn::new();
2443 bytes.push("red");
2444 let blob = Vector::flat(LogicalType::Blob, Data::Varlen(bytes)).unwrap();
2445 assert_eq!(blob.text_at(0), None, "a blob is not a varchar");
2446 }
2447
2448 /// The text of a value, for comparing `text_at` against `value_at` position by position.
2449 fn text_of(value: &Value) -> Option<String> {
2450 match value {
2451 Value::Varchar(text) => Some(text.clone()),
2452 _ => None,
2453 }
2454 }
2455
2456 #[test]
2457 fn a_dictionary_code_past_the_end_is_refused() {
2458 // The alternative is a silent read of the wrong value, which is the failure mode the
2459 // entire M3 design has to be careful about.
2460 let values = integers(&[1, 2]);
2461 assert!(Vector::dictionary(vec![0, 2], values).is_err());
2462 }
2463
2464 #[test]
2465 fn every_form_flattens_to_the_same_values_it_reads_out() {
2466 // This is the shape of the equivalence testing in spec/16-testing.md section 16.2, in
2467 // miniature and long before there is an encoded kernel to point it at. A form that reads
2468 // out one way and flattens another is the exact bug that testing exists to catch.
2469 let mut column = StringColumn::new();
2470 column.push("alpha");
2471 column.push("beta");
2472 let dictionary = Vector::dictionary(
2473 vec![1, 0, 1],
2474 Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
2475 )
2476 .unwrap();
2477 let cases = [
2478 Vector::constant(LogicalType::Integer, Value::Integer(3), 5),
2479 Vector::sequence(7, -2, 5),
2480 dictionary,
2481 ];
2482 for vector in cases {
2483 let flat = vector.flatten().unwrap();
2484 assert_eq!(flat.form(), Form::Flat);
2485 assert_eq!(flat.len(), vector.len());
2486 for index in 0..vector.len() {
2487 assert_eq!(flat.value_at(index), vector.value_at(index), "at {index}");
2488 }
2489 }
2490 }
2491
2492 #[test]
2493 fn a_null_still_occupies_a_position_after_flattening() {
2494 // The reason push_value writes a zero for a null rather than skipping it. A run of data
2495 // with a hole in it puts every value after the hole in the wrong place, and the validity
2496 // mask is what says the position is null.
2497 let vector = Vector::sequence(0, 1, 4).with_validity(Validity::from_iter(4, |i| i != 1));
2498 let flat = vector.flatten().unwrap();
2499 assert_eq!(flat.value_at(0), Value::BigInt(0));
2500 assert_eq!(flat.value_at(1), Value::Null);
2501 assert_eq!(flat.value_at(2), Value::BigInt(2));
2502 assert_eq!(flat.value_at(3), Value::BigInt(3));
2503 }
2504
2505 /// A dictionary holds its nulls in the vector it points at, so its own validity is all valid
2506 /// and reading that instead of the values turns a null into whatever zero means for the type.
2507 /// A filter over a nullable column produces exactly this vector, so the bug reaches a result
2508 /// set as `LEFT JOIN` padding that comes back as zeros.
2509 #[test]
2510 fn a_null_behind_a_dictionary_survives_flattening() {
2511 let values =
2512 Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
2513 let dictionary = Vector::dictionary(vec![1, 0, 1], values).unwrap();
2514 let flat = dictionary.flatten().unwrap();
2515 assert_eq!(flat.value_at(0), Value::Null);
2516 assert_eq!(flat.value_at(1), Value::Integer(3));
2517 assert_eq!(flat.value_at(2), Value::Null);
2518 }
2519
2520 /// The property that makes `gather` usable at all: it has to be the same function as reading the
2521 /// wanted positions one at a time, over every form, or compaction changes answers.
2522 #[test]
2523 fn gathering_reads_what_reading_one_position_at_a_time_reads() {
2524 let mut column = StringColumn::new();
2525 column.push("alpha");
2526 column.push("beta");
2527 column.push("gamma");
2528 let cases = [
2529 integers(&[10, 20, 30, 40]),
2530 integers(&[10, 20, 30, 40]).with_validity(Validity::from_iter(4, |i| i != 2)),
2531 Vector::constant(LogicalType::Integer, Value::Integer(9), 4),
2532 Vector::sequence(100, -7, 4),
2533 Vector::sequence(100, -7, 4).with_validity(Validity::from_iter(4, |i| i % 2 == 0)),
2534 Vector::dictionary(
2535 vec![2, 0, 1, 2],
2536 Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
2537 )
2538 .unwrap(),
2539 Vector::dictionary(
2540 vec![1, 0, 1, 0],
2541 Vector::from_values(LogicalType::Integer, &[Value::Integer(5), Value::Null])
2542 .unwrap(),
2543 )
2544 .unwrap(),
2545 ];
2546 let wanted = [3_u32, 0, 2, 2, 1];
2547 for vector in cases {
2548 let gathered = vector.gather(&wanted).unwrap();
2549 assert_eq!(gathered.len(), wanted.len());
2550 assert_eq!(gathered.logical_type(), vector.logical_type());
2551 for (slot, &index) in wanted.iter().enumerate() {
2552 assert_eq!(
2553 gathered.value_at(slot),
2554 vector.value_at(index as usize),
2555 "slot {slot} of {:?}",
2556 vector.form()
2557 );
2558 }
2559 }
2560 }
2561
2562 /// A gather past the end is not an error, because the selection that produced the indices is
2563 /// checked by its caller and the one thing that must not happen here is a read of the wrong
2564 /// value. An index nothing answers is null, which is what an outer join pad needs anyway.
2565 #[test]
2566 fn gathering_a_position_that_is_not_there_is_a_null_and_not_a_wrong_value() {
2567 let vector = integers(&[1, 2, 3]);
2568 let gathered = vector.gather(&[2, 9]).unwrap();
2569 assert_eq!(gathered.value_at(0), Value::Integer(3));
2570 assert_eq!(gathered.value_at(1), Value::Null);
2571 }
2572
2573 /// The vector with nothing in it at all, which is what an untyped `NULL` is stored as. Every
2574 /// position asked for is past its end, so the answer is nulls and the length has to be the
2575 /// length that was asked for rather than the length that was there.
2576 #[test]
2577 fn gathering_from_a_vector_of_no_values_is_that_many_nulls() {
2578 let vector = Vector::flat(LogicalType::Null, Data::Empty).unwrap();
2579 let gathered = vector.gather(&[0, 1, 2]).unwrap();
2580 assert_eq!(gathered.len(), 3);
2581 assert_eq!(gathered.value_at(0), Value::Null);
2582 assert_eq!(gathered.value_at(2), Value::Null);
2583 }
2584
2585 /// Every position holds the same value, so a gather with no hole in it has nothing to copy and
2586 /// the result is the constant again rather than a run of a thousand copies of it.
2587 #[test]
2588 fn gathering_a_constant_stays_a_constant() {
2589 let vector = Vector::constant(LogicalType::Integer, Value::Integer(4), 100);
2590 let gathered = vector.gather(&[7, 7, 99]).unwrap();
2591 assert_eq!(gathered.form(), Form::Constant);
2592 assert_eq!(gathered.len(), 3);
2593 assert_eq!(gathered.value_at(2), Value::Integer(4));
2594 }
2595
2596 /// A dictionary over a dictionary is what a second filter over an already filtered chunk builds,
2597 /// and the gather has to walk to the bottom of that chain rather than one step down it. The
2598 /// constructor composes the ordinary chain away, so the one built here is the kind it cannot,
2599 /// which is a level holding nulls of its own.
2600 #[test]
2601 fn gathering_walks_a_dictionary_over_a_dictionary_to_the_values() {
2602 let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9]))
2603 .unwrap()
2604 .with_validity(Validity::from_iter(3, |index| index != 2));
2605 let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
2606 let gathered = outer.gather(&[0, 1]).unwrap();
2607 assert_eq!(gathered.form(), Form::Flat);
2608 assert_eq!(gathered.value_at(0), Value::Integer(8));
2609 assert_eq!(gathered.value_at(1), Value::Null);
2610 }
2611
2612 /// Two filters over one chunk build a dictionary over a dictionary, four conjuncts pushed down
2613 /// separately build four levels of it, and every level is a dependent load on every later read
2614 /// of every row plus a code array that cannot be freed. Composing at construction is one pass
2615 /// over the codes the range check was walking anyway.
2616 #[test]
2617 fn a_dictionary_over_a_dictionary_is_composed_into_one_level() {
2618 let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9])).unwrap();
2619 let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
2620 let (codes, values) = outer.dictionary_parts().unwrap();
2621 assert_eq!(codes, [1, 0]);
2622 assert_eq!(values.form(), Form::Flat);
2623 assert_eq!(outer.value_at(0), Value::Integer(8));
2624 assert_eq!(outer.value_at(1), Value::Integer(7));
2625 }
2626
2627 /// The invariant stated as the thing it is there for, which is that the depth does not grow with
2628 /// the number of filters. Four levels stacked one at a time are one level at the end of it.
2629 #[test]
2630 fn stacking_dictionaries_does_not_make_them_deeper() {
2631 let mut vector = integers(&[10, 20, 30, 40]);
2632 for _ in 0..4 {
2633 vector = Vector::dictionary(vec![3, 2, 1, 0], vector).unwrap();
2634 }
2635 let (codes, values) = vector.dictionary_parts().unwrap();
2636 assert_eq!(values.form(), Form::Flat);
2637 assert_eq!(codes, [0, 1, 2, 3]);
2638 assert_eq!(
2639 vector.iter().collect::<Vec<_>>(),
2640 integers(&[10, 20, 30, 40]).iter().collect::<Vec<_>>()
2641 );
2642 }
2643
2644 /// Composing has to carry the nulls down with it. The values hold them, the codes point at them,
2645 /// and a composed code that lands on a null position is still a null.
2646 #[test]
2647 fn composing_a_dictionary_keeps_the_nulls_its_values_hold() {
2648 let values =
2649 Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
2650 let inner = Vector::dictionary(vec![1, 0, 1], values).unwrap();
2651 let outer = Vector::dictionary(vec![0, 1], inner).unwrap();
2652 assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Flat);
2653 assert_eq!(outer.value_at(0), Value::Null);
2654 assert_eq!(outer.value_at(1), Value::Integer(3));
2655 }
2656
2657 /// The one level composition cannot go past. A dictionary that was given a validity of its own is
2658 /// saying its nulls are at that level rather than in the values, and pointing the outer codes
2659 /// straight at the values would read through the holes instead of stopping at them.
2660 #[test]
2661 fn a_dictionary_holding_its_own_nulls_is_not_composed_past() {
2662 let inner = Vector::dictionary(vec![0, 1, 2], integers(&[1, 2, 3]))
2663 .unwrap()
2664 .with_validity(Validity::from_iter(3, |index| index != 1));
2665 let outer = Vector::dictionary(vec![1, 2, 0], inner).unwrap();
2666 assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Dictionary);
2667 assert_eq!(outer.value_at(0), Value::Null);
2668 assert_eq!(outer.value_at(1), Value::Integer(3));
2669 assert_eq!(outer.value_at(2), Value::Integer(1));
2670 }
2671
2672 #[test]
2673 fn flattening_a_flat_vector_is_the_same_vector() {
2674 let vector = integers(&[1, 2, 3]);
2675 assert_eq!(vector.flatten().unwrap(), vector);
2676 }
2677
2678 #[test]
2679 fn a_decimal_reads_its_width_and_scale_from_the_type_and_not_the_data() {
2680 let ty = LogicalType::decimal(9, 2).unwrap();
2681 let vector = Vector::flat(ty, Data::Int32(vec![1234].into())).unwrap();
2682 assert_eq!(vector.value_at(0), Value::Decimal { unscaled: 1234, width: 9, scale: 2 });
2683 assert_eq!(vector.value_at(0).to_string(), "12.34");
2684 }
2685
2686 #[test]
2687 fn a_decimal_writes_into_whichever_of_the_four_runs_its_precision_chose() {
2688 // The read path worked at every width and the write path only accepted the 128 bit run, so
2689 // `SELECT 2.5` produced a value nothing could store. All four widths round trip now.
2690 for (width, scale, unscaled) in
2691 [(4u8, 1u8, 25i128), (9, 2, 1234), (18, 3, 123_456), (38, 4, 1_234_567)]
2692 {
2693 let ty = LogicalType::decimal(width, scale).unwrap();
2694 let value = Value::Decimal { unscaled, width, scale };
2695 let vector = Vector::from_values(ty, &[value.clone(), Value::Null]).unwrap();
2696 assert_eq!(vector.value_at(0), value, "a decimal of width {width}");
2697 assert_eq!(vector.value_at(1), Value::Null, "a null decimal of width {width}");
2698 }
2699 }
2700
2701 /// The bytes a blob holds are not required to be text, and a vector of them used to refuse the
2702 /// ones that were not. A byte array column in a Parquet file that nothing annotated is a blob,
2703 /// which is what ClickHouse writes and what ten of the ClickBench queries compare against, so
2704 /// this is the path those take rather than a corner of the type system.
2705 #[test]
2706 fn a_blob_holds_bytes_that_are_not_text() {
2707 let bytes = |raw: &[u8]| Value::Blob(raw.to_vec());
2708 let values = [
2709 bytes(b"a\xffb"),
2710 bytes(b"\x00\x01\x02"),
2711 Value::Null,
2712 bytes(b"\xed\xa0\x80 and long enough to leave the view"),
2713 bytes(b""),
2714 ];
2715 let vector = Vector::from_values(LogicalType::Blob, &values).unwrap();
2716 for (index, value) in values.iter().enumerate() {
2717 assert_eq!(&vector.value_at(index), value, "row {index}");
2718 }
2719 }
2720
2721 #[test]
2722 fn a_decimal_too_wide_for_the_run_its_type_chose_is_an_error_and_not_a_wrong_number() {
2723 // Only reachable by hand, since a value's width is what picked the run. Truncating here
2724 // would store a different number and say nothing about it.
2725 let ty = LogicalType::decimal(4, 1).unwrap();
2726 let value = Value::Decimal { unscaled: 1_000_000, width: 4, scale: 1 };
2727 let error = Vector::from_values(ty, &[value]).unwrap_err();
2728 assert!(error.to_string().contains("does not fit"), "{error}");
2729 }
2730
2731 #[test]
2732 fn a_flat_vector_costs_its_values_and_a_constant_costs_one() {
2733 let flat = integers(&[1; 1000]);
2734 assert!(
2735 flat.footprint() >= 4000,
2736 "a thousand i32 are four thousand bytes: {}",
2737 flat.footprint()
2738 );
2739 // The forms that compute their values rather than storing them cost nothing per value,
2740 // which is the point of having them and is what the memory limit should see.
2741 let constant = Vector::constant(LogicalType::Integer, Value::Integer(1), 1_000_000);
2742 assert!(constant.footprint() < 200, "a constant is one value: {}", constant.footprint());
2743 let sequence = Vector::sequence(0, 1, 1_000_000);
2744 assert!(sequence.footprint() < 200, "a sequence is two numbers: {}", sequence.footprint());
2745 }
2746
2747 #[test]
2748 fn a_string_vector_costs_the_bytes_of_its_long_strings() {
2749 let short =
2750 Vector::from_values(LogicalType::Varchar, &[Value::Varchar("red".into())]).unwrap();
2751 let long = "a string well past the sixteen bytes a view holds inline".to_string();
2752 let spilled =
2753 Vector::from_values(LogicalType::Varchar, &[Value::Varchar(long.clone())]).unwrap();
2754 assert!(
2755 spilled.footprint() >= short.footprint() + long.len(),
2756 "the arena is counted: {} against {}",
2757 spilled.footprint(),
2758 short.footprint()
2759 );
2760 }
2761
2762 /// The cases worth checking are the widths where a code straddles a word boundary, which is
2763 /// every width that does not divide sixty four, and the two ends of the range.
2764 #[test]
2765 fn a_narrow_column_packs_and_reads_back_the_same_at_every_width() {
2766 for width in 1..=20u32 {
2767 let span = (1i64 << width) - 1;
2768 let values: Vec<i64> =
2769 (0..1000).map(|row| 1_000_000 + (row * 7919) % (span + 1)).collect();
2770 let flat =
2771 Vector::flat(LogicalType::BigInt, Data::Int64(values.clone().into())).unwrap();
2772 let packed = flat.bit_packed().unwrap();
2773 assert_eq!(packed.len(), flat.len());
2774 assert_eq!(
2775 packed.iter().collect::<Vec<_>>(),
2776 flat.iter().collect::<Vec<_>>(),
2777 "width {width} read back differently"
2778 );
2779 }
2780 }
2781
2782 #[test]
2783 fn the_width_is_the_bits_the_range_needs_and_not_the_bits_the_type_has() {
2784 let values: Vec<i32> = (0..1024).map(|row| 40 + (row * 2560) / 1023).collect();
2785 let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into())).unwrap();
2786 let packed = flat.bit_packed().unwrap();
2787 assert_eq!(packed.form(), Form::BitPacked);
2788 let parts = packed.packed_parts().expect("packed");
2789 assert_eq!(parts.width(), 12, "0 to 2560 is twelve bits");
2790 assert_eq!(parts.base(), 40);
2791 assert!(
2792 packed.footprint() * 2 < flat.footprint(),
2793 "twelve bits against thirty two: {} against {}",
2794 packed.footprint(),
2795 flat.footprint()
2796 );
2797 }
2798
2799 /// The check is worth having in both directions, the way the run length one is. A form that is
2800 /// only ever bigger than what it replaced costs a pass over the column to decide not to use.
2801 #[test]
2802 fn a_column_that_uses_its_whole_type_is_left_flat() {
2803 let values: Vec<i32> = (0..1024).map(|row| row * 2_000_000 - 1_000_000_000).collect();
2804 let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into())).unwrap();
2805 assert_eq!(flat.bit_packed().unwrap().form(), Form::Flat);
2806 }
2807
2808 /// A column of one value would pack to no bits at all, and one run is smaller than any packing
2809 /// of it, so the two forms do not fight over that column.
2810 #[test]
2811 fn a_column_of_one_value_is_left_to_the_run_length_form() {
2812 let flat = integers(&[9; 1024]);
2813 assert_eq!(flat.bit_packed().unwrap().form(), Form::Flat);
2814 assert_eq!(flat.run_encoded().unwrap().form(), Form::Rle);
2815 }
2816
2817 #[test]
2818 fn a_string_column_has_no_range_to_pack() {
2819 let text = Vector::from_values(
2820 LogicalType::Varchar,
2821 &[Value::Varchar("red".into()), Value::Varchar("blue".into())],
2822 )
2823 .unwrap();
2824 assert_eq!(text.bit_packed().unwrap().form(), Form::Flat);
2825 }
2826
2827 /// The cut is the reason the form carries a row to start reading at. It stays packed, it shares
2828 /// the same words, and it reads the rows the range asked for.
2829 #[test]
2830 fn a_cut_of_a_packed_column_stays_packed_and_shares_its_bits() {
2831 let values: Vec<i32> = (0..1024).map(|row| 100 + row % 300).collect();
2832 let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into())).unwrap();
2833 let packed = flat.bit_packed().unwrap();
2834 let cut = packed.slice(500, 24).unwrap();
2835 assert_eq!(cut.form(), Form::BitPacked);
2836 assert_eq!(cut.len(), 24);
2837 assert_eq!(
2838 cut.iter().collect::<Vec<_>>(),
2839 flat.slice(500, 24).unwrap().iter().collect::<Vec<_>>()
2840 );
2841 assert!(
2842 cut.footprint() >= packed.footprint(),
2843 "a cut shares the words rather than copying a piece of them"
2844 );
2845 }
2846
2847 #[test]
2848 fn a_gather_of_a_packed_column_comes_out_flat_and_keeps_the_nulls() {
2849 let values: Vec<i32> = (0..64).map(|row| 10 + row).collect();
2850 let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into())).unwrap();
2851 let packed =
2852 flat.bit_packed().unwrap().with_validity(Validity::from_iter(64, |row| row % 3 != 0));
2853 let taken = packed.gather(&[0, 1, 2, 3, 62]).unwrap();
2854 assert_eq!(taken.form(), Form::Flat);
2855 assert_eq!(
2856 taken.iter().collect::<Vec<_>>(),
2857 vec![
2858 Value::Null,
2859 Value::Integer(11),
2860 Value::Integer(12),
2861 Value::Null,
2862 Value::Integer(72)
2863 ]
2864 );
2865 }
2866
2867 /// The pair a comparison kernel asks for before it reads a bit. A literal inside the range has a
2868 /// code and a literal outside it does not, which answers the whole vector at once.
2869 #[test]
2870 fn a_literal_outside_the_packed_range_has_no_code() {
2871 let values: Vec<i32> = (0..256).map(|row| 1000 + row).collect();
2872 let flat = Vector::flat(LogicalType::Integer, Data::Int32(values.into())).unwrap();
2873 let packed = flat.bit_packed().unwrap();
2874 let parts = packed.packed_parts().expect("packed");
2875 assert_eq!(parts.code_of(1000), Some(0));
2876 assert_eq!(parts.code_of(1100), Some(100));
2877 assert_eq!(parts.code_of(999), None);
2878 assert!(parts.ceiling() >= 1255);
2879 assert_eq!(parts.code_of(parts.ceiling() + 1), None);
2880 }
2881
2882 /// The bits arriving from a file rather than from a flat vector, which is what the form is for.
2883 #[test]
2884 fn packed_bits_can_be_handed_in_without_a_flat_vector_to_start_from() {
2885 let packed = Vector::packed(LogicalType::SmallInt, vec![0x0000_0000_0000_4321], 4, 7, 4)
2886 .expect("four codes of four bits");
2887 assert_eq!(
2888 packed.iter().collect::<Vec<_>>(),
2889 vec![Value::SmallInt(8), Value::SmallInt(9), Value::SmallInt(10), Value::SmallInt(11)]
2890 );
2891 }
2892
2893 #[test]
2894 fn packed_bits_that_could_not_hold_what_they_claim_are_refused() {
2895 assert!(Vector::packed(LogicalType::Varchar, vec![0], 4, 0, 4).is_err(), "not an integer");
2896 assert!(Vector::packed(LogicalType::Integer, vec![0], 0, 0, 4).is_err(), "no width");
2897 assert!(Vector::packed(LogicalType::Integer, vec![0], 64, 0, 4).is_err(), "too wide");
2898 assert!(Vector::packed(LogicalType::Integer, vec![0], 8, 0, 9).is_err(), "too few words");
2899 assert!(Vector::packed(LogicalType::TinyInt, vec![0], 8, 100, 8).is_err(), "would not fit");
2900 }
2901
2902 /// A column of strings long enough that the payload is in the arena rather than in the views.
2903 fn long_strings(count: usize) -> Vector {
2904 let values: Vec<Value> = (0..count)
2905 .map(|row| {
2906 Value::Varchar(format!("a string too long to sit inside a view, number {row}"))
2907 })
2908 .collect();
2909 Vector::from_values(LogicalType::Varchar, &values).unwrap()
2910 }
2911
2912 #[test]
2913 fn a_string_column_in_view_form_reads_back_the_same_strings() {
2914 let flat = long_strings(40);
2915 let shared = flat.clone().shared_text().unwrap();
2916 assert_eq!(shared.form(), Form::StringView);
2917 assert_eq!(shared.len(), 40);
2918 for row in 0..40 {
2919 assert_eq!(shared.value_at(row), flat.value_at(row), "row {row}");
2920 assert_eq!(shared.text_at(row), flat.text_at(row), "row {row}");
2921 }
2922 }
2923
2924 #[test]
2925 fn a_short_string_is_read_out_of_its_view_and_never_out_of_the_arena() {
2926 let flat = Vector::from_values(
2927 LogicalType::Varchar,
2928 &[Value::Varchar("red".into()), Value::Varchar("green".into()), Value::Null],
2929 )
2930 .unwrap();
2931 let shared = flat.shared_text().unwrap();
2932 // Nothing went to the arena, so the whole column resolves with an empty one.
2933 let (views, arena) = shared.text_parts().unwrap();
2934 assert!(arena.is_empty(), "three short strings need no arena");
2935 assert_eq!(views[0].bytes_in(arena), Some(&b"red"[..]));
2936 assert_eq!(shared.value_at(1), Value::Varchar("green".into()));
2937 assert_eq!(shared.value_at(2), Value::Null, "the validity came across");
2938 }
2939
2940 #[test]
2941 fn a_cut_of_a_view_column_shares_the_arena_rather_than_copying_the_bytes() {
2942 let shared = long_strings(64).shared_text().unwrap();
2943 let cut = shared.slice(16, 8).unwrap();
2944 assert_eq!(cut.form(), Form::StringView, "a cut of views is views");
2945 assert_eq!(cut.len(), 8);
2946 assert_eq!(cut.value_at(0), shared.value_at(16));
2947 assert_eq!(cut.value_at(7), shared.value_at(23));
2948 // The arena is the same bytes at the same address, which is the whole point of the form.
2949 let (_, whole) = shared.text_parts().unwrap();
2950 let (_, piece) = cut.text_parts().unwrap();
2951 assert_eq!(piece.as_ptr(), whole.as_ptr(), "the cut shares the page");
2952 assert_eq!(piece.len(), whole.len());
2953 }
2954
2955 #[test]
2956 fn a_flat_string_column_has_to_copy_the_bytes_its_cut_keeps() {
2957 let flat = long_strings(64);
2958 let cut = flat.slice(16, 8).unwrap();
2959 assert_eq!(cut.form(), Form::Flat);
2960 let (_, whole) = flat.text_parts().unwrap();
2961 let (_, piece) = cut.text_parts().unwrap();
2962 assert!(piece.len() < whole.len(), "the flat cut carries only what it kept");
2963 }
2964
2965 #[test]
2966 fn a_gather_of_a_view_column_keeps_the_form_and_a_flatten_copies_out_of_it() {
2967 let shared = long_strings(32).shared_text().unwrap();
2968 let picked: Vec<u32> = (0..32).step_by(3).collect();
2969 let gathered = shared.gather(&picked).unwrap();
2970 assert_eq!(gathered.form(), Form::StringView, "selecting rows moves views, not bytes");
2971 assert_eq!(gathered.len(), picked.len());
2972 for (row, &from) in picked.iter().enumerate() {
2973 assert_eq!(gathered.value_at(row), shared.value_at(from as usize), "row {row}");
2974 }
2975 let flattened = gathered.flatten().unwrap();
2976 assert_eq!(flattened.form(), Form::Flat);
2977 assert_eq!(flattened.iter().collect::<Vec<_>>(), gathered.iter().collect::<Vec<_>>());
2978 // The flatten is what narrows the bytes, so the arena it built holds only the rows it kept.
2979 let (_, narrowed) = flattened.text_parts().unwrap();
2980 let (_, whole) = shared.text_parts().unwrap();
2981 assert!(narrowed.len() < whole.len(), "flattening lets the page go");
2982 }
2983
2984 #[test]
2985 fn a_null_in_a_view_column_survives_being_gathered_and_flattened() {
2986 let shared = long_strings(8)
2987 .with_validity(Validity::from_iter(8, |row| row % 3 != 0))
2988 .shared_text()
2989 .unwrap();
2990 let gathered = shared.gather(&[0, 1, 2, 3, 4]).unwrap();
2991 let expected =
2992 [Value::Null, shared.value_at(1), shared.value_at(2), Value::Null, shared.value_at(4)];
2993 assert_eq!(gathered.iter().collect::<Vec<_>>(), expected);
2994 assert_eq!(gathered.flatten().unwrap().iter().collect::<Vec<_>>(), expected);
2995 }
2996
2997 #[test]
2998 fn both_string_forms_hand_a_kernel_the_same_views_and_the_same_bytes() {
2999 let flat = long_strings(6);
3000 let shared = flat.clone().shared_text().unwrap();
3001 let (flat_views, flat_arena) = flat.text_parts().unwrap();
3002 let (shared_views, shared_arena) = shared.text_parts().unwrap();
3003 assert_eq!(flat_views.len(), shared_views.len());
3004 for row in 0..6 {
3005 assert_eq!(
3006 flat_views[row].bytes_in(flat_arena),
3007 shared_views[row].bytes_in(shared_arena),
3008 "row {row}"
3009 );
3010 }
3011 // Nothing else answers this, which is what keeps a kernel from taking it for a string column.
3012 assert!(Vector::sequence(0, 1, 4).text_parts().is_none());
3013 assert!(integers(&[1, 2, 3]).text_parts().is_none());
3014 }
3015
3016 #[test]
3017 fn a_column_that_is_not_strings_cannot_be_held_as_views() {
3018 let views = vec![StringView::inline("red")];
3019 let arena = Arc::new(Buffer::new());
3020 let wrong = Vector::string_views(LogicalType::Integer, views, arena);
3021 assert!(wrong.is_err(), "an integer column has no views");
3022 assert_eq!(integers(&[1, 2]).shared_text().unwrap().form(), Form::Flat, "left alone");
3023 }
3024
3025 /// A column with enough repeated structure for a symbol table to find something, which is what
3026 /// a real text column has and a column of random bytes does not.
3027 fn sentences(count: usize) -> Vector {
3028 let values: Vec<Value> = (0..count)
3029 .map(|row| {
3030 Value::Varchar(format!(
3031 "http://example.test/catalogue/section/{}/item/{row}",
3032 row % 7
3033 ))
3034 })
3035 .collect();
3036 Vector::from_values(LogicalType::Varchar, &values).unwrap()
3037 }
3038
3039 #[test]
3040 fn a_compressed_column_reads_back_the_strings_that_went_into_it() {
3041 let flat = sentences(64);
3042 let coded = flat.clone().compressed().unwrap();
3043 assert_eq!(coded.form(), Form::Fsst, "a text column compresses");
3044 assert_eq!(coded.len(), 64);
3045 for row in 0..64 {
3046 assert_eq!(coded.value_at(row), flat.value_at(row), "row {row}");
3047 }
3048 assert_eq!(coded.flatten().unwrap(), flat, "flattening is the column it came from");
3049 }
3050
3051 #[test]
3052 fn compressing_halves_the_bytes_or_the_column_is_left_flat() {
3053 let flat = sentences(200);
3054 let coded = flat.clone().compressed().unwrap();
3055 let parts = coded.coded_parts().expect("compressed");
3056 // Read through the flat column, because the compressed one has no bytes to hand back where
3057 // they are and answers `None` to `text_at` rather than decompressing into a borrow.
3058 assert_eq!(coded.text_at(0), None, "nothing to borrow until it is flattened");
3059 let plain: usize = (0..200).map(|row| flat.text_at(row).map_or(0, str::len)).sum();
3060 let codes: usize = (0..200).map(|row| parts.row(row).map_or(0, <[u8]>::len)).sum();
3061 assert!(codes * FSST_PAYS_AT <= plain, "{codes} codes against {plain} bytes");
3062 // Text with no repeated structure in it gives a table nothing longer than a byte to find,
3063 // so the codes are the bytes and the column stays where it is rather than paying a
3064 // decompression per read to save nothing.
3065 let mut seed = 0x2545_f491_4f6c_dd1du64;
3066 let values: Vec<Value> = (0..256)
3067 .map(|_| {
3068 let mut text = String::new();
3069 while text.len() < 12 {
3070 seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
3071 text.push(char::from(b'!' + ((seed >> 33) % 90) as u8));
3072 }
3073 Value::Varchar(text)
3074 })
3075 .collect();
3076 let noise = Vector::from_values(LogicalType::Varchar, &values).unwrap();
3077 assert_eq!(noise.compressed().unwrap().form(), Form::Flat);
3078 }
3079
3080 #[test]
3081 fn a_cut_of_a_compressed_column_shares_the_codes_and_the_table() {
3082 let coded = sentences(64).compressed().unwrap();
3083 let cut = coded.slice(8, 16).unwrap();
3084 assert_eq!(cut.form(), Form::Fsst);
3085 assert_eq!(cut.len(), 16);
3086 for row in 0..16 {
3087 assert_eq!(cut.value_at(row), coded.value_at(8 + row), "row {row}");
3088 }
3089 let (whole, piece) = (coded.coded_parts().unwrap(), cut.coded_parts().unwrap());
3090 assert_eq!(piece.row(0), whole.row(8), "the spans point into the same codes");
3091 }
3092
3093 #[test]
3094 fn a_gather_of_a_compressed_column_stays_compressed_and_keeps_the_nulls() {
3095 let coded = sentences(32)
3096 .with_validity(Validity::from_iter(32, |row| row % 5 != 2))
3097 .compressed()
3098 .unwrap();
3099 let picked: Vec<u32> = (0..32).step_by(2).collect();
3100 let gathered = coded.gather(&picked).unwrap();
3101 assert_eq!(gathered.form(), Form::Fsst, "selecting rows moves spans, not bytes");
3102 for (row, &from) in picked.iter().enumerate() {
3103 assert_eq!(gathered.value_at(row), coded.value_at(from as usize), "row {row}");
3104 }
3105 assert_eq!(
3106 gathered.flatten().unwrap().iter().collect::<Vec<_>>(),
3107 gathered.iter().collect::<Vec<_>>()
3108 );
3109 }
3110
3111 #[test]
3112 fn a_literal_lands_in_the_same_codes_the_row_holding_it_does() {
3113 let coded = sentences(40).compressed().unwrap();
3114 let parts = coded.coded_parts().expect("compressed");
3115 let text = coded.value_at(11);
3116 let Value::Varchar(text) = text else { panic!("a string column reads back strings") };
3117 assert_eq!(parts.encode(text.as_bytes()), parts.row(11).expect("row 11"));
3118 assert_ne!(parts.encode(b"something else entirely"), parts.row(11).unwrap());
3119 }
3120
3121 #[test]
3122 fn codes_that_run_past_what_is_there_are_refused() {
3123 let table = Arc::new(SymbolTable::empty());
3124 let codes = Arc::new(vec![1u8, 2, 3, 4]);
3125 let good = vec![(0u32, 2u32), (2, 4)];
3126 assert!(
3127 Vector::coded(LogicalType::Varchar, Arc::clone(&codes), good, Arc::clone(&table))
3128 .is_ok()
3129 );
3130 let past = vec![(0u32, 9u32)];
3131 assert!(
3132 Vector::coded(LogicalType::Varchar, Arc::clone(&codes), past, Arc::clone(&table))
3133 .is_err(),
3134 "a span past the end of the codes"
3135 );
3136 let backwards = vec![(3u32, 1u32)];
3137 assert!(
3138 Vector::coded(LogicalType::Varchar, Arc::clone(&codes), backwards, Arc::clone(&table))
3139 .is_err(),
3140 "a span that ends before it starts"
3141 );
3142 let wrong = vec![(0u32, 2u32)];
3143 assert!(
3144 Vector::coded(LogicalType::Integer, codes, wrong, table).is_err(),
3145 "an integer column has no codes"
3146 );
3147 }
3148
3149 #[test]
3150 fn a_view_pointing_past_its_arena_is_refused_at_construction() {
3151 let long = "a string too long to sit inside a view";
3152 let arena: Arc<Buffer<u8>> = Arc::new(long.as_bytes().to_vec().into());
3153 let good = vec![StringView::over(long.as_bytes(), 0)];
3154 assert!(Vector::string_views(LogicalType::Varchar, good, Arc::clone(&arena)).is_ok());
3155 let bad = vec![StringView::over(long.as_bytes(), 4)];
3156 assert!(
3157 Vector::string_views(LogicalType::Varchar, bad, arena).is_err(),
3158 "four bytes short of what the view claims"
3159 );
3160 }
3161}