Skip to main content

rudb_vector/
assemble.rs

1//! One vector built out of pieces that each answer a different set of its rows.
2//!
3//! The inverse of [`Vector::gather`]. A gather says where each output row reads from, so it wants
4//! one source and a position per row. An assembly says where each input row writes to, so it takes
5//! several sources and a position per row of each of them, and the rows no source claims come out
6//! null.
7//!
8//! `CASE` is the shape that wants this. Each arm is evaluated over the rows no earlier arm claimed,
9//! which is a correctness rule rather than a performance one, since `CASE WHEN x <> 0 THEN 1 / x
10//! ELSE 0 END` divides by zero on the rows the arm excludes if the arm is evaluated for them. So the
11//! arms produce several short answers that have to end up interleaved in the order the rows arrived
12//! in, and interleaving them is what this is. A join assembling a payload out of a matched side and
13//! an unmatched side wants the same thing.
14//!
15//! # Why it is not a `Vec<Value>`
16//!
17//! Because that is a heap allocation per string and a drop per string afterwards, on top of the
18//! walk through the enum that a `Value` is. On the ClickBench query that groups by a `CASE` over
19//! `Referer`, building the answer that way was about a quarter of the whole query: a quarter of the
20//! instructions were in `value_at`, the `Value` drop glue, `malloc` and `free`, for an answer whose
21//! bytes were already sitting in a string arena and only needed to be pointed at.
22//!
23//! What happens instead is that the pieces are laid end to end into one run of data and the
24//! interleave is then a single gather over that run, which is a typed loop per physical layout and
25//! is the same loop [`Vector::gather`] already goes down. A string's bytes are copied once, into one
26//! arena that was sized before any of them moved.
27//!
28//! # The shape of the interface
29//!
30//! A builder rather than a function taking a slice of pieces, because the caller producing the
31//! pieces is usually borrowing scratch space to produce each one and cannot hold two of them at
32//! once. [`Assembly::place`] reads a piece and is done with it, so the borrow ends between arms and
33//! nothing has to be cloned to keep it alive.
34//!
35//! # The simpler thing next to it
36//!
37//! [`concat()`] is the case where the pieces arrive in order and claim every row, which is what a row
38//! group of a stored table is built out of. An assembly would answer it, and it would pay for a
39//! position per row and a flatten per piece to answer something that is a run of `memcpy`s, so it is
40//! its own function. What the two share is the typed append underneath both of them.
41
42use std::collections::HashMap;
43use std::ops::Range;
44use std::sync::Arc;
45
46use rudb_common::{Error, LogicalType, Result, Value};
47
48use crate::buffer::Buffer;
49use crate::string::{Arenas, StringView};
50use crate::validity::Validity;
51use crate::vector::{
52    Data, Form, NOWHERE, Vector, copy_of, data_for, empty_data_for, layout_of, placed_of,
53};
54
55/// A vector being built out of pieces, each landing at the positions it is given.
56///
57/// Build one with [`Assembly::new`], call [`Assembly::place`] once per piece, and finish it with
58/// [`Assembly::finish`]. A row no piece claims is null.
59#[derive(Debug)]
60pub struct Assembly {
61    ty: LogicalType,
62    rows: usize,
63    /// The pieces laid end to end, for a type that has a flat layout to lay them in.
64    data: Data,
65    /// Where each output row reads from in `data`, or [`NOWHERE`] for a row no piece claimed.
66    at: Vec<usize>,
67    /// Whether each output row holds a value rather than a null.
68    live: Vec<bool>,
69    /// The fallback for the nested types, which have no run of data to lay anything end to end in.
70    ///
71    /// A `LIST`, a `STRUCT` and a `MAP` are a child vector and a run of entries rather than a run of
72    /// values, so laying two of them end to end is not an append to one buffer and the copy loop
73    /// this is built around has nothing to walk. They go through values, which is what they did
74    /// before this existed and is not a regression for them. Nothing on a ClickBench or TPC-H path
75    /// reaches it.
76    values: Option<Vec<Value>>,
77}
78
79impl Assembly {
80    /// An assembly of `rows` rows of `ty`, with every row null until a piece claims it.
81    ///
82    /// # Errors
83    ///
84    /// If the type is one there is no flat layout for yet, which today means `ARRAY` and `UNION`.
85    pub fn new(ty: LogicalType, rows: usize) -> Result<Self> {
86        let nested =
87            matches!(ty, LogicalType::List(_) | LogicalType::Struct(_) | LogicalType::Map(_, _));
88        let values = if nested { Some(vec![Value::Null; rows]) } else { None };
89        let data = if nested { Data::Empty } else { empty_data_for(&ty)? };
90        Ok(Self { ty, rows, data, at: vec![NOWHERE; rows], live: vec![false; rows], values })
91    }
92
93    /// How many rows the finished vector will have.
94    #[must_use]
95    pub fn rows(&self) -> usize {
96        self.rows
97    }
98
99    /// Writes row `n` of `piece` at output row `positions[n]`, for every row of `piece`.
100    ///
101    /// A row claimed twice takes the value the later call gave it, which is not a case `CASE`
102    /// produces, since its arms run over disjoint sets of rows, and is defined rather than left
103    /// open so that a caller that does it gets an answer instead of whichever of the two the copy
104    /// loop happened to reach.
105    ///
106    /// # Errors
107    ///
108    /// If `piece` has a different number of rows than there are positions, if a position is past the
109    /// end of the assembly, or if `piece` is not of a layout that can be laid after what is already
110    /// there.
111    pub fn place(&mut self, positions: &[u32], piece: &Vector) -> Result<()> {
112        if positions.len() != piece.len() {
113            return Err(Error::internal(format!(
114                "a piece of {} rows placed at {} positions",
115                piece.len(),
116                positions.len()
117            )));
118        }
119        for &row in positions {
120            if row as usize >= self.rows {
121                return Err(Error::internal(format!(
122                    "row {row} placed in an assembly of {} rows",
123                    self.rows
124                )));
125            }
126        }
127        if let Some(values) = &mut self.values {
128            // row at a time: the nested fallback named on the field above. A `LIST`, a `STRUCT` and
129            // a `MAP` are a child vector and a run of entries rather than a run of values, so there
130            // is no buffer to lay one after another and no typed copy to do the interleave with.
131            for (slot, &row) in positions.iter().enumerate() {
132                values[row as usize] = piece.value_at(slot);
133            }
134            return Ok(());
135        }
136        // flatten: the copy loop that does the interleave reads a run of data, and a piece can
137        // arrive constant, dictionary encoded or bit packed. Flattening is itself a typed loop per
138        // layout, so writing the piece out once here is what stops it being read a value at a time
139        // later, and a piece that is already flat is not copied at all.
140        let flat = piece.flatten()?;
141        let Some(from) = flat.data() else {
142            return Err(Error::internal("a flattened vector with no run of data in it"));
143        };
144        let start = self.data.len();
145        let appended = extend(&mut self.data, from, &mut Arenas::default())?;
146        for (slot, &row) in positions.iter().enumerate() {
147            let row = row as usize;
148            // A piece whose data is empty is the untyped null, so it claims its rows and they are
149            // null, which is what leaving them at `NOWHERE` says.
150            if slot < appended {
151                self.at[row] = start + slot;
152                self.live[row] = !piece.is_null_at(slot);
153            } else {
154                self.at[row] = NOWHERE;
155                self.live[row] = false;
156            }
157        }
158        Ok(())
159    }
160
161    /// The finished vector.
162    ///
163    /// # Errors
164    ///
165    /// If the run of data that came out of the pieces is not one the type can hold.
166    pub fn finish(self) -> Result<Vector> {
167        if let Some(values) = self.values {
168            return Vector::from_values(self.ty, &values);
169        }
170        // An untyped null has no run of data to gather out of, and a gather over one would give a
171        // vector of no values calling itself `rows` long.
172        if matches!(self.data, Data::Empty) {
173            return Ok(Vector::constant(self.ty, Value::Null, self.rows));
174        }
175        let validity = Validity::from_run(&self.live);
176        // A string column is finished by permuting views rather than by copying bytes. Sixteen
177        // bytes a row move and the payload stays in the arena the pieces were appended into, which
178        // is the same trade [`crate::vector::Body::Views`] is for a page. It matters most where the
179        // permutation is the identity and the whole thing is a move, which is every column of a
180        // hash join's gathered side.
181        if let Data::Varlen(column) = self.data {
182            let (laid, arena) = column.into_parts();
183            let arena = Arc::new(arena);
184            if straight(&self.at) {
185                return Ok(Vector::string_views(self.ty, laid, arena)?.with_validity(validity));
186            }
187            let views = self
188                .at
189                .iter()
190                .map(|&index| laid.get(index).copied().unwrap_or_else(StringView::empty))
191                .collect();
192            return Ok(Vector::string_views(self.ty, views, arena)?.with_validity(validity));
193        }
194        // Row `n` reads position `n`, so the copy below would be a copy of the run onto itself. An
195        // assembly whose pieces arrived in order and claimed every row is exactly that, and laying
196        // the chunks of a join's gathered side end to end is exactly that.
197        if straight(&self.at) {
198            return Ok(Vector::flat(self.ty, self.data)?.with_validity(validity));
199        }
200        let gathered = copy_of(&self.data, &self.at);
201        Ok(Vector::flat(self.ty, gathered)?.with_validity(validity))
202    }
203}
204
205/// Several vectors of one type laid end to end as one page, or `None` for a run this will not lay.
206///
207/// What a row group of a stored table is built out of. The chunks arrive one at a time and each one
208/// is a separate allocation, and holding a hundred and twenty of them is a hundred and twenty places
209/// a scan of the column has to jump to instead of one run it walks. So they are copied once, into a
210/// page, and every chunk the table hands out afterwards is a window cut out of that page.
211///
212/// # What it will not lay
213///
214/// Anything that is neither a flat run nor a stable dictionary over the same shared values is
215/// answered with `None` rather than with an error, because a caller that gets one has somewhere to
216/// put the pieces and this is a choice about layout rather than a failure. Stable dictionary pieces
217/// sharing one value vector are the encoded exception: laying them is just appending their codes.
218/// An ordinary dictionary has no cross-piece code-space promise, and flattening it would make it
219/// larger and throw away the thing that made it useful, so it is still left to the caller.
220///
221/// # Strings
222///
223/// A flat varchar piece owns its arena, so a window cut out of a flat varchar page copies every byte
224/// of every long string in the window, which is the whole reason [`Form::StringView`] exists.
225/// So the varlen page comes back as views over one shared arena: the bytes are copied once
226/// here and never again, and a cut afterwards moves sixteen bytes a row the same way it does for a
227/// column of integers.
228///
229/// # Errors
230///
231/// If the type has no flat layout, or if a piece holds fewer values than it says it has rows.
232pub fn concat<V: AsRef<Vector>>(ty: &LogicalType, pieces: &[V]) -> Result<Option<Vector>> {
233    let pieces: Vec<&Vector> = pieces.iter().map(AsRef::as_ref).collect();
234    laid(ty, &pieces)
235}
236
237/// The body of [`concat()`], over borrowed pieces.
238///
239/// A caller holding its pieces inside chunks would otherwise clone each one into a list, and a
240/// clone of a flat vector that owns its values copies every one of them, which is the copy this
241/// function exists to make once.
242fn laid(ty: &LogicalType, pieces: &[&Vector]) -> Result<Option<Vector>> {
243    if pieces.is_empty() {
244        return Ok(None);
245    }
246    let rows = pieces.iter().map(|piece| piece.len()).sum();
247    let shared = pieces[0].stable_dictionary_parts().map(|(_, values)| values).filter(|values| {
248        pieces.iter().all(|piece| {
249            piece.logical_type() == ty
250                && !piece.is_empty()
251                && piece
252                    .stable_dictionary_parts()
253                    .is_some_and(|(_, held)| Arc::ptr_eq(held, values))
254        })
255    });
256    if let Some(values) = shared {
257        let mut codes = Vec::with_capacity(rows);
258        for piece in pieces {
259            if let Some((held, _)) = piece.stable_dictionary_parts() {
260                codes.extend_from_slice(held);
261            }
262        }
263        let validity = run_of(pieces, rows);
264        return Ok(Some(
265            Vector::stable_dictionary(codes, Arc::clone(values))?.with_validity(validity),
266        ));
267    }
268    // String views that all point into one arena, which is what a string column gathered out of a
269    // join's build side is, chunk after chunk. Laid end to end they are the same views over the same
270    // arena, so sixteen bytes a row move and no string is copied.
271    if let Some(arena) = pieces[0].shared_views().map(|(_, arena)| arena).filter(|arena| {
272        pieces.iter().all(|piece| {
273            piece.logical_type() == ty
274                && piece.shared_views().is_some_and(|(_, held)| Arc::ptr_eq(held, arena))
275        })
276    }) {
277        let mut views = Vec::with_capacity(rows);
278        for piece in pieces {
279            if let Some((held, _)) = piece.shared_views() {
280                views.extend_from_slice(held);
281            }
282        }
283        let validity = run_of(pieces, rows);
284        return Ok(Some(
285            Vector::string_views(ty.clone(), views, Arc::clone(arena))?.with_validity(validity),
286        ));
287    }
288    // Checked before anything is copied, because the fallback is for the caller to keep the pieces
289    // it already has and a half built page would be work thrown away.
290    let laid = pieces
291        .iter()
292        .all(|piece| piece.form() == Form::Flat && piece.logical_type() == ty && !piece.is_empty());
293    if !laid {
294        return Ok(None);
295    }
296    if let Some(data) = adjoined(pieces) {
297        let validity = run_of(pieces, rows);
298        return Ok(Some(Vector::flat(ty.clone(), data)?.with_validity(validity)));
299    }
300    // Sized before the first value moves, so the page is one allocation and holds no more than the
301    // rows that went into it. Growing from empty instead ends at the next power of two, which on a
302    // full row group is eight thousand values of slack carried for the life of the table.
303    let mut data = data_for(ty, rows)?;
304    let mut arenas = arenas_of(pieces);
305    for piece in pieces {
306        let from = piece
307            .data()
308            .ok_or_else(|| Error::internal("a flat vector with no run of data in it"))?;
309        let appended = extend(&mut data, from, &mut arenas)?;
310        if appended != piece.len() {
311            return Err(Error::internal(format!(
312                "a piece of {} rows laid {appended} values end to end",
313                piece.len()
314            )));
315        }
316    }
317    let validity = run_of(pieces, rows);
318    if let Data::Varlen(column) = data {
319        let (views, arena) = column.into_parts();
320        let page = Vector::string_views(ty.clone(), views, Arc::new(arena))?;
321        return Ok(Some(page.with_validity(validity)));
322    }
323    Ok(Some(Vector::flat(ty.clone(), data)?.with_validity(validity).into_pages()))
324}
325
326/// Several vectors of one type laid end to end and then read back in `order`.
327///
328/// What a sort is. Row `n` of the answer is row `order[n]` of the pieces laid end to end, so the
329/// pieces are laid once and the answer is one gather, which writes the answer front to back. An
330/// [`Assembly`] answers the same question the other way round, with a position per input row that
331/// it scatters into, and that costs it a map of the whole column and a scatter per column, which for
332/// a sort is the same permutation worked out again for every column. Here the caller works it out
333/// once and every column reads it.
334///
335/// A string column comes back as views over the one arena its bytes were laid into, the same as
336/// [`concat()`] gives, so the gather moves sixteen bytes a row.
337///
338/// # Errors
339///
340/// If the type has no layout this can lay, if a piece holds fewer values than it has rows, or if
341/// an entry of `order` is past the end of the pieces.
342pub fn interleave(ty: &LogicalType, pieces: &[Vector], order: &[usize]) -> Result<Vector> {
343    interleave_placed(ty, pieces, order, None)
344}
345
346/// The same as [`interleave()`], writing each row where it goes rather than reading each row from
347/// where it came when the caller also has `inverse`, the place in the answer of every row laid.
348///
349/// Which of the two is cheaper is decided by how many ascending runs `order` is made of, and only
350/// the caller knows that without a pass of its own. Reading through `order` jumps between the runs,
351/// so when there are few of them each row of the answer costs a cache line of the laid column to
352/// use eight bytes of it, and with a dozen columns on a dozen threads that is the memory bus full.
353/// Writing through `inverse` reads the laid column front to back and writes one stream per run,
354/// each of them front to back too. On SF1 `lineitem` sorted by ship month, 84 runs, twelve columns
355/// on twelve threads went from 145ms to 50ms in a standalone test of just these loops. With runs
356/// in the tens of thousands the two come out even and with a run every few rows the writes are the
357/// ones that miss, so a caller with an order like that passes `None`.
358///
359/// This is not the scatter #1365 took out. That one built a map of the whole column per column to
360/// scatter into. This one writes into the answer's own run and the inverse is worked out once.
361///
362/// # Errors
363///
364/// As [`interleave()`], or if `inverse` is given and `order` and `inverse` are not both as long as
365/// the pieces, which is the only shape in which one can be the other turned round.
366pub fn interleave_placed(
367    ty: &LogicalType,
368    pieces: &[Vector],
369    order: &[usize],
370    inverse: Option<&[u32]>,
371) -> Result<Vector> {
372    let rows: usize = pieces.iter().map(Vector::len).sum();
373    if let Some(inverse) = inverse.filter(|inverse| inverse.len() != rows || order.len() != rows) {
374        return Err(Error::internal(format!(
375            "{} places and {} positions for a permutation of {rows} rows",
376            inverse.len(),
377            order.len()
378        )));
379    }
380    if let Some(&past) = order.iter().find(|&&index| index >= rows) {
381        return Err(Error::internal(format!("row {past} read out of pieces of {rows} rows")));
382    }
383    if matches!(ty, LogicalType::List(_) | LogicalType::Struct(_) | LogicalType::Map(_, _)) {
384        // row at a time: the nested types, for the reason `Assembly::values` gives. They have no run
385        // of data to lay end to end and no typed copy to gather with.
386        let laid: Vec<Value> = pieces
387            .iter()
388            .flat_map(|piece| (0..piece.len()).map(|row| piece.value_at(row)))
389            .collect();
390        let values: Vec<Value> =
391            order.iter().map(|&index| laid.get(index).cloned().unwrap_or(Value::Null)).collect();
392        return Vector::from_values(ty.clone(), &values);
393    }
394    if let Some(merged) = merged_dictionary(ty, pieces, order, inverse)? {
395        return Ok(merged);
396    }
397    if let Some(inverse) = inverse {
398        if let Some(placed) = placed_strings(ty, pieces, inverse, 0..rows)? {
399            return Ok(placed);
400        }
401        if let Some(placed) = placed_fixed(ty, pieces, inverse)? {
402            return Ok(placed);
403        }
404    }
405    let mut data = data_for(ty, rows)?;
406    // The untyped null, which has no run of data to lay or to gather out of, and is null whatever
407    // the order is.
408    if matches!(data, Data::Empty) {
409        return Ok(Vector::constant(ty.clone(), Value::Null, order.len()));
410    }
411    let mut arenas = arenas_of(pieces);
412    // Reserved whole, because an arena grown by doubling as the pieces arrive copies what it holds
413    // at every step and faults each new allocation in again. The sorted SF1 comments lay 183MB.
414    if let Data::Varlen(column) = &mut data {
415        column.reserve_bytes(arenas.bytes());
416    }
417    // Each piece's validity, taken after it is flattened, because a constant null keeps its null in
418    // its value rather than in its mask and a flattened one has it in the mask like any other row.
419    let mut masks = Vec::with_capacity(pieces.len());
420    for piece in pieces {
421        // flatten: the gather below reads one run of data, and a piece can arrive dictionary
422        // encoded, constant or bit packed. The flatten is a typed loop per layout, a flat piece is
423        // not copied by it, and one piece is flattened at a time so a column is never held twice.
424        let flat = piece.flatten()?;
425        let from = flat.data().ok_or_else(|| Error::internal("a flattened vector with no data"))?;
426        let appended = extend(&mut data, from, &mut arenas)?;
427        if appended != piece.len() {
428            return Err(Error::internal(format!(
429                "a piece of {} rows laid {appended} values end to end",
430                piece.len()
431            )));
432        }
433        masks.push((flat.len(), flat.validity().clone()));
434    }
435    let laid = if masks.iter().all(|(_, mask)| matches!(mask, Validity::AllValid)) {
436        Validity::AllValid
437    } else {
438        let mut live = Vec::with_capacity(rows);
439        for (len, mask) in &masks {
440            // row at a time: a bit a row for the mixed case, once a column rather than once a
441            // piece of every column the way it would be read otherwise.
442            live.extend((0..*len).map(|row| mask.is_valid(row)));
443        }
444        Validity::from_run(&live)
445    };
446    if laid.count_valid(rows) == 0 {
447        return Ok(Vector::constant(ty.clone(), Value::Null, order.len()));
448    }
449    let validity = match (laid, inverse) {
450        (Validity::AllValid, _) => Validity::AllValid,
451        (laid, Some(inverse)) => {
452            let mut live = vec![false; order.len()];
453            for (row, &to) in inverse.iter().enumerate() {
454                if let Some(slot) = live.get_mut(to as usize) {
455                    *slot = laid.is_valid(row);
456                }
457            }
458            Validity::from_run(&live)
459        }
460        (laid, None) => Validity::from_iter(order.len(), |row| {
461            order.get(row).is_some_and(|&index| laid.is_valid(index))
462        }),
463    };
464    if let Data::Varlen(column) = data {
465        let (views, arena) = column.into_parts();
466        let gathered = match inverse {
467            Some(inverse) => {
468                let mut placed = vec![StringView::empty(); order.len()];
469                for (view, &to) in views.iter().zip(inverse) {
470                    if let Some(slot) = placed.get_mut(to as usize) {
471                        *slot = *view;
472                    }
473                }
474                placed
475            }
476            None => order
477                .iter()
478                .map(|&index| views.get(index).copied().unwrap_or_else(StringView::empty))
479                .collect(),
480        };
481        return Ok(
482            Vector::string_views(ty.clone(), gathered, Arc::new(arena))?.with_validity(validity)
483        );
484    }
485    let data = match inverse {
486        Some(inverse) => placed_of(&data, inverse),
487        None => copy_of(&data, order),
488    };
489    Ok(Vector::flat(ty.clone(), data)?.with_validity(validity))
490}
491
492/// A fixed width column written through `inverse` straight from its pieces, or `None` for a type
493/// whose layout is not fixed width.
494///
495/// The general path lays every piece end to end first and then writes that run through `inverse`,
496/// and it flattens every piece that is not flat on the way. On the sorted SF1 `lineitem` those were
497/// three passes over every column: the flatten was 7 percent of the busy samples, mostly decoding
498/// the dictionary pieces the Parquet reader hands on, the laying was 5 percent more, and the write
499/// through `inverse` was 6.5. Here each piece is written to its places as it is read, and a
500/// dictionary piece whose values are a flat run with no nulls is written by looking each code up,
501/// so a column is read once and written once. A piece in any other form is flattened on its own and
502/// then written the same way.
503fn placed_fixed(ty: &LogicalType, pieces: &[Vector], inverse: &[u32]) -> Result<Option<Vector>> {
504    let rows = inverse.len();
505    let mut live: Option<Vec<bool>> = None;
506    let mut base = 0;
507    // The nulls of one piece written to their places, once a piece with any has arrived.
508    let mut mark = |mask: &Validity, places: &[u32]| {
509        if matches!(mask, Validity::AllValid) {
510            return;
511        }
512        let live = live.get_or_insert_with(|| vec![true; rows]);
513        for (row, &to) in places.iter().enumerate() {
514            if let Some(slot) = live.get_mut(to as usize) {
515                *slot = mask.is_valid(row);
516            }
517        }
518    };
519    macro_rules! placed {
520        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
521            match data_for(ty, 0)? {
522                $(Data::$variant(_) => {
523                    let mut out: Vec<$native> = vec![$zero; rows];
524                    for piece in pieces {
525                        let len = piece.len();
526                        let places = inverse.get(base..base + len).ok_or_else(|| {
527                            Error::internal("pieces longer than the places they are written to")
528                        })?;
529                        base += len;
530                        let coded = piece.dictionary_parts().and_then(|(codes, values)| {
531                            match (values.form(), values.validity(), values.data()) {
532                                (Form::Flat, Validity::AllValid, Some(Data::$variant(held))) => {
533                                    Some((codes, held.as_slice()))
534                                }
535                                _ => None,
536                            }
537                        });
538                        if let Some((codes, held)) = coded {
539                            for (&code, &to) in codes.iter().zip(places) {
540                                if let (Some(slot), Some(value)) =
541                                    (out.get_mut(to as usize), held.get(code as usize))
542                                {
543                                    *slot = *value;
544                                }
545                            }
546                            mark(piece.validity(), places);
547                            continue;
548                        }
549                        // A flat piece is read where it lies.
550                        let flat;
551                        let piece = if piece.form() == Form::Flat {
552                            piece
553                        } else {
554                            // flatten: a piece that is neither flat nor a dictionary over a flat
555                            // run, which a sort's input rarely is.
556                            flat = piece.flatten()?;
557                            &flat
558                        };
559                        let Some(Data::$variant(values)) = piece.data() else {
560                            return Err(Error::internal(format!(
561                                "a piece of {} laid into a column of {ty}",
562                                piece.logical_type()
563                            )));
564                        };
565                        if values.len() != len {
566                            return Err(Error::internal(format!(
567                                "a piece of {len} rows holds {} values",
568                                values.len()
569                            )));
570                        }
571                        for (value, &to) in values.iter().zip(places) {
572                            if let Some(slot) = out.get_mut(to as usize) {
573                                *slot = *value;
574                            }
575                        }
576                        mark(piece.validity(), places);
577                    }
578                    let validity = match live {
579                        None => Validity::AllValid,
580                        Some(live) if !live.contains(&true) => {
581                            return Ok(Some(Vector::constant(ty.clone(), Value::Null, rows)));
582                        }
583                        Some(live) => Validity::from_run(&live),
584                    };
585                    let data = Data::$variant(Buffer::from_vec(out));
586                    Ok(Some(Vector::flat(ty.clone(), data)?.with_validity(validity)))
587                })+
588                _ => Ok(None),
589            }
590        };
591    }
592    crate::for_each_layout!(fixed, placed)
593}
594
595/// The string column written through `inverse` into an arena laid in the order of the result.
596///
597/// Laying the pieces' arenas end to end and pushing the views keeps the bytes in the order they
598/// arrived, so everything after the sort that reads the strings in their new order reads the arena
599/// at random. On the sorted `lineitem` that is `l_comment`, whose distinct count in the append
600/// took 550 to 1570 ms of CPU across the threads at full width, nearly all of it waiting on memory,
601/// and takes 190 to 250 ms with the arena in order. A table built by the sort also keeps its strings
602/// in the order it is read in from then on. Here each piece is read once in order, every long
603/// string's length is written to its place first so a prefix sum gives each one its offset, and
604/// then its bytes are copied there. A sort's output is a few long runs of its input, so both
605/// passes write a few streams that each move forward.
606///
607/// `None` when the column is not a string, or when a piece is not flat views and has to be
608/// flattened on the general path first.
609fn placed_strings(
610    ty: &LogicalType,
611    pieces: &[Vector],
612    inverse: &[u32],
613    range: Range<usize>,
614) -> Result<Option<Vector>> {
615    if !strings_placeable(ty, pieces) {
616        return Ok(None);
617    }
618    let first = range.start;
619    let rows = range.len();
620    // The place of a row in this range, or `None` for a row another range lays.
621    let local = |to: u32| (to as usize).checked_sub(first).filter(|&at| at < rows);
622    let mut offsets = vec![0u64; rows + 1];
623    let mut places = inverse.iter();
624    for piece in pieces {
625        let (views, _) = piece.text_parts().unwrap_or_default();
626        for (view, &to) in views.iter().zip(places.by_ref()) {
627            if view.is_inline() {
628                continue;
629            }
630            if let Some(slot) = local(to).and_then(|at| offsets.get_mut(at + 1)) {
631                *slot = view.len() as u64;
632            }
633        }
634    }
635    let mut total = 0;
636    for offset in &mut offsets {
637        total += *offset;
638        *offset = total;
639    }
640    let mut arena =
641        vec![0u8; usize::try_from(total).map_err(|_| Error::internal("an arena too large"))?];
642    let mut placed = vec![StringView::empty(); rows];
643    let mut live = vec![true; rows];
644    let mut places = inverse.iter();
645    for piece in pieces {
646        let (views, from) = piece.text_parts().unwrap_or_default();
647        let validity = piece.validity();
648        for (row, (view, &to)) in views.iter().zip(places.by_ref()).enumerate() {
649            let Some(to) = local(to) else {
650                continue;
651            };
652            if !validity.is_valid(row) {
653                if let Some(slot) = live.get_mut(to) {
654                    *slot = false;
655                }
656                continue;
657            }
658            let (Some(bytes), Some(&at), Some(slot)) =
659                (view.bytes_in(from), offsets.get(to), placed.get_mut(to))
660            else {
661                continue;
662            };
663            if view.is_inline() {
664                *slot = *view;
665                continue;
666            }
667            if let Some(into) = arena.get_mut(at as usize..at as usize + bytes.len()) {
668                into.copy_from_slice(bytes);
669            }
670            *slot = StringView::over(bytes, at);
671        }
672    }
673    let validity = if live.iter().all(|&valid| valid) {
674        Validity::AllValid
675    } else {
676        Validity::from_run(&live)
677    };
678    let vector = Vector::string_views(ty.clone(), placed, Arc::new(Buffer::from_vec(arena)))?;
679    Ok(Some(vector.with_validity(validity)))
680}
681
682/// Whether [`interleave_placed`] lays this string column through [`placed_string_rows`], which is
683/// when it is a string and every piece is flat views.
684#[must_use]
685pub fn strings_placeable(ty: &LogicalType, pieces: &[Vector]) -> bool {
686    matches!(ty, LogicalType::Varchar | LogicalType::Blob)
687        && pieces.iter().all(|piece| piece.text_parts().is_some())
688}
689
690/// The rows in `range` of the string column [`interleave_placed`] would lay through `inverse`,
691/// with an arena of their own.
692///
693/// This is how a sort builds one long string column on several threads. Each range reads every
694/// piece and all of `inverse` and copies only its own strings, so each has a few forward streams
695/// to write the way the whole column does, and the ranges share nothing they write.
696///
697/// # Errors
698///
699/// If `inverse` is not as long as the pieces or `range` runs past it, or if a piece is not flat
700/// views, which [`strings_placeable`] says beforehand.
701pub fn placed_string_rows(
702    ty: &LogicalType,
703    pieces: &[Vector],
704    inverse: &[u32],
705    range: Range<usize>,
706) -> Result<Vector> {
707    let rows: usize = pieces.iter().map(Vector::len).sum();
708    if inverse.len() != rows || range.end > rows || range.start > range.end {
709        return Err(Error::internal(format!(
710            "rows {range:?} of {} places for {rows} rows",
711            inverse.len()
712        )));
713    }
714    placed_strings(ty, pieces, inverse, range)?
715        .ok_or_else(|| Error::internal("a string column placed that is not flat views"))
716}
717
718/// How many rows a merged dictionary entry has to stand for on average before a string column is
719/// gathered as codes rather than as views.
720///
721/// A Parquet file carries one dictionary per row group, so a sorted `lineitem` column arrives as
722/// 733 pieces over 49 dictionaries. The low cardinality columns have 98 to 343 entries between all
723/// of them, and merging those is nothing next to gathering six million views. A column whose
724/// dictionaries are nearly as long as the column is one the writer should not have encoded, and
725/// merging it would hash every value to save nothing, so it is gathered flat.
726const ROWS_PER_MERGED_ENTRY: usize = 8;
727
728/// The string column gathered by `order` as one dictionary, when every piece is a dictionary.
729///
730/// The pieces' dictionaries are merged into one with each distinct value once, so the codes mean
731/// the same thing on every page cut from the result and the result is a stable dictionary. The
732/// gather is then four bytes a row instead of sixteen, and the append after the sort gets the
733/// dictionary the scan handed up rather than a flat column it has to read a row at a time.
734///
735/// `None` when the column is not a string, when any piece is not a dictionary or has nulls at its
736/// own level, or when the dictionaries are too long for the merge to pay.
737fn merged_dictionary(
738    ty: &LogicalType,
739    pieces: &[Vector],
740    order: &[usize],
741    inverse: Option<&[u32]>,
742) -> Result<Option<Vector>> {
743    if !matches!(ty, LogicalType::Varchar | LogicalType::Blob) || pieces.is_empty() {
744        return Ok(None);
745    }
746    let rows: usize = pieces.iter().map(Vector::len).sum();
747    let mut dictionaries: Vec<&Arc<Vector>> = Vec::new();
748    let mut which = Vec::with_capacity(pieces.len());
749    let mut entries = 0;
750    for piece in pieces {
751        let Some((_, values)) = piece.shared_dictionary_parts() else {
752            return Ok(None);
753        };
754        if !matches!(piece.validity(), Validity::AllValid) {
755            return Ok(None);
756        }
757        let at = match dictionaries.iter().position(|seen| Arc::ptr_eq(seen, values)) {
758            Some(at) => at,
759            None => {
760                entries += values.len();
761                if entries.saturating_mul(ROWS_PER_MERGED_ENTRY) > rows {
762                    return Ok(None);
763                }
764                dictionaries.push(values);
765                dictionaries.len() - 1
766            }
767        };
768        which.push(at);
769    }
770    // A null entry has no bytes, so it merges with every other null entry.
771    let mut merged: HashMap<Option<&[u8]>, u32> = HashMap::new();
772    let mut values = Vec::new();
773    let mut remaps = Vec::with_capacity(dictionaries.len());
774    for dictionary in &dictionaries {
775        let mut remap = Vec::with_capacity(dictionary.len());
776        // row at a time: over the dictionary entries, a few hundred of them against millions of
777        // rows, and only the first sighting of each value becomes one.
778        for entry in 0..dictionary.len() {
779            let next = u32::try_from(values.len())
780                .map_err(|_| Error::internal("a merged dictionary past four billion entries"))?;
781            let code = *merged.entry(dictionary.bytes_at(entry)).or_insert_with(|| {
782                values.push(dictionary.value_at(entry));
783                next
784            });
785            remap.push(code);
786        }
787        remaps.push(remap);
788    }
789    let mut laid = Vec::with_capacity(rows);
790    for (piece, &at) in pieces.iter().zip(&which) {
791        let (codes, _) = piece
792            .dictionary_parts()
793            .ok_or_else(|| Error::internal("a dictionary piece lost its dictionary"))?;
794        let remap = &remaps[at];
795        laid.extend(codes.iter().map(|&code| remap[code as usize]));
796    }
797    // Written through the inverse when there is one, for the reason `interleave_placed` gives: a
798    // few long runs read through `order` spend a cache line on every four byte code.
799    let codes = match inverse {
800        Some(inverse) => {
801            let mut codes = vec![0u32; order.len()];
802            for (&code, &to) in laid.iter().zip(inverse) {
803                if let Some(slot) = codes.get_mut(to as usize) {
804                    *slot = code;
805                }
806            }
807            codes
808        }
809        None => order.iter().map(|&index| laid[index]).collect(),
810    };
811    let values = Vector::from_values(ty.clone(), &values)?;
812    Ok(Some(Vector::stable_dictionary(codes, Arc::new(values))?))
813}
814
815/// The validity of the pieces laid end to end, in `rows` rows.
816///
817/// The two cheap answers are checked for first because they are the answers real data gives. A
818/// column that was never null anywhere is a page with no mask on it at all, and a bit per row read
819/// out of every piece to build a mask that is all ones would be throwing that away.
820fn run_of(pieces: &[&Vector], rows: usize) -> Validity {
821    if pieces.iter().all(|piece| matches!(piece.validity(), Validity::AllValid)) {
822        return Validity::AllValid;
823    }
824    if pieces.iter().all(|piece| matches!(piece.validity(), Validity::AllInvalid)) {
825        return Validity::AllInvalid;
826    }
827    let mut live = Vec::with_capacity(rows);
828    for piece in pieces {
829        // row at a time: the mixed case, which is a bit per row however it is written, and it runs
830        // once per column per row group rather than once per chunk.
831        for row in 0..piece.len() {
832            live.push(!piece.is_null_at(row));
833        }
834    }
835    Validity::from_run(&live)
836}
837
838/// Whether row `n` reads position `n` for every row, which makes the final gather a copy onto itself.
839fn straight(at: &[usize]) -> bool {
840    at.iter().enumerate().all(|(row, &index)| row == index)
841}
842
843/// The arenas the flat string pieces among `pieces` share, counted before any of them is laid.
844fn arenas_of<V: AsRef<Vector>>(pieces: &[V]) -> Arenas {
845    let mut arenas = Arenas::default();
846    for piece in pieces {
847        if let Some(Data::Varlen(column)) = piece.as_ref().data() {
848            arenas.count(column);
849        }
850    }
851    arenas
852}
853
854/// The pieces as one window, when they are windows of one page that follow each other in it.
855///
856/// A sorted load is the case. Its answer is laid as one page a column and handed on in chunks cut
857/// out of that page, and a table then lays those chunks end to end into row groups, which without
858/// this copies every value back into a run the page already holds. A string column joins when its
859/// views are a page and every piece shares one arena, which is what a sorted string column is.
860fn adjoined(pieces: &[&Vector]) -> Option<Data> {
861    macro_rules! joined {
862        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
863            match pieces.first()?.data()? {
864                $(Data::$variant(first) => {
865                    if !first.is_shared() {
866                        return None;
867                    }
868                    let mut run = first.clone();
869                    for piece in &pieces[1..] {
870                        let Some(Data::$variant(next)) = piece.data() else { return None };
871                        run = run.joined(next)?;
872                    }
873                    Some(Data::$variant(run))
874                })+
875                Data::Varlen(first) => {
876                    if !first.is_paged() {
877                        return None;
878                    }
879                    let mut run = first.clone();
880                    for piece in &pieces[1..] {
881                        let Some(Data::Varlen(next)) = piece.data() else { return None };
882                        run = run.joined(next)?;
883                    }
884                    Some(Data::Varlen(run))
885                }
886                _ => None,
887            }
888        };
889    }
890    crate::for_each_layout!(fixed, joined)
891}
892
893/// Lays a run of data end to end after another, answering how many values it appended.
894///
895/// The typed loop per layout is the whole point: an append of a thousand `i64` is one `memcpy` and
896/// an append of a thousand strings is at most one copy of an arena and a thousand sixteen byte views,
897/// neither of which touches a `Value`. `arenas` is what says whether the arena is copied whole.
898fn extend(into: &mut Data, from: &Data, arenas: &mut Arenas) -> Result<usize> {
899    macro_rules! extended {
900        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
901            match (&mut *into, from) {
902                // Nothing to append, which is what an untyped null piece is. The caller reads the
903                // count and leaves those rows null rather than pointing them anywhere.
904                (_, Data::Empty) => Ok(0),
905                $((Data::$variant(out), Data::$variant(values)) => {
906                    out.extend_from_slice(values.as_slice());
907                    Ok(values.len())
908                })+
909                // The one layout where an append is a copy of bytes rather than a copy of fixed
910                // width slots, and the column decides whether that is one copy or one a string.
911                (Data::Varlen(out), Data::Varlen(values)) => {
912                    out.push_column(values, arenas);
913                    Ok(values.len())
914                }
915                (out, from) => Err(Error::internal(format!(
916                    "a run of {:?} values cannot be laid after a run of {:?} ones",
917                    layout_of(from),
918                    layout_of(out)
919                ))),
920            }
921        };
922    }
923    crate::for_each_layout!(fixed, extended)
924}
925
926/// Unit tests for the assembly.
927///
928/// That the engine actually goes through here rather than through the old path was checked rather
929/// than assumed, by gating a panic on [`Assembly::place`] and running the `rudb` suite with it
930/// armed. Three tests failed and no others: the one that is a `CASE` by name, the one that runs the
931/// catalog views the engine ships with, and the one over the native frequency synopsis. All three
932/// have a `CASE` in them and nothing else in the suite does.
933#[cfg(test)]
934mod tests {
935    use super::*;
936    use crate::{Chunk, Form};
937
938    /// Every row of a vector, as values, which is what an assembly is checked against.
939    fn values(vector: &Vector) -> Vec<Value> {
940        (0..vector.len()).map(|row| vector.value_at(row)).collect()
941    }
942
943    /// The answer the assembly has to reach, written the slow obvious way.
944    ///
945    /// A `Vec<Value>` filled by scattering and then handed to [`Vector::from_values`] is exactly
946    /// what `CASE` used to do, so this is the reference rather than a second opinion.
947    fn scattered(ty: &LogicalType, rows: usize, pieces: &[(Vec<u32>, Vector)]) -> Vector {
948        let mut answers = vec![Value::Null; rows];
949        for (positions, piece) in pieces {
950            for (slot, &row) in positions.iter().enumerate() {
951                answers[row as usize] = piece.value_at(slot);
952            }
953        }
954        Vector::from_values(ty.clone(), &answers).expect("the reference builds")
955    }
956
957    /// Builds an assembly out of pieces and checks it against the slow way of getting there.
958    fn agrees(ty: &LogicalType, rows: usize, pieces: &[(Vec<u32>, Vector)]) -> Vector {
959        let mut assembly = Assembly::new(ty.clone(), rows).expect("an assembly of this type");
960        for (positions, piece) in pieces {
961            assembly.place(positions, piece).expect("the piece is placed");
962        }
963        let built = assembly.finish().expect("the assembly finishes");
964        assert_eq!(built.len(), rows, "an assembly of {rows} rows");
965        assert_eq!(values(&built), values(&scattered(ty, rows, pieces)), "against the slow way");
966        built
967    }
968
969    #[test]
970    fn two_pieces_interleave_back_into_the_order_the_rows_came_in() {
971        let evens = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(0), Value::BigInt(2)])
972            .expect("a vector");
973        let odds = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1), Value::BigInt(3)])
974            .expect("a vector");
975        let built = agrees(&LogicalType::BigInt, 4, &[(vec![0, 2], evens), (vec![1, 3], odds)]);
976        assert_eq!(
977            values(&built),
978            vec![Value::BigInt(0), Value::BigInt(1), Value::BigInt(2), Value::BigInt(3)]
979        );
980    }
981
982    #[test]
983    fn a_row_no_piece_claims_is_null() {
984        // Which is what a `CASE` with no `ELSE` leaves behind, and the case where a run of data with
985        // a hole in it would put every value after the hole at the wrong index.
986        let piece =
987            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(7)]).expect("a vector");
988        let built = agrees(&LogicalType::BigInt, 3, &[(vec![1], piece)]);
989        assert_eq!(values(&built), vec![Value::Null, Value::BigInt(7), Value::Null]);
990    }
991
992    #[test]
993    fn no_pieces_at_all_is_a_column_of_nulls_of_the_right_length() {
994        let built = agrees(&LogicalType::Integer, 5, &[]);
995        assert!(built.is_null_at(4), "every row of it is null");
996    }
997
998    #[test]
999    fn a_null_inside_a_piece_stays_null_where_the_piece_put_it() {
1000        // The validity has to survive the copy, and the value under it has to not be read, which is
1001        // two different things a single run of data with a mask over it can get wrong separately.
1002        let piece = Vector::from_values(
1003            LogicalType::BigInt,
1004            &[Value::BigInt(1), Value::Null, Value::BigInt(3)],
1005        )
1006        .expect("a vector");
1007        let built = agrees(&LogicalType::BigInt, 3, &[(vec![2, 0, 1], piece)]);
1008        assert!(built.is_null_at(0), "the null landed where the piece put it");
1009        assert_eq!(built.value_at(2), Value::BigInt(1));
1010    }
1011
1012    #[test]
1013    fn strings_are_assembled_without_going_through_a_value_each() {
1014        let left = Vector::from_values(
1015            LogicalType::Varchar,
1016            &[Value::Varchar("a short one".into()), Value::Varchar("another".into())],
1017        )
1018        .expect("a vector");
1019        let right = Vector::from_values(
1020            LogicalType::Varchar,
1021            &[Value::Varchar("a string that is far too long to live inline in a view".into())],
1022        )
1023        .expect("a vector");
1024        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 2], left), (vec![1], right)]);
1025        assert_eq!(built.value_at(0), Value::Varchar("a short one".into()));
1026        assert_eq!(
1027            built.value_at(1),
1028            Value::Varchar("a string that is far too long to live inline in a view".into())
1029        );
1030        assert_eq!(built.value_at(2), Value::Varchar("another".into()));
1031    }
1032
1033    #[test]
1034    fn strings_laid_end_to_end_in_order_come_back_as_views_over_the_arena_they_went_into() {
1035        // The shape a hash join's gathered side is: chunk after chunk, each claiming the rows
1036        // straight after the last, so the permutation is the identity and nothing needs moving.
1037        let first = Vector::from_values(
1038            LogicalType::Varchar,
1039            &[Value::Varchar("one".into()), Value::Varchar("two".into())],
1040        )
1041        .expect("a vector");
1042        let second = Vector::from_values(
1043            LogicalType::Varchar,
1044            &[Value::Varchar("a third one long enough to be out of line".into())],
1045        )
1046        .expect("a vector");
1047        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 1], first), (vec![2], second)]);
1048        assert_eq!(built.form(), Form::StringView, "the bytes stay where they were appended");
1049        assert_eq!(
1050            built.value_at(2),
1051            Value::Varchar("a third one long enough to be out of line".into())
1052        );
1053    }
1054
1055    #[test]
1056    fn a_string_row_no_piece_claims_is_null_rather_than_empty() {
1057        // The hole a `CASE` with no `ELSE` leaves, on the path that permutes views instead of
1058        // copying bytes, where an unclaimed row has no view to read and has to come out null.
1059        let piece = Vector::from_values(
1060            LogicalType::Varchar,
1061            &[Value::Varchar("a value long enough to be out of line".into())],
1062        )
1063        .expect("a vector");
1064        let built = agrees(&LogicalType::Varchar, 3, &[(vec![2], piece)]);
1065        assert_eq!(built.value_at(0), Value::Null);
1066        assert_eq!(built.value_at(1), Value::Null);
1067        assert_eq!(
1068            built.value_at(2),
1069            Value::Varchar("a value long enough to be out of line".into())
1070        );
1071    }
1072
1073    #[test]
1074    fn a_constant_piece_is_written_out_rather_than_read_a_row_at_a_time() {
1075        // The `ELSE ''` half of the ClickBench query this was built for, which arrives as a constant
1076        // over however many rows the arms did not claim.
1077        let arm = Vector::from_values(LogicalType::Varchar, &[Value::Varchar("kept".into())])
1078            .expect("a vector");
1079        let otherwise = Vector::constant(LogicalType::Varchar, Value::Varchar("".into()), 3);
1080        let built = agrees(&LogicalType::Varchar, 4, &[(vec![2], arm), (vec![0, 1, 3], otherwise)]);
1081        assert_eq!(built.value_at(0), Value::Varchar("".into()));
1082        assert_eq!(built.value_at(2), Value::Varchar("kept".into()));
1083    }
1084
1085    #[test]
1086    fn a_dictionary_piece_is_walked_to_its_values() {
1087        // A scanned string column arrives as codes over a shared dictionary, so this is the form the
1088        // `THEN Referer` arm of the ClickBench query actually hands over.
1089        let dictionary = Vector::from_values(
1090            LogicalType::Varchar,
1091            &[Value::Varchar("one".into()), Value::Varchar("two".into())],
1092        )
1093        .expect("a dictionary");
1094        let piece = Vector::dictionary(vec![1, 0, 1], dictionary).expect("a dictionary vector");
1095        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 1, 2], piece)]);
1096        assert_eq!(
1097            values(&built),
1098            vec![
1099                Value::Varchar("two".into()),
1100                Value::Varchar("one".into()),
1101                Value::Varchar("two".into())
1102            ]
1103        );
1104    }
1105
1106    #[test]
1107    fn a_piece_placed_at_the_wrong_number_of_positions_is_an_error() {
1108        let piece =
1109            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a vector");
1110        let mut assembly = Assembly::new(LogicalType::BigInt, 4).expect("an assembly");
1111        assert!(assembly.place(&[0, 1], &piece).is_err(), "two positions for one row");
1112    }
1113
1114    #[test]
1115    fn a_position_past_the_end_is_an_error_rather_than_a_lost_row() {
1116        let piece =
1117            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a vector");
1118        let mut assembly = Assembly::new(LogicalType::BigInt, 2).expect("an assembly");
1119        assert!(assembly.place(&[9], &piece).is_err(), "a row past the end of the assembly");
1120    }
1121
1122    #[test]
1123    fn a_piece_of_the_wrong_layout_is_an_error_rather_than_a_wrong_answer() {
1124        // Two runs of data that cannot be laid end to end, which is a bug in whoever built the
1125        // pieces and has to say so rather than silently keep the first one.
1126        let piece =
1127            Vector::from_values(LogicalType::Varchar, &[Value::Varchar("x".into())]).expect("text");
1128        let mut assembly = Assembly::new(LogicalType::BigInt, 1).expect("an assembly");
1129        assert!(assembly.place(&[0], &piece).is_err(), "text laid after integers");
1130    }
1131
1132    #[test]
1133    fn every_layout_assembles_the_way_it_scatters() {
1134        // One case per physical layout, because the copy loop is generated per layout and a layout
1135        // missing from it is a wrong answer for that type alone, which no single typed test finds.
1136        let cases: Vec<(LogicalType, Vec<Value>)> = vec![
1137            (LogicalType::Boolean, vec![Value::Boolean(true), Value::Boolean(false)]),
1138            (LogicalType::TinyInt, vec![Value::TinyInt(1), Value::TinyInt(-2)]),
1139            (LogicalType::SmallInt, vec![Value::SmallInt(3), Value::SmallInt(-4)]),
1140            (LogicalType::Integer, vec![Value::Integer(5), Value::Integer(-6)]),
1141            (LogicalType::BigInt, vec![Value::BigInt(7), Value::BigInt(-8)]),
1142            (LogicalType::HugeInt, vec![Value::HugeInt(9), Value::HugeInt(-10)]),
1143            (LogicalType::UTinyInt, vec![Value::UTinyInt(11), Value::UTinyInt(12)]),
1144            (LogicalType::USmallInt, vec![Value::USmallInt(13), Value::USmallInt(14)]),
1145            (LogicalType::UInteger, vec![Value::UInteger(15), Value::UInteger(16)]),
1146            (LogicalType::UBigInt, vec![Value::UBigInt(17), Value::UBigInt(18)]),
1147            (LogicalType::Float, vec![Value::Float(1.5), Value::Float(-2.5)]),
1148            (LogicalType::Double, vec![Value::Double(3.5), Value::Double(-4.5)]),
1149            (
1150                LogicalType::Varchar,
1151                vec![Value::Varchar("first".into()), Value::Varchar("second".into())],
1152            ),
1153            (LogicalType::Date, vec![Value::Date(19), Value::Date(20)]),
1154        ];
1155        for (ty, pair) in cases {
1156            let left = Vector::from_values(ty.clone(), &pair[..1]).expect("a vector");
1157            let right = Vector::from_values(ty.clone(), &pair[1..]).expect("a vector");
1158            let built = agrees(&ty, 2, &[(vec![1], left), (vec![0], right)]);
1159            assert_eq!(built.value_at(0), pair[1], "{ty:?} at row 0");
1160            assert_eq!(built.value_at(1), pair[0], "{ty:?} at row 1");
1161        }
1162    }
1163
1164    #[test]
1165    fn an_assembly_is_a_chunk_column_like_any_other() {
1166        // The point of building a vector rather than a `Vec<Value>` is that what comes out goes
1167        // straight into a chunk, so this checks it actually does.
1168        let piece = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1), Value::BigInt(2)])
1169            .expect("a vector");
1170        let built = agrees(&LogicalType::BigInt, 2, &[(vec![1, 0], piece)]);
1171        let chunk = Chunk::new(vec![built]).expect("a chunk of one column");
1172        assert_eq!(chunk.len(), 2, "two rows");
1173    }
1174
1175    /// A run of pieces, as values, in the order they were given.
1176    fn all_of(pieces: &[Vector]) -> Vec<Value> {
1177        pieces.iter().flat_map(values).collect()
1178    }
1179
1180    /// The pieces laid end to end, checked against the values that went in.
1181    fn laid(ty: &LogicalType, pieces: &[Vector]) -> Vector {
1182        let built = concat(ty, pieces).expect("the pieces lay").expect("this run lays");
1183        assert_eq!(built.len(), pieces.iter().map(Vector::len).sum::<usize>(), "the row count");
1184        assert_eq!(values(&built), all_of(pieces), "the values laid end to end");
1185        built
1186    }
1187
1188    #[test]
1189    fn pieces_laid_end_to_end_read_back_in_the_order_they_were_given() {
1190        let piece = |from: i64, to: i64| {
1191            let held: Vec<Value> = (from..to).map(Value::BigInt).collect();
1192            Vector::from_values(LogicalType::BigInt, &held).expect("a run of bigints")
1193        };
1194        let pieces = [piece(0, 4), piece(4, 9), piece(9, 10)];
1195        let built = laid(&LogicalType::BigInt, &pieces);
1196        assert_eq!(built.form(), Form::Flat, "a run of flat pieces lays flat");
1197        // The point of the page: a window cut out of it is a reference count bump and not a copy,
1198        // which is what the table cuts a chunk with.
1199        let window = built.slice(4, 5).expect("a window into the page");
1200        assert_eq!(values(&window), all_of(&pieces[1..2]), "the second piece, cut back out");
1201    }
1202
1203    /// Windows of one page that follow each other lay as one window over it, and anything else
1204    /// still lays by copying, which is what a sorted load hands a table.
1205    #[test]
1206    fn neighbouring_windows_of_one_page_lay_without_a_copy() {
1207        let held: Vec<Value> =
1208            (0..20).map(|at| if at % 7 == 3 { Value::Null } else { Value::BigInt(at) }).collect();
1209        let page = Vector::from_values(LogicalType::BigInt, &held).expect("a run").into_pages();
1210        let cut = |from: usize, len: usize| page.slice(from, len).expect("a window");
1211        let address = |vector: &Vector| match vector.data() {
1212            Some(Data::Int64(run)) => run.as_slice().as_ptr() as usize,
1213            other => panic!("a bigint run laid as {other:?}"),
1214        };
1215        let built = laid(&LogicalType::BigInt, &[cut(2, 5), cut(7, 8), cut(15, 3)]);
1216        assert_eq!(address(&built), address(&page) + 2 * 8, "the neighbours were copied");
1217        // A gap, a piece out of order, and a piece of another page all fall back to the copy.
1218        let other = Vector::from_values(LogicalType::BigInt, &held).expect("a run").into_pages();
1219        let other_cut = other.slice(7, 3).expect("a window");
1220        for pieces in [
1221            vec![cut(2, 5), cut(8, 3)],
1222            vec![cut(7, 3), cut(2, 5)],
1223            vec![cut(2, 5), other_cut],
1224            vec![Vector::from_values(LogicalType::BigInt, &held[..4]).expect("owned"), cut(4, 2)],
1225        ] {
1226            let built = laid(&LogicalType::BigInt, &pieces);
1227            assert_ne!(address(&built), address(&page) + 2 * 8, "a copy was expected");
1228        }
1229    }
1230
1231    /// The same for strings: cuts of one paged column lay back as a window over its views and
1232    /// its arena, and a cut of a column whose views are its own is copied.
1233    #[test]
1234    fn neighbouring_cuts_of_a_paged_string_column_lay_without_a_copy() {
1235        let held: Vec<Value> = (0..20)
1236            .map(|at| Value::Varchar(format!("a string long enough for the arena {at}")))
1237            .collect();
1238        let page = Vector::from_values(LogicalType::Varchar, &held).expect("a run").into_pages();
1239        let cut = |from: usize, len: usize| page.slice(from, len).expect("a window");
1240        let views = |vector: &Vector| match vector.data() {
1241            Some(Data::Varlen(column)) => column.views().as_ptr() as usize,
1242            other => panic!("a varchar run laid as {other:?}"),
1243        };
1244        let built = laid(&LogicalType::Varchar, &[cut(2, 5), cut(7, 8), cut(15, 3)]);
1245        assert_eq!(views(&built), views(&page) + 2 * size_of::<StringView>(), "views copied");
1246        let owned = Vector::from_values(LogicalType::Varchar, &held).expect("a run");
1247        let copied = laid(&LogicalType::Varchar, &[owned.slice(0, 4).expect("a cut"), cut(4, 2)]);
1248        assert_eq!(copied.len(), 6);
1249    }
1250
1251    #[test]
1252    fn a_null_in_a_piece_is_a_null_in_the_same_row_of_the_page() {
1253        let ty = LogicalType::Integer;
1254        let whole = Vector::from_values(ty.clone(), &[Value::Integer(1), Value::Integer(2)])
1255            .expect("no nulls");
1256        let holed =
1257            Vector::from_values(ty.clone(), &[Value::Null, Value::Integer(4)]).expect("one null");
1258        let built = laid(&ty, &[whole.clone(), holed.clone()]);
1259        assert!(!built.is_null_at(1), "a row that was not null became one");
1260        assert!(built.is_null_at(2), "the null did not come through");
1261        // A run with no null anywhere keeps the cheap answer rather than growing a mask of ones.
1262        let clean = laid(&ty, &[whole.clone(), whole]);
1263        assert_eq!(clean.validity(), &Validity::AllValid, "a mask nothing needed");
1264        let empty = laid(&ty, &[holed.clone(), holed]);
1265        assert!(empty.is_null_at(0) && empty.is_null_at(2), "both nulls came through");
1266    }
1267
1268    /// The string case, which is the one that would be a byte copy per cut if it laid flat.
1269    #[test]
1270    fn strings_lay_into_one_arena_and_come_back_as_views() {
1271        let ty = LogicalType::Varchar;
1272        let word = |text: &str| {
1273            Vector::from_values(ty.clone(), &[Value::Varchar(text.to_string())]).expect("a string")
1274        };
1275        let pieces = [word("a string too long to sit inside a view"), word("short")];
1276        let built = laid(&ty, &pieces);
1277        assert_eq!(
1278            built.form(),
1279            Form::StringView,
1280            "a varchar page that is not views cuts by copying"
1281        );
1282        let window = built.slice(0, 1).expect("a window into the page");
1283        assert_eq!(values(&window), all_of(&pieces[..1]), "the long string, cut back out");
1284    }
1285
1286    #[test]
1287    fn stable_dictionary_pieces_sharing_values_lay_as_codes() {
1288        let ty = LogicalType::Varchar;
1289        let values = Arc::new(
1290            Vector::from_values(
1291                ty.clone(),
1292                &[Value::Varchar("a".to_string()), Value::Varchar("b".to_string())],
1293            )
1294            .expect("dictionary values"),
1295        );
1296        let first = Vector::stable_dictionary(vec![1, 0], Arc::clone(&values)).expect("codes");
1297        let second = Vector::stable_dictionary(vec![1], Arc::clone(&values)).expect("codes");
1298        let built = concat(&ty, &[first, second]).expect("no error").expect("shared codes lay");
1299        let (codes, held) = built.stable_dictionary_parts().expect("the stable form survives");
1300        assert_eq!(codes, &[1, 0, 1]);
1301        assert!(Arc::ptr_eq(held, &values));
1302    }
1303
1304    /// What will not lay, which is a layout answer and not an error.
1305    #[test]
1306    fn an_encoded_piece_is_left_alone_rather_than_flattened() {
1307        let ty = LogicalType::BigInt;
1308        let flat = Vector::from_values(ty.clone(), &[Value::BigInt(1)]).expect("a flat piece");
1309        let values = Vector::from_values(ty.clone(), &[Value::BigInt(7), Value::BigInt(8)])
1310            .expect("two distinct values");
1311        let coded = Vector::dictionary(vec![0, 1, 0], values).expect("a dictionary piece");
1312        let one = std::slice::from_ref(&coded);
1313        assert!(concat(&ty, one).expect("no error").is_none(), "a dictionary laid");
1314        assert!(
1315            concat(&ty, &[flat.clone(), coded]).expect("no error").is_none(),
1316            "a mixed run laid"
1317        );
1318        assert!(
1319            concat::<Vector>(&ty, &[]).expect("no error").is_none(),
1320            "nothing laid into something"
1321        );
1322        // A piece of another type is the caller's mistake and is still answered as a layout it will
1323        // not build, because the fallback keeps the pieces and keeping them is always correct.
1324        let other =
1325            Vector::from_values(LogicalType::Integer, &[Value::Integer(1)]).expect("an int");
1326        assert!(concat(&ty, &[flat, other]).expect("no error").is_none(), "two types laid");
1327    }
1328
1329    /// Pieces of every form a sort hands over, read back through an order, against the same order
1330    /// read a value at a time.
1331    #[test]
1332    fn an_interleave_reads_the_pieces_in_the_order_it_is_given() {
1333        let words: Vec<Value> = ["a long enough word to leave the inline view", "b", "c"]
1334            .iter()
1335            .map(|word| Value::Varchar((*word).to_string()))
1336            .collect();
1337        let dictionary = Vector::from_values(LogicalType::Varchar, &words).expect("words");
1338        let strings = [
1339            Vector::dictionary(vec![2, 0, 1], dictionary).expect("a dictionary"),
1340            Vector::from_values(
1341                LogicalType::Varchar,
1342                &[Value::Null, Value::Varchar("another string past twelve bytes".to_string())],
1343            )
1344            .expect("flat"),
1345        ];
1346        let numbers = [
1347            Vector::from_values(
1348                LogicalType::BigInt,
1349                &[Value::BigInt(7), Value::Null, Value::BigInt(9)],
1350            )
1351            .expect("flat"),
1352            Vector::constant(LogicalType::BigInt, Value::BigInt(4), 1),
1353            Vector::constant(LogicalType::BigInt, Value::Null, 1),
1354        ];
1355        let lists = [
1356            Vector::from_values(
1357                LogicalType::List(Box::new(LogicalType::Integer)),
1358                &[
1359                    Value::List { element: LogicalType::Integer, values: vec![Value::Integer(1)] },
1360                    Value::Null,
1361                    Value::List { element: LogicalType::Integer, values: vec![] },
1362                ],
1363            )
1364            .expect("lists"),
1365            Vector::from_values(
1366                LogicalType::List(Box::new(LogicalType::Integer)),
1367                &[
1368                    Value::List {
1369                        element: LogicalType::Integer,
1370                        values: vec![Value::Integer(2), Value::Integer(3)],
1371                    },
1372                    Value::Null,
1373                ],
1374            )
1375            .expect("lists"),
1376        ];
1377        let order = [4, 0, 3, 1, 2, 3];
1378        for pieces in [&strings[..], &numbers[..], &lists[..]] {
1379            let ty = pieces[0].logical_type().clone();
1380            let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1381            let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1382            let got = interleave(&ty, pieces, &order).expect("an interleave");
1383            assert_eq!(values(&got), expected, "{ty}");
1384        }
1385        assert!(interleave(&LogicalType::BigInt, &numbers, &[5]).is_err(), "row 5 of 5 rows");
1386        // The same answers written through the inverse of a permutation, which is the way round a
1387        // sort takes when its order is a few long runs.
1388        let order = [4, 0, 3, 1, 2];
1389        let mut inverse = [0u32; 5];
1390        for (at, &row) in order.iter().enumerate() {
1391            inverse[row] = at as u32;
1392        }
1393        let texts: Vec<Value> = ["a string past the twelve bytes of a view", "short", "x"]
1394            .iter()
1395            .map(|text| Value::Varchar((*text).to_string()))
1396            .chain([Value::Varchar("another long string for the arena".to_string())])
1397            .collect();
1398        let valid = [
1399            Vector::from_values(LogicalType::Varchar, &texts[..2]).expect("flat"),
1400            Vector::from_values(LogicalType::Varchar, &texts[2..]).expect("flat"),
1401            Vector::constant(LogicalType::Varchar, Value::Varchar("one more".to_string()), 1),
1402        ];
1403        for pieces in [&strings[..], &numbers[..], &lists[..], &valid[..]] {
1404            let ty = pieces[0].logical_type().clone();
1405            let pulled = interleave(&ty, pieces, &order).expect("an interleave");
1406            let pushed =
1407                interleave_placed(&ty, pieces, &order, Some(&inverse)).expect("a placed one");
1408            assert_eq!(values(&pushed), values(&pulled), "{ty}");
1409        }
1410        assert!(
1411            interleave_placed(&LogicalType::BigInt, &numbers, &order, Some(&inverse[..4])).is_err(),
1412            "four places for five rows"
1413        );
1414        let untyped = [Vector::constant(LogicalType::Null, Value::Null, 3)];
1415        let got = interleave(&LogicalType::Null, &untyped, &[2, 0]).expect("an untyped null");
1416        assert_eq!(values(&got), vec![Value::Null, Value::Null]);
1417    }
1418
1419    /// A fixed width column written through `inverse` from pieces of every form the sort sees,
1420    /// against the answer read through `order`, and a column of nothing but nulls.
1421    #[test]
1422    fn a_fixed_width_column_is_written_to_its_places_from_pieces_of_any_form() {
1423        let ty = LogicalType::BigInt;
1424        let int = Value::BigInt;
1425        let flat = Vector::from_values(ty.clone(), &[int(1), Value::Null, int(3)]).expect("flat");
1426        let paged = Vector::from_values(ty.clone(), &[int(4), int(5)]).expect("flat").into_pages();
1427        let words = Vector::from_values(ty.clone(), &[int(70), int(80)]).expect("values");
1428        let coded = Vector::dictionary(vec![1, 0, 1], words).expect("coded");
1429        let nulled = Vector::from_values(ty.clone(), &[int(90), Value::Null]).expect("values");
1430        let chained = Vector::dictionary(vec![1, 0], nulled).expect("coded over nulls");
1431        let constant = Vector::constant(ty.clone(), int(6), 2);
1432        let pieces = [flat, paged, coded, chained, constant];
1433        let rows: usize = pieces.iter().map(Vector::len).sum();
1434        let order: Vec<usize> = (0..rows).map(|at| (at * 5 + 3) % rows).collect();
1435        let mut inverse = vec![0u32; rows];
1436        for (to, &from) in order.iter().enumerate() {
1437            inverse[from] = u32::try_from(to).expect("a small row");
1438        }
1439        let read = interleave_placed(&ty, &pieces, &order, None).expect("read through order");
1440        let written =
1441            interleave_placed(&ty, &pieces, &order, Some(&inverse)).expect("written to places");
1442        assert_eq!(values(&written), values(&read));
1443        assert_eq!(values(&written)[inverse[1] as usize], Value::Null, "the flat piece's null");
1444        assert_eq!(values(&written)[inverse[8] as usize], Value::Null, "the dictionary's null");
1445
1446        let nothing = Vector::from_values(ty.clone(), &[Value::Null, Value::Null]).expect("nulls");
1447        let written = interleave_placed(&ty, &[nothing], &[1, 0], Some(&[1, 0])).expect("nulls");
1448        assert_eq!(values(&written), [Value::Null, Value::Null]);
1449    }
1450
1451    #[test]
1452    fn placed_strings_are_laid_in_the_order_of_the_result() {
1453        let word = |text: &str| Value::Varchar(text.to_string());
1454        let flat = Vector::from_values(
1455            LogicalType::Varchar,
1456            &[word("the first string past twelve bytes"), Value::Null, word("short")],
1457        )
1458        .expect("flat");
1459        let arena = b"xxa second string past twelve bytesyy".to_vec();
1460        let views = vec![StringView::over(&arena[2..35], 2), StringView::inline("tiny")];
1461        let viewed =
1462            Vector::string_views(LogicalType::Varchar, views, Arc::new(Buffer::from_vec(arena)))
1463                .expect("views");
1464        let pieces = [flat, viewed];
1465        let order = [3, 0, 4, 2, 1];
1466        let mut inverse = vec![0u32; order.len()];
1467        for (to, &from) in order.iter().enumerate() {
1468            inverse[from] = u32::try_from(to).expect("a small row");
1469        }
1470        let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1471        let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1472        let got = interleave_placed(&LogicalType::Varchar, &pieces, &order, Some(&inverse))
1473            .expect("a placed interleave");
1474        assert_eq!(values(&got), expected);
1475        let (_, arena) = got.text_parts().expect("views");
1476        assert_eq!(
1477            arena, b"a second string past twelve bytesthe first string past twelve bytes",
1478            "the long strings in the order they come out, and nothing else"
1479        );
1480        assert!(strings_placeable(&LogicalType::Varchar, &pieces));
1481        for split in 0..=order.len() {
1482            let mut joined = Vec::new();
1483            for range in [0..split, split..order.len()] {
1484                let part = placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, range)
1485                    .expect("a range of rows");
1486                joined.extend(values(&part));
1487            }
1488            assert_eq!(joined, expected, "split at {split}");
1489        }
1490        let (_, arena) = placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, 1..3)
1491            .expect("the middle rows")
1492            .text_parts()
1493            .map(|(views, arena)| (views.len(), arena.to_vec()))
1494            .expect("views");
1495        assert_eq!(arena, b"the first string past twelve bytes", "only the range's own strings");
1496        assert!(placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, 4..6).is_err());
1497    }
1498
1499    #[test]
1500    fn an_interleave_of_dictionaries_merges_them_into_one() {
1501        let word = |text: &str| Value::Varchar(text.to_string());
1502        let first = [word("MAIL"), word("a word long enough to leave the inline view")];
1503        let second = [Value::Null, word("MAIL"), word("SHIP")];
1504        let first = Arc::new(Vector::from_values(LogicalType::Varchar, &first).expect("words"));
1505        let second = Arc::new(Vector::from_values(LogicalType::Varchar, &second).expect("words"));
1506        let over = |codes: Vec<u32>, dictionary: &Arc<Vector>| {
1507            Vector::dictionary_over(codes, Arc::clone(dictionary)).expect("a dictionary")
1508        };
1509        let pieces = [
1510            over((0..16).map(|row| row % 2).collect(), &first),
1511            over((0..16).map(|row| row % 3).collect(), &second),
1512            over(vec![1; 8], &first),
1513        ];
1514        let order: Vec<usize> = (0..40).rev().collect();
1515        let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1516        let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1517        let got = interleave(&LogicalType::Varchar, &pieces, &order).expect("an interleave");
1518        assert_eq!(values(&got), expected);
1519        let (_, merged) = got.stable_dictionary_parts().expect("one stable dictionary");
1520        assert_eq!(merged.len(), 4, "MAIL once, the long word, the null and SHIP");
1521        let mut inverse = vec![0u32; order.len()];
1522        for (to, &from) in order.iter().enumerate() {
1523            inverse[from] = u32::try_from(to).expect("a small row");
1524        }
1525        let placed = interleave_placed(&LogicalType::Varchar, &pieces, &order, Some(&inverse))
1526            .expect("a placed interleave");
1527        assert_eq!(values(&placed), expected, "placed codes land where pulled ones do");
1528        assert!(placed.stable_dictionary_parts().is_some(), "and stay one dictionary");
1529
1530        let mixed = [pieces[0].clone(), pieces[1].flatten().expect("flat")];
1531        let got = interleave(&LogicalType::Varchar, &mixed, &order[8..]).expect("an interleave");
1532        assert!(got.dictionary_parts().is_none(), "a flat piece gathers flat");
1533        let few = &pieces[..1];
1534        let got = interleave(&LogicalType::Varchar, few, &[3, 2]).expect("an interleave");
1535        assert_eq!(
1536            values(&got),
1537            vec![word("a word long enough to leave the inline view"), word("MAIL")]
1538        );
1539    }
1540}