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. The four forms are the ones in `spec/04-architecture.md` section
9//! 4.3: flat, constant, sequence and dictionary. Encoded, the fifth, is the M3 work and it arrives
10//! with the specialization contract rather than before it.
11//!
12//! **What is not here yet.** Buffers are owned. Section 7.1 says a vector borrowed from a buffer
13//! managed page carries a pin, and there is no buffer manager until M2, so there is nothing to pin
14//! and pretending otherwise would be an interface built against an imaginary caller. Nested types
15//! are not stored yet either, for the same reason: a `LIST(STRUCT(...))` is offsets plus child
16//! column chunks, and child column chunks are storage.
17
18use rudb_common::{Error, LogicalType, Result, Value};
19
20use crate::buffer::Buffer;
21use crate::string::{StringColumn, StringView};
22use crate::validity::Validity;
23
24/// How many values are in a full vector.
25///
26/// 1024 rather than DuckDB's 2048, per `spec/04-architecture.md` section 4.3. It is the FastLanes
27/// unit, it makes a validity mask exactly 16 `u64` words, and it keeps a vector of 16 byte string
28/// views at 16 KiB, which is the size at which several of these fit in L1 together rather than
29/// evicting each other.
30pub const VECTOR_SIZE: usize = 1024;
31
32/// Which physical form a vector is in.
33///
34/// An operator asks this once per vector and then takes the path it wants, which is the one branch
35/// per vector that the whole design is willing to spend.
36///
37/// Not exhaustive, and that is a decision rather than an oversight. `Encoded` is the fifth form
38/// and it arrives at layer three with the specialization contract. If this enum were exhaustive,
39/// the day it lands is the day every kernel in the workspace stops compiling, and the pressure at
40/// that moment would be to add an arm to each of them in a hurry rather than to think about what
41/// each one should do with an encoded vector. A required fallback arm means each kernel already
42/// has a correct answer for a form it has never seen, and specializing it is then a change that
43/// can be made one kernel at a time with a benchmark next to it.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45#[non_exhaustive]
46pub enum Form {
47 /// One value per position.
48 Flat,
49 /// One value, repeated.
50 Constant,
51 /// A start and a step, computed rather than stored.
52 Sequence,
53 /// Codes into a smaller vector of distinct values.
54 Dictionary,
55}
56
57/// The values of a flat vector, one Rust vector per physical type.
58///
59/// The variants are physical rather than logical, which is what lets `DATE` and `INTEGER` share
60/// storage and share a kernel. What a run of `i32` means is the vector's logical type's business.
61#[derive(Debug, Clone, PartialEq)]
62#[non_exhaustive]
63pub enum Data {
64 /// No values, for the type of an untyped `NULL`.
65 Empty,
66 /// One byte per value.
67 Bool(Buffer<bool>),
68 /// 8 bit signed.
69 Int8(Buffer<i8>),
70 /// 16 bit signed.
71 Int16(Buffer<i16>),
72 /// 32 bit signed.
73 Int32(Buffer<i32>),
74 /// 64 bit signed.
75 Int64(Buffer<i64>),
76 /// 128 bit signed.
77 Int128(Buffer<i128>),
78 /// 8 bit unsigned.
79 UInt8(Buffer<u8>),
80 /// 16 bit unsigned.
81 UInt16(Buffer<u16>),
82 /// 32 bit unsigned.
83 UInt32(Buffer<u32>),
84 /// 64 bit unsigned.
85 UInt64(Buffer<u64>),
86 /// 128 bit unsigned.
87 UInt128(Buffer<u128>),
88 /// IEEE 754 binary32.
89 Float32(Buffer<f32>),
90 /// IEEE 754 binary64.
91 Float64(Buffer<f64>),
92 /// The months, days and microseconds triple.
93 Interval(Buffer<(i32, i32, i64)>),
94 /// Strings, as 16 byte views plus the arena the long ones live in.
95 Varlen(StringColumn),
96}
97
98impl Data {
99 /// How many values are stored.
100 ///
101 /// The match below has no wildcard arm, and that is what makes this function the check that
102 /// keeps [`for_each_layout`](crate::for_each_layout) honest. A variant added to this enum
103 /// without being added to the `all` group fails to compile here, which is a line in a build log
104 /// rather than a layout quietly missing from six kernels.
105 #[must_use]
106 pub fn len(&self) -> usize {
107 macro_rules! lengths {
108 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
109 match self {
110 Self::Empty => 0,
111 $(Self::$variant(values) => values.len(),)+
112 }
113 };
114 }
115 crate::for_each_layout!(all, lengths)
116 }
117
118 /// Whether there are no values.
119 #[must_use]
120 pub fn is_empty(&self) -> bool {
121 self.len() == 0
122 }
123
124 /// An integer at `index`, widened, for any of the signed integer layouts.
125 ///
126 /// Used by the decimal path, which needs the unscaled value out of whichever width the width
127 /// and scale picked, and by anything else that would otherwise repeat the same five arms.
128 #[must_use]
129 pub fn signed_at(&self, index: usize) -> Option<i128> {
130 macro_rules! widened {
131 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
132 match self {
133 $(Self::$variant(v) => v.get(index).map(|&x| i128::from(x)),)+
134 _ => None,
135 }
136 };
137 }
138 crate::for_each_layout!(signed, widened)
139 }
140
141 /// An unsigned integer at `index`, widened.
142 #[must_use]
143 pub fn unsigned_at(&self, index: usize) -> Option<u128> {
144 macro_rules! widened {
145 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
146 match self {
147 $(Self::$variant(v) => v.get(index).map(|&x| u128::from(x)),)+
148 _ => None,
149 }
150 };
151 }
152 crate::for_each_layout!(unsigned, widened)
153 }
154
155 /// The string at `index`, for a `Varlen`.
156 #[must_use]
157 pub fn str_at(&self, index: usize) -> Option<&str> {
158 match self {
159 Self::Varlen(column) => column.get(index),
160 _ => None,
161 }
162 }
163}
164
165/// A type, a length, a validity representation and some data.
166#[derive(Debug, Clone, PartialEq)]
167pub struct Vector {
168 ty: LogicalType,
169 len: usize,
170 validity: Validity,
171 body: Body,
172}
173
174/// What the vector holds, which is what its form is decided by.
175#[derive(Debug, Clone, PartialEq)]
176enum Body {
177 Flat(Data),
178 Constant(Box<Value>),
179 Sequence { start: i64, step: i64 },
180 Dictionary { codes: Vec<u32>, values: Box<Vector> },
181}
182
183impl Vector {
184 /// A flat vector of `data`, all valid.
185 ///
186 /// # Errors
187 ///
188 /// If the data's physical layout is not the one the type calls for. That check is here rather
189 /// than left to the caller because a vector whose type and layout disagree is a wrong answer
190 /// waiting to be read out, and it costs one comparison at construction to prevent.
191 pub fn flat(ty: LogicalType, data: Data) -> Result<Self> {
192 let len = data.len();
193 if !matches!(data, Data::Empty) && layout_of(&data) != ty.physical() {
194 return Err(Error::internal(format!(
195 "a {ty} vector cannot hold {:?} data",
196 layout_of(&data)
197 )));
198 }
199 Ok(Self { ty, len, validity: Validity::AllValid, body: Body::Flat(data) })
200 }
201
202 /// A flat vector built from single values, with the nulls among them turning into validity.
203 ///
204 /// The slow way in, and the only way in that anything outside this crate has. It is what an
205 /// `INSERT`, a `VALUES` clause and a test build a column with, all of which arrive holding
206 /// values rather than a run of `i32`. Nothing on a scan path calls it: a scan produces a run of
207 /// data directly and hands it to [`Self::flat`].
208 ///
209 /// # Errors
210 ///
211 /// If a value is not one the type can hold, or if the type is one that cannot be stored flat
212 /// yet, which today means the nested types.
213 pub fn from_values(ty: LogicalType, values: &[Value]) -> Result<Self> {
214 let mut data = empty_data_for(&ty)?;
215 for value in values {
216 push_value(&mut data, value)?;
217 }
218 let validity = Validity::from_iter(values.len(), |index| !values[index].is_null());
219 Ok(Self { ty, len: values.len(), validity, body: Body::Flat(data) })
220 }
221
222 /// A vector of `len` copies of one value.
223 ///
224 /// Costs one value regardless of the length, which is what makes a literal in a predicate free
225 /// and what makes a projection of a constant free.
226 #[must_use]
227 pub fn constant(ty: LogicalType, value: Value, len: usize) -> Self {
228 let validity = if value.is_null() { Validity::AllInvalid } else { Validity::AllValid };
229 Self { ty, len, validity, body: Body::Constant(Box::new(value)) }
230 }
231
232 /// A vector of `len` values starting at `start` and stepping by `step`.
233 ///
234 /// This is what a row identifier column is, and it costs sixteen bytes rather than eight
235 /// kilobytes. A scan that produces row ids for a later fetch produces one of these.
236 #[must_use]
237 pub fn sequence(start: i64, step: i64, len: usize) -> Self {
238 Self {
239 ty: LogicalType::BigInt,
240 len,
241 validity: Validity::AllValid,
242 body: Body::Sequence { start, step },
243 }
244 }
245
246 /// A vector of codes into a smaller vector of distinct values.
247 ///
248 /// The form the whole M3 thesis rests on. A dictionary vector handed to a group by is an
249 /// integer column, and an aggregate over one is an aggregate over integers no matter what the
250 /// logical type says.
251 ///
252 /// A dictionary over a dictionary is composed into one level here rather than left as two, so
253 /// the form has a depth of one always and a kernel that reads [`Self::dictionary_parts`] is
254 /// reading the values rather than another layer of codes. Two filters over the same chunk build
255 /// the second case and four conjuncts pushed down separately build four of it.
256 ///
257 /// The cost of leaving them stacked turned out to be a cliff rather than a slope. Every loop in
258 /// `rudb-kernels` reaches for the values behind the codes with [`Self::data`], a dictionary
259 /// pointing at a dictionary has no data to hand back, so the second level does not make the
260 /// kernels slower, it turns them off and drops the work onto the row at a time path that exists
261 /// to be correct rather than fast. Measured on server3 over a chunk of two numeric columns and a
262 /// consumer of two vectorized passes, one level reads at 3.5 nanoseconds a row and two levels at
263 /// 104, and the third and fourth levels cost almost nothing more because the first one had
264 /// already given up everything there was to give. Composing is one pass over the outer codes,
265 /// which the range check above is already making.
266 ///
267 /// The one dictionary that is not composed past is one carrying a validity of its own. A
268 /// dictionary is built all valid and only [`Self::with_validity`] can change that, so such a
269 /// vector is saying that its nulls are at this level rather than in the values it points at, and
270 /// composing past it would drop them.
271 ///
272 /// # Errors
273 ///
274 /// If any code is past the end of the value vector.
275 pub fn dictionary(codes: Vec<u32>, values: Vector) -> Result<Self> {
276 if let Some(&bad) = codes.iter().find(|&&code| code as usize >= values.len()) {
277 return Err(Error::internal(format!(
278 "dictionary code {bad} is past the end of a {} value dictionary",
279 values.len()
280 )));
281 }
282 let (codes, values) = compose(codes, values);
283 Ok(Self {
284 ty: values.ty.clone(),
285 len: codes.len(),
286 validity: Validity::AllValid,
287 body: Body::Dictionary { codes, values: Box::new(values) },
288 })
289 }
290
291 /// The same vector with a different validity.
292 #[must_use]
293 pub fn with_validity(mut self, validity: Validity) -> Self {
294 self.validity = validity;
295 self
296 }
297
298 /// What kind of values these are.
299 #[must_use]
300 pub fn logical_type(&self) -> &LogicalType {
301 &self.ty
302 }
303
304 /// How many values there are.
305 #[must_use]
306 pub fn len(&self) -> usize {
307 self.len
308 }
309
310 /// Whether there are no values.
311 #[must_use]
312 pub fn is_empty(&self) -> bool {
313 self.len == 0
314 }
315
316 /// Which of the values are not null.
317 #[must_use]
318 pub fn validity(&self) -> &Validity {
319 &self.validity
320 }
321
322 /// Which physical form this vector is in.
323 #[must_use]
324 pub fn form(&self) -> Form {
325 match self.body {
326 Body::Flat(_) => Form::Flat,
327 Body::Constant(_) => Form::Constant,
328 Body::Sequence { .. } => Form::Sequence,
329 Body::Dictionary { .. } => Form::Dictionary,
330 }
331 }
332
333 /// The data, for a flat vector, and `None` for any other form.
334 ///
335 /// A kernel that wants a slice asks for it and takes the flat path if it gets one. A kernel
336 /// that can do better on a constant or a dictionary checks [`Self::form`] first.
337 #[must_use]
338 pub fn data(&self) -> Option<&Data> {
339 match &self.body {
340 Body::Flat(data) => Some(data),
341 _ => None,
342 }
343 }
344
345 /// The one value, for a constant vector, and `None` for any other form.
346 ///
347 /// A kernel comparing a column against a literal wants the literal once rather than 1024
348 /// times, and [`Self::value_at`] on a constant clones it on every call because it has to be
349 /// able to hand back a `Value` for any form. This is the accessor that lets the specialized
350 /// path hoist the clone out of the loop.
351 #[must_use]
352 pub fn constant_value(&self) -> Option<&Value> {
353 match &self.body {
354 Body::Constant(value) => Some(value.as_ref()),
355 _ => None,
356 }
357 }
358
359 /// The codes and the values, for a dictionary vector, and `None` for any other form.
360 ///
361 /// The reason a kernel needs this rather than reading the dictionary through
362 /// [`Self::value_at`] is the entire argument for the form existing. A filter against a
363 /// dictionary column of 1024 rows and 40 distinct values is 40 comparisons and 1024 lookups,
364 /// not 1024 comparisons, and there is no way to write that loop without seeing the codes.
365 ///
366 /// Note what the validity of the returned vector means. A dictionary keeps its nulls in the
367 /// vector it points at, and the dictionary's own validity says nothing about them, so a caller
368 /// deciding whether row `i` is null has to ask the value vector about `codes[i]` rather than
369 /// asking this vector about `i`. [`Self::flatten`] has the same note on it for the same
370 /// reason, because getting this wrong is a null that survives being selected and comes out as
371 /// a zero.
372 #[must_use]
373 pub fn dictionary_parts(&self) -> Option<(&[u32], &Self)> {
374 match &self.body {
375 Body::Dictionary { codes, values } => Some((codes, values.as_ref())),
376 _ => None,
377 }
378 }
379
380 /// The start and the step, for a sequence vector, and `None` for any other form.
381 #[must_use]
382 pub fn sequence_parts(&self) -> Option<(i64, i64)> {
383 match self.body {
384 Body::Sequence { start, step } => Some((start, step)),
385 _ => None,
386 }
387 }
388
389 /// The value at `index`, as a single value.
390 ///
391 /// This is the slow path on purpose. It is what a result set is read out with and what a test
392 /// asserts on, and an operator that calls it per row is an operator that has already lost the
393 /// argument the vector interface exists to win.
394 #[must_use]
395 pub fn value_at(&self, index: usize) -> Value {
396 if index >= self.len || !self.validity.is_valid(index) {
397 return Value::Null;
398 }
399 match &self.body {
400 Body::Constant(value) => value.as_ref().clone(),
401 Body::Sequence { start, step } => Value::BigInt(start + step * index as i64),
402 Body::Dictionary { codes, values } => match codes.get(index) {
403 Some(&code) => values.value_at(code as usize),
404 None => Value::Null,
405 },
406 Body::Flat(data) => value_from(&self.ty, data, index),
407 }
408 }
409
410 /// Every value in order, as single values.
411 pub fn iter(&self) -> impl Iterator<Item = Value> + '_ {
412 (0..self.len).map(|index| self.value_at(index))
413 }
414
415 /// The same values in flat form.
416 ///
417 /// Flattening a vector that is already flat is free. Flattening any other form costs a copy,
418 /// which is exactly why the other forms exist and why nothing on the hot path should call
419 /// this. It is here for the operators that genuinely cannot do better and for the tests that
420 /// check the other forms against it.
421 ///
422 /// # Errors
423 ///
424 /// If the type is one this crate cannot store flat yet, which today means the nested types.
425 pub fn flatten(&self) -> Result<Self> {
426 if let Body::Flat(_) = self.body {
427 return Ok(self.clone());
428 }
429 self.copied((0..self.len).collect(), false)
430 }
431
432 /// The values at the given positions, copied, in a form that does not point back at this vector.
433 ///
434 /// This is the copying counterpart to [`Self::dictionary`], and the two are the two halves of
435 /// the decision `spec/07-execution.md` section 7.1 describes. Which half is right is measured
436 /// rather than argued, and [`Chunk::compact`](crate::Chunk::compact) is where the measurement
437 /// is written down.
438 ///
439 /// A dictionary chain is walked to its leaf first and the codes composed on the way down, so the
440 /// copy runs once over the data rather than once per level, and a position that is null at any
441 /// level comes out null here. The copy is a typed loop per physical layout rather than a `Value`
442 /// per row, which is the whole point of it and is what [`Self::flatten`] now goes through too.
443 ///
444 /// # Errors
445 ///
446 /// If the type has no flat layout, which today means the nested types.
447 pub fn gather(&self, indices: &[u32]) -> Result<Self> {
448 self.copied(indices.iter().map(|&index| index as usize).collect(), true)
449 }
450
451 /// The copy both [`Self::gather`] and [`Self::flatten`] are.
452 ///
453 /// `constants_stay` is the one thing the two want differently. A gather of a constant is a
454 /// shorter constant and copying it out would be a thousand writes of the same value for nothing,
455 /// but flattening promises flat form to a caller that is about to read the data slice, so for
456 /// that one the constant has to be written out.
457 fn copied(&self, at: Vec<usize>, constants_stay: bool) -> Result<Self> {
458 let rows = at.len();
459 let (at, leaf) = self.resolve(at);
460 let live: Vec<bool> = at.iter().map(|&index| index != NOWHERE).collect();
461 let validity = Validity::from_run(&live);
462 let body = match &leaf.body {
463 // Every position holds the same value, so the only thing the gather can change is the
464 // length and which positions are null. A gather with no null in it is still a constant.
465 Body::Constant(value) => {
466 if constants_stay && matches!(validity, Validity::AllValid) {
467 return Ok(Self::constant(self.ty.clone(), value.as_ref().clone(), rows));
468 }
469 let mut data = empty_data_for(&self.ty)?;
470 for &index in &at {
471 push_value(&mut data, if index == NOWHERE { &Value::Null } else { value })?;
472 }
473 Body::Flat(data)
474 }
475 // A sequence is arithmetic rather than storage, so the gather is the arithmetic done at
476 // the positions asked for, and a null writes the zero every other layout writes.
477 Body::Sequence { start, step } => Body::Flat(Data::Int64(
478 at.iter()
479 .map(|&index| if index == NOWHERE { 0 } else { start + step * index as i64 })
480 .collect(),
481 )),
482 // A flat body with no values is the untyped null, so every position asked for is null
483 // whatever was asked for. Going through the copy would build a run of no values and
484 // call it `rows` long, which is a vector whose length and data disagree.
485 Body::Flat(Data::Empty) => {
486 return Ok(Self::constant(self.ty.clone(), Value::Null, rows));
487 }
488 Body::Flat(data) => Body::Flat(copy_of(data, &at)),
489 // Unreachable, because `resolve` stops at the first body that is not a dictionary.
490 Body::Dictionary { .. } => {
491 return Err(Error::internal("a dictionary survived being resolved"));
492 }
493 };
494 Ok(Self { ty: self.ty.clone(), len: rows, validity, body })
495 }
496
497 /// Where each wanted position lives in the first body that is not a dictionary, and that body.
498 ///
499 /// A position that is null anywhere on the way down, or past the end of anything on the way
500 /// down, comes back as [`NOWHERE`]. That single sentinel is what keeps the copy loop from
501 /// carrying a validity mask alongside the positions it is already walking.
502 fn resolve(&self, mut at: Vec<usize>) -> (Vec<usize>, &Self) {
503 let mut source = self;
504 loop {
505 for slot in &mut at {
506 if *slot >= source.len || !source.validity.is_valid(*slot) {
507 *slot = NOWHERE;
508 }
509 }
510 let Body::Dictionary { codes, values } = &source.body else {
511 return (at, source);
512 };
513 for slot in &mut at {
514 *slot = match codes.get(*slot) {
515 Some(&code) => code as usize,
516 None => NOWHERE,
517 };
518 }
519 source = values.as_ref();
520 }
521 }
522}
523
524/// So that a kernel can take its operands as either a list of vectors or a list of references.
525///
526/// A caller that built a `Vec<Vector>` and a caller whose operands are already somewhere else, in a
527/// chunk or in an evaluator's scratch, want the same kernel. Without this the second kind has to
528/// clone every operand into a `Vec` to satisfy the signature, and a clone of a vector is a copy of
529/// the whole column, so the type would be charging real memory traffic for nothing.
530impl AsRef<Vector> for Vector {
531 fn as_ref(&self) -> &Vector {
532 self
533 }
534}
535
536/// One level of dictionary out of however many levels were handed to [`Vector::dictionary`].
537///
538/// Every dictionary in the system is built through that constructor and every one of them comes
539/// through here first, so the invariant this maintains is that the vector a dictionary points at is
540/// never itself a dictionary that could have been composed away. That makes the work a single `if`
541/// rather than a loop: the inner vector was already composed when it was built, so composing the
542/// outer codes through it leaves the result no deeper than the inner vector already was.
543///
544/// The codes are indexed rather than fetched with `get`, because the caller has already walked the
545/// whole outer array to check that every code is in range and the inner array is exactly as long as
546/// the vector those codes were checked against.
547fn compose(codes: Vec<u32>, values: Vector) -> (Vec<u32>, Vector) {
548 // A dictionary carrying a validity of its own is one whose nulls live at this level rather than
549 // in the values, which is the one thing composition cannot carry down with it.
550 if !matches!(values.validity, Validity::AllValid) {
551 return (codes, values);
552 }
553 let Vector { ty, len, validity, body } = values;
554 match body {
555 Body::Dictionary { codes: inner, values: leaf } => {
556 debug_assert!(
557 !matches!(leaf.body, Body::Dictionary { .. })
558 || !matches!(leaf.validity, Validity::AllValid),
559 "a dictionary was stacked on a dictionary without going through the constructor"
560 );
561 (codes.iter().map(|&code| inner[code as usize]).collect(), *leaf)
562 }
563 body => (codes, Vector { ty, len, validity, body }),
564 }
565}
566
567/// The position of a value that is not anywhere, because it is null or out of range.
568///
569/// `usize::MAX` rather than an `Option<usize>`, because the copy loop's bounds check rejects it for
570/// free and an `Option` would put a second branch next to the one already there.
571const NOWHERE: usize = usize::MAX;
572
573/// A run of data copied at the given positions, with a zero wherever the position is [`NOWHERE`].
574///
575/// A zero and not a skip, because every layout here is a parallel array to a validity mask and a
576/// short one would put every value after the first null at the wrong index. It is the same rule
577/// [`push_value`] follows for a null.
578fn copy_of(data: &Data, at: &[usize]) -> Data {
579 macro_rules! copied {
580 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
581 match data {
582 Data::Empty => Data::Empty,
583 $(Data::$variant(values) => {
584 let mut out = Buffer::with_capacity(at.len());
585 for &index in at {
586 // One bounds check rather than a null test and a bounds check, because
587 // `NOWHERE` is past the end of every slice there can be.
588 out.push(values.get(index).copied().unwrap_or($zero));
589 }
590 Data::$variant(out)
591 })+
592 // The one layout where a gather is a copy of bytes rather than a copy of fixed
593 // width slots, and the reason compaction is a decision rather than a default on a
594 // string column.
595 Data::Varlen(values) => {
596 let mut out = StringColumn::with_capacity(at.len());
597 // The bytes are known before any of them are copied, because a view carries its
598 // length and the wanted positions are already in hand, so the arena is one
599 // allocation rather than a run of doublings that each copy what the last one
600 // copied.
601 let views = values.views();
602 out.reserve_bytes(
603 at.iter()
604 .filter_map(|&index| views.get(index))
605 .filter(|view| !view.is_inline())
606 .map(StringView::len)
607 .sum(),
608 );
609 for &index in at {
610 out.push(values.get(index).unwrap_or(""));
611 }
612 Data::Varlen(out)
613 }
614 }
615 };
616 }
617 crate::for_each_layout!(fixed, copied)
618}
619
620/// The physical layout a run of data is in, for the check that it matches its type.
621///
622/// The two enums name their variants the same way on purpose, so this is one generated arm rather
623/// than sixteen chances to pair the wrong two up.
624fn layout_of(data: &Data) -> rudb_common::PhysicalType {
625 use rudb_common::PhysicalType as P;
626 macro_rules! layouts {
627 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
628 match data {
629 Data::Empty => P::Empty,
630 $(Data::$variant(_) => P::$variant,)+
631 }
632 };
633 }
634 crate::for_each_layout!(all, layouts)
635}
636
637/// One value out of a run of data, given what the run means.
638///
639/// The match is on the logical type rather than on the data, because the data cannot tell a `DATE`
640/// from an `INTEGER` and that is the whole reason the two are kept apart.
641fn value_from(ty: &LogicalType, data: &Data, index: usize) -> Value {
642 let signed = || data.signed_at(index);
643 let unsigned = || data.unsigned_at(index);
644 let value = match ty {
645 LogicalType::Boolean => match data {
646 Data::Bool(v) => v.get(index).map(|&x| Value::Boolean(x)),
647 _ => None,
648 },
649 LogicalType::TinyInt => signed().and_then(|x| i8::try_from(x).ok()).map(Value::TinyInt),
650 LogicalType::SmallInt => signed().and_then(|x| i16::try_from(x).ok()).map(Value::SmallInt),
651 LogicalType::Integer => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Integer),
652 LogicalType::BigInt => signed().and_then(|x| i64::try_from(x).ok()).map(Value::BigInt),
653 LogicalType::HugeInt => signed().map(Value::HugeInt),
654 LogicalType::UTinyInt => unsigned().and_then(|x| u8::try_from(x).ok()).map(Value::UTinyInt),
655 LogicalType::USmallInt => {
656 unsigned().and_then(|x| u16::try_from(x).ok()).map(Value::USmallInt)
657 }
658 LogicalType::UInteger => {
659 unsigned().and_then(|x| u32::try_from(x).ok()).map(Value::UInteger)
660 }
661 LogicalType::UBigInt => unsigned().and_then(|x| u64::try_from(x).ok()).map(Value::UBigInt),
662 LogicalType::UHugeInt => unsigned().map(Value::UHugeInt),
663 LogicalType::Float => match data {
664 Data::Float32(v) => v.get(index).map(|&x| Value::Float(x)),
665 _ => None,
666 },
667 LogicalType::Double => match data {
668 Data::Float64(v) => v.get(index).map(|&x| Value::Double(x)),
669 _ => None,
670 },
671 LogicalType::Decimal { width, scale } => {
672 signed().map(|unscaled| Value::Decimal { unscaled, width: *width, scale: *scale })
673 }
674 LogicalType::Varchar => data.str_at(index).map(|s| Value::Varchar(s.to_string())),
675 LogicalType::Blob | LogicalType::Bit => {
676 data.str_at(index).map(|s| Value::Blob(s.as_bytes().to_vec()))
677 }
678 LogicalType::Date => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Date),
679 LogicalType::Time | LogicalType::TimeTz => {
680 signed().and_then(|x| i64::try_from(x).ok()).map(Value::Time)
681 }
682 LogicalType::Timestamp
683 | LogicalType::TimestampS
684 | LogicalType::TimestampMs
685 | LogicalType::TimestampNs
686 | LogicalType::TimestampTz => {
687 signed().and_then(|x| i64::try_from(x).ok()).map(Value::Timestamp)
688 }
689 LogicalType::Interval => match data {
690 Data::Interval(v) => {
691 v.get(index).map(|&(months, days, micros)| Value::Interval { months, days, micros })
692 }
693 _ => None,
694 },
695 _ => None,
696 };
697 value.unwrap_or(Value::Null)
698}
699
700/// An empty run of data of the right layout for a type.
701fn empty_data_for(ty: &LogicalType) -> Result<Data> {
702 use rudb_common::PhysicalType as P;
703 macro_rules! empties {
704 ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
705 match ty.physical() {
706 P::Empty => Data::Empty,
707 $(P::$variant => Data::$variant(Buffer::new()),)+
708 P::Varlen => Data::Varlen(StringColumn::new()),
709 other => {
710 return Err(Error::not_implemented(format!(
711 "a flat vector of {other:?} data, which arrives with the storage layer"
712 )));
713 }
714 }
715 };
716 }
717 Ok(crate::for_each_layout!(fixed, empties))
718}
719
720/// Appends one value to a run of data, or a zero of the right shape when it is null.
721///
722/// The zero matters. A null still occupies a position, the validity mask is what says it is null,
723/// and a run of data with a hole in it would put every value after the hole in the wrong place.
724fn push_value(data: &mut Data, value: &Value) -> Result<()> {
725 macro_rules! push {
726 ($vec:expr, $variant:path, $zero:expr) => {
727 match value {
728 Value::Null => $vec.push($zero),
729 $variant(x) => $vec.push(*x),
730 other => {
731 return Err(Error::internal(format!(
732 "{other:?} does not belong in this vector"
733 )));
734 }
735 }
736 };
737 }
738 // A decimal is stored as its unscaled integer in whatever width its precision needs, which
739 // `LogicalType::physical` decides and which is why the same `Value::Decimal` is at home in four
740 // different runs. The narrowing cannot fail for a value the binder produced, because the width
741 // that chose the run is the width in the value, but it is checked rather than assumed because
742 // an unchecked cast here would silently store a different number.
743 macro_rules! decimal {
744 ($vec:expr, $ty:ty, $unscaled:expr) => {
745 match <$ty>::try_from(*$unscaled) {
746 Ok(x) => $vec.push(x),
747 Err(_) => {
748 return Err(Error::internal(format!(
749 "an unscaled decimal of {} does not fit the run its precision chose",
750 $unscaled
751 )));
752 }
753 }
754 };
755 }
756 match data {
757 Data::Empty => {}
758 Data::Bool(v) => push!(v, Value::Boolean, false),
759 Data::Int8(v) => push!(v, Value::TinyInt, 0),
760 Data::Int16(v) => match value {
761 Value::Null => v.push(0),
762 Value::SmallInt(x) => v.push(*x),
763 Value::Decimal { unscaled, .. } => decimal!(v, i16, unscaled),
764 other => return Err(Error::internal(format!("{other:?} is not a 16 bit value"))),
765 },
766 Data::Int32(v) => match value {
767 Value::Null => v.push(0),
768 Value::Integer(x) | Value::Date(x) => v.push(*x),
769 Value::Decimal { unscaled, .. } => decimal!(v, i32, unscaled),
770 other => return Err(Error::internal(format!("{other:?} is not a 32 bit value"))),
771 },
772 Data::Int64(v) => match value {
773 Value::Null => v.push(0),
774 Value::BigInt(x) | Value::Time(x) | Value::Timestamp(x) => v.push(*x),
775 Value::Decimal { unscaled, .. } => decimal!(v, i64, unscaled),
776 other => return Err(Error::internal(format!("{other:?} is not a 64 bit value"))),
777 },
778 Data::Int128(v) => match value {
779 Value::Null => v.push(0),
780 Value::HugeInt(x) => v.push(*x),
781 Value::Decimal { unscaled, .. } => v.push(*unscaled),
782 other => return Err(Error::internal(format!("{other:?} is not a 128 bit value"))),
783 },
784 Data::UInt8(v) => push!(v, Value::UTinyInt, 0),
785 Data::UInt16(v) => push!(v, Value::USmallInt, 0),
786 Data::UInt32(v) => push!(v, Value::UInteger, 0),
787 Data::UInt64(v) => push!(v, Value::UBigInt, 0),
788 Data::UInt128(v) => push!(v, Value::UHugeInt, 0),
789 Data::Float32(v) => push!(v, Value::Float, 0.0),
790 Data::Float64(v) => push!(v, Value::Double, 0.0),
791 Data::Interval(v) => match value {
792 Value::Null => v.push((0, 0, 0)),
793 Value::Interval { months, days, micros } => v.push((*months, *days, *micros)),
794 other => return Err(Error::internal(format!("{other:?} is not an interval"))),
795 },
796 Data::Varlen(column) => match value {
797 Value::Null => {
798 column.push("");
799 }
800 Value::Varchar(text) => {
801 column.push(text);
802 }
803 // A blob is bytes and this column is text, so the only blobs that survive a round trip
804 // here are the ones that happen to be valid UTF-8. Real blob storage is a byte column
805 // and it arrives with the storage layer at M2 rather than being faked now.
806 Value::Blob(bytes) => match std::str::from_utf8(bytes) {
807 Ok(text) => {
808 column.push(text);
809 }
810 Err(_) => {
811 return Err(Error::not_implemented(
812 "a blob that is not valid UTF-8, which needs the byte column from M2",
813 ));
814 }
815 },
816 other => return Err(Error::internal(format!("{other:?} is not a string"))),
817 },
818 }
819 Ok(())
820}
821
822#[cfg(test)]
823mod tests {
824 use rudb_common::{LogicalType, Value};
825
826 use super::{Data, Form, VECTOR_SIZE, Vector};
827 use crate::string::StringColumn;
828 use crate::validity::Validity;
829
830 fn integers(values: &[i32]) -> Vector {
831 Vector::flat(LogicalType::Integer, Data::Int32(values.to_vec().into())).unwrap()
832 }
833
834 #[test]
835 fn the_vector_size_is_the_one_the_design_is_built_around() {
836 // 1024 and not DuckDB's 2048. A validity mask is 16 u64 words and a vector of string views
837 // is 16 KiB, both of which are consequences of this number rather than coincidences.
838 assert_eq!(VECTOR_SIZE, 1024);
839 assert_eq!(VECTOR_SIZE / 64, 16);
840 }
841
842 #[test]
843 fn a_flat_vector_reads_back_what_was_put_in_it() {
844 let vector = integers(&[1, 2, 3]);
845 assert_eq!(vector.form(), Form::Flat);
846 assert_eq!(vector.len(), 3);
847 assert_eq!(vector.value_at(1), Value::Integer(2));
848 assert_eq!(
849 vector.iter().collect::<Vec<_>>(),
850 vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)]
851 );
852 }
853
854 #[test]
855 fn a_vector_built_from_values_reads_the_same_values_back() {
856 let vector = Vector::from_values(
857 LogicalType::Varchar,
858 &[
859 Value::Varchar("a".to_string()),
860 Value::Null,
861 Value::Varchar("a string too long to sit inside a view".to_string()),
862 ],
863 )
864 .expect("strings and a null");
865 assert_eq!(vector.len(), 3);
866 assert_eq!(vector.value_at(0), Value::Varchar("a".to_string()));
867 assert_eq!(vector.value_at(1), Value::Null);
868 assert_eq!(
869 vector.value_at(2),
870 Value::Varchar("a string too long to sit inside a view".to_string())
871 );
872 }
873
874 /// A null still occupies a position. If it did not then every value after it would read back
875 /// one place to the left, which is the kind of bug that looks like a storage bug for a week.
876 #[test]
877 fn a_null_in_the_middle_does_not_move_the_values_after_it() {
878 let vector = Vector::from_values(
879 LogicalType::Integer,
880 &[Value::Integer(1), Value::Null, Value::Integer(3)],
881 )
882 .expect("integers and a null");
883 assert_eq!(vector.value_at(2), Value::Integer(3));
884 assert!(vector.validity().has_nulls(3), "the middle one is null");
885 }
886
887 #[test]
888 fn a_value_the_type_cannot_hold_is_refused() {
889 let wrong = Vector::from_values(LogicalType::Integer, &[Value::Varchar("x".to_string())]);
890 assert!(wrong.is_err(), "a string is not an integer");
891 }
892
893 #[test]
894 fn a_type_that_does_not_match_its_layout_is_refused_at_construction() {
895 // One comparison here against a wrong answer read out three layers later.
896 let wrong = Vector::flat(LogicalType::Varchar, Data::Int32(vec![1].into()));
897 assert!(wrong.is_err());
898 let right = Vector::flat(LogicalType::Date, Data::Int32(vec![1].into()));
899 assert!(right.is_ok(), "a date is stored in an i32 and that has to be allowed");
900 }
901
902 #[test]
903 fn a_constant_vector_costs_one_value_whatever_its_length() {
904 let vector = Vector::constant(LogicalType::Integer, Value::Integer(7), VECTOR_SIZE);
905 assert_eq!(vector.form(), Form::Constant);
906 assert_eq!(vector.len(), VECTOR_SIZE);
907 assert_eq!(vector.value_at(0), Value::Integer(7));
908 assert_eq!(vector.value_at(VECTOR_SIZE - 1), Value::Integer(7));
909 assert_eq!(vector.value_at(VECTOR_SIZE), Value::Null, "past the end is null, not a panic");
910 }
911
912 #[test]
913 fn a_constant_null_is_all_invalid_without_being_told() {
914 let vector = Vector::constant(LogicalType::Integer, Value::Null, 8);
915 assert_eq!(vector.validity(), &Validity::AllInvalid);
916 assert_eq!(vector.value_at(3), Value::Null);
917 }
918
919 #[test]
920 fn a_sequence_vector_is_sixteen_bytes_of_row_identifiers() {
921 let vector = Vector::sequence(100, 1, VECTOR_SIZE);
922 assert_eq!(vector.form(), Form::Sequence);
923 assert_eq!(vector.value_at(0), Value::BigInt(100));
924 assert_eq!(vector.value_at(923), Value::BigInt(1023));
925 let stepped = Vector::sequence(0, 5, 4);
926 assert_eq!(
927 stepped.iter().collect::<Vec<_>>(),
928 vec![Value::BigInt(0), Value::BigInt(5), Value::BigInt(10), Value::BigInt(15)]
929 );
930 }
931
932 #[test]
933 fn a_dictionary_vector_reads_through_its_codes() {
934 let mut column = StringColumn::new();
935 column.push("red");
936 column.push("green");
937 let values = Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap();
938 let vector = Vector::dictionary(vec![0, 1, 1, 0], values).unwrap();
939 assert_eq!(vector.form(), Form::Dictionary);
940 assert_eq!(vector.logical_type(), &LogicalType::Varchar);
941 assert_eq!(vector.value_at(2), Value::Varchar("green".into()));
942 assert_eq!(vector.len(), 4);
943 }
944
945 #[test]
946 fn a_dictionary_code_past_the_end_is_refused() {
947 // The alternative is a silent read of the wrong value, which is the failure mode the
948 // entire M3 design has to be careful about.
949 let values = integers(&[1, 2]);
950 assert!(Vector::dictionary(vec![0, 2], values).is_err());
951 }
952
953 #[test]
954 fn every_form_flattens_to_the_same_values_it_reads_out() {
955 // This is the shape of the equivalence testing in spec/16-testing.md section 16.2, in
956 // miniature and long before there is an encoded kernel to point it at. A form that reads
957 // out one way and flattens another is the exact bug that testing exists to catch.
958 let mut column = StringColumn::new();
959 column.push("alpha");
960 column.push("beta");
961 let dictionary = Vector::dictionary(
962 vec![1, 0, 1],
963 Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
964 )
965 .unwrap();
966 let cases = [
967 Vector::constant(LogicalType::Integer, Value::Integer(3), 5),
968 Vector::sequence(7, -2, 5),
969 dictionary,
970 ];
971 for vector in cases {
972 let flat = vector.flatten().unwrap();
973 assert_eq!(flat.form(), Form::Flat);
974 assert_eq!(flat.len(), vector.len());
975 for index in 0..vector.len() {
976 assert_eq!(flat.value_at(index), vector.value_at(index), "at {index}");
977 }
978 }
979 }
980
981 #[test]
982 fn a_null_still_occupies_a_position_after_flattening() {
983 // The reason push_value writes a zero for a null rather than skipping it. A run of data
984 // with a hole in it puts every value after the hole in the wrong place, and the validity
985 // mask is what says the position is null.
986 let vector = Vector::sequence(0, 1, 4).with_validity(Validity::from_iter(4, |i| i != 1));
987 let flat = vector.flatten().unwrap();
988 assert_eq!(flat.value_at(0), Value::BigInt(0));
989 assert_eq!(flat.value_at(1), Value::Null);
990 assert_eq!(flat.value_at(2), Value::BigInt(2));
991 assert_eq!(flat.value_at(3), Value::BigInt(3));
992 }
993
994 /// A dictionary holds its nulls in the vector it points at, so its own validity is all valid
995 /// and reading that instead of the values turns a null into whatever zero means for the type.
996 /// A filter over a nullable column produces exactly this vector, so the bug reaches a result
997 /// set as `LEFT JOIN` padding that comes back as zeros.
998 #[test]
999 fn a_null_behind_a_dictionary_survives_flattening() {
1000 let values =
1001 Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
1002 let dictionary = Vector::dictionary(vec![1, 0, 1], values).unwrap();
1003 let flat = dictionary.flatten().unwrap();
1004 assert_eq!(flat.value_at(0), Value::Null);
1005 assert_eq!(flat.value_at(1), Value::Integer(3));
1006 assert_eq!(flat.value_at(2), Value::Null);
1007 }
1008
1009 /// The property that makes `gather` usable at all: it has to be the same function as reading the
1010 /// wanted positions one at a time, over every form, or compaction changes answers.
1011 #[test]
1012 fn gathering_reads_what_reading_one_position_at_a_time_reads() {
1013 let mut column = StringColumn::new();
1014 column.push("alpha");
1015 column.push("beta");
1016 column.push("gamma");
1017 let cases = [
1018 integers(&[10, 20, 30, 40]),
1019 integers(&[10, 20, 30, 40]).with_validity(Validity::from_iter(4, |i| i != 2)),
1020 Vector::constant(LogicalType::Integer, Value::Integer(9), 4),
1021 Vector::sequence(100, -7, 4),
1022 Vector::sequence(100, -7, 4).with_validity(Validity::from_iter(4, |i| i % 2 == 0)),
1023 Vector::dictionary(
1024 vec![2, 0, 1, 2],
1025 Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
1026 )
1027 .unwrap(),
1028 Vector::dictionary(
1029 vec![1, 0, 1, 0],
1030 Vector::from_values(LogicalType::Integer, &[Value::Integer(5), Value::Null])
1031 .unwrap(),
1032 )
1033 .unwrap(),
1034 ];
1035 let wanted = [3_u32, 0, 2, 2, 1];
1036 for vector in cases {
1037 let gathered = vector.gather(&wanted).unwrap();
1038 assert_eq!(gathered.len(), wanted.len());
1039 assert_eq!(gathered.logical_type(), vector.logical_type());
1040 for (slot, &index) in wanted.iter().enumerate() {
1041 assert_eq!(
1042 gathered.value_at(slot),
1043 vector.value_at(index as usize),
1044 "slot {slot} of {:?}",
1045 vector.form()
1046 );
1047 }
1048 }
1049 }
1050
1051 /// A gather past the end is not an error, because the selection that produced the indices is
1052 /// checked by its caller and the one thing that must not happen here is a read of the wrong
1053 /// value. An index nothing answers is null, which is what an outer join pad needs anyway.
1054 #[test]
1055 fn gathering_a_position_that_is_not_there_is_a_null_and_not_a_wrong_value() {
1056 let vector = integers(&[1, 2, 3]);
1057 let gathered = vector.gather(&[2, 9]).unwrap();
1058 assert_eq!(gathered.value_at(0), Value::Integer(3));
1059 assert_eq!(gathered.value_at(1), Value::Null);
1060 }
1061
1062 /// The vector with nothing in it at all, which is what an untyped `NULL` is stored as. Every
1063 /// position asked for is past its end, so the answer is nulls and the length has to be the
1064 /// length that was asked for rather than the length that was there.
1065 #[test]
1066 fn gathering_from_a_vector_of_no_values_is_that_many_nulls() {
1067 let vector = Vector::flat(LogicalType::Null, Data::Empty).unwrap();
1068 let gathered = vector.gather(&[0, 1, 2]).unwrap();
1069 assert_eq!(gathered.len(), 3);
1070 assert_eq!(gathered.value_at(0), Value::Null);
1071 assert_eq!(gathered.value_at(2), Value::Null);
1072 }
1073
1074 /// Every position holds the same value, so a gather with no hole in it has nothing to copy and
1075 /// the result is the constant again rather than a run of a thousand copies of it.
1076 #[test]
1077 fn gathering_a_constant_stays_a_constant() {
1078 let vector = Vector::constant(LogicalType::Integer, Value::Integer(4), 100);
1079 let gathered = vector.gather(&[7, 7, 99]).unwrap();
1080 assert_eq!(gathered.form(), Form::Constant);
1081 assert_eq!(gathered.len(), 3);
1082 assert_eq!(gathered.value_at(2), Value::Integer(4));
1083 }
1084
1085 /// A dictionary over a dictionary is what a second filter over an already filtered chunk builds,
1086 /// and the gather has to walk to the bottom of that chain rather than one step down it. The
1087 /// constructor composes the ordinary chain away, so the one built here is the kind it cannot,
1088 /// which is a level holding nulls of its own.
1089 #[test]
1090 fn gathering_walks_a_dictionary_over_a_dictionary_to_the_values() {
1091 let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9]))
1092 .unwrap()
1093 .with_validity(Validity::from_iter(3, |index| index != 2));
1094 let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
1095 let gathered = outer.gather(&[0, 1]).unwrap();
1096 assert_eq!(gathered.form(), Form::Flat);
1097 assert_eq!(gathered.value_at(0), Value::Integer(8));
1098 assert_eq!(gathered.value_at(1), Value::Null);
1099 }
1100
1101 /// Two filters over one chunk build a dictionary over a dictionary, four conjuncts pushed down
1102 /// separately build four levels of it, and every level is a dependent load on every later read
1103 /// of every row plus a code array that cannot be freed. Composing at construction is one pass
1104 /// over the codes the range check was walking anyway.
1105 #[test]
1106 fn a_dictionary_over_a_dictionary_is_composed_into_one_level() {
1107 let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9])).unwrap();
1108 let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
1109 let (codes, values) = outer.dictionary_parts().unwrap();
1110 assert_eq!(codes, [1, 0]);
1111 assert_eq!(values.form(), Form::Flat);
1112 assert_eq!(outer.value_at(0), Value::Integer(8));
1113 assert_eq!(outer.value_at(1), Value::Integer(7));
1114 }
1115
1116 /// The invariant stated as the thing it is there for, which is that the depth does not grow with
1117 /// the number of filters. Four levels stacked one at a time are one level at the end of it.
1118 #[test]
1119 fn stacking_dictionaries_does_not_make_them_deeper() {
1120 let mut vector = integers(&[10, 20, 30, 40]);
1121 for _ in 0..4 {
1122 vector = Vector::dictionary(vec![3, 2, 1, 0], vector).unwrap();
1123 }
1124 let (codes, values) = vector.dictionary_parts().unwrap();
1125 assert_eq!(values.form(), Form::Flat);
1126 assert_eq!(codes, [0, 1, 2, 3]);
1127 assert_eq!(
1128 vector.iter().collect::<Vec<_>>(),
1129 integers(&[10, 20, 30, 40]).iter().collect::<Vec<_>>()
1130 );
1131 }
1132
1133 /// Composing has to carry the nulls down with it. The values hold them, the codes point at them,
1134 /// and a composed code that lands on a null position is still a null.
1135 #[test]
1136 fn composing_a_dictionary_keeps_the_nulls_its_values_hold() {
1137 let values =
1138 Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
1139 let inner = Vector::dictionary(vec![1, 0, 1], values).unwrap();
1140 let outer = Vector::dictionary(vec![0, 1], inner).unwrap();
1141 assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Flat);
1142 assert_eq!(outer.value_at(0), Value::Null);
1143 assert_eq!(outer.value_at(1), Value::Integer(3));
1144 }
1145
1146 /// The one level composition cannot go past. A dictionary that was given a validity of its own is
1147 /// saying its nulls are at that level rather than in the values, and pointing the outer codes
1148 /// straight at the values would read through the holes instead of stopping at them.
1149 #[test]
1150 fn a_dictionary_holding_its_own_nulls_is_not_composed_past() {
1151 let inner = Vector::dictionary(vec![0, 1, 2], integers(&[1, 2, 3]))
1152 .unwrap()
1153 .with_validity(Validity::from_iter(3, |index| index != 1));
1154 let outer = Vector::dictionary(vec![1, 2, 0], inner).unwrap();
1155 assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Dictionary);
1156 assert_eq!(outer.value_at(0), Value::Null);
1157 assert_eq!(outer.value_at(1), Value::Integer(3));
1158 assert_eq!(outer.value_at(2), Value::Integer(1));
1159 }
1160
1161 #[test]
1162 fn flattening_a_flat_vector_is_the_same_vector() {
1163 let vector = integers(&[1, 2, 3]);
1164 assert_eq!(vector.flatten().unwrap(), vector);
1165 }
1166
1167 #[test]
1168 fn a_decimal_reads_its_width_and_scale_from_the_type_and_not_the_data() {
1169 let ty = LogicalType::decimal(9, 2).unwrap();
1170 let vector = Vector::flat(ty, Data::Int32(vec![1234].into())).unwrap();
1171 assert_eq!(vector.value_at(0), Value::Decimal { unscaled: 1234, width: 9, scale: 2 });
1172 assert_eq!(vector.value_at(0).to_string(), "12.34");
1173 }
1174
1175 #[test]
1176 fn a_decimal_writes_into_whichever_of_the_four_runs_its_precision_chose() {
1177 // The read path worked at every width and the write path only accepted the 128 bit run, so
1178 // `SELECT 2.5` produced a value nothing could store. All four widths round trip now.
1179 for (width, scale, unscaled) in
1180 [(4u8, 1u8, 25i128), (9, 2, 1234), (18, 3, 123_456), (38, 4, 1_234_567)]
1181 {
1182 let ty = LogicalType::decimal(width, scale).unwrap();
1183 let value = Value::Decimal { unscaled, width, scale };
1184 let vector = Vector::from_values(ty, &[value.clone(), Value::Null]).unwrap();
1185 assert_eq!(vector.value_at(0), value, "a decimal of width {width}");
1186 assert_eq!(vector.value_at(1), Value::Null, "a null decimal of width {width}");
1187 }
1188 }
1189
1190 #[test]
1191 fn a_decimal_too_wide_for_the_run_its_type_chose_is_an_error_and_not_a_wrong_number() {
1192 // Only reachable by hand, since a value's width is what picked the run. Truncating here
1193 // would store a different number and say nothing about it.
1194 let ty = LogicalType::decimal(4, 1).unwrap();
1195 let value = Value::Decimal { unscaled: 1_000_000, width: 4, scale: 1 };
1196 let error = Vector::from_values(ty, &[value]).unwrap_err();
1197 assert!(error.to_string().contains("does not fit"), "{error}");
1198 }
1199}