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(ty: &LogicalType, pieces: &[Vector]) -> Result<Option<Vector>> {
233    if pieces.is_empty() {
234        return Ok(None);
235    }
236    let rows = pieces.iter().map(Vector::len).sum();
237    let shared = pieces[0].stable_dictionary_parts().map(|(_, values)| values).filter(|values| {
238        pieces.iter().all(|piece| {
239            piece.logical_type() == ty
240                && !piece.is_empty()
241                && piece
242                    .stable_dictionary_parts()
243                    .is_some_and(|(_, held)| Arc::ptr_eq(held, values))
244        })
245    });
246    if let Some(values) = shared {
247        let mut codes = Vec::with_capacity(rows);
248        for piece in pieces {
249            if let Some((held, _)) = piece.stable_dictionary_parts() {
250                codes.extend_from_slice(held);
251            }
252        }
253        let validity = run_of(pieces, rows);
254        return Ok(Some(
255            Vector::stable_dictionary(codes, Arc::clone(values))?.with_validity(validity),
256        ));
257    }
258    // Checked before anything is copied, because the fallback is for the caller to keep the pieces
259    // it already has and a half built page would be work thrown away.
260    let laid = pieces
261        .iter()
262        .all(|piece| piece.form() == Form::Flat && piece.logical_type() == ty && !piece.is_empty());
263    if !laid {
264        return Ok(None);
265    }
266    // Sized before the first value moves, so the page is one allocation and holds no more than the
267    // rows that went into it. Growing from empty instead ends at the next power of two, which on a
268    // full row group is eight thousand values of slack carried for the life of the table.
269    let mut data = data_for(ty, rows)?;
270    let mut arenas = arenas_of(pieces);
271    for piece in pieces {
272        let from = piece
273            .data()
274            .ok_or_else(|| Error::internal("a flat vector with no run of data in it"))?;
275        let appended = extend(&mut data, from, &mut arenas)?;
276        if appended != piece.len() {
277            return Err(Error::internal(format!(
278                "a piece of {} rows laid {appended} values end to end",
279                piece.len()
280            )));
281        }
282    }
283    let validity = run_of(pieces, rows);
284    if let Data::Varlen(column) = data {
285        let (views, arena) = column.into_parts();
286        let page = Vector::string_views(ty.clone(), views, Arc::new(arena))?;
287        return Ok(Some(page.with_validity(validity)));
288    }
289    Ok(Some(Vector::flat(ty.clone(), data)?.with_validity(validity).into_pages()))
290}
291
292/// Several vectors of one type laid end to end and then read back in `order`.
293///
294/// What a sort is. Row `n` of the answer is row `order[n]` of the pieces laid end to end, so the
295/// pieces are laid once and the answer is one gather, which writes the answer front to back. An
296/// [`Assembly`] answers the same question the other way round, with a position per input row that
297/// it scatters into, and that costs it a map of the whole column and a scatter per column, which for
298/// a sort is the same permutation worked out again for every column. Here the caller works it out
299/// once and every column reads it.
300///
301/// A string column comes back as views over the one arena its bytes were laid into, the same as
302/// [`concat()`] gives, so the gather moves sixteen bytes a row.
303///
304/// # Errors
305///
306/// If the type has no layout this can lay, if a piece holds fewer values than it has rows, or if
307/// an entry of `order` is past the end of the pieces.
308pub fn interleave(ty: &LogicalType, pieces: &[Vector], order: &[usize]) -> Result<Vector> {
309    interleave_placed(ty, pieces, order, None)
310}
311
312/// The same as [`interleave()`], writing each row where it goes rather than reading each row from
313/// where it came when the caller also has `inverse`, the place in the answer of every row laid.
314///
315/// Which of the two is cheaper is decided by how many ascending runs `order` is made of, and only
316/// the caller knows that without a pass of its own. Reading through `order` jumps between the runs,
317/// so when there are few of them each row of the answer costs a cache line of the laid column to
318/// use eight bytes of it, and with a dozen columns on a dozen threads that is the memory bus full.
319/// Writing through `inverse` reads the laid column front to back and writes one stream per run,
320/// each of them front to back too. On SF1 `lineitem` sorted by ship month, 84 runs, twelve columns
321/// on twelve threads went from 145ms to 50ms in a standalone test of just these loops. With runs
322/// in the tens of thousands the two come out even and with a run every few rows the writes are the
323/// ones that miss, so a caller with an order like that passes `None`.
324///
325/// This is not the scatter #1365 took out. That one built a map of the whole column per column to
326/// scatter into. This one writes into the answer's own run and the inverse is worked out once.
327///
328/// # Errors
329///
330/// As [`interleave()`], or if `inverse` is given and `order` and `inverse` are not both as long as
331/// the pieces, which is the only shape in which one can be the other turned round.
332pub fn interleave_placed(
333    ty: &LogicalType,
334    pieces: &[Vector],
335    order: &[usize],
336    inverse: Option<&[u32]>,
337) -> Result<Vector> {
338    let rows: usize = pieces.iter().map(Vector::len).sum();
339    if let Some(inverse) = inverse.filter(|inverse| inverse.len() != rows || order.len() != rows) {
340        return Err(Error::internal(format!(
341            "{} places and {} positions for a permutation of {rows} rows",
342            inverse.len(),
343            order.len()
344        )));
345    }
346    if let Some(&past) = order.iter().find(|&&index| index >= rows) {
347        return Err(Error::internal(format!("row {past} read out of pieces of {rows} rows")));
348    }
349    if matches!(ty, LogicalType::List(_) | LogicalType::Struct(_) | LogicalType::Map(_, _)) {
350        // row at a time: the nested types, for the reason `Assembly::values` gives. They have no run
351        // of data to lay end to end and no typed copy to gather with.
352        let laid: Vec<Value> = pieces
353            .iter()
354            .flat_map(|piece| (0..piece.len()).map(|row| piece.value_at(row)))
355            .collect();
356        let values: Vec<Value> =
357            order.iter().map(|&index| laid.get(index).cloned().unwrap_or(Value::Null)).collect();
358        return Vector::from_values(ty.clone(), &values);
359    }
360    if let Some(merged) = merged_dictionary(ty, pieces, order, inverse)? {
361        return Ok(merged);
362    }
363    if let Some(inverse) = inverse {
364        if let Some(placed) = placed_strings(ty, pieces, inverse, 0..rows)? {
365            return Ok(placed);
366        }
367    }
368    let mut data = data_for(ty, rows)?;
369    // The untyped null, which has no run of data to lay or to gather out of, and is null whatever
370    // the order is.
371    if matches!(data, Data::Empty) {
372        return Ok(Vector::constant(ty.clone(), Value::Null, order.len()));
373    }
374    let mut arenas = arenas_of(pieces);
375    // Reserved whole, because an arena grown by doubling as the pieces arrive copies what it holds
376    // at every step and faults each new allocation in again. The sorted SF1 comments lay 183MB.
377    if let Data::Varlen(column) = &mut data {
378        column.reserve_bytes(arenas.bytes());
379    }
380    // Each piece's validity, taken after it is flattened, because a constant null keeps its null in
381    // its value rather than in its mask and a flattened one has it in the mask like any other row.
382    let mut masks = Vec::with_capacity(pieces.len());
383    for piece in pieces {
384        // flatten: the gather below reads one run of data, and a piece can arrive dictionary
385        // encoded, constant or bit packed. The flatten is a typed loop per layout, a flat piece is
386        // not copied by it, and one piece is flattened at a time so a column is never held twice.
387        let flat = piece.flatten()?;
388        let from = flat.data().ok_or_else(|| Error::internal("a flattened vector with no data"))?;
389        let appended = extend(&mut data, from, &mut arenas)?;
390        if appended != piece.len() {
391            return Err(Error::internal(format!(
392                "a piece of {} rows laid {appended} values end to end",
393                piece.len()
394            )));
395        }
396        masks.push((flat.len(), flat.validity().clone()));
397    }
398    let laid = if masks.iter().all(|(_, mask)| matches!(mask, Validity::AllValid)) {
399        Validity::AllValid
400    } else {
401        let mut live = Vec::with_capacity(rows);
402        for (len, mask) in &masks {
403            // row at a time: a bit a row for the mixed case, once a column rather than once a
404            // piece of every column the way it would be read otherwise.
405            live.extend((0..*len).map(|row| mask.is_valid(row)));
406        }
407        Validity::from_run(&live)
408    };
409    if laid.count_valid(rows) == 0 {
410        return Ok(Vector::constant(ty.clone(), Value::Null, order.len()));
411    }
412    let validity = match (laid, inverse) {
413        (Validity::AllValid, _) => Validity::AllValid,
414        (laid, Some(inverse)) => {
415            let mut live = vec![false; order.len()];
416            for (row, &to) in inverse.iter().enumerate() {
417                if let Some(slot) = live.get_mut(to as usize) {
418                    *slot = laid.is_valid(row);
419                }
420            }
421            Validity::from_run(&live)
422        }
423        (laid, None) => Validity::from_iter(order.len(), |row| {
424            order.get(row).is_some_and(|&index| laid.is_valid(index))
425        }),
426    };
427    if let Data::Varlen(column) = data {
428        let (views, arena) = column.into_parts();
429        let gathered = match inverse {
430            Some(inverse) => {
431                let mut placed = vec![StringView::empty(); order.len()];
432                for (view, &to) in views.iter().zip(inverse) {
433                    if let Some(slot) = placed.get_mut(to as usize) {
434                        *slot = *view;
435                    }
436                }
437                placed
438            }
439            None => order
440                .iter()
441                .map(|&index| views.get(index).copied().unwrap_or_else(StringView::empty))
442                .collect(),
443        };
444        return Ok(
445            Vector::string_views(ty.clone(), gathered, Arc::new(arena))?.with_validity(validity)
446        );
447    }
448    let data = match inverse {
449        Some(inverse) => placed_of(&data, inverse),
450        None => copy_of(&data, order),
451    };
452    Ok(Vector::flat(ty.clone(), data)?.with_validity(validity))
453}
454
455/// The string column written through `inverse` into an arena laid in the order of the result.
456///
457/// Laying the pieces' arenas end to end and pushing the views keeps the bytes in the order they
458/// arrived, so everything after the sort that reads the strings in their new order reads the arena
459/// at random. On the sorted `lineitem` that is `l_comment`, whose distinct count in the append
460/// took 550 to 1570 ms of CPU across the threads at full width, nearly all of it waiting on memory,
461/// and takes 190 to 250 ms with the arena in order. A table built by the sort also keeps its strings
462/// in the order it is read in from then on. Here each piece is read once in order, every long
463/// string's length is written to its place first so a prefix sum gives each one its offset, and
464/// then its bytes are copied there. A sort's output is a few long runs of its input, so both
465/// passes write a few streams that each move forward.
466///
467/// `None` when the column is not a string, or when a piece is not flat views and has to be
468/// flattened on the general path first.
469fn placed_strings(
470    ty: &LogicalType,
471    pieces: &[Vector],
472    inverse: &[u32],
473    range: Range<usize>,
474) -> Result<Option<Vector>> {
475    if !strings_placeable(ty, pieces) {
476        return Ok(None);
477    }
478    let first = range.start;
479    let rows = range.len();
480    // The place of a row in this range, or `None` for a row another range lays.
481    let local = |to: u32| (to as usize).checked_sub(first).filter(|&at| at < rows);
482    let mut offsets = vec![0u64; rows + 1];
483    let mut places = inverse.iter();
484    for piece in pieces {
485        let (views, _) = piece.text_parts().unwrap_or_default();
486        for (view, &to) in views.iter().zip(places.by_ref()) {
487            if view.is_inline() {
488                continue;
489            }
490            if let Some(slot) = local(to).and_then(|at| offsets.get_mut(at + 1)) {
491                *slot = view.len() as u64;
492            }
493        }
494    }
495    let mut total = 0;
496    for offset in &mut offsets {
497        total += *offset;
498        *offset = total;
499    }
500    let mut arena =
501        vec![0u8; usize::try_from(total).map_err(|_| Error::internal("an arena too large"))?];
502    let mut placed = vec![StringView::empty(); rows];
503    let mut live = vec![true; rows];
504    let mut places = inverse.iter();
505    for piece in pieces {
506        let (views, from) = piece.text_parts().unwrap_or_default();
507        let validity = piece.validity();
508        for (row, (view, &to)) in views.iter().zip(places.by_ref()).enumerate() {
509            let Some(to) = local(to) else {
510                continue;
511            };
512            if !validity.is_valid(row) {
513                if let Some(slot) = live.get_mut(to) {
514                    *slot = false;
515                }
516                continue;
517            }
518            let (Some(bytes), Some(&at), Some(slot)) =
519                (view.bytes_in(from), offsets.get(to), placed.get_mut(to))
520            else {
521                continue;
522            };
523            if view.is_inline() {
524                *slot = *view;
525                continue;
526            }
527            if let Some(into) = arena.get_mut(at as usize..at as usize + bytes.len()) {
528                into.copy_from_slice(bytes);
529            }
530            *slot = StringView::over(bytes, at);
531        }
532    }
533    let validity = if live.iter().all(|&valid| valid) {
534        Validity::AllValid
535    } else {
536        Validity::from_run(&live)
537    };
538    let vector = Vector::string_views(ty.clone(), placed, Arc::new(Buffer::from_vec(arena)))?;
539    Ok(Some(vector.with_validity(validity)))
540}
541
542/// Whether [`interleave_placed`] lays this string column through [`placed_string_rows`], which is
543/// when it is a string and every piece is flat views.
544#[must_use]
545pub fn strings_placeable(ty: &LogicalType, pieces: &[Vector]) -> bool {
546    matches!(ty, LogicalType::Varchar | LogicalType::Blob)
547        && pieces.iter().all(|piece| piece.text_parts().is_some())
548}
549
550/// The rows in `range` of the string column [`interleave_placed`] would lay through `inverse`,
551/// with an arena of their own.
552///
553/// This is how a sort builds one long string column on several threads. Each range reads every
554/// piece and all of `inverse` and copies only its own strings, so each has a few forward streams
555/// to write the way the whole column does, and the ranges share nothing they write.
556///
557/// # Errors
558///
559/// If `inverse` is not as long as the pieces or `range` runs past it, or if a piece is not flat
560/// views, which [`strings_placeable`] says beforehand.
561pub fn placed_string_rows(
562    ty: &LogicalType,
563    pieces: &[Vector],
564    inverse: &[u32],
565    range: Range<usize>,
566) -> Result<Vector> {
567    let rows: usize = pieces.iter().map(Vector::len).sum();
568    if inverse.len() != rows || range.end > rows || range.start > range.end {
569        return Err(Error::internal(format!(
570            "rows {range:?} of {} places for {rows} rows",
571            inverse.len()
572        )));
573    }
574    placed_strings(ty, pieces, inverse, range)?
575        .ok_or_else(|| Error::internal("a string column placed that is not flat views"))
576}
577
578/// How many rows a merged dictionary entry has to stand for on average before a string column is
579/// gathered as codes rather than as views.
580///
581/// A Parquet file carries one dictionary per row group, so a sorted `lineitem` column arrives as
582/// 733 pieces over 49 dictionaries. The low cardinality columns have 98 to 343 entries between all
583/// of them, and merging those is nothing next to gathering six million views. A column whose
584/// dictionaries are nearly as long as the column is one the writer should not have encoded, and
585/// merging it would hash every value to save nothing, so it is gathered flat.
586const ROWS_PER_MERGED_ENTRY: usize = 8;
587
588/// The string column gathered by `order` as one dictionary, when every piece is a dictionary.
589///
590/// The pieces' dictionaries are merged into one with each distinct value once, so the codes mean
591/// the same thing on every page cut from the result and the result is a stable dictionary. The
592/// gather is then four bytes a row instead of sixteen, and the append after the sort gets the
593/// dictionary the scan handed up rather than a flat column it has to read a row at a time.
594///
595/// `None` when the column is not a string, when any piece is not a dictionary or has nulls at its
596/// own level, or when the dictionaries are too long for the merge to pay.
597fn merged_dictionary(
598    ty: &LogicalType,
599    pieces: &[Vector],
600    order: &[usize],
601    inverse: Option<&[u32]>,
602) -> Result<Option<Vector>> {
603    if !matches!(ty, LogicalType::Varchar | LogicalType::Blob) || pieces.is_empty() {
604        return Ok(None);
605    }
606    let rows: usize = pieces.iter().map(Vector::len).sum();
607    let mut dictionaries: Vec<&Arc<Vector>> = Vec::new();
608    let mut which = Vec::with_capacity(pieces.len());
609    let mut entries = 0;
610    for piece in pieces {
611        let Some((_, values)) = piece.shared_dictionary_parts() else {
612            return Ok(None);
613        };
614        if !matches!(piece.validity(), Validity::AllValid) {
615            return Ok(None);
616        }
617        let at = match dictionaries.iter().position(|seen| Arc::ptr_eq(seen, values)) {
618            Some(at) => at,
619            None => {
620                entries += values.len();
621                if entries.saturating_mul(ROWS_PER_MERGED_ENTRY) > rows {
622                    return Ok(None);
623                }
624                dictionaries.push(values);
625                dictionaries.len() - 1
626            }
627        };
628        which.push(at);
629    }
630    // A null entry has no bytes, so it merges with every other null entry.
631    let mut merged: HashMap<Option<&[u8]>, u32> = HashMap::new();
632    let mut values = Vec::new();
633    let mut remaps = Vec::with_capacity(dictionaries.len());
634    for dictionary in &dictionaries {
635        let mut remap = Vec::with_capacity(dictionary.len());
636        // row at a time: over the dictionary entries, a few hundred of them against millions of
637        // rows, and only the first sighting of each value becomes one.
638        for entry in 0..dictionary.len() {
639            let next = u32::try_from(values.len())
640                .map_err(|_| Error::internal("a merged dictionary past four billion entries"))?;
641            let code = *merged.entry(dictionary.bytes_at(entry)).or_insert_with(|| {
642                values.push(dictionary.value_at(entry));
643                next
644            });
645            remap.push(code);
646        }
647        remaps.push(remap);
648    }
649    let mut laid = Vec::with_capacity(rows);
650    for (piece, &at) in pieces.iter().zip(&which) {
651        let (codes, _) = piece
652            .dictionary_parts()
653            .ok_or_else(|| Error::internal("a dictionary piece lost its dictionary"))?;
654        let remap = &remaps[at];
655        laid.extend(codes.iter().map(|&code| remap[code as usize]));
656    }
657    // Written through the inverse when there is one, for the reason `interleave_placed` gives: a
658    // few long runs read through `order` spend a cache line on every four byte code.
659    let codes = match inverse {
660        Some(inverse) => {
661            let mut codes = vec![0u32; order.len()];
662            for (&code, &to) in laid.iter().zip(inverse) {
663                if let Some(slot) = codes.get_mut(to as usize) {
664                    *slot = code;
665                }
666            }
667            codes
668        }
669        None => order.iter().map(|&index| laid[index]).collect(),
670    };
671    let values = Vector::from_values(ty.clone(), &values)?;
672    Ok(Some(Vector::stable_dictionary(codes, Arc::new(values))?))
673}
674
675/// The validity of the pieces laid end to end, in `rows` rows.
676///
677/// The two cheap answers are checked for first because they are the answers real data gives. A
678/// column that was never null anywhere is a page with no mask on it at all, and a bit per row read
679/// out of every piece to build a mask that is all ones would be throwing that away.
680fn run_of(pieces: &[Vector], rows: usize) -> Validity {
681    if pieces.iter().all(|piece| matches!(piece.validity(), Validity::AllValid)) {
682        return Validity::AllValid;
683    }
684    if pieces.iter().all(|piece| matches!(piece.validity(), Validity::AllInvalid)) {
685        return Validity::AllInvalid;
686    }
687    let mut live = Vec::with_capacity(rows);
688    for piece in pieces {
689        // row at a time: the mixed case, which is a bit per row however it is written, and it runs
690        // once per column per row group rather than once per chunk.
691        for row in 0..piece.len() {
692            live.push(!piece.is_null_at(row));
693        }
694    }
695    Validity::from_run(&live)
696}
697
698/// Whether row `n` reads position `n` for every row, which makes the final gather a copy onto itself.
699fn straight(at: &[usize]) -> bool {
700    at.iter().enumerate().all(|(row, &index)| row == index)
701}
702
703/// The arenas the flat string pieces among `pieces` share, counted before any of them is laid.
704fn arenas_of(pieces: &[Vector]) -> Arenas {
705    let mut arenas = Arenas::default();
706    for piece in pieces {
707        if let Some(Data::Varlen(column)) = piece.data() {
708            arenas.count(column);
709        }
710    }
711    arenas
712}
713
714/// Lays a run of data end to end after another, answering how many values it appended.
715///
716/// The typed loop per layout is the whole point: an append of a thousand `i64` is one `memcpy` and
717/// an append of a thousand strings is at most one copy of an arena and a thousand sixteen byte views,
718/// neither of which touches a `Value`. `arenas` is what says whether the arena is copied whole.
719fn extend(into: &mut Data, from: &Data, arenas: &mut Arenas) -> Result<usize> {
720    macro_rules! extended {
721        ($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
722            match (&mut *into, from) {
723                // Nothing to append, which is what an untyped null piece is. The caller reads the
724                // count and leaves those rows null rather than pointing them anywhere.
725                (_, Data::Empty) => Ok(0),
726                $((Data::$variant(out), Data::$variant(values)) => {
727                    out.extend_from_slice(values.as_slice());
728                    Ok(values.len())
729                })+
730                // The one layout where an append is a copy of bytes rather than a copy of fixed
731                // width slots, and the column decides whether that is one copy or one a string.
732                (Data::Varlen(out), Data::Varlen(values)) => {
733                    out.push_column(values, arenas);
734                    Ok(values.len())
735                }
736                (out, from) => Err(Error::internal(format!(
737                    "a run of {:?} values cannot be laid after a run of {:?} ones",
738                    layout_of(from),
739                    layout_of(out)
740                ))),
741            }
742        };
743    }
744    crate::for_each_layout!(fixed, extended)
745}
746
747/// Unit tests for the assembly.
748///
749/// That the engine actually goes through here rather than through the old path was checked rather
750/// than assumed, by gating a panic on [`Assembly::place`] and running the `rudb` suite with it
751/// armed. Three tests failed and no others: the one that is a `CASE` by name, the one that runs the
752/// catalog views the engine ships with, and the one over the native frequency synopsis. All three
753/// have a `CASE` in them and nothing else in the suite does.
754#[cfg(test)]
755mod tests {
756    use super::*;
757    use crate::{Chunk, Form};
758
759    /// Every row of a vector, as values, which is what an assembly is checked against.
760    fn values(vector: &Vector) -> Vec<Value> {
761        (0..vector.len()).map(|row| vector.value_at(row)).collect()
762    }
763
764    /// The answer the assembly has to reach, written the slow obvious way.
765    ///
766    /// A `Vec<Value>` filled by scattering and then handed to [`Vector::from_values`] is exactly
767    /// what `CASE` used to do, so this is the reference rather than a second opinion.
768    fn scattered(ty: &LogicalType, rows: usize, pieces: &[(Vec<u32>, Vector)]) -> Vector {
769        let mut answers = vec![Value::Null; rows];
770        for (positions, piece) in pieces {
771            for (slot, &row) in positions.iter().enumerate() {
772                answers[row as usize] = piece.value_at(slot);
773            }
774        }
775        Vector::from_values(ty.clone(), &answers).expect("the reference builds")
776    }
777
778    /// Builds an assembly out of pieces and checks it against the slow way of getting there.
779    fn agrees(ty: &LogicalType, rows: usize, pieces: &[(Vec<u32>, Vector)]) -> Vector {
780        let mut assembly = Assembly::new(ty.clone(), rows).expect("an assembly of this type");
781        for (positions, piece) in pieces {
782            assembly.place(positions, piece).expect("the piece is placed");
783        }
784        let built = assembly.finish().expect("the assembly finishes");
785        assert_eq!(built.len(), rows, "an assembly of {rows} rows");
786        assert_eq!(values(&built), values(&scattered(ty, rows, pieces)), "against the slow way");
787        built
788    }
789
790    #[test]
791    fn two_pieces_interleave_back_into_the_order_the_rows_came_in() {
792        let evens = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(0), Value::BigInt(2)])
793            .expect("a vector");
794        let odds = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1), Value::BigInt(3)])
795            .expect("a vector");
796        let built = agrees(&LogicalType::BigInt, 4, &[(vec![0, 2], evens), (vec![1, 3], odds)]);
797        assert_eq!(
798            values(&built),
799            vec![Value::BigInt(0), Value::BigInt(1), Value::BigInt(2), Value::BigInt(3)]
800        );
801    }
802
803    #[test]
804    fn a_row_no_piece_claims_is_null() {
805        // Which is what a `CASE` with no `ELSE` leaves behind, and the case where a run of data with
806        // a hole in it would put every value after the hole at the wrong index.
807        let piece =
808            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(7)]).expect("a vector");
809        let built = agrees(&LogicalType::BigInt, 3, &[(vec![1], piece)]);
810        assert_eq!(values(&built), vec![Value::Null, Value::BigInt(7), Value::Null]);
811    }
812
813    #[test]
814    fn no_pieces_at_all_is_a_column_of_nulls_of_the_right_length() {
815        let built = agrees(&LogicalType::Integer, 5, &[]);
816        assert!(built.is_null_at(4), "every row of it is null");
817    }
818
819    #[test]
820    fn a_null_inside_a_piece_stays_null_where_the_piece_put_it() {
821        // The validity has to survive the copy, and the value under it has to not be read, which is
822        // two different things a single run of data with a mask over it can get wrong separately.
823        let piece = Vector::from_values(
824            LogicalType::BigInt,
825            &[Value::BigInt(1), Value::Null, Value::BigInt(3)],
826        )
827        .expect("a vector");
828        let built = agrees(&LogicalType::BigInt, 3, &[(vec![2, 0, 1], piece)]);
829        assert!(built.is_null_at(0), "the null landed where the piece put it");
830        assert_eq!(built.value_at(2), Value::BigInt(1));
831    }
832
833    #[test]
834    fn strings_are_assembled_without_going_through_a_value_each() {
835        let left = Vector::from_values(
836            LogicalType::Varchar,
837            &[Value::Varchar("a short one".into()), Value::Varchar("another".into())],
838        )
839        .expect("a vector");
840        let right = Vector::from_values(
841            LogicalType::Varchar,
842            &[Value::Varchar("a string that is far too long to live inline in a view".into())],
843        )
844        .expect("a vector");
845        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 2], left), (vec![1], right)]);
846        assert_eq!(built.value_at(0), Value::Varchar("a short one".into()));
847        assert_eq!(
848            built.value_at(1),
849            Value::Varchar("a string that is far too long to live inline in a view".into())
850        );
851        assert_eq!(built.value_at(2), Value::Varchar("another".into()));
852    }
853
854    #[test]
855    fn strings_laid_end_to_end_in_order_come_back_as_views_over_the_arena_they_went_into() {
856        // The shape a hash join's gathered side is: chunk after chunk, each claiming the rows
857        // straight after the last, so the permutation is the identity and nothing needs moving.
858        let first = Vector::from_values(
859            LogicalType::Varchar,
860            &[Value::Varchar("one".into()), Value::Varchar("two".into())],
861        )
862        .expect("a vector");
863        let second = Vector::from_values(
864            LogicalType::Varchar,
865            &[Value::Varchar("a third one long enough to be out of line".into())],
866        )
867        .expect("a vector");
868        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 1], first), (vec![2], second)]);
869        assert_eq!(built.form(), Form::StringView, "the bytes stay where they were appended");
870        assert_eq!(
871            built.value_at(2),
872            Value::Varchar("a third one long enough to be out of line".into())
873        );
874    }
875
876    #[test]
877    fn a_string_row_no_piece_claims_is_null_rather_than_empty() {
878        // The hole a `CASE` with no `ELSE` leaves, on the path that permutes views instead of
879        // copying bytes, where an unclaimed row has no view to read and has to come out null.
880        let piece = Vector::from_values(
881            LogicalType::Varchar,
882            &[Value::Varchar("a value long enough to be out of line".into())],
883        )
884        .expect("a vector");
885        let built = agrees(&LogicalType::Varchar, 3, &[(vec![2], piece)]);
886        assert_eq!(built.value_at(0), Value::Null);
887        assert_eq!(built.value_at(1), Value::Null);
888        assert_eq!(
889            built.value_at(2),
890            Value::Varchar("a value long enough to be out of line".into())
891        );
892    }
893
894    #[test]
895    fn a_constant_piece_is_written_out_rather_than_read_a_row_at_a_time() {
896        // The `ELSE ''` half of the ClickBench query this was built for, which arrives as a constant
897        // over however many rows the arms did not claim.
898        let arm = Vector::from_values(LogicalType::Varchar, &[Value::Varchar("kept".into())])
899            .expect("a vector");
900        let otherwise = Vector::constant(LogicalType::Varchar, Value::Varchar("".into()), 3);
901        let built = agrees(&LogicalType::Varchar, 4, &[(vec![2], arm), (vec![0, 1, 3], otherwise)]);
902        assert_eq!(built.value_at(0), Value::Varchar("".into()));
903        assert_eq!(built.value_at(2), Value::Varchar("kept".into()));
904    }
905
906    #[test]
907    fn a_dictionary_piece_is_walked_to_its_values() {
908        // A scanned string column arrives as codes over a shared dictionary, so this is the form the
909        // `THEN Referer` arm of the ClickBench query actually hands over.
910        let dictionary = Vector::from_values(
911            LogicalType::Varchar,
912            &[Value::Varchar("one".into()), Value::Varchar("two".into())],
913        )
914        .expect("a dictionary");
915        let piece = Vector::dictionary(vec![1, 0, 1], dictionary).expect("a dictionary vector");
916        let built = agrees(&LogicalType::Varchar, 3, &[(vec![0, 1, 2], piece)]);
917        assert_eq!(
918            values(&built),
919            vec![
920                Value::Varchar("two".into()),
921                Value::Varchar("one".into()),
922                Value::Varchar("two".into())
923            ]
924        );
925    }
926
927    #[test]
928    fn a_piece_placed_at_the_wrong_number_of_positions_is_an_error() {
929        let piece =
930            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a vector");
931        let mut assembly = Assembly::new(LogicalType::BigInt, 4).expect("an assembly");
932        assert!(assembly.place(&[0, 1], &piece).is_err(), "two positions for one row");
933    }
934
935    #[test]
936    fn a_position_past_the_end_is_an_error_rather_than_a_lost_row() {
937        let piece =
938            Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a vector");
939        let mut assembly = Assembly::new(LogicalType::BigInt, 2).expect("an assembly");
940        assert!(assembly.place(&[9], &piece).is_err(), "a row past the end of the assembly");
941    }
942
943    #[test]
944    fn a_piece_of_the_wrong_layout_is_an_error_rather_than_a_wrong_answer() {
945        // Two runs of data that cannot be laid end to end, which is a bug in whoever built the
946        // pieces and has to say so rather than silently keep the first one.
947        let piece =
948            Vector::from_values(LogicalType::Varchar, &[Value::Varchar("x".into())]).expect("text");
949        let mut assembly = Assembly::new(LogicalType::BigInt, 1).expect("an assembly");
950        assert!(assembly.place(&[0], &piece).is_err(), "text laid after integers");
951    }
952
953    #[test]
954    fn every_layout_assembles_the_way_it_scatters() {
955        // One case per physical layout, because the copy loop is generated per layout and a layout
956        // missing from it is a wrong answer for that type alone, which no single typed test finds.
957        let cases: Vec<(LogicalType, Vec<Value>)> = vec![
958            (LogicalType::Boolean, vec![Value::Boolean(true), Value::Boolean(false)]),
959            (LogicalType::TinyInt, vec![Value::TinyInt(1), Value::TinyInt(-2)]),
960            (LogicalType::SmallInt, vec![Value::SmallInt(3), Value::SmallInt(-4)]),
961            (LogicalType::Integer, vec![Value::Integer(5), Value::Integer(-6)]),
962            (LogicalType::BigInt, vec![Value::BigInt(7), Value::BigInt(-8)]),
963            (LogicalType::HugeInt, vec![Value::HugeInt(9), Value::HugeInt(-10)]),
964            (LogicalType::UTinyInt, vec![Value::UTinyInt(11), Value::UTinyInt(12)]),
965            (LogicalType::USmallInt, vec![Value::USmallInt(13), Value::USmallInt(14)]),
966            (LogicalType::UInteger, vec![Value::UInteger(15), Value::UInteger(16)]),
967            (LogicalType::UBigInt, vec![Value::UBigInt(17), Value::UBigInt(18)]),
968            (LogicalType::Float, vec![Value::Float(1.5), Value::Float(-2.5)]),
969            (LogicalType::Double, vec![Value::Double(3.5), Value::Double(-4.5)]),
970            (
971                LogicalType::Varchar,
972                vec![Value::Varchar("first".into()), Value::Varchar("second".into())],
973            ),
974            (LogicalType::Date, vec![Value::Date(19), Value::Date(20)]),
975        ];
976        for (ty, pair) in cases {
977            let left = Vector::from_values(ty.clone(), &pair[..1]).expect("a vector");
978            let right = Vector::from_values(ty.clone(), &pair[1..]).expect("a vector");
979            let built = agrees(&ty, 2, &[(vec![1], left), (vec![0], right)]);
980            assert_eq!(built.value_at(0), pair[1], "{ty:?} at row 0");
981            assert_eq!(built.value_at(1), pair[0], "{ty:?} at row 1");
982        }
983    }
984
985    #[test]
986    fn an_assembly_is_a_chunk_column_like_any_other() {
987        // The point of building a vector rather than a `Vec<Value>` is that what comes out goes
988        // straight into a chunk, so this checks it actually does.
989        let piece = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1), Value::BigInt(2)])
990            .expect("a vector");
991        let built = agrees(&LogicalType::BigInt, 2, &[(vec![1, 0], piece)]);
992        let chunk = Chunk::new(vec![built]).expect("a chunk of one column");
993        assert_eq!(chunk.len(), 2, "two rows");
994    }
995
996    /// A run of pieces, as values, in the order they were given.
997    fn all_of(pieces: &[Vector]) -> Vec<Value> {
998        pieces.iter().flat_map(values).collect()
999    }
1000
1001    /// The pieces laid end to end, checked against the values that went in.
1002    fn laid(ty: &LogicalType, pieces: &[Vector]) -> Vector {
1003        let built = concat(ty, pieces).expect("the pieces lay").expect("this run lays");
1004        assert_eq!(built.len(), pieces.iter().map(Vector::len).sum::<usize>(), "the row count");
1005        assert_eq!(values(&built), all_of(pieces), "the values laid end to end");
1006        built
1007    }
1008
1009    #[test]
1010    fn pieces_laid_end_to_end_read_back_in_the_order_they_were_given() {
1011        let piece = |from: i64, to: i64| {
1012            let held: Vec<Value> = (from..to).map(Value::BigInt).collect();
1013            Vector::from_values(LogicalType::BigInt, &held).expect("a run of bigints")
1014        };
1015        let pieces = [piece(0, 4), piece(4, 9), piece(9, 10)];
1016        let built = laid(&LogicalType::BigInt, &pieces);
1017        assert_eq!(built.form(), Form::Flat, "a run of flat pieces lays flat");
1018        // The point of the page: a window cut out of it is a reference count bump and not a copy,
1019        // which is what the table cuts a chunk with.
1020        let window = built.slice(4, 5).expect("a window into the page");
1021        assert_eq!(values(&window), all_of(&pieces[1..2]), "the second piece, cut back out");
1022    }
1023
1024    #[test]
1025    fn a_null_in_a_piece_is_a_null_in_the_same_row_of_the_page() {
1026        let ty = LogicalType::Integer;
1027        let whole = Vector::from_values(ty.clone(), &[Value::Integer(1), Value::Integer(2)])
1028            .expect("no nulls");
1029        let holed =
1030            Vector::from_values(ty.clone(), &[Value::Null, Value::Integer(4)]).expect("one null");
1031        let built = laid(&ty, &[whole.clone(), holed.clone()]);
1032        assert!(!built.is_null_at(1), "a row that was not null became one");
1033        assert!(built.is_null_at(2), "the null did not come through");
1034        // A run with no null anywhere keeps the cheap answer rather than growing a mask of ones.
1035        let clean = laid(&ty, &[whole.clone(), whole]);
1036        assert_eq!(clean.validity(), &Validity::AllValid, "a mask nothing needed");
1037        let empty = laid(&ty, &[holed.clone(), holed]);
1038        assert!(empty.is_null_at(0) && empty.is_null_at(2), "both nulls came through");
1039    }
1040
1041    /// The string case, which is the one that would be a byte copy per cut if it laid flat.
1042    #[test]
1043    fn strings_lay_into_one_arena_and_come_back_as_views() {
1044        let ty = LogicalType::Varchar;
1045        let word = |text: &str| {
1046            Vector::from_values(ty.clone(), &[Value::Varchar(text.to_string())]).expect("a string")
1047        };
1048        let pieces = [word("a string too long to sit inside a view"), word("short")];
1049        let built = laid(&ty, &pieces);
1050        assert_eq!(
1051            built.form(),
1052            Form::StringView,
1053            "a varchar page that is not views cuts by copying"
1054        );
1055        let window = built.slice(0, 1).expect("a window into the page");
1056        assert_eq!(values(&window), all_of(&pieces[..1]), "the long string, cut back out");
1057    }
1058
1059    #[test]
1060    fn stable_dictionary_pieces_sharing_values_lay_as_codes() {
1061        let ty = LogicalType::Varchar;
1062        let values = Arc::new(
1063            Vector::from_values(
1064                ty.clone(),
1065                &[Value::Varchar("a".to_string()), Value::Varchar("b".to_string())],
1066            )
1067            .expect("dictionary values"),
1068        );
1069        let first = Vector::stable_dictionary(vec![1, 0], Arc::clone(&values)).expect("codes");
1070        let second = Vector::stable_dictionary(vec![1], Arc::clone(&values)).expect("codes");
1071        let built = concat(&ty, &[first, second]).expect("no error").expect("shared codes lay");
1072        let (codes, held) = built.stable_dictionary_parts().expect("the stable form survives");
1073        assert_eq!(codes, &[1, 0, 1]);
1074        assert!(Arc::ptr_eq(held, &values));
1075    }
1076
1077    /// What will not lay, which is a layout answer and not an error.
1078    #[test]
1079    fn an_encoded_piece_is_left_alone_rather_than_flattened() {
1080        let ty = LogicalType::BigInt;
1081        let flat = Vector::from_values(ty.clone(), &[Value::BigInt(1)]).expect("a flat piece");
1082        let values = Vector::from_values(ty.clone(), &[Value::BigInt(7), Value::BigInt(8)])
1083            .expect("two distinct values");
1084        let coded = Vector::dictionary(vec![0, 1, 0], values).expect("a dictionary piece");
1085        let one = std::slice::from_ref(&coded);
1086        assert!(concat(&ty, one).expect("no error").is_none(), "a dictionary laid");
1087        assert!(
1088            concat(&ty, &[flat.clone(), coded]).expect("no error").is_none(),
1089            "a mixed run laid"
1090        );
1091        assert!(concat(&ty, &[]).expect("no error").is_none(), "nothing laid into something");
1092        // A piece of another type is the caller's mistake and is still answered as a layout it will
1093        // not build, because the fallback keeps the pieces and keeping them is always correct.
1094        let other =
1095            Vector::from_values(LogicalType::Integer, &[Value::Integer(1)]).expect("an int");
1096        assert!(concat(&ty, &[flat, other]).expect("no error").is_none(), "two types laid");
1097    }
1098
1099    /// Pieces of every form a sort hands over, read back through an order, against the same order
1100    /// read a value at a time.
1101    #[test]
1102    fn an_interleave_reads_the_pieces_in_the_order_it_is_given() {
1103        let words: Vec<Value> = ["a long enough word to leave the inline view", "b", "c"]
1104            .iter()
1105            .map(|word| Value::Varchar((*word).to_string()))
1106            .collect();
1107        let dictionary = Vector::from_values(LogicalType::Varchar, &words).expect("words");
1108        let strings = [
1109            Vector::dictionary(vec![2, 0, 1], dictionary).expect("a dictionary"),
1110            Vector::from_values(
1111                LogicalType::Varchar,
1112                &[Value::Null, Value::Varchar("another string past twelve bytes".to_string())],
1113            )
1114            .expect("flat"),
1115        ];
1116        let numbers = [
1117            Vector::from_values(
1118                LogicalType::BigInt,
1119                &[Value::BigInt(7), Value::Null, Value::BigInt(9)],
1120            )
1121            .expect("flat"),
1122            Vector::constant(LogicalType::BigInt, Value::BigInt(4), 1),
1123            Vector::constant(LogicalType::BigInt, Value::Null, 1),
1124        ];
1125        let lists = [
1126            Vector::from_values(
1127                LogicalType::List(Box::new(LogicalType::Integer)),
1128                &[
1129                    Value::List { element: LogicalType::Integer, values: vec![Value::Integer(1)] },
1130                    Value::Null,
1131                    Value::List { element: LogicalType::Integer, values: vec![] },
1132                ],
1133            )
1134            .expect("lists"),
1135            Vector::from_values(
1136                LogicalType::List(Box::new(LogicalType::Integer)),
1137                &[
1138                    Value::List {
1139                        element: LogicalType::Integer,
1140                        values: vec![Value::Integer(2), Value::Integer(3)],
1141                    },
1142                    Value::Null,
1143                ],
1144            )
1145            .expect("lists"),
1146        ];
1147        let order = [4, 0, 3, 1, 2, 3];
1148        for pieces in [&strings[..], &numbers[..], &lists[..]] {
1149            let ty = pieces[0].logical_type().clone();
1150            let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1151            let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1152            let got = interleave(&ty, pieces, &order).expect("an interleave");
1153            assert_eq!(values(&got), expected, "{ty}");
1154        }
1155        assert!(interleave(&LogicalType::BigInt, &numbers, &[5]).is_err(), "row 5 of 5 rows");
1156        // The same answers written through the inverse of a permutation, which is the way round a
1157        // sort takes when its order is a few long runs.
1158        let order = [4, 0, 3, 1, 2];
1159        let mut inverse = [0u32; 5];
1160        for (at, &row) in order.iter().enumerate() {
1161            inverse[row] = at as u32;
1162        }
1163        let texts: Vec<Value> = ["a string past the twelve bytes of a view", "short", "x"]
1164            .iter()
1165            .map(|text| Value::Varchar((*text).to_string()))
1166            .chain([Value::Varchar("another long string for the arena".to_string())])
1167            .collect();
1168        let valid = [
1169            Vector::from_values(LogicalType::Varchar, &texts[..2]).expect("flat"),
1170            Vector::from_values(LogicalType::Varchar, &texts[2..]).expect("flat"),
1171            Vector::constant(LogicalType::Varchar, Value::Varchar("one more".to_string()), 1),
1172        ];
1173        for pieces in [&strings[..], &numbers[..], &lists[..], &valid[..]] {
1174            let ty = pieces[0].logical_type().clone();
1175            let pulled = interleave(&ty, pieces, &order).expect("an interleave");
1176            let pushed =
1177                interleave_placed(&ty, pieces, &order, Some(&inverse)).expect("a placed one");
1178            assert_eq!(values(&pushed), values(&pulled), "{ty}");
1179        }
1180        assert!(
1181            interleave_placed(&LogicalType::BigInt, &numbers, &order, Some(&inverse[..4])).is_err(),
1182            "four places for five rows"
1183        );
1184        let untyped = [Vector::constant(LogicalType::Null, Value::Null, 3)];
1185        let got = interleave(&LogicalType::Null, &untyped, &[2, 0]).expect("an untyped null");
1186        assert_eq!(values(&got), vec![Value::Null, Value::Null]);
1187    }
1188
1189    #[test]
1190    fn placed_strings_are_laid_in_the_order_of_the_result() {
1191        let word = |text: &str| Value::Varchar(text.to_string());
1192        let flat = Vector::from_values(
1193            LogicalType::Varchar,
1194            &[word("the first string past twelve bytes"), Value::Null, word("short")],
1195        )
1196        .expect("flat");
1197        let arena = b"xxa second string past twelve bytesyy".to_vec();
1198        let views = vec![StringView::over(&arena[2..35], 2), StringView::inline("tiny")];
1199        let viewed =
1200            Vector::string_views(LogicalType::Varchar, views, Arc::new(Buffer::from_vec(arena)))
1201                .expect("views");
1202        let pieces = [flat, viewed];
1203        let order = [3, 0, 4, 2, 1];
1204        let mut inverse = vec![0u32; order.len()];
1205        for (to, &from) in order.iter().enumerate() {
1206            inverse[from] = u32::try_from(to).expect("a small row");
1207        }
1208        let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1209        let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1210        let got = interleave_placed(&LogicalType::Varchar, &pieces, &order, Some(&inverse))
1211            .expect("a placed interleave");
1212        assert_eq!(values(&got), expected);
1213        let (_, arena) = got.text_parts().expect("views");
1214        assert_eq!(
1215            arena, b"a second string past twelve bytesthe first string past twelve bytes",
1216            "the long strings in the order they come out, and nothing else"
1217        );
1218        assert!(strings_placeable(&LogicalType::Varchar, &pieces));
1219        for split in 0..=order.len() {
1220            let mut joined = Vec::new();
1221            for range in [0..split, split..order.len()] {
1222                let part = placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, range)
1223                    .expect("a range of rows");
1224                joined.extend(values(&part));
1225            }
1226            assert_eq!(joined, expected, "split at {split}");
1227        }
1228        let (_, arena) = placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, 1..3)
1229            .expect("the middle rows")
1230            .text_parts()
1231            .map(|(views, arena)| (views.len(), arena.to_vec()))
1232            .expect("views");
1233        assert_eq!(arena, b"the first string past twelve bytes", "only the range's own strings");
1234        assert!(placed_string_rows(&LogicalType::Varchar, &pieces, &inverse, 4..6).is_err());
1235    }
1236
1237    #[test]
1238    fn an_interleave_of_dictionaries_merges_them_into_one() {
1239        let word = |text: &str| Value::Varchar(text.to_string());
1240        let first = [word("MAIL"), word("a word long enough to leave the inline view")];
1241        let second = [Value::Null, word("MAIL"), word("SHIP")];
1242        let first = Arc::new(Vector::from_values(LogicalType::Varchar, &first).expect("words"));
1243        let second = Arc::new(Vector::from_values(LogicalType::Varchar, &second).expect("words"));
1244        let over = |codes: Vec<u32>, dictionary: &Arc<Vector>| {
1245            Vector::dictionary_over(codes, Arc::clone(dictionary)).expect("a dictionary")
1246        };
1247        let pieces = [
1248            over((0..16).map(|row| row % 2).collect(), &first),
1249            over((0..16).map(|row| row % 3).collect(), &second),
1250            over(vec![1; 8], &first),
1251        ];
1252        let order: Vec<usize> = (0..40).rev().collect();
1253        let laid: Vec<Value> = pieces.iter().flat_map(values).collect();
1254        let expected: Vec<Value> = order.iter().map(|&index| laid[index].clone()).collect();
1255        let got = interleave(&LogicalType::Varchar, &pieces, &order).expect("an interleave");
1256        assert_eq!(values(&got), expected);
1257        let (_, merged) = got.stable_dictionary_parts().expect("one stable dictionary");
1258        assert_eq!(merged.len(), 4, "MAIL once, the long word, the null and SHIP");
1259        let mut inverse = vec![0u32; order.len()];
1260        for (to, &from) in order.iter().enumerate() {
1261            inverse[from] = u32::try_from(to).expect("a small row");
1262        }
1263        let placed = interleave_placed(&LogicalType::Varchar, &pieces, &order, Some(&inverse))
1264            .expect("a placed interleave");
1265        assert_eq!(values(&placed), expected, "placed codes land where pulled ones do");
1266        assert!(placed.stable_dictionary_parts().is_some(), "and stay one dictionary");
1267
1268        let mixed = [pieces[0].clone(), pieces[1].flatten().expect("flat")];
1269        let got = interleave(&LogicalType::Varchar, &mixed, &order[8..]).expect("an interleave");
1270        assert!(got.dictionary_parts().is_none(), "a flat piece gathers flat");
1271        let few = &pieces[..1];
1272        let got = interleave(&LogicalType::Varchar, few, &[3, 2]).expect("an interleave");
1273        assert_eq!(
1274            values(&got),
1275            vec![word("a word long enough to leave the inline view"), word("MAIL")]
1276        );
1277    }
1278}