Skip to main content

rudb_native/
lib.rs

1//! Rudb's single-file columnar snapshot format.
2//!
3//! A committed directory names independently readable column pages. It has two levels: a catalog
4//! directory naming every table in the file, which is what a footer slot points at and what opening
5//! a database reads, and one directory per table under it holding that table's stripes, pages and
6//! statistics. One slot write publishes all of them, so a commit is atomic across tables.
7//!
8//! This version handles scalar columns; the file header has two generation slots so an unfinished
9//! replacement directory cannot hide the last complete one. See
10//! `spec/storage-v3/12-many-tables-in-one-file.md`.
11//!
12//! # Parts and stripes
13//!
14//! A part is one appended chunk, which is a thousand rows, and it is the unit a scan decodes and
15//! hands to the pipeline. A stripe is sixty four parts, and it is the unit the directory describes
16//! and the unit the file is laid out in: one page per column per stripe, holding that column's
17//! sixty four part payloads end to end.
18//!
19//! The two are separate because they are sized by different pressures. A part wants to be small
20//! because it is a vector and vectors live in cache. A stripe wants to be large because everything
21//! the directory holds is per stripe and the directory is one buffer that has to be read and
22//! decoded before a single row can be answered. A hundred million rows of the hundred and five
23//! column ClickBench table is ninety seven thousand parts, and a directory with a page entry and a
24//! pair of bounds per part per column is several hundred megabytes, which is what made that load
25//! fail before this split existed. Sixty four parts to a stripe divides that by sixty four.
26//!
27//! Where the parts of a page start is not in the directory either, for the same reason. Each
28//! stripe writes one index page holding a length and a checksum per part per column, and a reader
29//! preads the sixty four entries belonging to the column it wants. A scan reads the whole column
30//! page once and slices it; a sparse row fetch reads the index entries and then only the part it
31//! needs.
32
33#![forbid(unsafe_code)]
34
35use std::borrow::Cow;
36use std::cmp::Ordering;
37use std::collections::{HashMap, VecDeque};
38use std::fs::{File, OpenOptions};
39use std::io::{Read, Seek, SeekFrom};
40use std::mem::{size_of, size_of_val};
41use std::path::Path;
42use std::slice;
43use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as Atomic};
44use std::sync::{Arc, Mutex, OnceLock};
45
46use rudb_common::bounds::{self, Bound, Op, scaled_as};
47use rudb_common::{Clustering, Error, Field, LogicalType, PhysicalType, Result, Value, Width};
48use rudb_encoding::{bitpack, chooser, integer, string};
49use rudb_metrics::{LoadProfile, Stage};
50use rudb_storage::sieve::Sieve;
51use rudb_storage::{Probe, Range, Zone};
52use rudb_vector::string::StringColumn;
53use rudb_vector::validity::Validity;
54use rudb_vector::{Buffer, Chunk, Data, Packed, TextSource, Vector, search_below};
55
56mod distinct;
57pub mod graph;
58pub mod host;
59mod prepare;
60pub mod section;
61pub mod stats;
62mod zones;
63
64pub use prepare::{Merged, Paged, Prepared, Preparer};
65pub use section::Section;
66pub use zones::{Common, Stripes, ascending, distincts};
67
68const MAGIC: &[u8; 8] = b"RUDBNV10";
69const DIRECTORY: &[u8; 8] = b"RUDBDI10";
70const CATALOG: &[u8; 8] = b"RUDBCA10";
71const FORMAT: u32 = 28;
72
73/// Formats this build can open.
74///
75/// More than one, for the first time, and the reason is spec/graph/10-milestones.md's G1 exit
76/// criterion: a build with the section table in it has to open a file written before the section
77/// table existed, unchanged and without a rewrite. Formats 22 and 23 are those files, and both read
78/// as a table with an empty section table, which is exactly what section 3.1 says a table with no
79/// graph sections is.
80///
81/// All three of the older ones are readable for the same reason. What took the format from 22 to 23
82/// was tags for fourteen more column types, and a file written before that has none of them in it,
83/// so nothing in an older file is a tag this build cannot read. What took it from 23 to 24 is the
84/// section table, which a file written before it simply does not have. What takes it from 24 to 25
85/// is the view section on the end of the catalog, which an older file does not have either, and a
86/// catalog that ends where the tables end reads as a catalog with no views in it. What takes it
87/// from 25 to 26 is that a global dictionary's payload blocks now say where they are, and a file
88/// written before that has them behind one another, which [`open_global_dictionary`] reads by
89/// turning the ends it finds into the same places the newer files name outright. What takes it
90/// from 26 to 27 is that those blocks are written into the file as the load goes, between the
91/// stripes, rather than behind the dictionary's index at the end, so the dictionary's page is the
92/// index and the sorted order and nothing else. A format 26 file has its blocks inside the page,
93/// and the reader tells the two apart by whether the page has room left over for them.
94///
95/// Format 28 adds per-payload-block substring signatures to global string dictionaries. Older
96/// files have no signatures and use the ordinary exact string filter.
97///
98/// This is not a general compatibility promise. Seven formats are readable because there was a
99/// specific reason for each, and the list shrinks again the moment the older ones stop being worth
100/// carrying.
101const READABLE: &[u32] = &[22, 23, 24, 25, 26, 27, FORMAT];
102
103const HEADER: u64 = 80;
104const SLOT_BYTES: usize = 28;
105const MAX_PAGE: usize = 256 * 1024 * 1024;
106const MAX_DIRECTORY: usize = 128 * 1024 * 1024;
107const FREQUENCIES_V2: &[u8; 8] = b"RUDBFQ2\0";
108const FREQUENCIES: &[u8; 8] = b"RUDBFQ3\0";
109/// Inline spellings for string entries in the bounded frequency synopsis.
110///
111/// A planner usually asks about one literal such as the empty string. Without this block it opens
112/// a multi-million-value global dictionary and visits the payload blocks of every retained entry
113/// merely to compare that literal with at most 512 heavy hitters. The spellings are already in
114/// memory while the writer sorts the dictionary, so storing this bounded copy makes planning a
115/// directory read and leaves the dictionary unopened.
116const FREQUENCY_TEXTS: &[u8; 8] = b"RUDBFT1\0";
117/// Certified host aggregate state for the version-one anchored replacement expression.
118const HOST_GROUPS: &[u8; 8] = b"RUDBHG1\0";
119/// Exact leading counts for a bounded pair of dictionary-backed grouping keys.
120///
121/// This is a separate optional directory block rather than another frequency format. Readers that
122/// predate it still understand every earlier directory, and a table without a pair worth keeping
123/// writes no block at all.
124const PAIR_FREQUENCIES: &[u8; 8] = b"RUDBPF1\0";
125/// The clustering declaration, written after the frequencies and only when there is one.
126///
127/// No format bump for this, which is the convention the frequency section set in #728: a new
128/// optional trailing section with its own magic leaves every file that does not use it byte for
129/// byte what it was, and the version is bumped for a change to a layout that already exists, as
130/// #1029 did. A file with no declaration is the same bytes this build wrote yesterday.
131///
132/// The width byte in this block gained a fifth value for #1285, for a declaration that leaves the
133/// bucket to the row count, and that did not bump the format either. It is the one case where the
134/// reasoning needs saying out loud, because it is a new value in a layout that already exists
135/// rather than a new section. A build without it reading one of these says `clustering width
136/// tag differs` and refuses the table, which is what that message was written for. Bumping the
137/// format instead would have made every file this build writes unreadable to an older one, whether
138/// it has a declaration in it or not, to warn about a case that only arises when it does.
139const CLUSTERING: &[u8; 8] = b"RUDBCL1\0";
140/// The graph section table, written after the clustering declaration and written even when empty.
141///
142/// Same convention and the same reason as the block above it, with one difference: this one is
143/// always there, so a file written by this build says which sections it has rather than leaving a
144/// reader to infer it from where the bytes ran out. Section 3.1 of the graph spec is what makes
145/// that safe to add without a format bump, because a table with no sections answers every query
146/// the way it did before, only without the graph path.
147const SECTIONS: &[u8; 8] = b"RUDBSE1\0";
148/// How many bytes of each column's global dictionary live outside its page, written only when any do.
149///
150/// From format 27 a dictionary's payload blocks are written into the file while the load runs, so
151/// they sit between the stripes and the dictionary's page covers only its index and sorted order.
152/// Nothing needs the total to read the file, because the index names every block. It is here for
153/// what a file costs a column, which [`Reader::layout`] and the statistics budget both report, and
154/// which would otherwise lose most of the bytes of every large string column.
155const DICTIONARY_PAYLOADS: &[u8; 8] = b"RUDBDP1\0";
156
157/// The most sections one table's directory may name.
158///
159/// A relationship contributes at most three sections, so this bounds a table at a few thousand
160/// relationships, which is far past anything a schema has. The bound is here so that a torn
161/// directory naming four billion of them is refused at decode rather than turned into an
162/// allocation, the same reason the extent count has one.
163const MAX_SECTIONS: usize = 4096;
164const FREQUENCY_CANDIDATES: usize = 32_768;
165const FREQUENCY_ENTRIES: usize = 512;
166const FREQUENCY_BUILD_RANK: usize = 10;
167const FREQUENCY_ORDINALS: usize = 131_072;
168const MAX_PAIR_FREQUENCIES: usize = 1024;
169/// The most exact heavy-hitter text one column may copy into the directory.
170///
171/// A column with unusually large leading values keeps the old code-only synopsis instead. The
172/// optimization must never turn a valid load into a directory-size failure.
173const FREQUENCY_TEXT_BUDGET: usize = 1024 * 1024;
174/// The most threads the two per column passes at the end of a commit are spread over.
175///
176/// A table like `hits` has ninety numeric columns, so on a machine with more cores than this the
177/// cap is what decides how long the frequencies take rather than the columns are. It is here at all
178/// because each worker holds a candidate table and a decoded part, and a hundred of those at once
179/// on a narrow machine would be worse than waiting.
180const MAX_FREQUENCY_WORKERS: usize = 32;
181
182/// How many threads the passes at the end of a commit are spread over on this machine.
183fn close_workers() -> usize {
184    std::thread::available_parallelism().map_or(1, usize::from).min(MAX_FREQUENCY_WORKERS)
185}
186
187/// The most threads one stripe's encode is spread over.
188///
189/// Higher than the frequency cap because this is the load itself rather than a pass at the end of
190/// it, and the work is one column of sixty four parts, which is large enough that a thread that
191/// takes one is not a thread that was started for nothing. A machine with more cores than this has
192/// the rest of them on the Parquet read, which is still one thread and is the other half of #808.
193const MAX_ENCODE_WORKERS: usize = 32;
194
195/// The most bytes one column of one part may spend on a membership sieve.
196///
197/// A part is a thousand rows, so a filter sized for every one of them being distinct is about
198/// thirteen hundred bytes and this never binds in practice. It is here so that a part that somehow
199/// arrives much wider than a vector cannot put an unbounded index in the file. What does bind is the
200/// rule in `Writer::encode_pages` that a sieve may not be as large as the part it indexes, which is a cap
201/// per column rather than one number for the whole file.
202const SIEVE_BUDGET: usize = 8 * 1024;
203
204/// The most bytes one end of a per part range may spend on a string.
205///
206/// A bound is allowed to be wider than the truth and never narrower, so a long string is cut down to
207/// this many bytes for the low end and cut down and then stepped up for the high end. The reason for
208/// a cap at all is that there are nine hundred and seventy four parts of a hundred and five columns
209/// in a million rows of ClickBench and `URL` runs to hundreds of bytes, so keeping every end whole
210/// would put more in the directory than the skipping is worth. Twenty four bytes is past the point
211/// where two URLs of the same site still look alike.
212const PART_BOUND_BYTES: usize = 24;
213
214fn io(error: std::io::Error) -> Error {
215    Error::io(error.to_string())
216}
217
218fn invalid(message: &str) -> Error {
219    Error::invalid_input(format!("invalid rudb native file: {message}"))
220}
221
222/// Adds a sequence of byte counts without an overflow the caller has to think about.
223fn sum(counts: impl Iterator<Item = u64>) -> u64 {
224    counts.fold(0, u64::saturating_add)
225}
226
227/// One column's span out of a per column list, or zero when the list is shorter than the column.
228fn span_bytes(spans: &[Span], at: usize) -> u64 {
229    spans.get(at).map_or(0, |span| u64::from(span.length))
230}
231
232/// One column's page out of a per column list, or zero when that column has no page at all.
233fn page_bytes(pages: &[Option<Page>], at: usize) -> u64 {
234    pages.get(at).and_then(Option::as_ref).map_or(0, Page::bytes)
235}
236
237/// Everything one column's global dictionary costs the file, its page and the blocks outside it.
238fn dictionary_bytes(table: &Table, at: usize) -> u64 {
239    page_bytes(&table.dictionaries, at)
240        .saturating_add(table.dictionary_payloads.get(at).copied().unwrap_or(0))
241}
242
243/// The xxHash64 of `bytes`, which is what every span this format stores is checked against.
244///
245/// It walks the input as chunks rather than as offsets into it, and that is the only thing about it
246/// worth a comment. The offset form reads `bytes[at..at + 8]`, and neither the slicing nor the
247/// `try_into` behind it can be proved in range by a compiler that does not know where `at` stopped,
248/// so each of the four lanes paid for a bounds check and a length check on every thirty two bytes.
249/// A chunk carries its own length, so both fold away and the loop is the multiplies and rotates it
250/// was meant to be. That loop runs over every byte of every span a query reads, which on ClickBench
251/// 8 is about five percent of the query.
252fn checksum(bytes: &[u8]) -> u64 {
253    seeded_checksum(bytes, 0)
254}
255
256/// A hundred and twenty eight bit name for `bytes`, as two xxHash64 walks under different seeds,
257/// with the format this build writes folded in so that a name made by one format is never taken
258/// for the name of a file in another.
259///
260/// For a caller outside this crate that has to name a file by what went into it, which is what a
261/// Parquet mirror's key is. See the global dictionary's use of the same pair for the arithmetic.
262#[must_use]
263pub fn content_name(bytes: &[u8]) -> u128 {
264    let seed = u64::from(FORMAT);
265    u128::from(seeded_checksum(bytes, seed)) << 64 | u128::from(seeded_checksum(bytes, !seed))
266}
267
268/// The xxHash64 of `bytes` started from `seed`, which is the same walk with a different beginning.
269///
270/// A seed is here for one caller: a global dictionary decides whether two values are the same by
271/// their hashes rather than by their bytes, and one sixty four bit hash is not enough to do that
272/// with. Twenty million distinct values collide on sixty four bits about once in a hundred thousand
273/// loads, which for a wrong answer is far too often. Two hashes of the same value under different
274/// seeds are independent, so the pair is a hundred and twenty eight bits and the same arithmetic
275/// puts that at around one in 1e24.
276fn seeded_checksum(bytes: &[u8], seed: u64) -> u64 {
277    // Asked for before the loop rather than after it, because a `ChunksExact` settles what it
278    // cannot divide when it is built and hands back the same tail whether it has been walked or not.
279    let mut blocks = bytes.chunks_exact(32);
280    let rest = blocks.remainder();
281    if bytes.len() < 32 {
282        return checksum_tail(seed.wrapping_add(XXH_P5).wrapping_add(bytes.len() as u64), rest);
283    }
284    let mut lanes = [
285        seed.wrapping_add(XXH_P1).wrapping_add(XXH_P2),
286        seed.wrapping_add(XXH_P2),
287        seed,
288        seed.wrapping_sub(XXH_P1),
289    ];
290    for block in blocks.by_ref() {
291        checksum_block(&mut lanes, block);
292    }
293    finish_checksum(lanes, rest, bytes.len() as u64)
294}
295
296const XXH_P1: u64 = 11_400_714_785_074_694_791;
297const XXH_P2: u64 = 14_029_467_366_897_019_727;
298const XXH_P3: u64 = 1_609_587_929_392_839_161;
299const XXH_P4: u64 = 9_650_029_242_287_828_579;
300const XXH_P5: u64 = 2_870_177_450_012_600_261;
301
302fn checksum_round(state: u64, word: u64) -> u64 {
303    state.wrapping_add(word.wrapping_mul(XXH_P2)).rotate_left(31).wrapping_mul(XXH_P1)
304}
305
306fn checksum_word(chunk: &[u8]) -> u64 {
307    u64::from_le_bytes(chunk.try_into().expect("eight checksum bytes"))
308}
309
310/// One thirty two byte block into the four lanes.
311fn checksum_block(lanes: &mut [u64; 4], block: &[u8]) {
312    for (lane, chunk) in lanes.iter_mut().zip(block.chunks_exact(8)) {
313        *lane = checksum_round(*lane, checksum_word(chunk));
314    }
315}
316
317/// The lanes after every whole block, folded together with what was left over and the length.
318fn finish_checksum(lanes: [u64; 4], rest: &[u8], length: u64) -> u64 {
319    let merge = |state: u64, lane: u64| {
320        (state ^ checksum_round(0, lane)).wrapping_mul(XXH_P1).wrapping_add(XXH_P4)
321    };
322    let [one, two, three, four] = lanes;
323    let combined = one
324        .rotate_left(1)
325        .wrapping_add(two.rotate_left(7))
326        .wrapping_add(three.rotate_left(12))
327        .wrapping_add(four.rotate_left(18));
328    let hash = merge(merge(merge(merge(combined, one), two), three), four);
329    checksum_tail(hash.wrapping_add(length), rest)
330}
331
332/// The fewer than thirty two bytes after the last whole block, and the final mix.
333fn checksum_tail(mut hash: u64, mut rest: &[u8]) -> u64 {
334    let mut words = rest.chunks_exact(8);
335    for chunk in words.by_ref() {
336        hash ^= checksum_round(0, checksum_word(chunk));
337        hash = hash.rotate_left(27).wrapping_mul(XXH_P1).wrapping_add(XXH_P4);
338    }
339    rest = words.remainder();
340    if rest.len() >= 4 {
341        let (head, tail) = rest.split_at(4);
342        let quarter = u32::from_le_bytes(head.try_into().expect("four checksum bytes"));
343        hash ^= u64::from(quarter).wrapping_mul(XXH_P1);
344        hash = hash.rotate_left(23).wrapping_mul(XXH_P2).wrapping_add(XXH_P3);
345        rest = tail;
346    }
347    for &byte in rest {
348        hash ^= u64::from(byte).wrapping_mul(XXH_P5);
349        hash = hash.rotate_left(11).wrapping_mul(XXH_P1);
350    }
351    hash ^= hash >> 33;
352    hash = hash.wrapping_mul(XXH_P2);
353    hash ^= hash >> 29;
354    hash = hash.wrapping_mul(XXH_P3);
355    hash ^ (hash >> 32)
356}
357
358/// The checksum of `length` bytes of `file` from `offset`, read [`DIRECTORY_WINDOW`] at a time.
359///
360/// The same xxHash64 as [`checksum`], carried across reads rather than over one buffer, so that a
361/// directory can be checked without all of it being in memory at once. The four lanes take whole
362/// thirty two byte blocks, and a read that ends partway through one keeps the tail for the next.
363fn file_checksum(file: &File, offset: u64, length: usize) -> Result<u64> {
364    if length < 32 {
365        let mut bytes = vec![0; length];
366        read_at(file, offset, &mut bytes)?;
367        return Ok(checksum(&bytes));
368    }
369    let mut lanes = [XXH_P1.wrapping_add(XXH_P2), XXH_P2, 0, 0_u64.wrapping_sub(XXH_P1)];
370    let mut buffer = vec![0; DIRECTORY_WINDOW.min(length)];
371    let mut kept = 0;
372    let mut read = 0;
373    while read < length {
374        let want = (buffer.len() - kept).min(length - read);
375        read_at(file, offset + read as u64, &mut buffer[kept..kept + want])?;
376        read += want;
377        let filled = kept + want;
378        let whole = filled / 32 * 32;
379        for block in buffer[..whole].chunks_exact(32) {
380            checksum_block(&mut lanes, block);
381        }
382        buffer.copy_within(whole..filled, 0);
383        kept = filled - whole;
384    }
385    Ok(finish_checksum(lanes, &buffer[..kept], length as u64))
386}
387
388#[derive(Debug, Clone, Copy)]
389struct Slot {
390    offset: u64,
391    length: u32,
392    generation: u64,
393    hash: u64,
394}
395
396impl Slot {
397    fn bytes(self) -> [u8; SLOT_BYTES] {
398        let mut result = [0; SLOT_BYTES];
399        result[..8].copy_from_slice(&self.offset.to_le_bytes());
400        result[8..12].copy_from_slice(&self.length.to_le_bytes());
401        result[12..20].copy_from_slice(&self.generation.to_le_bytes());
402        result[20..28].copy_from_slice(&self.hash.to_le_bytes());
403        result
404    }
405
406    fn read(bytes: &[u8]) -> Self {
407        Self {
408            offset: u64::from_le_bytes(bytes[..8].try_into().expect("eight bytes")),
409            length: u32::from_le_bytes(bytes[8..12].try_into().expect("four bytes")),
410            generation: u64::from_le_bytes(bytes[12..20].try_into().expect("eight bytes")),
411            hash: u64::from_le_bytes(bytes[20..28].try_into().expect("eight bytes")),
412        }
413    }
414}
415
416#[derive(Debug, Clone, Copy)]
417struct Page {
418    offset: u64,
419    length: u32,
420    hash: u64,
421}
422
423impl Page {
424    /// How much of the file this page takes, for [`Reader::layout`].
425    fn bytes(&self) -> u64 {
426        u64::from(self.length)
427    }
428}
429
430#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
431enum FrequencyValue {
432    Null,
433    Integer(i128),
434    Code(u32),
435}
436
437/// A table keyed by the sixty four bits of the values the numeric frequency pass counts.
438///
439/// Every integer of every numeric column goes through one of these at least once when a table
440/// closes, and with the standard hasher that was a fifth of the close on its own, all of it SipHash
441/// guarding against an attacker who would have to choose the rows of the file being written.
442type FrequencyMap<V> = HashMap<u64, V, Spread>;
443
444/// Builds the hasher for [`FrequencyMap`].
445#[derive(Debug, Default, Clone, Copy)]
446struct Spread;
447
448impl std::hash::BuildHasher for Spread {
449    type Hasher = SpreadHasher;
450
451    fn build_hasher(&self) -> SpreadHasher {
452        SpreadHasher(0)
453    }
454}
455
456/// Folds each word in with a full width multiply whose two halves are xored together.
457///
458/// A plain multiply leaves the low bits of the hash as poor as the low bits of the key, and the
459/// table picks its bucket from the low bits, so a timestamp column, whose values are all multiples
460/// of a million microseconds, would pile into a sixty fourth of the buckets. Folding the high half
461/// of the product back in is what gives the low bits the whole word.
462#[derive(Debug)]
463struct SpreadHasher(u64);
464
465impl SpreadHasher {
466    fn mix(&mut self, word: u64) {
467        let product = u128::from(self.0 ^ word) * 0x9E37_79B9_7F4A_7C15_u128;
468        self.0 = (product as u64) ^ ((product >> 64) as u64);
469    }
470}
471
472impl std::hash::Hasher for SpreadHasher {
473    fn write(&mut self, bytes: &[u8]) {
474        for part in bytes.chunks(8) {
475            let mut word = [0; 8];
476            word[..part.len()].copy_from_slice(part);
477            self.mix(u64::from_le_bytes(word));
478        }
479    }
480
481    fn write_u32(&mut self, value: u32) {
482        self.mix(u64::from(value));
483    }
484
485    fn write_u64(&mut self, value: u64) {
486        self.mix(value);
487    }
488
489    fn write_i128(&mut self, value: i128) {
490        self.mix(value as u64);
491        self.mix((value >> 64) as u64);
492    }
493
494    fn write_isize(&mut self, value: isize) {
495        self.mix(value as u64);
496    }
497
498    fn finish(&self) -> u64 {
499        self.0
500    }
501}
502
503#[derive(Debug, Clone)]
504struct FrequencyEntry {
505    value: FrequencyValue,
506    count: u64,
507}
508
509/// Exact leading frequencies for one column.
510///
511/// Values outside `entries` occur at most `omitted_max` times. This lets a count-descending TopN
512/// use the synopsis only when its last winner is strictly above every omitted value.
513#[derive(Debug, Clone)]
514struct FrequencySummary {
515    entries: Vec<FrequencyEntry>,
516    omitted_max: u64,
517    ordinals: Vec<u64>,
518    ordinal_entries: Vec<u16>,
519}
520
521#[derive(Debug, Clone)]
522struct PairFrequencyEntry {
523    first_entry: u16,
524    second: Option<u32>,
525    count: u64,
526}
527
528/// Exact leading counts for one numeric frequency anchor and one stable string code space.
529///
530/// `omitted_max` covers both first-key values outside the numeric synopsis and pairs below the
531/// retained prefix. A TopN may therefore use the entries only when its boundary strictly exceeds
532/// this number.
533#[derive(Debug, Clone)]
534struct PairFrequencySummary {
535    first: u16,
536    second: u16,
537    entries: Vec<PairFrequencyEntry>,
538    omitted_max: u64,
539}
540
541/// One column's frequency synopsis, in memory or left where it is in the file.
542///
543/// A writer holds what it counted. A reader leaves every synopsis in the file and reads one back
544/// when a query asks about its column, because they are the largest thing in a directory once they
545/// are decoded, forty eight bytes an entry and nearly twenty thousand entries over `hits`, and
546/// most queries ask about none of them. Where one sits is found at open, by reading it through and
547/// checking it, so a torn synopsis is still refused when the table is opened.
548#[derive(Debug, Clone)]
549enum Frequencies {
550    Held(FrequencySummary),
551    /// Where the synopsis sits, and whether it was written with the value of each ordinal, which
552    /// is what the directory's frequency magic says and the synopsis itself does not.
553    Stored {
554        span: Span,
555        values: bool,
556    },
557}
558
559/// The values one column's frequency synopsis lists, with a bound on everything it left out.
560///
561/// What [`Reader::frequency_prefix`] answers. The counts are exact, and `omitted_max` is how many
562/// rows any value not in the list can hold, which is zero when nothing was left out at all.
563#[derive(Debug, Clone)]
564pub struct FrequencyPrefix {
565    /// Every value the synopsis lists, with the number of rows holding it, count descending.
566    pub entries: Vec<(Value, u64)>,
567    /// How many rows the most common value outside the list holds, and zero for a complete list.
568    pub omitted_max: u64,
569}
570
571/// Sparse row ordinals covered by a numeric frequency candidate set.
572#[derive(Debug, Clone, PartialEq)]
573pub struct FrequencyOccurrences {
574    /// Upper bound for the frequency of every value absent from the fetched rows.
575    pub omitted_max: u64,
576    /// Table-wide row ordinals in ascending order.
577    pub ordinals: Vec<u64>,
578    /// The retained heavy-hitter values named by `anchor_indices`.
579    pub anchors: Vec<Value>,
580    /// The index in `anchors` at each ordinal, or empty for a legacy FQ2 directory.
581    pub anchor_indices: Vec<u16>,
582}
583
584/// Exact grouped counts for a pair of values, in descending count order.
585pub type PairFrequencyCounts = Vec<(Vec<Value>, u64)>;
586
587/// Where one column's page for one stripe sits in the file.
588///
589/// A column page has no checksum of its own because every part inside it carries one, and the
590/// stripe's index page holds those. Checking a part on the way out of the page covers exactly the
591/// bytes a reader is about to decode, and covers them once whether the reader took the whole page
592/// or pulled one part out of the middle of it.
593#[derive(Debug, Clone, Copy, Default)]
594struct Span {
595    offset: u64,
596    length: u32,
597}
598
599/// One optional page for each column of a stripe, holding only the pages that are there.
600///
601/// A stripe has three of these, the membership, sieve and part range pages. As a
602/// `Vec<Option<Page>>` each was thirty two bytes a column whether the page was there or not, and
603/// over the ten million rows of `hits` that is half a megabyte at open for 7171 pages out of 16380
604/// slots. Kept sparse and packed, a page that is there is twenty four bytes and one that is not is
605/// nothing.
606#[derive(Debug, Clone, Default)]
607struct Pages {
608    columns: usize,
609    held: Box<[StripePage]>,
610}
611
612/// A page and the column it is for, packed so that the column sits where the padding was.
613#[derive(Debug, Clone, Copy)]
614struct StripePage {
615    offset: u64,
616    hash: u64,
617    length: u32,
618    column: u32,
619}
620
621impl Pages {
622    /// The pages of `columns` columns, one slot each in column order.
623    fn from_slots(slots: Vec<Option<Page>>) -> Result<Self> {
624        let mut held = Vec::with_capacity(slots.iter().flatten().count());
625        for (column, page) in slots.iter().enumerate() {
626            if let Some(page) = page {
627                let column =
628                    u32::try_from(column).map_err(|_| invalid("too many columns for a page"))?;
629                held.push(StripePage {
630                    offset: page.offset,
631                    hash: page.hash,
632                    length: page.length,
633                    column,
634                });
635            }
636        }
637        Ok(Self { columns: slots.len(), held: held.into_boxed_slice() })
638    }
639
640    /// The page of one column, if it has one.
641    fn get(&self, column: usize) -> Option<Page> {
642        let at = self.held.binary_search_by_key(&column, |placed| placed.column as usize).ok()?;
643        let placed = self.held[at];
644        Some(Page { offset: placed.offset, length: placed.length, hash: placed.hash })
645    }
646
647    /// One slot per column, in column order, the way the directory writes them.
648    fn slots(&self) -> impl Iterator<Item = Option<Page>> + '_ {
649        (0..self.columns).map(|column| self.get(column))
650    }
651
652    /// How much of the file one column's page takes, or zero when it has none.
653    fn bytes(&self, column: usize) -> u64 {
654        self.get(column).map_or(0, |page| page.bytes())
655    }
656}
657
658/// One independently readable stripe of a table.
659#[derive(Debug, Clone)]
660pub struct Stripe {
661    rows: usize,
662    /// Rows in each part, in source order. Kept in the directory so that mapping a row ordinal to a
663    /// part, which every sparse fetch does, never reads the file.
664    parts: Vec<u32>,
665    /// The index page: one section per column, holding a length and a checksum for every part and
666    /// then a checksum of the section itself, so that a reader can pread one column's section and
667    /// still know it is intact.
668    index: Span,
669    pages: Vec<Span>,
670    memberships: Pages,
671    /// One page per column holding the membership sieve of every part of the stripe, for the
672    /// columns that have one. A column whose parts all declined a sieve has no page at all.
673    sieves: Pages,
674    /// One page per column holding the two ends and the null count of every part of the stripe.
675    ///
676    /// The stripe's own `zone` below covers sixty four times as many rows, and on a column that is
677    /// not the one the rows are ordered by that is the difference between skipping half the file and
678    /// skipping all but three percent of it. On ClickBench 24 the cutoff the answer settles at
679    /// leaves eight stripes of sixteen alive and thirty parts of nine hundred and seventy four.
680    ///
681    /// A page per column rather than one page for the stripe, so that a query that compares one
682    /// column reads the ends of that column and not of the hundred and four beside it. Read lazily
683    /// for the same reason, like the sieves.
684    part_ranges: Pages,
685    zone: Zone,
686}
687
688impl Stripe {
689    /// Number of rows in this stripe.
690    #[must_use]
691    pub fn rows(&self) -> usize {
692        self.rows
693    }
694
695    /// Number of parts in this stripe.
696    #[must_use]
697    pub fn parts(&self) -> usize {
698        self.parts.len()
699    }
700
701    /// The two ends and the null count of every column over the whole stripe.
702    ///
703    /// In the directory and so in memory, which is what makes it the one a planner can ask. The
704    /// finer ones are a page per column per stripe in the file, read by [`Reader::skips`] when a
705    /// scan wants to know which parts to open.
706    #[must_use]
707    pub fn zone(&self) -> &Zone {
708        &self.zone
709    }
710}
711
712/// The committed table directory.
713#[derive(Debug, Clone)]
714pub struct Table {
715    name: String,
716    fields: Vec<Field>,
717    stripes: Vec<Stripe>,
718    rows: usize,
719    dictionaries: Vec<Option<Page>>,
720    /// Bytes of each column's dictionary payload that are outside its page, which is all of them
721    /// from format 27 and none of them before. See [`DICTIONARY_PAYLOADS`].
722    ///
723    /// Empty rather than a row of zeros on a table that has none, and read with `get` for that
724    /// reason, so that a table built by hand in a test does not have to know about it.
725    dictionary_payloads: Vec<u64>,
726    frequencies: Vec<Option<Frequencies>>,
727    pair_frequencies: Vec<PairFrequencySummary>,
728    /// String spellings aligned with each column's frequency entries.
729    ///
730    /// Empty for files written before `RUDBFT1`. A `None` entry is the null frequency entry; every
731    /// code entry in a column named by the block has its exact bytes here.
732    frequency_texts: Vec<Vec<Option<Vec<u8>>>>,
733    /// Exact candidate host aggregates and an upper bound for every omitted host.
734    host_groups: Option<host::HostSummary>,
735    /// How many distinct values each column holds, for the columns that know.
736    ///
737    /// A dictionary entry is made the first time a value is seen and nothing ever removes one, so
738    /// the size of the dictionary is the number of distinct values in the column. That is the whole
739    /// story for a column with no null in it, and the wrong number by one for a column with a null
740    /// in it, because a null row is written as the code for the empty string and makes an entry the
741    /// dictionary would not otherwise have. The writer knows which case it is, since it counts the
742    /// non-null rows that use each code while it builds the frequency summary, and the reader cannot
743    /// work it out from the dictionary alone. So the writer settles it here.
744    distincts: Vec<Option<u64>>,
745    /// The order the rows of this table are meant to be stored in, if anybody declared one.
746    ///
747    /// A declaration and not a measurement. Nothing here checks that the stripes actually arrived
748    /// in this order, and the reason it is worth storing anyway is that the order is the only thing
749    /// about a table that a rewrite destroys without anybody noticing. The fragment ranges prune on
750    /// whatever order the rows came in, so a table loaded sorted prunes and the same table after a
751    /// checkpoint that did not know to keep the order quietly stops pruning and nothing says why.
752    clustering: Option<Clustering>,
753    /// The file generation of the commit that last wrote this table's column pages.
754    ///
755    /// This is what spec/graph/03-the-file-format.md section 3.2 calls the table generation, and
756    /// the definition is deliberately about the pages rather than about the directory. A graph
757    /// section is a restatement of a column in terms of row ids, so what invalidates one is the
758    /// rows being renumbered, and nothing else. Adding a second table to the file, or attaching a
759    /// section to this one, commits a new file generation without touching a single row of this
760    /// table, and a definition that moved with those would declare every section in the file stale
761    /// for no reason.
762    ///
763    /// Zero on a table written before format 23, where nothing recorded it. Real generations start
764    /// at one, so zero can never match a section's stamp, and a table from format 22 has no
765    /// sections for it to match anyway.
766    generation: u64,
767    /// The graph sections this table carries, per spec/graph/03-the-file-format.md section 3.2.
768    ///
769    /// Empty for every table written before the section table existed, and empty is not a
770    /// degraded state: section 3.1 says deleting every graph section from a file changes no answer,
771    /// only the time, so a table with none here answers every query the same way and slower. That
772    /// is what lets this field arrive without a migration.
773    sections: Vec<Section>,
774}
775
776impl Table {
777    /// The SQL table name held by this snapshot.
778    #[must_use]
779    pub fn name(&self) -> &str {
780        &self.name
781    }
782
783    /// Columns in their SQL order.
784    #[must_use]
785    pub fn fields(&self) -> &[Field] {
786        &self.fields
787    }
788
789    /// Committed row count.
790    #[must_use]
791    pub fn rows(&self) -> usize {
792        self.rows
793    }
794
795    /// Independently readable stripes.
796    #[must_use]
797    pub fn stripes(&self) -> &[Stripe] {
798        &self.stripes
799    }
800
801    /// The order the rows are meant to be stored in, if this table was declared with one.
802    #[must_use]
803    pub fn clustering(&self) -> Option<&Clustering> {
804        self.clustering.as_ref()
805    }
806
807    /// The generation every section of this table is judged against.
808    ///
809    /// See the field. A caller deciding whether to read a section asks [`Section::usable`] with
810    /// this.
811    #[must_use]
812    pub fn generation(&self) -> u64 {
813        self.generation
814    }
815
816    /// Every graph section this table names, including the kinds this build does not know.
817    ///
818    /// Including them is the point. A caller that wants only the ones it can use asks
819    /// [`Section::usable`], and a caller rewriting the directory carries the rest through, so a
820    /// file opened by an older build and written again does not silently lose a section that build
821    /// had no name for.
822    #[must_use]
823    pub fn sections(&self) -> &[Section] {
824        &self.sections
825    }
826}
827
828/// One table's line in the catalog directory.
829///
830/// The small level of the two. It holds what opening a database needs and nothing else: the name to
831/// bind, the shape to plan against, the row count, and where the table's own directory sits. A file
832/// of eight tables is eight of these, and reading them costs the same whether the tables hold a
833/// thousand rows or a billion.
834///
835/// The name, the fields and the row count are repeated here rather than pointed at inside the table
836/// directory, which is the entire point of having two levels. A catalog that pointed at them would
837/// have to read every table directory at open to answer what tables there are, which is the cost
838/// this level exists to avoid.
839#[derive(Debug, Clone)]
840struct Entry {
841    name: String,
842    fields: Vec<Field>,
843    rows: usize,
844    /// Where this table's own directory sits, with the checksum it was committed under.
845    directory: Page,
846}
847
848/// One view's line in the catalog directory.
849///
850/// A view has no pages, so unlike a table it is entirely here and there is no second level under it.
851/// What it is made of is text: the body the binder binds again at every reference, and the whole
852/// statement written back out, which is what `duckdb_views()` reports and nothing else reads.
853///
854/// The columns are a cache and they are written down anyway, which is worth saying out loud because
855/// a cache in a file looks like a mistake. It is what the pin does. Create a view on a file, open
856/// the file again in another process, and `duckdb_views()` answers `column_count` and `is_bound`
857/// true without anything having bound the body, so the list survived the write. Not writing it
858/// would answer null and false there, and the only way back would be to bind every view at open,
859/// which is the thing the cache exists to avoid.
860#[derive(Debug, Clone, PartialEq, Eq)]
861pub struct ViewEntry {
862    /// The view's own name, without the schema, the way a table entry holds its name.
863    pub name: String,
864    /// The query the view stands for, as the text that was written.
865    pub sql: String,
866    /// The whole `CREATE VIEW` written back out.
867    pub statement: String,
868    /// The column names the statement gave, which rename a prefix of what the body produces.
869    pub aliases: Vec<String>,
870    /// The columns the last bind of the body produced.
871    pub columns: Vec<Field>,
872}
873
874/// Where one column's bytes went, taken from the directory rather than by reading pages.
875#[derive(Debug, Clone)]
876pub struct ColumnLayout {
877    /// The column's name, so a report does not have to carry the field list beside this.
878    pub name: String,
879    /// The type, spelled the way the catalog spells it.
880    pub kind: String,
881    /// Every stripe's page of this column added up, which is the encoded data itself.
882    pub pages: u64,
883    /// Every stripe's exact code membership page for this column.
884    pub memberships: u64,
885    /// Every stripe's membership sieve page for this column.
886    pub sieves: u64,
887    /// Every stripe's per part range page for this column.
888    pub part_ranges: u64,
889    /// The table wide dictionary of this column, if it has one.
890    pub dictionary: u64,
891}
892
893impl ColumnLayout {
894    /// Everything this column costs, which is what the file would lose if the column went.
895    #[must_use]
896    pub fn total(&self) -> u64 {
897        self.pages
898            .saturating_add(self.memberships)
899            .saturating_add(self.sieves)
900            .saturating_add(self.part_ranges)
901            .saturating_add(self.dictionary)
902    }
903}
904
905/// Where a whole file's bytes went.
906///
907/// Every number here comes out of the committed directory, so taking it costs one directory read
908/// however large the file is. That is the point: a 45 GB table has to be able to say where it went
909/// without being read, or nobody will ask.
910///
911/// The parts that are not a column are kept apart rather than shared out over the columns. The
912/// stripe index page holds a section per column and could be split, and the directory and the
913/// header cannot be, so splitting one of the three and not the others would read as if the columns
914/// accounted for everything. They do not, and the gap is the thing worth looking at.
915#[derive(Debug, Clone)]
916pub struct Layout {
917    /// The size of the file on disk.
918    pub file: u64,
919    /// Committed rows.
920    pub rows: usize,
921    /// Committed stripes.
922    pub stripes: usize,
923    /// Committed parts, which is how many chunks a scan reads.
924    pub parts: usize,
925    /// One entry per column, in the table's column order.
926    pub columns: Vec<ColumnLayout>,
927    /// Every stripe's index page, which carries a length and a checksum for every part of every
928    /// column and is charged per stripe rather than per column.
929    pub indexes: u64,
930    /// The committed directory itself, the one that was read to build this.
931    pub directory: u64,
932    /// The fixed header, which holds the magic, the format and the two directory slots.
933    pub header: u64,
934}
935
936impl Layout {
937    /// Everything the columns cost together.
938    #[must_use]
939    pub fn columns_total(&self) -> u64 {
940        self.columns.iter().map(ColumnLayout::total).fold(0, u64::saturating_add)
941    }
942
943    /// What the file holds that this does not account for.
944    ///
945    /// A committed file is written once and never rewritten in place, so an earlier directory and
946    /// the pages of an earlier snapshot are still in it. That is the honest place for them: they
947    /// are bytes on disk that no column owns.
948    #[must_use]
949    pub fn unaccounted(&self) -> u64 {
950        self.file
951            .saturating_sub(self.columns_total())
952            .saturating_sub(self.indexes)
953            .saturating_sub(self.directory)
954            .saturating_sub(self.header)
955    }
956}
957
958/// How one part of one column is stored, which is one row of `pragma_storage_info`.
959///
960/// Everything here is read off the file rather than worked out from the schema, because the whole
961/// question this answers is what the encoder chose, and the encoder chooses per part. Two files
962/// holding the same rows in a different order give different answers and that difference is the
963/// reason to ask.
964///
965/// The encoding costs a read of the column's page, so this is not free the way [`Layout`] is. It is
966/// one read per column per stripe rather than one per part, because a part is a few kilobytes out
967/// of a page that is a quarter of a megabyte.
968#[derive(Debug, Clone)]
969pub struct StoredPart {
970    /// Which stripe the part belongs to.
971    pub stripe: usize,
972    /// Which part of that stripe it is, counting from zero inside the stripe.
973    pub part: usize,
974    /// The table wide row number the part starts at.
975    pub row: usize,
976    /// How many rows it holds.
977    pub rows: usize,
978    /// What the encoder made of it, as a line of text like `DICT(PACKED, PACKED)`.
979    pub encoding: String,
980    /// The stored bytes of the part, which is what it costs in the file.
981    pub bytes: u64,
982    /// Where in the file the column page holding this part starts.
983    pub page: u64,
984    /// Where in that page the part starts.
985    pub offset: u64,
986    /// The smallest value the part holds, when the stored ranges say.
987    pub low: Option<Value>,
988    /// The largest, same.
989    pub high: Option<Value>,
990    /// How many of its rows are null, when the stored ranges say.
991    pub nulls: Option<usize>,
992}
993
994/// Seeds the second hash a global dictionary tells its values apart by.
995///
996/// Any value that is not zero does, since zero is the seed [`checksum`] already uses and the point
997/// is only that the two hashes of one value are not the same number. This one is the fractional part
998/// of the golden ratio in sixty four bits, which is the constant everything else here is built out
999/// of and is as good a nothing-up-my-sleeve number as any.
1000const DICTIONARY_CHECK_SEED: u64 = 11_400_714_819_323_198_485;
1001
1002/// One column's table wide dictionary while the load is running.
1003///
1004/// The thing to understand about this is what it does not hold. A dictionary of `URL` at a hundred
1005/// million ClickBench rows has about eighteen million distinct values and 1.3 GB of bytes in them,
1006/// and five columns like it are twelve of the seventeen gigabytes a load of `hits` peaks at. So the
1007/// bytes are not kept. A value's bytes go into [`GlobalDictionary::filling`], and when that reaches
1008/// [`TEXT_PAYLOAD_VALUES`] values the block is sealed, handed to [`encode_ready`] at the end of the
1009/// stripe and never seen in that form again. What is left is the encoded block, which is two to
1010/// three times smaller, and that is the same bytes the file is going to hold anyway.
1011///
1012/// Two things needed the raw bytes and neither needs them now. Deciding whether a value has been
1013/// seen before was a hash lookup and then a comparison of the bytes, and is now a hash lookup and a
1014/// comparison of a second hash under a different seed, which is [`DICTIONARY_CHECK_SEED`] and the
1015/// argument for why that is sound. Sorting the values at the end needed all of them at once, and
1016/// now reads the blocks back through [`GlobalDictionary::decoded`] one column at a time, which is
1017/// one column's bytes rather than every column's.
1018///
1019/// The offsets going block relative comes free with it, and takes the four gigabyte wall with it.
1020/// They were `u32` into a per column payload, so a column could not hold more than four gigabytes of
1021/// values however much memory the machine had, and `URL` and `Referer` are within a small factor of
1022/// that at a hundred million rows. A `u32` into a block of 1,024 values is not a bound anything real
1023/// reaches. The stored form is unchanged, because [`encode_offsets`] was already subtracting a per
1024/// block base before writing.
1025#[derive(Debug)]
1026struct GlobalDictionary {
1027    primary: HashMap<u64, u32>,
1028    collisions: HashMap<u64, Vec<u32>>,
1029    /// Every value's hash under [`DICTIONARY_CHECK_SEED`], in code order.
1030    checks: Vec<u64>,
1031    /// Where every value ends inside the payload block it is in, in code order.
1032    ends: Vec<u32>,
1033    counts: Vec<u64>,
1034    nulls: u64,
1035    /// The values of the block being filled, back to back.
1036    filling: Vec<u8>,
1037    /// One conservative four-byte substring signature per sealed payload block.
1038    grams: Vec<[u8; TEXT_GRAM_BYTES]>,
1039    /// Blocks that have filled and not been encoded yet, each with its block number.
1040    ///
1041    /// Empty except between a block filling and the end of the stripe that filled it, and while the
1042    /// column is still too small to settle a shape on.
1043    waiting: Vec<(usize, Vec<u8>)>,
1044    /// Blocks kept raw to settle a shape on, spread across the column, each with its number.
1045    ///
1046    /// At most [`PAYLOAD_SAMPLE_BLOCKS`] of them and so at most a few megabytes. Spread rather than
1047    /// taken off the front for the reason [`settle_shape`] gives, and kept rather than read back
1048    /// because reading back is a decode and this is a sample of a column that is still growing.
1049    sample: Vec<(usize, Vec<u8>)>,
1050    /// How far apart the blocks in `sample` are, which doubles every time there are too many.
1051    stride: usize,
1052    /// What the blocks encoded so far were encoded with, once the column is big enough to settle it.
1053    shape: Option<chooser::Settled>,
1054    /// How many blocks had filled when that shape was settled.
1055    settled: usize,
1056    /// The blocks that are encoded and not yet in the file, in block order, following `placed`.
1057    ///
1058    /// Empty between stripes, because [`Writer::place_blocks`] writes them the moment
1059    /// [`encode_ready`] hands them back. Only a dictionary that never meets a writer, which is a
1060    /// test's, keeps them here.
1061    blocks: Vec<Vec<u8>>,
1062    /// Where every block already written to the file is, in block order.
1063    placed: Vec<Placed>,
1064}
1065
1066/// Where one payload block of a global dictionary is in the file, and its checksum.
1067#[derive(Debug, Clone, Copy)]
1068struct Placed {
1069    start: u64,
1070    length: u64,
1071    hash: u64,
1072}
1073
1074/// Sorted `(head, code)` entries and the decoded bytes and block bases they were sorted over.
1075type RankedDictionary = (Vec<(u64, u32)>, Vec<u8>, Vec<u64>);
1076
1077impl GlobalDictionary {
1078    fn new() -> Self {
1079        Self {
1080            primary: HashMap::new(),
1081            collisions: HashMap::new(),
1082            checks: Vec::new(),
1083            ends: Vec::new(),
1084            counts: Vec::new(),
1085            nulls: 0,
1086            filling: Vec::new(),
1087            grams: Vec::new(),
1088            waiting: Vec::new(),
1089            sample: Vec::new(),
1090            stride: 1,
1091            shape: None,
1092            settled: 0,
1093            blocks: Vec::new(),
1094            placed: Vec::new(),
1095        }
1096    }
1097
1098    /// How many distinct values this dictionary holds, which is one past its largest code.
1099    fn values(&self) -> usize {
1100        self.ends.len()
1101    }
1102
1103    /// How many blocks are encoded, written or not, which is the number the next one has to have.
1104    fn encoded(&self) -> usize {
1105        self.placed.len() + self.blocks.len()
1106    }
1107
1108    #[cfg(test)]
1109    fn code(&mut self, text: &str) -> Result<u32> {
1110        let bytes = text.as_bytes();
1111        self.code_hashed(bytes, checksum(bytes), seeded_checksum(bytes, DICTIONARY_CHECK_SEED))
1112    }
1113
1114    /// The code for a value whose two hashes the caller already has.
1115    ///
1116    /// A stripe prepared outside the writer's lock hashed every value it holds while it was coding
1117    /// them, and merging it into this dictionary is one of these a distinct value rather than two
1118    /// hashes of every row. See [`prepare`].
1119    fn code_hashed(&mut self, text: &[u8], hash: u64, check: u64) -> Result<u32> {
1120        if let Some(&code) = self.primary.get(&hash) {
1121            if self.checks.get(code as usize) == Some(&check) {
1122                return Ok(code);
1123            }
1124            if let Some(codes) = self.collisions.get(&hash) {
1125                if let Some(code) =
1126                    codes.iter().copied().find(|&code| self.checks[code as usize] == check)
1127                {
1128                    return Ok(code);
1129                }
1130            }
1131            let code = self.insert(text, check)?;
1132            self.collisions.entry(hash).or_default().push(code);
1133            return Ok(code);
1134        }
1135        let code = self.insert(text, check)?;
1136        self.primary.insert(hash, code);
1137        Ok(code)
1138    }
1139
1140    fn insert(&mut self, text: &[u8], check: u64) -> Result<u32> {
1141        let code = u32::try_from(self.ends.len())
1142            .map_err(|_| invalid("global dictionary has too many values"))?;
1143        self.filling.extend_from_slice(text);
1144        self.ends.push(
1145            u32::try_from(self.filling.len())
1146                .map_err(|_| invalid("a global dictionary value exceeds 4 GiB"))?,
1147        );
1148        self.checks.push(check);
1149        self.counts.push(0);
1150        if self.ends.len() % TEXT_PAYLOAD_VALUES == 0 {
1151            self.seal();
1152        }
1153        Ok(code)
1154    }
1155
1156    /// Closes the block being filled and puts it in the queue to be encoded.
1157    ///
1158    /// Also keeps a copy of it if it lands on the sample's stride, and halves the sample when that
1159    /// has left too many, which is what keeps the kept blocks spread evenly over however much of the
1160    /// column exists rather than bunched at whichever end was cheap to remember.
1161    fn seal(&mut self) {
1162        let at = self.ends.len().div_ceil(TEXT_PAYLOAD_VALUES) - 1;
1163        let bytes = std::mem::take(&mut self.filling);
1164        let mut grams = [0_u8; TEXT_GRAM_BYTES];
1165        for value in self.slices(at, &bytes) {
1166            for gram in value.windows(4) {
1167                for bit in gram_bits(gram) {
1168                    grams[bit / 8] |= 1 << (bit % 8);
1169                }
1170            }
1171        }
1172        self.grams.push(grams);
1173        if at % self.stride == 0 {
1174            self.sample.push((at, bytes.clone()));
1175            if self.sample.len() > PAYLOAD_SAMPLE_BLOCKS {
1176                self.stride *= 2;
1177                let stride = self.stride;
1178                self.sample.retain(|(at, _)| at % stride == 0);
1179            }
1180        }
1181        self.waiting.push((at, bytes));
1182    }
1183
1184    /// The values of one block, as slices into the bytes the block was filled with.
1185    fn slices<'a>(&self, at: usize, bytes: &'a [u8]) -> Vec<&'a [u8]> {
1186        let first = at * TEXT_PAYLOAD_VALUES;
1187        let last = (first + TEXT_PAYLOAD_VALUES).min(self.ends.len());
1188        let mut out = Vec::with_capacity(last.saturating_sub(first));
1189        let mut from = 0;
1190        for value in first..last {
1191            let to = self.ends[value] as usize;
1192            out.push(&bytes[from..to]);
1193            from = to;
1194        }
1195        out
1196    }
1197
1198    /// Settles the shape the waiting blocks are about to be encoded with, if there is enough column
1199    /// to settle one on.
1200    ///
1201    /// Settled again once the column has grown fourfold, because the sample it was settled on then
1202    /// covered a quarter of what exists now and a dictionary in first seen order does not look the
1203    /// same at both ends. Blocks already encoded keep the shape they were encoded with. They can,
1204    /// because a block says what it is: nothing reading one asks the column what shape to expect.
1205    fn settle(&mut self) -> Result<()> {
1206        if self.sample.len() < PAYLOAD_SAMPLE_BLOCKS {
1207            return Ok(());
1208        }
1209        let complete = self.ends.len() / TEXT_PAYLOAD_VALUES;
1210        if self.shape.is_some() && complete < self.settled.saturating_mul(4) {
1211            return Ok(());
1212        }
1213        let sample =
1214            self.sample.iter().map(|(at, bytes)| self.slices(*at, bytes)).collect::<Vec<_>>();
1215        self.shape = Some(settle_shape(&sample)?);
1216        self.settled = complete;
1217        Ok(())
1218    }
1219
1220    /// Seals the part block at the end of the load, if there is one.
1221    fn seal_rest(&mut self) {
1222        // Asked of the values rather than of the bytes, because a block of empty strings has values
1223        // in it and no bytes, and a column of nulls is exactly that.
1224        if self.ends.len() % TEXT_PAYLOAD_VALUES != 0 {
1225            self.seal();
1226        }
1227    }
1228
1229    /// Encodes the waiting block at `at`, with the settled shape when there is one and by trying
1230    /// everything when the column was too small to settle one.
1231    fn encode_waiting(&self, at: usize) -> Result<Vec<u8>> {
1232        let (block, bytes) = &self.waiting[at];
1233        let values = self.slices(*block, bytes);
1234        match &self.shape {
1235            Some(shape) => string::encode_with(&values, shape),
1236            None => string::encode(&values),
1237        }
1238    }
1239
1240    /// [`finish_dictionaries`] for one dictionary on this thread, for the tests that hold one.
1241    #[cfg(test)]
1242    fn finish_blocks(&mut self) -> Result<()> {
1243        self.seal_rest();
1244        let made = (0..self.waiting.len())
1245            .map(|at| self.encode_waiting(at))
1246            .collect::<Result<Vec<_>>>()?;
1247        for ((at, _), bytes) in std::mem::take(&mut self.waiting).into_iter().zip(made) {
1248            if self.encoded() != at {
1249                return Err(Error::internal("a dictionary block was encoded out of order"));
1250            }
1251            self.blocks.push(bytes);
1252        }
1253        Ok(())
1254    }
1255
1256    /// Every value of this dictionary read back out of its encoded blocks, as the bytes back to back
1257    /// and where each block starts in them.
1258    ///
1259    /// This is the one place the whole column is in memory at once and the reason [`Writer::close`]
1260    /// takes the columns one at a time rather than across threads. One column's values is 1.3 GB on
1261    /// the worst ClickBench column, and five columns of that at once is the peak this was all meant
1262    /// to remove.
1263    ///
1264    /// The blocks are spread over threads instead. Each block's decoded length is already known from
1265    /// the ends of its values, so the answer is laid out before anything is decoded and every thread
1266    /// decodes its own run of blocks straight into its own part of it. On the 10m ClickBench sample
1267    /// this was a second of the close for `URL` alone, on one core of thirty two, and the close is
1268    /// what a load waits on once its stripes are written.
1269    ///
1270    /// The blocks already written are read back out of `file`, so what a thread holds beyond the
1271    /// answer is one encoded block. They were written moments or minutes ago and are almost always
1272    /// still in the page cache, so this is a copy rather than a read of the disk.
1273    fn decoded(&self, file: Option<&File>) -> Result<(Vec<u8>, Vec<u64>)> {
1274        let count = self.placed.len() + self.blocks.len();
1275        if count != self.values().div_ceil(TEXT_PAYLOAD_VALUES) {
1276            return Err(invalid("global dictionary blocks do not cover its values"));
1277        }
1278        let mut bases = Vec::with_capacity(count);
1279        let mut total = 0_usize;
1280        for block in 0..count {
1281            bases.push(total as u64);
1282            let last = ((block + 1) * TEXT_PAYLOAD_VALUES).min(self.values()) - 1;
1283            total = total
1284                .checked_add(self.ends[last] as usize)
1285                .ok_or_else(|| invalid("global dictionary does not fit in memory"))?;
1286        }
1287        let mut flat = vec![0_u8; total];
1288        let mut outs = Vec::with_capacity(count);
1289        let mut rest = flat.as_mut_slice();
1290        for block in 0..count {
1291            let end = bases.get(block + 1).map_or(total, |&base| base as usize);
1292            let (out, after) = rest.split_at_mut(end - bases[block] as usize);
1293            outs.push((block, out));
1294            rest = after;
1295        }
1296        let one = |run: &mut [(usize, &mut [u8])]| -> Result<()> {
1297            let mut stored = Vec::new();
1298            for (block, out) in run {
1299                let encoded = match self.placed.get(*block) {
1300                    Some(place) => {
1301                        let file = file.ok_or_else(|| {
1302                            Error::internal("a written dictionary block has no file")
1303                        })?;
1304                        let length = usize::try_from(place.length).map_err(|_| {
1305                            invalid("global dictionary block does not fit in memory")
1306                        })?;
1307                        stored.resize(length, 0);
1308                        read_at(file, place.start, &mut stored)?;
1309                        if checksum(&stored) != place.hash {
1310                            return Err(invalid(
1311                                "a global dictionary block did not read back as written",
1312                            ));
1313                        }
1314                        stored.as_slice()
1315                    }
1316                    None => &self.blocks[*block - self.placed.len()],
1317                };
1318                let decoded = string::decode_flat(encoded)?;
1319                if decoded.bytes().len() != out.len() {
1320                    return Err(invalid(
1321                        "a global dictionary block is not the length its ends say",
1322                    ));
1323                }
1324                out.copy_from_slice(decoded.bytes());
1325            }
1326            Ok(())
1327        };
1328        // Sixteen blocks a thread at the least, because a thread costs about what decoding a few
1329        // blocks does and most columns have one or two.
1330        let workers = close_workers().min(count / 16).max(1);
1331        if workers <= 1 {
1332            one(&mut outs)?;
1333        } else {
1334            let per = count.div_ceil(workers);
1335            std::thread::scope(|scope| {
1336                outs.chunks_mut(per)
1337                    .map(|run| scope.spawn(|| one(run)))
1338                    .collect::<Vec<_>>()
1339                    .into_iter()
1340                    .try_for_each(|handle| {
1341                        handle.join().map_err(|_| {
1342                            Error::internal("a global dictionary decode worker panicked")
1343                        })?
1344                    })
1345            })?;
1346        }
1347        drop(outs);
1348        Ok((flat, bases))
1349    }
1350
1351    /// Where the value at `code` sits in the bytes [`GlobalDictionary::decoded`] handed back.
1352    ///
1353    /// A block's first value starts at the block, and every other value starts where the one before
1354    /// it ended, which is what makes 1,024 values 1,024 numbers rather than 1,025.
1355    fn value_span(ends: &[u32], bases: &[u64], code: usize) -> (usize, usize) {
1356        let Some(&base) = bases.get(code / TEXT_PAYLOAD_VALUES) else { return (0, 0) };
1357        let Some(&end) = ends.get(code) else { return (0, 0) };
1358        let base = base as usize;
1359        let from = if code % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[code - 1] as usize };
1360        (base + from, base + end as usize)
1361    }
1362
1363    /// This dictionary's values in sorted order, each as the first eight bytes of the value and the
1364    /// code that holds it, so entry `rank` describes the value that sits at `rank` when the values
1365    /// are sorted by their bytes.
1366    ///
1367    /// Codes themselves stay in first appearance order, which is what lets the writer hand one out
1368    /// the moment it sees a value rather than waiting for the last stripe, and which also keeps a
1369    /// stripe's codes close together because the data is clustered. This is what puts the values
1370    /// back in order for anything that needs it, and it is separate from the codes so that getting
1371    /// it costs a sort of the distinct values at the end rather than a rewrite of every code page.
1372    ///
1373    /// The order is the byte order of the values and nothing else. The heads are attached after the
1374    /// sort rather than sorted on, because padding with zero on the right is order preserving for
1375    /// byte strings and so sorting by head and then by bytes lands in the same place as sorting by
1376    /// bytes: a shorter value differs from a longer one that starts the same way at a position
1377    /// where the shorter one has run out, and zero is below every byte that could be there.
1378    ///
1379    /// The heads are kept because a reader searching this order wants a comparison it can make out
1380    /// of the index alone. What they buy there depends entirely on the column and is much less than
1381    /// it looks on the columns that cost the most, which [`sort_by_value`] measures.
1382    fn ranked_with_values(&self, file: Option<&File>) -> Result<RankedDictionary> {
1383        let (flat, bases) = self.decoded(file)?;
1384        let value = |code: u32| {
1385            let (from, to) = Self::value_span(&self.ends, &bases, code as usize);
1386            flat.get(from..to).unwrap_or_default()
1387        };
1388        let mut codes = (0..self.values() as u32).collect::<Vec<_>>();
1389        sort_by_value_across(&mut codes, value, close_workers());
1390        let order = codes.into_iter().map(|code| (head(value(code)), code)).collect();
1391        Ok((order, flat, bases))
1392    }
1393
1394    #[cfg(test)]
1395    fn ranked(&self, file: Option<&File>) -> Result<Vec<(u64, u32)>> {
1396        self.ranked_with_values(file).map(|(order, _, _)| order)
1397    }
1398}
1399
1400/// Appends pages and commits a new directory.
1401///
1402/// One writer covers a whole file rather than one table. [`Writer::next`] closes the table it is on
1403/// and opens another over the same file, and [`Writer::finish`] commits every table it has closed in
1404/// one generation. That is what makes a checkpoint atomic across tables: there is one slot write at
1405/// the end of it and a reader sees every table at the generation before it or every table at the
1406/// generation after it.
1407#[derive(Debug)]
1408pub struct Writer {
1409    file: File,
1410    /// Where the next write goes, counted here rather than asked of the file.
1411    ///
1412    /// The file's own cursor is not ours. Building the numeric frequencies reads pages back through
1413    /// [`read_at`], and a positional read is only positional about where it reads from: `pread`
1414    /// leaves the cursor alone, and the call Windows has for it moves the cursor to the end of what
1415    /// it read. A writer that asked the file where it was would then write the directory over a
1416    /// page it had already written, which is what it did.
1417    at: u64,
1418    table: Table,
1419    generation: u64,
1420    /// The first and the last source position in every stripe, in the order the stripes were
1421    /// written.
1422    order: Vec<((u64, u64), (u64, u64))>,
1423    next_order: u64,
1424    dictionaries: Vec<Option<GlobalDictionary>>,
1425    /// Which columns still have a global dictionary, shared with every [`Preparer`] this writer
1426    /// hands out so that a stripe prepared after a column lost its dictionary is not coded for it.
1427    coded: Arc<[AtomicBool]>,
1428    /// One per column, folding the rows into a summary and a sketch as they go past.
1429    ///
1430    /// `None` for a column with no hash rule, which is the interval and the nested types. See
1431    /// [`stats::Gather`] for why the statistics are built here rather than by reading the file back
1432    /// once it is committed.
1433    gathers: Vec<Option<stats::Gather>>,
1434    pending: Vec<PendingChunk>,
1435    /// The tables already closed in this generation, in the order they were written.
1436    closed: Vec<Entry>,
1437    /// The views the next commit writes down, which [`Writer::with_views`] sets.
1438    ///
1439    /// Carried forward from the committed generation by [`Writer::open`], so a writer that was only
1440    /// opened to append a table does not have to know about views to avoid dropping them.
1441    views: Vec<ViewEntry>,
1442    /// Where the stages this writer runs are charged, which [`Writer::with_profile`] sets.
1443    ///
1444    /// The writer runs the page builder, the dictionary blocks, the writes and the publish, and it
1445    /// charges them once per stripe and once per worker, never per chunk. See
1446    /// `rudb_metrics::LoadProfile` for why that is the grain.
1447    profile: Option<Arc<LoadProfile>>,
1448}
1449
1450/// A chunk that has arrived and is waiting for the rest of its stripe.
1451///
1452/// The rows are kept rather than the pages they encode to, which is the whole of #808's first half.
1453/// Encoding on arrival put every column of every part on the thread that called `append_at`, and
1454/// that thread is the only one the load has. Encoding at the flush instead means a stripe's worth
1455/// of work is on the table at once, and a stripe splits by column into a hundred and five pieces
1456/// that share nothing.
1457#[derive(Debug)]
1458struct PendingChunk {
1459    order: (u64, u64),
1460    chunk: Chunk,
1461}
1462
1463/// What the writer still needs of a part once its columns are encoded: where in the source it came
1464/// from, how many rows it has and how large those rows were.
1465///
1466/// A stripe waiting for the writer's lock carries these rather than its chunks, so its rows are
1467/// freed as soon as they are encoded and not after the stripe is written. See [`prepare`].
1468#[derive(Debug, Clone, Copy)]
1469struct Part {
1470    order: (u64, u64),
1471    rows: usize,
1472    footprint: usize,
1473}
1474
1475impl Part {
1476    fn of(pending: &PendingChunk) -> Self {
1477        Self {
1478            order: pending.order,
1479            rows: pending.chunk.len(),
1480            footprint: pending.chunk.footprint(),
1481        }
1482    }
1483}
1484
1485/// One column's share of a stripe, which is what one encode worker produces.
1486///
1487/// Indexed by part, so a stripe is a column of these and the write loop reads down one of them.
1488/// That is also the order the loop wanted: `flush_pending` walks a column at a time and lays its
1489/// parts next to each other, and it used to reach across a row of parts to do it.
1490#[derive(Debug)]
1491struct ColumnStripe {
1492    pages: Vec<Vec<u8>>,
1493    codes: Vec<Option<Vec<u32>>>,
1494    sieves: Vec<Option<Sieve>>,
1495    ranges: Vec<Range>,
1496}
1497
1498/// Roughly what encoding a column of this type costs, for ordering the encode queue.
1499///
1500/// Only the order matters and only roughly. A string column hashes and copies every value into a
1501/// dictionary and is in a different class from everything else, and among the fixed widths the wide
1502/// ones carry more bytes through the cascade than the narrow ones. Anything finer than that would
1503/// be a cost model, and the queue already absorbs a wrong guess: it only has to avoid finishing on
1504/// a column nobody else can help with.
1505fn weight(ty: &LogicalType) -> usize {
1506    match ty {
1507        LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit => 64,
1508        LogicalType::HugeInt
1509        | LogicalType::UHugeInt
1510        | LogicalType::Uuid
1511        | LogicalType::Interval => 16,
1512        LogicalType::BigInt
1513        | LogicalType::UBigInt
1514        | LogicalType::Timestamp
1515        | LogicalType::Time
1516        | LogicalType::TimeTz
1517        | LogicalType::TimestampTz
1518        | LogicalType::TimestampS
1519        | LogicalType::TimestampMs
1520        | LogicalType::TimestampNs
1521        | LogicalType::Double
1522        | LogicalType::Decimal { .. } => 8,
1523        LogicalType::Integer | LogicalType::UInteger | LogicalType::Date | LogicalType::Float => 4,
1524        LogicalType::SmallInt | LogicalType::USmallInt => 2,
1525        _ => 1,
1526    }
1527}
1528
1529/// Parts in one stripe.
1530///
1531/// Sixty four thousand rows is the smallest stripe that keeps the ClickBench directory in single
1532/// digit megabytes at a hundred million rows, and it puts a four byte column's page at a quarter of
1533/// a megabyte, which is the size a sequential read wants. Larger stripes buy a smaller directory
1534/// and cost a sparse fetch, which has to read a page index before it can reach one part.
1535pub const STRIPE_PARTS: usize = 64;
1536
1537/// How many rows the writer wants to see before it decides whether a varchar column gets to keep
1538/// its global dictionary.
1539///
1540/// See [`prepare::drops_dictionary`]. A stripe is up to [`STRIPE_PARTS`] parts, so most tables give it
1541/// far more than this and it binds only on a table that is smaller than one stripe. A handful of
1542/// rows says nothing about whether a column repeats itself, and the answer that costs nothing when
1543/// the sample is that small is the one the writer has always given, which is to keep the dictionary.
1544const DICTIONARY_DECIDE_ROWS: usize = 4_096;
1545
1546/// Out of ten. A varchar column loses its dictionary when more than this many rows in ten of the
1547/// first stripe held a value that stripe had not seen before.
1548///
1549/// See [`prepare::drops_dictionary`]. Nine and not five, because the properties a dictionary buys are
1550/// worth keeping everywhere they are real. On ClickBench the widest string column is `Referer` at
1551/// 0.131 of its first stripe and every other one is below that, so nothing there is near this and
1552/// every one of them keeps its dictionary, which is what a group by on codes wants. On TPC-H
1553/// `o_comment` and `c_comment` are at 0.97 and are what this catches.
1554///
1555/// `l_comment` sits at 0.883 and so keeps its dictionary. Eight was built and measured rather than
1556/// argued about, and it is not a clear win: it takes `select l_comment from lineitem` from 4.335 G
1557/// instructions to 3.473 G and the file from 280.2 MB to 260.4 MB, and it takes a `like` over the
1558/// same column from 3.29 G to 4.27 G, because a dictionary runs the predicate once a distinct value
1559/// and there are 3.6 M of those to 6.0 M rows. The 22 query suite came out 6.91 s against 7.07 s in
1560/// favour of nine. So nine stays until there is a reason to prefer one of those shapes. See #1137.
1561const DICTIONARY_DISTINCT_IN_TEN: usize = 9;
1562
1563/// Bytes one part takes in a stripe's index page: four for the length, eight for the checksum.
1564const INDEX_ENTRY: usize = size_of::<u32>() + size_of::<u64>();
1565
1566/// Bytes one column's section of a stripe's index page takes, including its own trailing checksum.
1567fn index_section(parts: usize) -> Result<usize> {
1568    parts
1569        .checked_mul(INDEX_ENTRY)
1570        .and_then(|bytes| bytes.checked_add(size_of::<u64>()))
1571        .ok_or_else(|| invalid("index page length overflow"))
1572}
1573
1574impl Writer {
1575    /// Opens a committed file and starts a table in the generation after the one it holds.
1576    ///
1577    /// The tables already in the file are carried forward by name and by directory pointer, and
1578    /// their pages are not read. Nothing in the file is overwritten: the new table's pages and the
1579    /// new catalog go on the end, past the catalog the committed generation points at, and the one
1580    /// write that is not an append is the slot in the header that [`Writer::finish`] does last.
1581    ///
1582    /// That slot is the other one. A file committed at generation 1 is named by the slot at 16 and
1583    /// generation 2 writes the one at 44, so until the last four bytes of the commit land the file
1584    /// still reads as the generation before it, and a slot torn across a write fails its checksum
1585    /// and the reader falls back to the one beside it. This is what the second slot has always been
1586    /// for.
1587    ///
1588    /// # Errors
1589    ///
1590    /// If the file has no valid committed directory, is not this build's format, repeats the name
1591    /// of a table already in it that holds rows, has a field with no scalar encoding, or cannot be
1592    /// written.
1593    pub fn open(
1594        path: impl AsRef<Path>,
1595        name: impl Into<String>,
1596        fields: Vec<Field>,
1597    ) -> Result<Self> {
1598        for field in &fields {
1599            type_tag(&field.ty)?;
1600        }
1601        let name = name.into();
1602        let path = path.as_ref();
1603        let (_, size, slot, bytes, _) = slot_bytes(path)?;
1604        let (mut closed, views) = decode_catalog(&bytes, size)?;
1605        // A table already in the file under this name is only in the way if it holds rows. One that
1606        // holds none has no pages for this generation to carry and no reader that could lose
1607        // anything, so the table being started here takes its place in the catalog rather than
1608        // colliding with it, and `finish` writes the new entry where the old one was.
1609        //
1610        // That is not a corner. It is the shape every loading script writes: the schema goes in one
1611        // statement and the rows go in the next, and a checkpoint between them commits the empty
1612        // table. Before this, the second statement had to build the whole table in memory because
1613        // the first had already put the name in the file, which is how a load of a table larger
1614        // than memory became a load that needed memory the size of the table.
1615        if let Some(at) = closed.iter().position(|held| held.name == name) {
1616            if closed[at].rows > 0 {
1617                return Err(invalid("two tables in one native file have the same name"));
1618            }
1619            closed.remove(at);
1620        }
1621        // The generation of the slot whose bytes checksummed, and not the highest number in the
1622        // header. A slot torn across a write can hold any number at all, and taking that one would
1623        // be choosing which slot to overwrite from a value nothing has vouched for, which is how a
1624        // half written commit gets to destroy the one good copy beside it.
1625        let generation = slot
1626            .generation
1627            .checked_add(1)
1628            .ok_or_else(|| invalid("native file generation overflow"))?;
1629        let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
1630        Ok(Self {
1631            file,
1632            // The end of the file, so that the committed generation's catalog stays where its slot
1633            // says it is and keeps naming a file a reader can still open.
1634            at: size,
1635            dictionaries: fields
1636                .iter()
1637                .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1638                .collect(),
1639            coded: fields
1640                .iter()
1641                .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1642                .collect(),
1643            gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, generation)).collect(),
1644            table: Table {
1645                name,
1646                dictionaries: vec![None; fields.len()],
1647                dictionary_payloads: Vec::new(),
1648                distincts: vec![None; fields.len()],
1649                fields,
1650                stripes: Vec::new(),
1651                rows: 0,
1652                frequencies: Vec::new(),
1653                pair_frequencies: Vec::new(),
1654                frequency_texts: Vec::new(),
1655                host_groups: None,
1656                clustering: None,
1657                generation,
1658                sections: Vec::new(),
1659            },
1660            generation,
1661            order: Vec::new(),
1662            next_order: 0,
1663            pending: Vec::with_capacity(STRIPE_PARTS),
1664            closed,
1665            views,
1666            profile: None,
1667        })
1668    }
1669
1670    /// Creates a new v10 file and its first table.
1671    ///
1672    /// # Errors
1673    ///
1674    /// If the file exists, a field has no scalar encoding, or the path cannot be written.
1675    pub fn create(
1676        path: impl AsRef<Path>,
1677        name: impl Into<String>,
1678        fields: Vec<Field>,
1679    ) -> Result<Self> {
1680        for field in &fields {
1681            type_tag(&field.ty)?;
1682        }
1683        let file =
1684            OpenOptions::new().write(true).read(true).create_new(true).open(path).map_err(io)?;
1685        let mut header = [0; HEADER as usize];
1686        header[..8].copy_from_slice(MAGIC);
1687        header[8..12].copy_from_slice(&FORMAT.to_le_bytes());
1688        write_at(&file, 0, &header)?;
1689        Ok(Self {
1690            file,
1691            at: HEADER,
1692            dictionaries: fields
1693                .iter()
1694                .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1695                .collect(),
1696            coded: fields
1697                .iter()
1698                .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1699                .collect(),
1700            gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, 1)).collect(),
1701            table: Table {
1702                name: name.into(),
1703                dictionaries: vec![None; fields.len()],
1704                dictionary_payloads: Vec::new(),
1705                distincts: vec![None; fields.len()],
1706                fields,
1707                stripes: Vec::new(),
1708                rows: 0,
1709                frequencies: Vec::new(),
1710                pair_frequencies: Vec::new(),
1711                frequency_texts: Vec::new(),
1712                host_groups: None,
1713                clustering: None,
1714                generation: 1,
1715                sections: Vec::new(),
1716            },
1717            generation: 1,
1718            order: Vec::new(),
1719            next_order: 0,
1720            pending: Vec::with_capacity(STRIPE_PARTS),
1721            closed: Vec::new(),
1722            views: Vec::new(),
1723            profile: None,
1724        })
1725    }
1726
1727    /// Creates a new file that holds no table at all, committed and ready to open.
1728    ///
1729    /// A database somebody dropped the last table out of is still a database, and until this there
1730    /// was no way to write one down. Every other way into this file goes through a table, because
1731    /// [`Writer::create`] takes the first one and [`Writer::finish`] commits the one it is on, so a
1732    /// catalog with nothing in it could be read and not written. The format already allowed it: the
1733    /// catalog is a count and that many entries, and a count of nought encodes and decodes the same
1734    /// way every other count does, which is why nothing here is a version change.
1735    ///
1736    /// It hands back nothing rather than a writer, because a writer with no table is a writer with
1737    /// nothing to append to. A file that is going to hold a table is [`Writer::create`], and one
1738    /// that is going to have a table added to it later is [`Writer::open`], which reads what this
1739    /// wrote the same way it reads any other generation.
1740    ///
1741    /// It takes the views anyway, because a database with no table can still have views in it. A
1742    /// view over `range` or over another view names no table, so dropping the last table out of a
1743    /// database does not have to leave the catalog with nothing worth writing down.
1744    ///
1745    /// # Errors
1746    ///
1747    /// If the file exists or the path cannot be written.
1748    pub fn empty(path: impl AsRef<Path>, views: &[ViewEntry]) -> Result<()> {
1749        let file =
1750            OpenOptions::new().write(true).read(true).create_new(true).open(path).map_err(io)?;
1751        let mut header = [0; HEADER as usize];
1752        header[..8].copy_from_slice(MAGIC);
1753        header[8..12].copy_from_slice(&FORMAT.to_le_bytes());
1754        write_at(&file, 0, &header)?;
1755        let catalog = encode_catalog(&[], views)?;
1756        write_at(&file, HEADER, &catalog)?;
1757        // The same two syncs in the same order as [`Writer::finish`], and for the same reason. The
1758        // catalog is on the disk before the slot names it, so a file this is interrupted in the
1759        // middle of is a header with no valid slot rather than a slot pointing at nothing.
1760        file.sync_all().map_err(io)?;
1761        let slot = Slot {
1762            offset: HEADER,
1763            length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
1764            generation: 1,
1765            hash: checksum(&catalog),
1766        };
1767        write_at(&file, slot_offset(1), &slot.bytes())?;
1768        file.sync_all().map_err(io)?;
1769        Ok(())
1770    }
1771
1772    /// Closes the table this writer is on and starts another one in the same file.
1773    ///
1774    /// Nothing is published here. The closed table's directory is written so that the bytes are on
1775    /// disk and its span is known, and the catalog that names it is only written by
1776    /// [`Writer::finish`], so a crash between two tables leaves the previous generation intact.
1777    ///
1778    /// # Errors
1779    ///
1780    /// If the name repeats a table already closed, a field has no scalar encoding, or the table
1781    /// being closed cannot be written.
1782    pub fn next(mut self, name: impl Into<String>, fields: Vec<Field>) -> Result<Self> {
1783        for field in &fields {
1784            type_tag(&field.ty)?;
1785        }
1786        let name = name.into();
1787        let entry = self.close()?;
1788        if self.closed.iter().chain(std::iter::once(&entry)).any(|held| held.name == name) {
1789            return Err(invalid("two tables in one native file have the same name"));
1790        }
1791        let Self { file, at, generation, mut closed, views, .. } = self;
1792        closed.push(entry);
1793        Ok(Self {
1794            file,
1795            at,
1796            generation,
1797            closed,
1798            views,
1799            profile: None,
1800            dictionaries: fields
1801                .iter()
1802                .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1803                .collect(),
1804            coded: fields
1805                .iter()
1806                .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1807                .collect(),
1808            gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, generation)).collect(),
1809            table: Table {
1810                name,
1811                dictionaries: vec![None; fields.len()],
1812                dictionary_payloads: Vec::new(),
1813                distincts: vec![None; fields.len()],
1814                fields,
1815                stripes: Vec::new(),
1816                rows: 0,
1817                frequencies: Vec::new(),
1818                pair_frequencies: Vec::new(),
1819                frequency_texts: Vec::new(),
1820                host_groups: None,
1821                clustering: None,
1822                generation,
1823                sections: Vec::new(),
1824            },
1825            order: Vec::new(),
1826            next_order: 0,
1827            pending: Vec::with_capacity(STRIPE_PARTS),
1828        })
1829    }
1830
1831    /// Sets the views the next commit writes down, replacing whatever was carried forward.
1832    ///
1833    /// It replaces rather than adds because the caller has the whole catalog in front of it and the
1834    /// writer does not. A view that was dropped is a view that is not in the list any more, and
1835    /// there is no other way for the writer to hear about that, since nothing else it is told about
1836    /// mentions views at all.
1837    ///
1838    /// A writer that is never told anything writes back the views it read at [`Writer::open`], so a
1839    /// checkpoint that only had a table to append does not quietly drop them.
1840    #[must_use]
1841    pub fn with_views(mut self, views: Vec<ViewEntry>) -> Self {
1842        self.views = views;
1843        self
1844    }
1845
1846    /// Charges the stages this writer runs to `profile`.
1847    ///
1848    /// For the table being written now. [`Writer::next`] starts the next table without one,
1849    /// because a second table's stripes charged to the first table's load would be a profile of
1850    /// neither.
1851    #[must_use]
1852    pub fn with_profile(mut self, profile: Arc<LoadProfile>) -> Self {
1853        self.profile = Some(profile);
1854        self
1855    }
1856
1857    /// Records the order this table's rows are meant to be stored in.
1858    ///
1859    /// The declaration goes in the table directory and comes back out of
1860    /// [`Table::clustering`]. Nothing here sorts anything, and nothing here checks that the rows
1861    /// handed to [`Writer::append`] arrive in the order this claims. That is deliberate for now:
1862    /// the thing that was missing was a place to write the order down, and a loader that honours
1863    /// the declaration is the next piece rather than this one.
1864    ///
1865    /// The declaration applies to the table the writer is currently on, so it is set after
1866    /// [`Writer::next`] rather than once for the file.
1867    ///
1868    /// # Errors
1869    ///
1870    /// If the declaration names a column this table does not have.
1871    pub fn declare(mut self, clustering: Clustering) -> Result<Self> {
1872        // Rebuilt against this table's own column count rather than trusted, because the caller
1873        // built it against a catalog entry and the two could have drifted.
1874        self.table.clustering = Some(Clustering::new(
1875            clustering.columns().to_vec(),
1876            clustering.width(),
1877            &self.table.fields,
1878        )?);
1879        Ok(self)
1880    }
1881
1882    /// Appends bytes at the end of the file and moves the writer's own offset past them.
1883    ///
1884    /// Every write in here goes through this, so that [`Writer::at`] is the only answer to where
1885    /// anything is and the file's cursor is never consulted for it.
1886    fn put(&mut self, bytes: &[u8]) -> Result<()> {
1887        write_at(&self.file, self.at, bytes)?;
1888        self.at = self
1889            .at
1890            .checked_add(bytes.len() as u64)
1891            .ok_or_else(|| invalid("native file length overflow"))?;
1892        Ok(())
1893    }
1894
1895    /// Writes one chunk as independently readable column pages.
1896    ///
1897    /// # Errors
1898    ///
1899    /// If its width or types differ from the declared table, or a page exceeds its bound.
1900    pub fn append(&mut self, chunk: &Chunk) -> Result<()> {
1901        let order = (self.next_order, 0);
1902        self.next_order = self.next_order.saturating_add(1);
1903        self.append_at(order, chunk)
1904    }
1905
1906    /// Writes one chunk and records its source position for directory ordering.
1907    ///
1908    /// Pages may be encoded by parallel pipeline instances and reach the file in completion order.
1909    /// The stripe they land in is sorted by this key at commit, and [`Self::finish`] rejects a
1910    /// sequence whose parts do not come out in source order once the stripes are sorted, because a
1911    /// stripe groups whatever arrived together and cannot put a late part back where it belongs.
1912    ///
1913    /// # Errors
1914    ///
1915    /// The same as [`Self::append`].
1916    pub fn append_at(&mut self, order: (u64, u64), chunk: &Chunk) -> Result<()> {
1917        if chunk.is_empty() {
1918            return Ok(());
1919        }
1920        self.admit(chunk)?;
1921        if self.pending.last().is_some_and(|last| last.order > order) {
1922            self.flush_pending()?;
1923        }
1924        // Cloned rather than encoded, and a clone of a chunk that owns its buffers is a copy of
1925        // them. Sixty four parts of a hundred and five columns is tens of megabytes held for the
1926        // length of a stripe and a few seconds of memory traffic over a whole ClickBench load,
1927        // against the hundreds of seconds of encode this is what lets off one thread.
1928        self.pending.push(PendingChunk { order, chunk: chunk.clone() });
1929        if self.pending.len() == STRIPE_PARTS {
1930            self.flush_pending()?;
1931        }
1932        Ok(())
1933    }
1934
1935    /// Writes a run of chunks as one stripe of its own.
1936    ///
1937    /// [`Self::append_at`] decides where a stripe ends by watching the orders go past, which works
1938    /// when one caller hands over every chunk in source order and does not when several do. A
1939    /// writer being fed by more than one pipeline instance sees the orders interleave, and a stripe
1940    /// that ends every time two of them cross is a stripe of one or two parts.
1941    ///
1942    /// So the grouping moves to the caller. Whoever is buffering hands over a run it already knows
1943    /// is contiguous and in order, and gets a stripe holding exactly that run. The orders still
1944    /// have to come out in source order once the stripes are sorted, which [`Self::finish`] checks,
1945    /// so the runs from different callers may interleave with each other but may not overlap.
1946    ///
1947    /// # Errors
1948    ///
1949    /// The same as [`Self::append`], and if the run is longer than [`STRIPE_PARTS`].
1950    pub fn append_stripe(&mut self, parts: Vec<((u64, u64), Chunk)>) -> Result<()> {
1951        if parts.len() > STRIPE_PARTS {
1952            return Err(invalid("a stripe was handed more parts than it holds"));
1953        }
1954        // Whatever an earlier caller left behind is its own stripe rather than the front of this
1955        // one, because the two runs are from different places in the source and a stripe is a run.
1956        self.flush_pending()?;
1957        for (order, chunk) in parts {
1958            if chunk.is_empty() {
1959                continue;
1960            }
1961            self.admit(&chunk)?;
1962            self.pending.push(PendingChunk { order, chunk });
1963        }
1964        self.flush_pending()
1965    }
1966
1967    /// Checks a chunk against the declared table and counts its rows in.
1968    fn admit(&mut self, chunk: &Chunk) -> Result<()> {
1969        if chunk.width() != self.table.fields.len() {
1970            return Err(invalid("chunk width differs from table schema"));
1971        }
1972        for (index, field) in self.table.fields.iter().enumerate() {
1973            if chunk.column(index)?.logical_type() != &field.ty {
1974                return Err(invalid("chunk type differs from table schema"));
1975            }
1976        }
1977        self.table.rows = self
1978            .table
1979            .rows
1980            .checked_add(chunk.len())
1981            .ok_or_else(|| invalid("row count overflow"))?;
1982        Ok(())
1983    }
1984
1985    /// One column's parts of a stripe as pages, for a column with no global dictionary.
1986    fn encode_pages(columns: &[&Vector]) -> Result<ColumnStripe> {
1987        let mut stripe = ColumnStripe {
1988            pages: Vec::with_capacity(columns.len()),
1989            codes: Vec::with_capacity(columns.len()),
1990            sieves: Vec::with_capacity(columns.len()),
1991            ranges: Vec::with_capacity(columns.len()),
1992        };
1993        for &column in columns {
1994            let bytes = encode(column)?;
1995            if bytes.len() > MAX_PAGE {
1996                return Err(invalid("column page exceeds the configured bound"));
1997            }
1998            // The range is built first because the sieve reads it rather than walking the column a
1999            // second time to find out how wide it is.
2000            let range = Range::of(column);
2001            // A sieve at least as large as the part it indexes is not written. A reader reads the
2002            // sieve to decide whether to read the part, so when the sieve is the larger of the two
2003            // it has already spent more than the read it is trying to avoid, and that holds even if
2004            // it rejects every time. It is a necessary condition rather than the whole rule, which
2005            // is that a sieve pays when its bytes are under the rejection rate times the part's,
2006            // but the rejection rate depends on what a query probes for and the writer does not
2007            // know that. The necessary half needs two numbers that are both in hand here.
2008            //
2009            // A column with a global dictionary gets none, because it already has an exact
2010            // membership index per stripe. Those do not come through here. See [`prepare`].
2011            let sieve =
2012                Sieve::of(column, &range, SIEVE_BUDGET).filter(|sieve| sieve.len() < bytes.len());
2013            stripe.pages.push(bytes);
2014            stripe.codes.push(None);
2015            stripe.sieves.push(sieve);
2016            stripe.ranges.push(range);
2017        }
2018        Ok(stripe)
2019    }
2020
2021    /// Writes every encoded dictionary block that is not in the file yet and forgets its bytes.
2022    ///
2023    /// This is what keeps a load from holding its dictionaries' payload. The blocks land between
2024    /// stripes wherever the writer is, which is fine because the index says where each one is.
2025    fn place_blocks(&mut self) -> Result<()> {
2026        let mut dictionaries = std::mem::take(&mut self.dictionaries);
2027        let placed = dictionaries.iter_mut().flatten().try_for_each(|dictionary| {
2028            for block in std::mem::take(&mut dictionary.blocks) {
2029                let start = self.at;
2030                self.put(&block)?;
2031                dictionary.placed.push(Placed {
2032                    start,
2033                    length: block.len() as u64,
2034                    hash: checksum(&block),
2035                });
2036            }
2037            Ok(())
2038        });
2039        self.dictionaries = dictionaries;
2040        placed
2041    }
2042
2043    /// Writes the buffered parts as one stripe, each column's parts contiguous on disk.
2044    ///
2045    /// The same four steps a caller holding this writer behind a lock takes, with nobody else
2046    /// waiting between them. See [`prepare`].
2047    fn flush_pending(&mut self) -> Result<()> {
2048        if self.pending.is_empty() {
2049            return Ok(());
2050        }
2051        let held = std::mem::take(&mut self.pending);
2052        let prepared = self.preparer().prepare_held(held)?;
2053        let merged = self.merge_held(prepared)?;
2054        let paged = merged.pages()?;
2055        self.write_paged(paged)
2056    }
2057
2058    /// Writes one stripe whose pages are built, each column's parts contiguous on disk.
2059    fn write_stripe(&mut self, held: &[Part], encoded: Vec<ColumnStripe>) -> Result<()> {
2060        let width = self.table.fields.len();
2061        let parts = held.len();
2062        if encoded.len() != width {
2063            return Err(Error::internal("a stripe came to the writer with the wrong columns"));
2064        }
2065        let profile = self.profile.clone();
2066        if let Some(profile) = &profile {
2067            let rows = held.iter().map(|part| part.rows as u64).sum();
2068            let raw = held.iter().map(|part| part.footprint as u64).sum();
2069            let pages =
2070                encoded.iter().flat_map(|stripe| &stripe.pages).map(|page| page.len() as u64).sum();
2071            profile.moved(Stage::Pages, raw, pages, rows);
2072        }
2073        // Before a byte of the stripe is written, because the raw bytes this frees are the bytes the
2074        // load peaks on and the threads it uses are idle between here and the next chunk arriving.
2075        let timing = profile.as_deref().map(|profile| profile.span(Stage::Dictionary));
2076        let before = self.at;
2077        encode_ready(&mut self.dictionaries)?;
2078        self.place_blocks()?;
2079        drop(timing);
2080        if let Some(profile) = &profile {
2081            profile.moved(Stage::Dictionary, 0, self.at - before, 0);
2082        }
2083        let timing = profile.as_deref().map(|profile| profile.span(Stage::Write));
2084        let before = self.at;
2085        let mut pages = Vec::with_capacity(width);
2086        let mut memberships = vec![None; width];
2087        let mut ranges = Vec::with_capacity(width);
2088        let mut index = Vec::with_capacity(width.saturating_mul(index_section(parts)?));
2089        for stripe in &encoded {
2090            let offset = self.at;
2091            let section = index.len();
2092            let mut length = 0_usize;
2093            for bytes in &stripe.pages {
2094                write_at(&self.file, self.at + length as u64, bytes)?;
2095                put_u32(
2096                    &mut index,
2097                    u32::try_from(bytes.len()).map_err(|_| invalid("part length overflow"))?,
2098                );
2099                put_u64(&mut index, checksum(bytes));
2100                length = length
2101                    .checked_add(bytes.len())
2102                    .ok_or_else(|| invalid("column page length overflow"))?;
2103            }
2104            let hash = checksum(&index[section..]);
2105            put_u64(&mut index, hash);
2106            if length > MAX_PAGE {
2107                return Err(invalid("column page exceeds the configured bound"));
2108            }
2109            self.at = self
2110                .at
2111                .checked_add(length as u64)
2112                .ok_or_else(|| invalid("native file length overflow"))?;
2113            pages.push(Span {
2114                offset,
2115                length: u32::try_from(length).map_err(|_| invalid("page length overflow"))?,
2116            });
2117            ranges.push(merged_range(stripe.ranges.iter().cloned()));
2118        }
2119        for (membership, stripe) in memberships.iter_mut().zip(&encoded) {
2120            if stripe.codes.iter().all(Option::is_none) {
2121                continue;
2122            }
2123            let lists = stripe
2124                .codes
2125                .iter()
2126                .map(|codes| codes.clone().unwrap_or_default())
2127                .collect::<Vec<_>>();
2128            let bytes = encode_membership(&merged_codes(lists));
2129            let offset = self.at;
2130            self.put(&bytes)?;
2131            *membership = Some(Page {
2132                offset,
2133                length: u32::try_from(bytes.len())
2134                    .map_err(|_| invalid("membership page length overflow"))?,
2135                hash: checksum(&bytes),
2136            });
2137        }
2138        let mut sieves = vec![None; width];
2139        for (page, stripe) in sieves.iter_mut().zip(&encoded) {
2140            if stripe.sieves.iter().all(Option::is_none) {
2141                continue;
2142            }
2143            let bytes = encode_sieves(stripe.sieves.iter())?;
2144            let offset = self.at;
2145            self.put(&bytes)?;
2146            *page = Some(Page {
2147                offset,
2148                length: u32::try_from(bytes.len())
2149                    .map_err(|_| invalid("sieve page length overflow"))?,
2150                hash: checksum(&bytes),
2151            });
2152        }
2153        // A stripe of one part has the same rows in it as that part, so its own bounds are already
2154        // the part's and a page here would say what the directory says. Everywhere else the page is
2155        // written unless it comes to more than the column it indexes, which is the rule the sieves
2156        // go by and for the same reason: a reader reads this to decide whether to read the column,
2157        // so a page larger than the column has spent more than the read it is avoiding.
2158        let mut part_ranges = vec![None; width];
2159        if parts > 1 {
2160            for ((page, stripe), span) in part_ranges.iter_mut().zip(&encoded).zip(&pages) {
2161                let bytes = encode_part_ranges(&stripe.ranges)?;
2162                if bytes.len() >= span.length as usize {
2163                    continue;
2164                }
2165                let offset = self.at;
2166                self.put(&bytes)?;
2167                *page = Some(Page {
2168                    offset,
2169                    length: u32::try_from(bytes.len())
2170                        .map_err(|_| invalid("part range page length overflow"))?,
2171                    hash: checksum(&bytes),
2172                });
2173            }
2174        }
2175        let offset = self.at;
2176        self.put(&index)?;
2177        let index = Span {
2178            offset,
2179            length: u32::try_from(index.len())
2180                .map_err(|_| invalid("index page length overflow"))?,
2181        };
2182        let mut rows = 0_usize;
2183        let mut lengths = Vec::with_capacity(parts);
2184        let mut span = None;
2185        for part in held {
2186            rows = rows.checked_add(part.rows).ok_or_else(|| invalid("row count overflow"))?;
2187            lengths.push(u32::try_from(part.rows).map_err(|_| invalid("part row count overflow"))?);
2188            span = Some(span.map_or((part.order, part.order), |(first, _)| (first, part.order)));
2189        }
2190        self.order.push(span.ok_or_else(|| invalid("a stripe was flushed with no parts"))?);
2191        self.table.stripes.push(Stripe {
2192            rows,
2193            parts: lengths,
2194            index,
2195            pages,
2196            memberships: Pages::from_slots(memberships)?,
2197            sieves: Pages::from_slots(sieves)?,
2198            part_ranges: Pages::from_slots(part_ranges)?,
2199            zone: Zone::from_ranges(ranges),
2200        });
2201        drop(timing);
2202        if let Some(profile) = &profile {
2203            profile.moved(Stage::Write, 0, self.at - before, rows as u64);
2204        }
2205        Ok(())
2206    }
2207
2208    /// Finds exact heavy hitters without keeping a hash table for every numeric column while the
2209    /// load is live. The pages are already in the target file, so one column at a time uses a
2210    /// bounded Misra-Gries candidate table and then recounts only those candidates.
2211    ///
2212    /// The first of those passes also counts the column's distinct values exactly, up to the cap in
2213    /// [`distinct`], which is the number a string column gets from its dictionary. It comes back
2214    /// beside the summary because a column whose heavy hitters cannot be proved can still have been
2215    /// counted.
2216    ///
2217    /// The tables are keyed by a value's sixty four bits rather than by [`FrequencyValue`], and a
2218    /// null is counted beside them. Every integer type the format stores fits in those bits, so
2219    /// within one column two values share bits only if they are the same value, and a sixteen byte
2220    /// entry keeps the whole candidate table in the second level cache where the forty eight byte
2221    /// one did not. The null takes part in the candidate table exactly as a key would: it holds a
2222    /// place while its count is above zero, and it is decremented with the rest.
2223    fn numeric_frequency(&self, column: usize) -> Result<(Option<FrequencySummary>, Option<u64>)> {
2224        let signed = match self.table.fields[column].ty {
2225            LogicalType::TinyInt
2226            | LogicalType::SmallInt
2227            | LogicalType::Integer
2228            | LogicalType::BigInt
2229            | LogicalType::Date
2230            | LogicalType::Timestamp => true,
2231            LogicalType::UTinyInt
2232            | LogicalType::USmallInt
2233            | LogicalType::UInteger
2234            | LogicalType::UBigInt => false,
2235            _ => return Ok((None, None)),
2236        };
2237        let value_of = |bits: Option<u64>| match bits {
2238            None => FrequencyValue::Null,
2239            Some(bits) if signed => FrequencyValue::Integer(i128::from(bits as i64)),
2240            Some(bits) => FrequencyValue::Integer(i128::from(bits)),
2241        };
2242        let mut candidates: FrequencyMap<u32> = FrequencyMap::default();
2243        let mut nulls = 0_u32;
2244        let mut decrements = 0_u64;
2245        let mut distinct = distinct::ExactDistinct::new();
2246        self.visit_numeric(column, signed, |_, bits| {
2247            let held = match bits {
2248                Some(bits) => {
2249                    distinct.insert(bits);
2250                    candidates.get_mut(&bits)
2251                }
2252                None if nulls != 0 => Some(&mut nulls),
2253                None => None,
2254            };
2255            if let Some(count) = held {
2256                *count = count.saturating_add(1);
2257            } else if candidates.len() + usize::from(nulls != 0) < FREQUENCY_CANDIDATES {
2258                match bits {
2259                    Some(bits) => {
2260                        candidates.insert(bits, 1);
2261                    }
2262                    None => nulls = 1,
2263                }
2264            } else {
2265                candidates.retain(|_, count| {
2266                    *count -= 1;
2267                    *count != 0
2268                });
2269                nulls = nulls.saturating_sub(1);
2270                decrements = decrements.saturating_add(1);
2271            }
2272        })?;
2273        let (exact, null_count) = if decrements == 0 {
2274            let exact = candidates
2275                .into_iter()
2276                .map(|(bits, count)| (bits, u64::from(count)))
2277                .collect::<FrequencyMap<_>>();
2278            (exact, (nulls != 0).then_some(u64::from(nulls)))
2279        } else {
2280            let mut lower = candidates.values().copied().collect::<Vec<_>>();
2281            if nulls != 0 {
2282                lower.push(nulls);
2283            }
2284            lower.sort_unstable_by(|left, right| right.cmp(left));
2285            if lower.len() < FREQUENCY_BUILD_RANK
2286                || u64::from(lower[FREQUENCY_BUILD_RANK - 1]) <= decrements
2287            {
2288                return Ok((None, distinct.count()));
2289            }
2290            let mut exact =
2291                candidates.into_keys().map(|bits| (bits, 0_u64)).collect::<FrequencyMap<_>>();
2292            let mut null_count = (nulls != 0).then_some(0_u64);
2293            self.visit_numeric(column, signed, |_, bits| {
2294                let held = match bits {
2295                    Some(bits) => exact.get_mut(&bits),
2296                    None => null_count.as_mut(),
2297                };
2298                if let Some(count) = held {
2299                    *count = count.saturating_add(1);
2300                }
2301            })?;
2302            (exact, null_count)
2303        };
2304        let mut entries = exact
2305            .into_iter()
2306            .map(|(bits, count)| FrequencyEntry { value: value_of(Some(bits)), count })
2307            .chain(null_count.map(|count| FrequencyEntry { value: FrequencyValue::Null, count }))
2308            .collect::<Vec<_>>();
2309        let omitted_max = keep_most_frequent(&mut entries).max(decrements);
2310        let kept_rows = entries.iter().try_fold(0_u64, |total, entry| {
2311            total.checked_add(entry.count).filter(|&total| total <= FREQUENCY_ORDINALS as u64)
2312        });
2313        let mut ordinals = Vec::new();
2314        let mut ordinal_entries = Vec::new();
2315        if let Some(kept_rows) = kept_rows {
2316            let mut kept = FrequencyMap::default();
2317            let mut null_kept = None;
2318            for (at, entry) in entries.iter().enumerate() {
2319                let at = u16::try_from(at)
2320                    .map_err(|_| invalid("too many retained frequency entries"))?;
2321                match entry.value {
2322                    FrequencyValue::Integer(value) => {
2323                        kept.insert(value as u64, at);
2324                    }
2325                    FrequencyValue::Null => null_kept = Some(at),
2326                    FrequencyValue::Code(_) => {}
2327                }
2328            }
2329            ordinals.reserve(usize::try_from(kept_rows).unwrap_or(FREQUENCY_ORDINALS));
2330            ordinal_entries.reserve(usize::try_from(kept_rows).unwrap_or(FREQUENCY_ORDINALS));
2331            self.visit_numeric(column, signed, |ordinal, bits| {
2332                let held = match bits {
2333                    Some(bits) => kept.get(&bits).copied(),
2334                    None => null_kept,
2335                };
2336                if let Some(entry) = held {
2337                    ordinals.push(ordinal);
2338                    ordinal_entries.push(entry);
2339                }
2340            })?;
2341        }
2342        Ok((
2343            Some(FrequencySummary { entries, omitted_max, ordinals, ordinal_entries }),
2344            distinct.count(),
2345        ))
2346    }
2347
2348    /// Hands every row of an integer column to `visit` as its ordinal and its sixty four bits, or
2349    /// `None` for a null.
2350    ///
2351    /// `signed` says which of the two readings the column has. A packed unsigned column would come
2352    /// back from `signed_block` as a base plus a code in `i64`, which wraps for a value past the top
2353    /// of `BIGINT`, so only a signed column takes the block path.
2354    fn visit_numeric(
2355        &self,
2356        column: usize,
2357        signed: bool,
2358        mut visit: impl FnMut(u64, Option<u64>),
2359    ) -> Result<()> {
2360        let ty = &self.table.fields[column].ty;
2361        let mut start = 0_u64;
2362        let mut block = Vec::new();
2363        for stripe in &self.table.stripes {
2364            let spans = read_index(&self.file, stripe, column)?;
2365            let page = stripe.pages[column];
2366            let mut bytes = vec![0; page.length as usize];
2367            read_at(&self.file, page.offset, &mut bytes)?;
2368            for (span, &rows) in spans.iter().zip(&stripe.parts) {
2369                let part = part_bytes(&bytes, *span)?;
2370                if checksum(part) != span.hash {
2371                    return Err(invalid("column page checksum differs while building frequencies"));
2372                }
2373                let rows = rows as usize;
2374                let vector = decode(ty, rows, part, None)?;
2375                // Every signed layout a numeric column decodes to, which is every column of `hits`,
2376                // comes out as one run of `i64` and is walked as a slice. The row path below is for
2377                // the unsigned types and anything else that cannot be handed over that way.
2378                if signed && vector.signed_block(&mut block) && block.len() == rows {
2379                    if vector.none_null() {
2380                        for (row, &value) in block.iter().enumerate() {
2381                            visit(start.saturating_add(row as u64), Some(value as u64));
2382                        }
2383                    } else {
2384                        for (row, &value) in block.iter().enumerate() {
2385                            let bits = (!vector.is_null_at(row)).then_some(value as u64);
2386                            visit(start.saturating_add(row as u64), bits);
2387                        }
2388                    }
2389                    start = start.saturating_add(rows as u64);
2390                    continue;
2391                }
2392                // row at a time: frequency construction visits decoded values to update bounded candidates.
2393                for row in 0..rows {
2394                    let bits = if vector.is_null_at(row) {
2395                        None
2396                    } else {
2397                        // An unsigned column has no signed reading, and the documented fallback is
2398                        // the value itself. Every width the format stores fits in sixty four bits,
2399                        // so nothing is lost on the way through.
2400                        let widened = match vector.signed_at(row) {
2401                            Some(value) => Some(value as u64),
2402                            None => match vector.value_at(row) {
2403                                Value::UTinyInt(value) => Some(u64::from(value)),
2404                                Value::USmallInt(value) => Some(u64::from(value)),
2405                                Value::UInteger(value) => Some(u64::from(value)),
2406                                Value::UBigInt(value) => Some(value),
2407                                _ => None,
2408                            },
2409                        };
2410                        Some(widened.ok_or_else(|| {
2411                            invalid("numeric frequency page did not contain an integer value")
2412                        })?)
2413                    };
2414                    visit(start.saturating_add(row as u64), bits);
2415                }
2416                start = start.saturating_add(rows as u64);
2417            }
2418        }
2419        Ok(())
2420    }
2421
2422    /// Builds independent numeric synopses concurrently after all column pages are committed.
2423    ///
2424    /// The columns go through a queue rather than being cut into equal runs, because they are not
2425    /// equally expensive and they are not shuffled. A `BIGINT` column carries eight times the bytes
2426    /// of a `TINYINT` through the decode, and a run of them sits together in a schema the way it
2427    /// sits together in `hits`, so a worker that was handed the wrong six columns finishes long
2428    /// after one that was handed the right six and the whole phase waits for it.
2429    fn numeric_frequencies(&self) -> Result<Vec<(Option<FrequencySummary>, Option<u64>)>> {
2430        let mut columns = self
2431            .table
2432            .fields
2433            .iter()
2434            .enumerate()
2435            .filter_map(|(column, field)| {
2436                matches!(
2437                    field.ty,
2438                    LogicalType::TinyInt
2439                        | LogicalType::SmallInt
2440                        | LogicalType::Integer
2441                        | LogicalType::BigInt
2442                        | LogicalType::UTinyInt
2443                        | LogicalType::USmallInt
2444                        | LogicalType::UInteger
2445                        | LogicalType::UBigInt
2446                        | LogicalType::Date
2447                        | LogicalType::Timestamp
2448                )
2449                .then_some(column)
2450            })
2451            .collect::<Vec<_>>();
2452        let workers = std::thread::available_parallelism()
2453            .map_or(1, usize::from)
2454            .min(MAX_FREQUENCY_WORKERS)
2455            .min(columns.len());
2456        let profile = self.profile.as_deref();
2457        if workers <= 1 {
2458            let _timing = profile.map(|profile| profile.span(Stage::Publish));
2459            let mut frequencies = vec![(None, None); self.table.fields.len()];
2460            for column in columns {
2461                frequencies[column] = self.numeric_frequency(column)?;
2462            }
2463            return Ok(frequencies);
2464        }
2465        // Popped from the back, so the expensive columns are the ones taken first and the cheap ones
2466        // are what is left to fill in behind them.
2467        columns.sort_by_key(|&column| weight(&self.table.fields[column].ty));
2468        let queue = Mutex::new(columns);
2469        let pieces = std::thread::scope(|scope| {
2470            (0..workers)
2471                .map(|_| {
2472                    scope.spawn(|| {
2473                        let _timing = profile.map(|profile| profile.span(Stage::Publish));
2474                        let mut mine = Vec::new();
2475                        loop {
2476                            let taken = queue
2477                                .lock()
2478                                .map_err(|_| Error::internal("a native frequency worker panicked"))?
2479                                .pop();
2480                            let Some(column) = taken else { break };
2481                            mine.push((column, self.numeric_frequency(column)?));
2482                        }
2483                        Ok(mine)
2484                    })
2485                })
2486                .collect::<Vec<_>>()
2487                .into_iter()
2488                .map(|handle| {
2489                    handle
2490                        .join()
2491                        .map_err(|_| Error::internal("a native frequency worker panicked"))?
2492                })
2493                .collect::<Result<Vec<_>>>()
2494        })?;
2495        let mut frequencies = vec![(None, None); self.table.fields.len()];
2496        for piece in pieces {
2497            for (column, summary) in piece {
2498                frequencies[column] = summary;
2499            }
2500        }
2501        Ok(frequencies)
2502    }
2503
2504    /// Reads one stable dictionary code column only at sorted table-wide row ordinals.
2505    fn stable_codes_at(&self, column: usize, ordinals: &[u64]) -> Result<Option<Vec<Option<u32>>>> {
2506        if self.dictionaries.get(column).and_then(Option::as_ref).is_none() {
2507            return Ok(None);
2508        }
2509        if ordinals.windows(2).any(|pair| pair[0] >= pair[1]) {
2510            return Err(invalid("frequency ordinals are not sorted and unique"));
2511        }
2512        let mut out = Vec::with_capacity(ordinals.len());
2513        let mut wanted = 0;
2514        let mut stripe_start = 0_u64;
2515        for stripe in &self.table.stripes {
2516            let stripe_end = stripe_start.saturating_add(stripe.rows as u64);
2517            if wanted == ordinals.len() || ordinals[wanted] >= stripe_end {
2518                stripe_start = stripe_end;
2519                continue;
2520            }
2521            let spans = read_index(&self.file, stripe, column)?;
2522            let page = stripe.pages[column];
2523            let mut bytes = vec![0; page.length as usize];
2524            read_at(&self.file, page.offset, &mut bytes)?;
2525            let mut part_start = stripe_start;
2526            for (span, &rows) in spans.iter().zip(&stripe.parts) {
2527                let part_end = part_start.saturating_add(u64::from(rows));
2528                if wanted < ordinals.len() && ordinals[wanted] < part_end {
2529                    let part = part_bytes(&bytes, *span)?;
2530                    if checksum(part) != span.hash {
2531                        return Err(invalid(
2532                            "column page checksum differs while building pair frequencies",
2533                        ));
2534                    }
2535                    let upto = ordinals.partition_point(|&ordinal| ordinal < part_end);
2536                    let positions = ordinals[wanted..upto]
2537                        .iter()
2538                        .map(|&ordinal| {
2539                            usize::try_from(ordinal.saturating_sub(part_start))
2540                                .map_err(|_| invalid("frequency row offset does not fit in memory"))
2541                        })
2542                        .collect::<Result<Vec<_>>>()?;
2543                    if !decode_selected_stable_codes(rows as usize, part, &positions, &mut out)? {
2544                        return Ok(None);
2545                    }
2546                    wanted = upto;
2547                }
2548                part_start = part_end;
2549            }
2550            stripe_start = stripe_end;
2551        }
2552        if wanted != ordinals.len() {
2553            return Err(invalid("frequency ordinal is outside the table"));
2554        }
2555        Ok(Some(out))
2556    }
2557
2558    /// Derives bounded two-key leaders from numeric anchor ordinals and stable string codes.
2559    fn pair_frequencies(&self) -> Result<Vec<PairFrequencySummary>> {
2560        let anchors = self
2561            .table
2562            .frequencies
2563            .iter()
2564            .enumerate()
2565            .filter_map(|(column, summary)| {
2566                // A writer holds every synopsis it counted, so there is nothing stored to skip.
2567                match summary {
2568                    Some(Frequencies::Held(summary)) => Some(summary),
2569                    _ => None,
2570                }
2571                .filter(|summary| {
2572                    !summary.ordinals.is_empty()
2573                        && summary.ordinal_entries.len() == summary.ordinals.len()
2574                })
2575                .cloned()
2576                .map(|summary| (column, summary))
2577            })
2578            .collect::<Vec<_>>();
2579        let strings = self
2580            .dictionaries
2581            .iter()
2582            .enumerate()
2583            .filter_map(|(column, dictionary)| dictionary.as_ref().map(|_| column))
2584            .collect::<Vec<_>>();
2585        let mut summaries = Vec::new();
2586        for (first, anchors) in anchors {
2587            for &second in &strings {
2588                if summaries.len() == MAX_PAIR_FREQUENCIES {
2589                    return Ok(summaries);
2590                }
2591                let Some(codes) = self.stable_codes_at(second, &anchors.ordinals)? else {
2592                    continue;
2593                };
2594                if codes.len() != anchors.ordinal_entries.len() {
2595                    return Err(invalid("pair frequency columns have different lengths"));
2596                }
2597                let mut counts = HashMap::<(u16, Option<u32>), u64>::new();
2598                for (&anchor, code) in anchors.ordinal_entries.iter().zip(codes) {
2599                    *counts.entry((anchor, code)).or_default() += 1;
2600                }
2601                let mut entries = counts
2602                    .into_iter()
2603                    .map(|((first_entry, second), count)| PairFrequencyEntry {
2604                        first_entry,
2605                        second,
2606                        count,
2607                    })
2608                    .collect::<Vec<_>>();
2609                entries.sort_unstable_by(|left, right| {
2610                    right
2611                        .count
2612                        .cmp(&left.count)
2613                        .then_with(|| left.first_entry.cmp(&right.first_entry))
2614                        .then_with(|| left.second.cmp(&right.second))
2615                });
2616                let pair_omitted = entries.get(FREQUENCY_ENTRIES).map_or(0, |entry| entry.count);
2617                entries.truncate(FREQUENCY_ENTRIES);
2618                summaries.push(PairFrequencySummary {
2619                    first: u16::try_from(first)
2620                        .map_err(|_| invalid("pair frequency column index overflows"))?,
2621                    second: u16::try_from(second)
2622                        .map_err(|_| invalid("pair frequency column index overflows"))?,
2623                    entries,
2624                    omitted_max: anchors.omitted_max.max(pair_omitted),
2625                });
2626            }
2627        }
2628        Ok(summaries)
2629    }
2630
2631    /// Writes the directory of the table this writer is on and says where it went.
2632    ///
2633    /// Everything [`Writer::finish`] used to do except the two writes that publish. Pulling it out
2634    /// is what lets a second table follow a first: the bytes of a closed table are complete and
2635    /// addressable while nothing yet points at them, and the pointer is the last write of the
2636    /// commit.
2637    ///
2638    /// # Errors
2639    ///
2640    /// If directory encoding or writing fails.
2641    fn close(&mut self) -> Result<Entry> {
2642        self.flush_pending()?;
2643        // The rest of a table is its statistics, its dictionaries and its directory. The dictionary
2644        // work is charged as its own stage, because ranking a global dictionary can be most of what
2645        // this costs, and the rest as publish.
2646        let profile = self.profile.clone();
2647        let timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2648        let before = self.at;
2649        let mut stripes = std::mem::take(&mut self.order)
2650            .into_iter()
2651            .zip(std::mem::take(&mut self.table.stripes))
2652            .collect::<Vec<_>>();
2653        stripes.sort_by_key(|(order, _)| order.0);
2654        let mut previous: Option<(u64, u64)> = None;
2655        for ((first, last), _) in &stripes {
2656            if previous.is_some_and(|previous| previous >= *first) {
2657                return Err(invalid("chunks did not arrive in source order"));
2658            }
2659            previous = Some(*last);
2660        }
2661        self.table.stripes = stripes.into_iter().map(|(_, stripe)| stripe).collect();
2662        // The frequencies charge themselves, one span to each thread that counts, because they run
2663        // on threads of their own and a span on this one would see their wall time and none of
2664        // their CPU.
2665        drop(timing);
2666        let (frequencies, distincts): (Vec<Option<FrequencySummary>>, _) =
2667            self.numeric_frequencies()?.into_iter().unzip();
2668        self.table.frequencies =
2669            frequencies.into_iter().map(|held| held.map(Frequencies::Held)).collect();
2670        self.table.distincts = distincts;
2671        let timing = profile.as_deref().map(|profile| profile.span(Stage::Dictionary));
2672        let placing = self.at;
2673        finish_dictionaries(&mut self.dictionaries)?;
2674        self.place_blocks()?;
2675        self.table.pair_frequencies = self.pair_frequencies()?;
2676        let dictionaries = std::mem::take(&mut self.dictionaries);
2677        self.table.dictionary_payloads = vec![0; self.table.fields.len()];
2678        self.table.frequency_texts = vec![Vec::new(); self.table.fields.len()];
2679        self.table.host_groups = None;
2680        // One column at a time, and every column's values dropped before the next column's are read
2681        // back. Sorting the columns across threads is the obvious thing and was what this did, but
2682        // sorting a column now means decoding it, and five ClickBench string columns decoded at once
2683        // is the peak this change is about.
2684        for (index, dictionary) in dictionaries.into_iter().enumerate() {
2685            let Some(dictionary) = dictionary else { continue };
2686            let (order, flat, bases) = dictionary.ranked_with_values(Some(&self.file))?;
2687            // A code nothing counted is a code no non-null row of this column holds, which is the
2688            // empty string a null was written as and nothing else, because a code is only ever made
2689            // by a row asking for one.
2690            self.table.distincts[index] =
2691                Some(dictionary.counts.iter().filter(|count| **count != 0).count() as u64);
2692            let (frequencies, texts) = code_frequency(&dictionary, &flat, &bases)?;
2693            self.table.frequencies[index] = Some(Frequencies::Held(frequencies));
2694            self.table.frequency_texts[index] = texts;
2695            if self.table.fields[index].name.eq_ignore_ascii_case("Referer") {
2696                self.table.host_groups = host::build(index, &dictionary, &flat, &bases)?;
2697            }
2698            drop(flat);
2699            drop(bases);
2700            let encoded = encode_global_dictionary(&dictionary, &order, &dictionary.placed, true)?;
2701            drop(order);
2702            let offset = self.at;
2703            self.put(&encoded.index)?;
2704            self.put(&encoded.ranks)?;
2705            self.put(&encoded.grams)?;
2706            self.table.dictionary_payloads[index] = dictionary
2707                .placed
2708                .iter()
2709                .try_fold(0_u64, |sum, place| sum.checked_add(place.length))
2710                .ok_or_else(|| invalid("global dictionary payload overflow"))?;
2711            let length = encoded
2712                .index
2713                .len()
2714                .checked_add(encoded.ranks.len())
2715                .and_then(|len| len.checked_add(encoded.grams.len()))
2716                .ok_or_else(|| invalid("dictionary page length overflow"))?;
2717            self.table.dictionaries[index] = Some(Page {
2718                offset,
2719                length: u32::try_from(length)
2720                    .map_err(|_| invalid("dictionary page length overflow"))?,
2721                hash: checksum(&encoded.index),
2722            });
2723        }
2724        drop(timing);
2725        let timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2726        let placed = self.at - placing;
2727        self.write_stats()?;
2728        let directory = encode_directory(&self.table)?;
2729        if directory.len() > MAX_DIRECTORY {
2730            return Err(invalid("directory exceeds the configured bound"));
2731        }
2732        let offset = self.at;
2733        self.put(&directory)?;
2734        drop(timing);
2735        if let Some(profile) = &profile {
2736            profile.moved(Stage::Dictionary, 0, placed, 0);
2737            profile.moved(Stage::Publish, 0, self.at - before - placed, 0);
2738        }
2739        Ok(Entry {
2740            name: self.table.name.clone(),
2741            fields: self.table.fields.clone(),
2742            rows: self.table.rows,
2743            directory: Page {
2744                offset,
2745                length: u32::try_from(directory.len())
2746                    .map_err(|_| invalid("directory length overflow"))?,
2747                hash: checksum(&directory),
2748            },
2749        })
2750    }
2751
2752    /// Writes the statistics sections for the table being closed, as far as the budget reaches.
2753    ///
2754    /// Called from [`Self::close`] after the last stripe and after the dictionaries, which is the
2755    /// first moment the table's column bytes are final and the last moment before the directory is
2756    /// encoded. Both halves matter: the budget is a share of the column bytes, and a section that
2757    /// went in after the directory would be a section the directory does not name.
2758    ///
2759    /// Nothing here can fail the write. A column whose gather came back blind gets no sections, a
2760    /// column the budget could not reach gets none, and section 3.1 says both of those plan the way
2761    /// they planned before statistics existed. The two errors that are returned are an encode
2762    /// failure and a section count past the bound, and neither is a thing a column can cause.
2763    fn write_stats(&mut self) -> Result<()> {
2764        let gathers = std::mem::take(&mut self.gathers);
2765        let rows = self.table.rows as u64;
2766        let mut payloads = Vec::new();
2767        for (column, gather) in gathers.into_iter().enumerate() {
2768            let Some(gather) = gather else { continue };
2769            // A gather that saw a different number of rows than the table committed is a gather
2770            // that missed some, and a distinct count over some of a column is the one error an
2771            // estimator cannot see coming. This has no way of happening today, since a table is
2772            // written once and every chunk goes through `flush_pending`, and that is exactly why it
2773            // is worth a line: it stays true only while that stays true.
2774            if gather.rows() != rows {
2775                continue;
2776            }
2777            let Some(stats) = gather.finish() else { continue };
2778            let mut summary = Vec::new();
2779            stats.summary.encode(&mut summary)?;
2780            let mut sketches = Vec::new();
2781            stats.sketches.encode(&mut sketches)?;
2782            payloads.push((column, summary, sketches));
2783        }
2784        if payloads.is_empty() {
2785            return Ok(());
2786        }
2787        let costs = payloads
2788            .iter()
2789            .map(|(_, summary, sketches)| summary.len() + sketches.len())
2790            .collect::<Vec<_>>();
2791        let allowance = stats::allowance(stats::column_bytes(&self.table), stats::BUDGET_SHARE);
2792        // Nothing is spent yet. A table this writer is closing is one it wrote from nothing, so the
2793        // only statistics sections it can have are the ones about to go in.
2794        let keep = stats::within(&costs, allowance, 0);
2795        for ((column, summary, sketches), _) in
2796            payloads.iter().zip(&keep).filter(|&(_, &keep)| keep)
2797        {
2798            let id = u64::try_from(*column).map_err(|_| invalid("column index overflow"))?;
2799            for (kind, bytes, header_bytes) in [
2800                // A summary is a header the whole way down: there is nothing behind it a reader
2801                // could decide not to read.
2802                (*section::SUMMARY, summary, summary.len() as u32),
2803                (*section::SKETCHES, sketches, rudb_stats::sketches::HEADER_BYTES),
2804            ] {
2805                let written = write_section(
2806                    &self.file,
2807                    &mut self.at,
2808                    &section::Attachment { kind, id, flags: 0, header_bytes, bytes },
2809                    self.generation,
2810                )?;
2811                self.table.sections.push(written);
2812            }
2813        }
2814        if self.table.sections.len() > MAX_SECTIONS {
2815            return Err(invalid("the table would name more sections than the bound allows"));
2816        }
2817        Ok(())
2818    }
2819
2820    /// Commits every table this writer has written and syncs the file before publishing its header
2821    /// slot.
2822    ///
2823    /// The table handed back is the one the writer was on, which is the last of them. Callers that
2824    /// wrote several already know the others, since they named them.
2825    ///
2826    /// # Errors
2827    ///
2828    /// If directory encoding, writing, or syncing fails.
2829    pub fn finish(mut self) -> Result<Table> {
2830        let entry = self.close()?;
2831        let profile = self.profile.take();
2832        let _timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2833        let mut tables = std::mem::take(&mut self.closed);
2834        tables.push(entry);
2835        let catalog = encode_catalog(&tables, &self.views)?;
2836        if catalog.len() > MAX_DIRECTORY {
2837            return Err(invalid("catalog exceeds the configured bound"));
2838        }
2839        let offset = self.at;
2840        self.put(&catalog)?;
2841        if let Some(profile) = &profile {
2842            profile.moved(Stage::Publish, 0, catalog.len() as u64, 0);
2843        }
2844        // Every page and every table directory is on the disk before anything points at them. The
2845        // slot write below is what makes this generation the one a reader picks, so the order of
2846        // these two syncs is the whole of the commit.
2847        synced(&self.file, profile.as_deref())?;
2848        let slot = Slot {
2849            offset,
2850            length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
2851            generation: self.generation,
2852            hash: checksum(&catalog),
2853        };
2854        // The one write that is not an append, and the last one. It goes back over the slot in the
2855        // header, so it names its offset rather than going through `put`, and `at` does not move.
2856        // Which of the two slots it is alternates with the generation, so the one naming the
2857        // generation before this is still intact and still valid until this write lands.
2858        write_at(&self.file, slot_offset(self.generation), &slot.bytes())?;
2859        synced(&self.file, profile.as_deref())?;
2860        Ok(self.table)
2861    }
2862
2863    /// Commits a generation that changes the views and leaves every table exactly where it is.
2864    ///
2865    /// There was no way to do this before views existed, because everything that could change the
2866    /// catalog also wrote a table, so the only way to say something new about a file was to go
2867    /// through a table. A view is the first thing that can change on its own. Without this, adding
2868    /// a view to a database with eight tables in it would rewrite all eight, since the append path
2869    /// needs a table to append and the fallback is the whole file.
2870    ///
2871    /// It is the same commit as [`Writer::finish`] with nothing appended before it. The table
2872    /// entries are carried forward by directory pointer the way an append carries them, the new
2873    /// catalog goes on the end, and the slot write at the end is what publishes it.
2874    ///
2875    /// # Errors
2876    ///
2877    /// If the file has no valid committed directory, is not this build's format, or cannot be
2878    /// written.
2879    pub fn restate(path: impl AsRef<Path>, views: &[ViewEntry]) -> Result<()> {
2880        let path = path.as_ref();
2881        let (_, size, slot, bytes, _) = slot_bytes(path)?;
2882        let (closed, _) = decode_catalog(&bytes, size)?;
2883        let generation = slot
2884            .generation
2885            .checked_add(1)
2886            .ok_or_else(|| invalid("native file generation overflow"))?;
2887        let catalog = encode_catalog(&closed, views)?;
2888        if catalog.len() > MAX_DIRECTORY {
2889            return Err(invalid("catalog exceeds the configured bound"));
2890        }
2891        let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
2892        write_at(&file, size, &catalog)?;
2893        file.sync_all().map_err(io)?;
2894        let slot = Slot {
2895            offset: size,
2896            length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
2897            generation,
2898            hash: checksum(&catalog),
2899        };
2900        write_at(&file, slot_offset(generation), &slot.bytes())?;
2901        file.sync_all().map_err(io)?;
2902        Ok(())
2903    }
2904}
2905
2906/// Appends one run of bytes at `at` and moves it past them, answering where they went.
2907///
2908/// The append half of [`attach`], which cannot use [`Writer::put`] because it is not writing a
2909/// table. Every byte a section costs goes through here, so the offsets in an extent table come
2910/// from one place.
2911fn append(file: &File, at: &mut u64, bytes: &[u8]) -> Result<u64> {
2912    let offset = *at;
2913    write_at(file, offset, bytes)?;
2914    *at =
2915        at.checked_add(bytes.len() as u64).ok_or_else(|| invalid("native file length overflow"))?;
2916    Ok(offset)
2917}
2918
2919/// Writes one attachment's payload as extents and returns the entry that names it.
2920///
2921/// The split is by bytes, and the `first` of each extent is therefore a byte count. A section kind
2922/// whose extents should break on a row boundary instead will want to hand its extents over already
2923/// split; nothing needs that yet, and guessing at the shape of it now would be guessing.
2924fn write_section(
2925    file: &File,
2926    at: &mut u64,
2927    one: &section::Attachment<'_>,
2928    generation: u64,
2929) -> Result<Section> {
2930    // A payload of nothing is the exception, and it is not a special case so much as a different
2931    // reading of the same field: an entry with no bytes has no header to be longer than them, and
2932    // `header_bytes` is what the structure would have cost. See `Section::refused`.
2933    if !one.bytes.is_empty() && one.header_bytes as usize > one.bytes.len() {
2934        return Err(invalid("a section's header is longer than its payload"));
2935    }
2936    let mut extents = Vec::new();
2937    let mut first = 0_u64;
2938    for chunk in one.bytes.chunks(section::MAX_EXTENT as usize) {
2939        let offset = append(file, at, chunk)?;
2940        extents.push(section::Extent {
2941            offset,
2942            length: u32::try_from(chunk.len()).map_err(|_| invalid("extent length overflow"))?,
2943            hash: checksum(chunk),
2944            first,
2945        });
2946        first += chunk.len() as u64;
2947    }
2948    let mut table = Vec::with_capacity(extents.len() * section::EXTENT_BYTES);
2949    section::encode_extents(&extents, &mut table)?;
2950    // A payload of nothing is a section of no extents and no extent table, and its `extent_page`
2951    // is zero rather than the end of the file. Section 3.7 wants that entry to exist: it is how a
2952    // relationship that did not fit the budget is recorded as not built rather than forgotten.
2953    let extent_page = if table.is_empty() { 0 } else { append(file, at, &table)? };
2954    Ok(Section {
2955        kind: one.kind,
2956        id: one.id,
2957        generation,
2958        extents: u32::try_from(extents.len()).map_err(|_| invalid("too many extents"))?,
2959        extent_page,
2960        extent_bytes: u32::try_from(table.len()).map_err(|_| invalid("extent table overflow"))?,
2961        hash: checksum(&table),
2962        flags: one.flags,
2963        header_bytes: one.header_bytes,
2964    })
2965}
2966
2967/// Attaches graph sections to a table already committed in a file, without rewriting a page.
2968///
2969/// This is the second pass spec/graph/03-the-file-format.md section 3.8 asks for. A key map has to
2970/// exist before the link that uses it can be built, and it is built by reading the key column back,
2971/// so the structures of a table cannot be written during the load that wrote the table. They are
2972/// written afterwards, by this, and the file in between the two is a correct file that answers
2973/// every query more slowly.
2974///
2975/// Nothing is overwritten. The payloads, the extent tables, the new directory for this table and
2976/// the new catalog all go on the end of the file past the committed generation, and the last write
2977/// is the header slot, exactly as [`Writer::finish`] does it. So a crash anywhere in here leaves
2978/// the generation before it intact, and leaves unreferenced trailing bytes that the next commit
2979/// writes past.
2980///
2981/// An attachment replaces any section of the same kind and id, and every other section is carried
2982/// through untouched, including one whose kind this build does not know. The table's own generation
2983/// is carried through too, because attaching a section moves no row: see [`Table::generation`].
2984///
2985/// # Errors
2986///
2987/// If the file has no valid committed directory, is an older format than this build writes, holds
2988/// no table of that name, names a section whose payload cannot be written, or would end up naming
2989/// more sections than the format allows.
2990pub fn attach(
2991    path: impl AsRef<Path>,
2992    table: &str,
2993    attachments: &[section::Attachment<'_>],
2994) -> Result<Table> {
2995    let path = path.as_ref();
2996    let (_, size, slot, bytes, _) = slot_bytes(path)?;
2997    let (mut entries, views) = decode_catalog(&bytes, size)?;
2998    let at = entries
2999        .iter()
3000        .position(|entry| entry.name == table)
3001        .ok_or_else(|| invalid(&format!("the file holds no table called {table}")))?;
3002    let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
3003    let mut version = [0; 4];
3004    read_at(&file, 8, &mut version)?;
3005    let version = u32::from_le_bytes(version);
3006    // Readable is not the same as writable. A format 22 file has no section table, and giving its
3007    // directory one without moving the number in its header would leave a file that claims to be
3008    // format 22 and is not, which is worse than refusing. Rewriting it with this build is the
3009    // answer, and the format is at 0.3.x, so nobody has one of these that this project did not
3010    // just make.
3011    if version != FORMAT {
3012        return Err(invalid(&format!(
3013            "the file is format {version} and a graph section needs format {FORMAT}, so it has \
3014             to be written again"
3015        )));
3016    }
3017    let mut directory = vec![0; entries[at].directory.length as usize];
3018    read_at(&file, entries[at].directory.offset, &mut directory)?;
3019    if checksum(&directory) != entries[at].directory.hash {
3020        return Err(invalid(&format!("the directory of table {table} does not checksum")));
3021    }
3022    let mut held = decode_directory(&directory, size)?;
3023    let mut cursor = size;
3024    for one in attachments {
3025        let written = write_section(&file, &mut cursor, one, held.generation)?;
3026        held.sections.retain(|old| !(old.kind == one.kind && old.id == one.id));
3027        held.sections.push(written);
3028    }
3029    if held.sections.len() > MAX_SECTIONS {
3030        return Err(invalid("the table would name more sections than the bound allows"));
3031    }
3032    let encoded = encode_directory(&held)?;
3033    if encoded.len() > MAX_DIRECTORY {
3034        return Err(invalid("directory exceeds the configured bound"));
3035    }
3036    let offset = append(&file, &mut cursor, &encoded)?;
3037    entries[at].directory = Page {
3038        offset,
3039        length: u32::try_from(encoded.len()).map_err(|_| invalid("directory length overflow"))?,
3040        hash: checksum(&encoded),
3041    };
3042    // The views the file already had, written back unchanged. Attaching a section to a table says
3043    // nothing about a view and must not drop one.
3044    let catalog = encode_catalog(&entries, &views)?;
3045    if catalog.len() > MAX_DIRECTORY {
3046        return Err(invalid("catalog exceeds the configured bound"));
3047    }
3048    let offset = append(&file, &mut cursor, &catalog)?;
3049    file.sync_all().map_err(io)?;
3050    let generation =
3051        slot.generation.checked_add(1).ok_or_else(|| invalid("native file generation overflow"))?;
3052    let committed = Slot {
3053        offset,
3054        length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
3055        generation,
3056        hash: checksum(&catalog),
3057    };
3058    write_at(&file, slot_offset(generation), &committed.bytes())?;
3059    file.sync_all().map_err(io)?;
3060    Ok(held)
3061}
3062
3063/// One column's frequency synopsis as values with their row counts, shared by every clone of a
3064/// reader.
3065type Synopsis = Arc<Vec<(Value, u64)>>;
3066
3067/// Reads committed native column pages without holding the table in memory.
3068#[derive(Debug, Clone)]
3069pub struct Reader {
3070    file: Arc<File>,
3071    table: Arc<Table>,
3072    dictionaries: Arc<Vec<OnceLock<Arc<Vector>>>>,
3073    /// Held while a global dictionary is being opened, one per column.
3074    ///
3075    /// The [`OnceLock`] above says whether one has been opened, which is the question a reader that
3076    /// already has it needs answered and is free. It does not say whether one is being opened, and
3077    /// the difference matters because every worker of a scan wants the same dictionary at the same
3078    /// moment. Without this they all miss, all read the page, all verify it and all decode it, and
3079    /// all but one throw the answer away. ClickBench 38 reads the URL dictionary, which is 515,958
3080    /// entries, and was paying for it twice.
3081    loading: Arc<Vec<Mutex<()>>>,
3082    /// Each column's frequency synopsis as values, the first time anything asks for it. See
3083    /// [`Reader::decode_frequencies`].
3084    frequency_values: Arc<Vec<OnceLock<Synopsis>>>,
3085    /// Stored frequency sections are decoded once per open table. A small directory can hold the
3086    /// summary inline, but a larger one otherwise rereads and decodes the same section on every
3087    /// plan and every summary-backed aggregate.
3088    frequency_summaries: Arc<Vec<OnceLock<Arc<FrequencySummary>>>>,
3089    /// How many global dictionaries have been opened. A scan of a dictionary column should open its
3090    /// dictionary once however many workers it has, and the test that says so is the only thing
3091    /// keeping it that way.
3092    opened: Arc<AtomicUsize>,
3093    /// The membership sieves of one stripe of one column, by column and then by stripe, read the
3094    /// first time a probe asks about them. A query filters on one or two columns and never looks at
3095    /// the rest, so reading these at open would be the whole index for the sake of a fraction of it.
3096    sieves: Arc<Vec<Vec<SieveSlot>>>,
3097    /// The per part ranges of one stripe of one column, by column and then by stripe, read the
3098    /// first time something compares that column and kept after that.
3099    part_ranges: Arc<Vec<Vec<RangeSlot>>>,
3100    /// Which stripe and which part of it every part of the table is, by table wide part number.
3101    places: Arc<Vec<Place>>,
3102    cache: Arc<Vec<Mutex<Cached>>>,
3103    /// How many whole stripe pages have been read, which is what the sharing above is judged on. A
3104    /// scan of a column should read each of its stripes once however many workers it has.
3105    pages: Arc<AtomicUsize>,
3106    /// How many index sections have been read. A scan of a column should read each of its stripes
3107    /// once here too, and the test that says so is the only thing keeping it that way.
3108    indexes: Arc<AtomicUsize>,
3109    /// How many stripes of one column the page cache keeps. See [`CACHED_STRIPES_PER_COLUMN`] for
3110    /// what sets it and [`Reader::keep_stripes`] for who raises it.
3111    kept: Arc<AtomicUsize>,
3112    /// The file's size when it was opened, for [`Reader::layout`].
3113    size: u64,
3114    /// The committed directory's size, for [`Reader::layout`].
3115    directory: u64,
3116    /// What opening the file cost, which is a number rather than a claim.
3117    opening: Opening,
3118}
3119
3120/// What [`Reader::open`] read before it returned.
3121///
3122/// `spec/stats/04-in-memory.md` section 4.2 says opening a table reads the header and the directory
3123/// and nothing else, and once that document's statistics are in the file the tempting change is to
3124/// load a column summary or two on the way past, because they are small and the next query will
3125/// want them. A hundred milliseconds of that is a hundred milliseconds nobody asked for, and an
3126/// embedded database is opened by processes that are about to run one trivial query.
3127///
3128/// So the claim gets a number. Both of these are fixed by the schema and the stripe count and are
3129/// independent of how many rows the file holds, and the test that says so is what stops the
3130/// tempting change from landing quietly.
3131#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3132pub struct Opening {
3133    /// How many times the file was read. The header, then each directory slot that looked valid
3134    /// enough to check, so three at the most.
3135    pub reads: u32,
3136    /// How many bytes those reads asked for.
3137    pub bytes: u64,
3138}
3139
3140/// What a reader has read, while it was being opened and since.
3141#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3142pub struct Reads {
3143    /// What opening cost, before any query had been planned.
3144    pub opening: Opening,
3145    /// Whole stripe pages read since.
3146    pub pages: usize,
3147    /// Index sections read since.
3148    pub indexes: usize,
3149    /// Global dictionaries opened since. One per dictionary column that a query touched, however
3150    /// many workers touched it, which is a claim only a test can keep true.
3151    pub dictionaries: usize,
3152}
3153
3154/// Where one table wide part number lands.
3155#[derive(Debug, Clone, Copy)]
3156struct Place {
3157    stripe: u32,
3158    part: u32,
3159    rows: u32,
3160}
3161
3162/// One part's bytes inside one column page.
3163#[derive(Debug, Clone, Copy)]
3164struct PartSpan {
3165    start: usize,
3166    length: usize,
3167    hash: u64,
3168}
3169
3170/// What a reader holds for one stripe of one column.
3171///
3172/// The index is small and is loaded whether the caller wants the whole page or one part of it. The
3173/// page is loaded only by a scan, because a sparse fetch that wants a thousand rows out of sixty
3174/// four thousand would be reading sixty four times what it uses.
3175#[derive(Debug, Clone)]
3176struct CachedColumn {
3177    stripe: usize,
3178    index: Arc<Vec<PartSpan>>,
3179    page: Option<Arc<Vec<u8>>>,
3180}
3181
3182/// One column's stripes a reader holds, and which of them somebody is reading right now.
3183///
3184/// The pages are one slot per stripe of the table rather than a list of the ones being kept, so
3185/// finding a page is an index and not a walk. That matters because the walk happened under the
3186/// lock, once per part per column, and a scan that gives a whole stripe to each of thirty two
3187/// workers keeps enough pages that walking them was the longest thing the lock was held for. The
3188/// slots cost a pointer per stripe per column, which on the ClickBench file is eight kilobytes
3189/// against the forty megabytes of pages they point at. `order` is which of them are filled, oldest
3190/// first, because that is the one thing the slots cannot say by themselves.
3191///
3192/// `loading` is what keeps a scan from reading the same page once per worker. It is a list and not
3193/// a set because it holds at most one stripe per worker on the column and is walked far less often
3194/// than a hash of it would be built.
3195///
3196/// `index` is every index this reader has ever read for the column, one slot per stripe, and it is
3197/// never evicted. An index is a few hundred bytes and a page is a quarter of a megabyte, so the two
3198/// do not belong under the same budget. Riding in the page cache meant a worker that came back to a
3199/// stripe after its page had been evicted read the index again with it, which on the full
3200/// ClickBench file was about thirteen hundred reads out of a hundred and fourteen thousand.
3201#[derive(Debug, Default)]
3202struct Cached {
3203    pages: Vec<Option<Arc<Vec<u8>>>>,
3204    order: VecDeque<usize>,
3205    loading: Vec<usize>,
3206    index: Vec<Option<Arc<Vec<PartSpan>>>>,
3207}
3208
3209/// Stripes of one column a reader keeps the bytes of, when nobody has asked for more.
3210///
3211/// This has to hold at least as many stripes as a column has workers in it at once, or the workers
3212/// evict each other's pages and read them again. Four is what a scan that hands parts out in order
3213/// needs, because then every worker is within a few parts of every other and at most a couple of
3214/// stripes are open at a time. A scan that hands a whole stripe to each worker has one stripe open
3215/// per worker for the length of that stripe, and it says so with [`Reader::keep_stripes`] rather
3216/// than paying for sixteen slots on every table that is read one part at a time.
3217///
3218/// It multiplies by the page size, which is a quarter of a megabyte for a four byte column, and by
3219/// the number of columns a query touches.
3220const CACHED_STRIPES_PER_COLUMN: usize = 4;
3221
3222/// The sieves of one stripe of one column, once somebody has asked for them.
3223type SieveSlot = OnceLock<Arc<Vec<Option<Sieve>>>>;
3224
3225type RangeSlot = OnceLock<Arc<Vec<Range>>>;
3226
3227#[derive(Debug)]
3228struct NativeText {
3229    file: Arc<File>,
3230    /// How many values the dictionary holds.
3231    values: usize,
3232    /// Where each value ends inside its payload block, packed at `offset_bits` in runs of
3233    /// [`TEXT_OFFSET_RUN`].
3234    ///
3235    /// Ends rather than starts, because then a block of 1,024 values is 1,024 numbers rather than
3236    /// 1,025: the start of a value is the end of the one before it, and the first value of a block
3237    /// starts at zero by construction. Relative to the block rather than to the payload, because a
3238    /// reader decodes a whole block and slices it, so an offset into the payload is a number it
3239    /// would have to subtract a base from anyway.
3240    offsets: Vec<u8>,
3241    /// Bits one offset is packed at, which is what the largest block of this column spans and is the
3242    /// same for every block of it.
3243    offset_bits: usize,
3244    /// The same ends unpacked, built once enough readers have asked for one at a time.
3245    ///
3246    /// Reading one offset out of the packed form costs about fifty instructions: a division to find
3247    /// the run, a bounds check to slice it, a shift to reach the bit the value starts at and a
3248    /// narrowing on the way out. That is the right price for a reader that wants a handful. It is
3249    /// the wrong price for `STRLEN` over a column, which asks for one per row and nothing else, and
3250    /// where a million of them was a third of ClickBench 28.
3251    ///
3252    /// With the ends unpacked every read is a load, and a vector of lengths is one loop over them.
3253    /// The table is built only once the reads say it will be used, which is what
3254    /// [`Self::ends_worth_unpacking`] decides and [`Self::ends_asked`] counts towards, because a
3255    /// table built for a reader that wanted three values is four bytes a value spent on nothing.
3256    value_ends: OnceLock<Option<Vec<u32>>>,
3257    /// The length of every value, worked out of [`Self::value_ends`] the first time a vector of
3258    /// lengths is asked for.
3259    ///
3260    /// A length out of the ends is two loads, a test for whether the value opens its block and a
3261    /// check that it does not end before it starts, which came to thirteen instructions a row on
3262    /// ClickBench 28. Out of this it is one load. The order is checked once for the whole table
3263    /// while it is built, and a column that fails it gets no table and goes on reading the ends,
3264    /// which is where the error is reported. Four bytes a value, and only for a column something
3265    /// has asked the length of a vector at a time.
3266    value_lens: OnceLock<Option<Vec<u32>>>,
3267    /// How many single offset reads have come in while the table is not built.
3268    ///
3269    /// Relaxed, and read only against a threshold, so two threads racing here means the table is
3270    /// built one read early or one read late. Counting stops the moment the table exists, because
3271    /// [`OnceLock::get`] settles it before this is touched.
3272    ends_asked: AtomicUsize,
3273    /// How many entries the sorted order has, which is the value count.
3274    ranks: usize,
3275    /// Where the sorted order starts in the file. It is read a block at a time and only when
3276    /// something searches it, so a query that never compares this column against a literal never
3277    /// touches it at all.
3278    rank_at: u64,
3279    /// Where each block of the sorted order ends, as a byte offset from `rank_at`. A block is packed
3280    /// at whatever width its own heads need, so unlike the entries it replaced its length is not
3281    /// arithmetic on the block number.
3282    rank_ends: Vec<u64>,
3283    rank_hashes: Vec<u64>,
3284    rank_blocks: Vec<OnceLock<Result<Vec<u8>>>>,
3285    /// Bits one code is packed at, which is what the value count needs and is the same for every
3286    /// block of the column.
3287    code_bits: usize,
3288    /// The sorted order turned round, built the first time a reader asks for it.
3289    ///
3290    /// Four bytes per value against the four the offsets already hold, so a column that has this is
3291    /// carrying half again what it carried before rather than something of a new order. It is built
3292    /// only when something asks, which is a grouped min or max over this column and nothing else,
3293    /// and that reader was going to read the payload of this column once per row otherwise.
3294    code_ranks: OnceLock<Option<Vec<u32>>>,
3295    /// Where each block of the payload starts in the file, and how many stored bytes it is.
3296    ///
3297    /// Absolute rather than an offset from a base the blocks share, because a block is written the
3298    /// moment it fills and what comes after it in the file is whatever the load wrote next. A file
3299    /// old enough to have them back to back is read into these same two lists by adding the base to
3300    /// the ends it carries, so nothing below here knows which kind of file it came from.
3301    starts: Vec<u64>,
3302    lengths: Vec<u64>,
3303    hashes: Vec<u64>,
3304    /// Conservative four-byte substring signatures, read only by a compatible LIKE filter.
3305    grams: Option<NativeGrams>,
3306    /// The payload, read and decoded a block at a time and kept after that.
3307    blocks: Vec<OnceLock<Result<Vec<u8>>>>,
3308    /// How many decoded payload bytes this column keeps before a sweep stops keeping what it reads.
3309    /// [`TEXT_KEEP_BUDGET`] everywhere but in the test of the ceiling.
3310    keep_budget: usize,
3311    /// Roughly how many decoded payload bytes are being kept, which is what [`TEXT_KEEP_BUDGET`]
3312    /// is measured against.
3313    ///
3314    /// Roughly, because two threads that keep the same block at the same time both add its length
3315    /// while [`OnceLock`] keeps one of the two. That makes the count read high and the budget bind
3316    /// a little early, which is the harmless direction, and it costs one relaxed add a block rather
3317    /// than a lock on the path every scan of a string column goes through.
3318    payload_kept: AtomicUsize,
3319    /// Which payload blocks a sweep has decoded before, one flag a block.
3320    ///
3321    /// A sweep keeps a block the second time it decodes it and not the first. A process that runs
3322    /// one statement, which is how a benchmark or a script uses the engine, sweeps each block once
3323    /// and so keeps nothing: on ten million rows a `URL LIKE` held 396 MB with every block kept and
3324    /// 97 MB with none, for the same processor time. A session that asks again pays the decode one
3325    /// more time and reads kept blocks from then on, under the same [`TEXT_KEEP_BUDGET`].
3326    swept: Vec<AtomicBool>,
3327    /// The boundaries this dictionary has already been searched for, by the value searched for.
3328    ///
3329    /// A search is the expensive thing this type does. It settles a probe on the stored head where
3330    /// it can and reads a value where it cannot, and reading a value decodes the payload block it
3331    /// sits in, so one search can cost several blocks. The thing that makes remembering worth it is
3332    /// that the same search comes back: a top N asks once a chunk whether anything left can beat its
3333    /// worst candidate, and the worst candidate settles long before the chunks run out.
3334    ///
3335    /// Shared across the instances of a scan rather than kept per instance, because each of them has
3336    /// its own worst candidate and all of them are searching the same dictionary. One lock per chunk
3337    /// is nothing next to a probe of a file.
3338    ///
3339    /// Bounded by [`TEXT_SEARCH_MEMO`] and emptied rather than evicted when it is full. What fills
3340    /// it is a top N improving its bound, which happens a few dozen times and then stops, so the
3341    /// bound is there for the filter that searches for a different literal every chunk rather than
3342    /// for anything this is meant to help.
3343    searched: Mutex<HashMap<Vec<u8>, (usize, bool)>>,
3344}
3345
3346#[derive(Debug)]
3347struct NativeGrams {
3348    start: u64,
3349    length: usize,
3350    hash: u64,
3351    loaded: OnceLock<Result<Vec<u8>>>,
3352}
3353
3354/// How many searched for values a column's dictionary remembers the boundary of.
3355///
3356/// See [`NativeText::searched`]. Small because the case it is for repeats one value, not because a
3357/// larger one would be wrong.
3358const TEXT_SEARCH_MEMO: usize = 64;
3359
3360/// How many values of a dictionary go in one block of the payload.
3361///
3362/// The block is the unit the string cascade encodes, the unit a checksum covers, and the unit a
3363/// reader has to decode to get at a single value, so it is the one number the payload format turns
3364/// on. Blocking by values rather than by bytes is what keeps a value out of two blocks at once: the
3365/// block holding a code is `code / TEXT_PAYLOAD_VALUES` and nothing has to be stitched.
3366///
3367/// A probe on the five ClickBench columns that have a dictionary worth the name, written up on
3368/// #347, measured the ratio and the decode speed at 128, 256, 512, 1,024 and 4,096 values. Both get
3369/// better all the way up, because front coding and the LZ matcher have more to look back at and
3370/// because the per chunk setup is spread over more values. What stops it is the point read: a query
3371/// that wants ten values has to decode ten blocks, so the block is what a lookup costs. At 1,024
3372/// values a block is between 67 KB and 394 KB decoded across those five columns, and the ratios are
3373/// 2.3 to 4.5. Going up to 4,096 buys two to six percent more and makes a block as much as 1.5 MB.
3374/// Going down to 512 gives up five to nine percent.
3375const TEXT_PAYLOAD_VALUES: usize = 1024;
3376
3377/// Two KiB per payload block makes a four-byte substring a useful negative test without keeping a
3378/// large lookup table. The load and file-size costs must pass the same end-to-end gate as queries.
3379const TEXT_GRAM_BYTES: usize = 2048;
3380
3381/// A fast mixing step for exactly four bytes, shared by load and query.
3382fn gram_bits(bytes: &[u8]) -> [usize; 2] {
3383    let original = u32::from_le_bytes(bytes.try_into().expect("a four-byte gram"));
3384    let mut first = original ^ (original >> 16);
3385    first = first.wrapping_mul(0x7feb_352d);
3386    first ^= first >> 15;
3387    let mut second = original ^ (original >> 17);
3388    second = second.wrapping_mul(0x846c_a68b);
3389    second ^= second >> 16;
3390    let mask = TEXT_GRAM_BYTES * 8 - 1;
3391    [(first as usize) & mask, (second as usize) & mask]
3392}
3393
3394/// How many decoded payload bytes one dictionary keeps before a sweep stops keeping what it reads.
3395///
3396/// A sweep of the whole dictionary decodes every block whatever it does, and the only question is
3397/// whether it hangs on to them. Keeping all of them is 4.2 GB on ClickBench `URL` at a hundred
3398/// million rows, which is what #997 was right to stop. Keeping none of them means the next query
3399/// asking the same thing decodes all of it again, and on the same column at a million rows that
3400/// took a `LIKE` from 2.7 ms to 16.2 ms, because the decode used to be paid once by a session and
3401/// is now paid by every statement in it. Neither end is the answer. A bound is.
3402///
3403/// So a sweep keeps what it decodes until the column is holding this much and decodes without
3404/// keeping after that. At a million rows the five ClickBench string columns decode to between 8 MB
3405/// and 85 MB, so they sit inside it and a repeated `LIKE` reads a decoded block rather than a
3406/// stored one. At a hundred million rows `URL` fills it and the rest of that column is read and
3407/// dropped, which is the old cost on the part that does not fit and none of the old footprint.
3408///
3409/// Two hundred and fifty six megabytes a column is a number and not a policy, and the policy is
3410/// what should replace it: this wants to be a buffer pool over the whole database, sized against
3411/// the memory limit the session was given, with the blocks of every column competing for it and the
3412/// least useful one evicted. That is F2 work. What is here is the part of it that can be written
3413/// without an eviction order, which is a ceiling.
3414const TEXT_KEEP_BUDGET: usize = 256 * 1024 * 1024;
3415
3416/// The length of every value out of where each one ends inside its payload block, or `None` for
3417/// ends that go backwards somewhere inside a block.
3418///
3419/// A value that opens a block starts at zero and every other one starts where the value before it
3420/// ends, so a block is a run of differences.
3421fn lengths_of(ends: &[u32]) -> Option<Vec<u32>> {
3422    let mut lens = Vec::with_capacity(ends.len());
3423    for block in ends.chunks(TEXT_PAYLOAD_VALUES) {
3424        let mut start = 0;
3425        for &end in block {
3426            lens.push(end.checked_sub(start)?);
3427            start = end;
3428        }
3429    }
3430    Some(lens)
3431}
3432
3433/// How many offsets go in one packed run.
3434///
3435/// A payload block holds 1,024 values and `bitpack::pack_tail` takes fewer than 1,024 at a time,
3436/// since a whole unit of that many belongs in the transposed layout instead. So the offsets of a
3437/// block go in two runs. Five hundred and twelve values at any width is a whole number of bytes, so
3438/// a run starts where a multiply says it does and nothing is padded.
3439const TEXT_OFFSET_RUN: usize = 512;
3440
3441/// Bytes at the front of a global dictionary index: the value count, the values a payload block
3442/// holds, the block count and the bits an offset is packed at.
3443const DICTIONARY_HEADER: usize = 16;
3444
3445/// Set beside the offset width in the fourth word of a global dictionary index, meaning each
3446/// payload block says where in the file it starts and how long it is, rather than sitting directly
3447/// behind the block before it.
3448///
3449/// In that word rather than in a word of its own because the width is at most 32 and lives in a
3450/// `u32`, so the top of it has never been anything. A build old enough not to know the flag reads
3451/// the file's format before it reads any of this and refuses it there, and if it somehow did get
3452/// here it would find an offset width of two billion and say so.
3453///
3454/// The point of the flag is that a block written the moment it fills does not know what will be
3455/// written after it, so the payload of a column cannot be one run of bytes unless the whole column
3456/// is held until the file is closed. That is the memory the load cannot afford. What it costs is
3457/// eight bytes a block, against the block being a thousand values.
3458const DICTIONARY_SCATTERED: u32 = 1 << 31;
3459/// The dictionary index carries one four-byte substring signature per payload block.
3460const DICTIONARY_GRAMS: u32 = 1 << 30;
3461
3462/// How many entries of a dictionary's sorted order sit in one block that is read and checked as a
3463/// unit.
3464///
3465/// Five hundred and twelve entries is between two and three kilobytes on the ClickBench string
3466/// columns, which is well under a page. A binary search over half a million entries makes nineteen
3467/// probes, and the first ten land in ten different blocks while the last nine land in the one block
3468/// that holds the answer, so the whole search reads about thirty kilobytes of a megabyte of order. A
3469/// smaller block would save a little on the early probes, cost a checksum and an end list four times
3470/// as long, and give the heads less to share a base with. A larger one would read more than it uses
3471/// on every probe.
3472const TEXT_RANK_BLOCK: usize = 512;
3473
3474/// Bytes at the front of a rank block, which is the base of its heads and the width they are packed
3475/// at.
3476///
3477/// An entry used to be twelve bytes flat, eight for the head and four for the code, and on the five
3478/// ClickBench columns that have a dictionary worth the name that was 744 MB of a 12.2 GB file. Both
3479/// halves of it are nearly empty. The heads are the first eight bytes of the values in sorted order,
3480/// so a block of five hundred and twelve of them spans a tiny slice of the column, and on a column of
3481/// URLs they are all `http://w` and the block holds one distinct head. The codes are positions in a
3482/// dictionary of eighteen million, which is twenty five bits and not thirty two.
3483///
3484/// So a block now writes the smallest head in it, the bits the largest is above that, and the heads
3485/// and the codes packed at the width each needs. A block where every head agrees costs nine bytes
3486/// and the codes.
3487const RANK_BLOCK_HEADER: usize = size_of::<u64>() + 1;
3488
3489impl NativeText {
3490    /// One block of the payload, read and decoded the first time anything asks for a value in it.
3491    ///
3492    /// The bytes handed back are the values of the block laid end to end, which is what the offsets
3493    /// describe, so a caller slices it with the offsets it already has. Where the block sits in the
3494    /// file is the only thing the caller cannot work out for itself, because the stored form is
3495    /// shorter than the decoded one and by a different amount in every block.
3496    fn payload_block(&self, block: usize) -> Result<Option<&[u8]>> {
3497        let Some(slot) = self.blocks.get(block) else { return Ok(None) };
3498        let bytes = slot.get_or_init(|| self.decode_block(block)).as_ref().map_err(Clone::clone)?;
3499        Ok(Some(bytes.as_slice()))
3500    }
3501
3502    /// Reads and decodes one block of the payload, without deciding who keeps it.
3503    ///
3504    /// [`Self::payload_block`] keeps it forever, which is what a point read wants and what a walk
3505    /// of the whole dictionary must not do. Both call this and they differ in nothing else.
3506    fn decode_block(&self, block: usize) -> Result<Vec<u8>> {
3507        let len = self.lengths[block];
3508        let mut stored = vec![
3509            0;
3510            usize::try_from(len).map_err(|_| invalid(
3511                "global dictionary block does not fit in memory"
3512            ))?
3513        ];
3514        read_at(&self.file, self.starts[block], &mut stored)?;
3515        if checksum(&stored) != self.hashes[block] {
3516            return Err(invalid("global dictionary payload checksum differs"));
3517        }
3518        let first = block * TEXT_PAYLOAD_VALUES;
3519        let last = (first + TEXT_PAYLOAD_VALUES).min(self.values);
3520        let want = self.end_within(last - 1)? as usize;
3521        let values = string::decode_flat(&stored)?;
3522        if values.len() != last - first {
3523            return Err(invalid("global dictionary block holds the wrong value count"));
3524        }
3525        let bytes = values.into_bytes();
3526        if bytes.len() != want {
3527            return Err(invalid("global dictionary block decodes to the wrong length"));
3528        }
3529        Ok(bytes)
3530    }
3531
3532    /// How many single offset reads make [`Self::value_ends`] worth building.
3533    ///
3534    /// As many reads as the dictionary has values. Building the table costs about thirty
3535    /// instructions a value once the fresh pages it lands in are counted, and a read out of it saves
3536    /// about thirty five, so it repays itself after roughly one read per value. The reads so far are
3537    /// the only guess there is at the reads to come, and waiting until they match the size of the
3538    /// dictionary is betting that a column read that much will be read that much again.
3539    ///
3540    /// A sixteenth was the first answer, from counting the unpacking alone at three instructions a
3541    /// value. ClickBench 38 showed what that missed: it reads about twenty thousand titles a
3542    /// statement out of a dictionary of three hundred and fifty thousand, crossed a sixteenth in its
3543    /// second statement and was two percent slower for a table it did not read enough to repay. A
3544    /// scan asking for the length of every row crosses it part way through its first statement on
3545    /// ClickBench, where a string column has about two rows for every value, and a filter that keeps
3546    /// a few thousand rows never does. The floor is there
3547    /// because a short dictionary would otherwise build a table for a handful of reads.
3548    fn ends_worth_unpacking(&self) -> usize {
3549        self.values.max(TEXT_PAYLOAD_VALUES)
3550    }
3551
3552    /// The unpacked ends, if they are built or if this read is the one that makes them worth it.
3553    fn value_ends(&self) -> Option<&[u32]> {
3554        if let Some(built) = self.value_ends.get() {
3555            return built.as_deref();
3556        }
3557        if self.ends_asked.fetch_add(1, Atomic::Relaxed) < self.ends_worth_unpacking() {
3558            return None;
3559        }
3560        self.value_ends.get_or_init(|| self.unpack_ends()).as_deref()
3561    }
3562
3563    /// Every end of the column, a run at a time.
3564    ///
3565    /// `None` rather than an error on anything wrong, because this is a cache in front of a reader
3566    /// that answers the same question. A column whose offsets are short or whose ends do not fit in
3567    /// four bytes gets no table and the same error it would have got, from the read that wanted it.
3568    fn unpack_ends(&self) -> Option<Vec<u32>> {
3569        let mut ends = vec![0u32; self.values];
3570        for (run, into) in ends.chunks_mut(TEXT_OFFSET_RUN).enumerate() {
3571            let bytes = self.offsets.get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)?;
3572            bitpack::unpack_tail_into(bytes, self.offset_bits, into, |bits| {
3573                u32::try_from(bits).unwrap_or(u32::MAX)
3574            })
3575            .ok()?;
3576        }
3577        // An end that did not fit was stored as the sentinel, and a real one cannot reach it because
3578        // a payload block is far smaller than four gigabytes. So the column keeps the packed reader.
3579        if ends.contains(&u32::MAX) { None } else { Some(ends) }
3580    }
3581
3582    /// Where the value at `index` ends inside its payload block.
3583    fn end_within(&self, index: usize) -> Result<u32> {
3584        if let Some(ends) = self.value_ends() {
3585            return ends
3586                .get(index)
3587                .copied()
3588                .ok_or_else(|| invalid("global dictionary offsets are short"));
3589        }
3590        let run = index / TEXT_OFFSET_RUN;
3591        let bytes = self
3592            .offsets
3593            .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3594            .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3595        let end = bitpack::tail_at(bytes, self.offset_bits, index % TEXT_OFFSET_RUN)
3596            .map_err(|_| invalid("global dictionary offsets are short"))?;
3597        u32::try_from(end).map_err(|_| invalid("global dictionary offset is past the payload"))
3598    }
3599
3600    /// Where every value in `first..last` ends inside its payload block, in one pass over the runs.
3601    ///
3602    /// [`Self::end_within`] answers for one value and pays for it twice over: it shifts a window to
3603    /// the bit the value starts at, and the copy that fills that window is a length the compiler does
3604    /// not know, so it is a call to `memcpy` rather than a load. A sweep asked for two of those per
3605    /// value, one for the end and one for the start that is the end before it, and on the ClickBench
3606    /// `URL` dictionary of eighteen million that was most of the half second a `LIKE` over it took.
3607    ///
3608    /// [`bitpack::unpack_tail_into`] walks the run instead, which makes the window a fixed width and
3609    /// so an unaligned load, and reads the bit position off a counter. A run is five hundred and
3610    /// twelve values and a block is two of them, so a block of a thousand and twenty four values
3611    /// costs two calls here and nothing per value.
3612    ///
3613    /// The answer is written straight into the result. A run that is wanted from its first value,
3614    /// which is every run but the one the sweep starts in, unpacks into its own window of the result
3615    /// and is never copied. Only a run joined part way through needs the scratch buffer, and there is
3616    /// at most one of those per sweep, so the buffer is allocated the first time one turns up.
3617    fn ends_within(&self, first: usize, last: usize) -> Result<Vec<u64>> {
3618        let mut ends = vec![0u64; last.saturating_sub(first)];
3619        let mut scratch = Vec::new();
3620        let mut at = first;
3621        while at < last {
3622            let run = at / TEXT_OFFSET_RUN;
3623            let stop = ((run + 1) * TEXT_OFFSET_RUN).min(last);
3624            let held = self.values.saturating_sub(run * TEXT_OFFSET_RUN).min(TEXT_OFFSET_RUN);
3625            let bytes = self
3626                .offsets
3627                .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3628                .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3629            let from = at % TEXT_OFFSET_RUN;
3630            let upto = stop - run * TEXT_OFFSET_RUN;
3631            if upto > held || bytes.len() < bitpack::tail_len(held, self.offset_bits) {
3632                return Err(invalid("global dictionary offsets are short"));
3633            }
3634            let into = &mut ends[at - first..stop - first];
3635            if from == 0 {
3636                bitpack::unpack_tail_into(bytes, self.offset_bits, into, |bits| bits)
3637                    .map_err(|_| invalid("global dictionary offsets are short"))?;
3638            } else {
3639                scratch.resize(held, 0);
3640                bitpack::unpack_tail_into(bytes, self.offset_bits, &mut scratch, |bits| bits)
3641                    .map_err(|_| invalid("global dictionary offsets are short"))?;
3642                into.copy_from_slice(&scratch[from..upto]);
3643            }
3644            at = stop;
3645        }
3646        Ok(ends)
3647    }
3648
3649    /// Where the value at `index` starts inside its payload block, which is where the value before
3650    /// it ended unless it is the first of the block.
3651    fn start_within(&self, index: usize) -> Result<u32> {
3652        if index % TEXT_PAYLOAD_VALUES == 0 { Ok(0) } else { self.end_within(index - 1) }
3653    }
3654
3655    /// Where the value at `index` starts and ends inside its payload block.
3656    ///
3657    /// The two offsets sit next to each other in the same run unless the value opens one, and a run
3658    /// of seventeen bit offsets, which is what a block of a thousand strings needs, puts a pair of
3659    /// them inside one eight byte load. So the common case reads the packed bytes once rather than
3660    /// twice and does the bounds arithmetic once. This is asked once per string a text column hands
3661    /// out, and on ClickBench 27 the two reads together were a quarter of the query.
3662    fn span_within(&self, index: usize) -> Result<(u32, u32)> {
3663        if let Some(ends) = self.value_ends() {
3664            let end =
3665                *ends.get(index).ok_or_else(|| invalid("global dictionary offsets are short"))?;
3666            // The value before it in the same block, and zero where there is no value before it.
3667            // `index` is inside the table, so the one under it is too.
3668            let start = if index % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[index - 1] };
3669            if start > end {
3670                return Err(invalid("global dictionary value ends before it starts"));
3671            }
3672            return Ok((start, end));
3673        }
3674        let within = index % TEXT_OFFSET_RUN;
3675        let (start, end) = if within == 0 {
3676            (self.start_within(index)?, self.end_within(index)?)
3677        } else {
3678            let run = index / TEXT_OFFSET_RUN;
3679            let bytes = self
3680                .offsets
3681                .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3682                .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3683            let (start, end) = bitpack::tail_pair(bytes, self.offset_bits, within)
3684                .map_err(|_| invalid("global dictionary offsets are short"))?;
3685            let ends = u32::try_from(end)
3686                .map_err(|_| invalid("global dictionary offset is past the payload"))?;
3687            let starts = u32::try_from(start)
3688                .map_err(|_| invalid("global dictionary offset is past the payload"))?;
3689            (starts, ends)
3690        };
3691        if start > end {
3692            return Err(invalid("global dictionary value ends before it starts"));
3693        }
3694        Ok((start, end))
3695    }
3696
3697    /// The block of the sorted order that holds `rank`, and where in it that rank sits.
3698    ///
3699    /// The block is read from the file and checked against the hash the index carries for it the
3700    /// first time anything asks, and kept after that, the same way a payload block is. A search
3701    /// makes about as many probes as the order has bits, so the whole search reads a handful of
3702    /// these and never the rest.
3703    fn rank_parts(&self, rank: usize) -> Result<(&[u8], usize)> {
3704        let slot = self
3705            .rank_blocks
3706            .get(rank / TEXT_RANK_BLOCK)
3707            .ok_or_else(|| invalid("global dictionary rank is past the order"))?;
3708        let block = slot
3709            .get_or_init(|| {
3710                let which = rank / TEXT_RANK_BLOCK;
3711                let start = if which == 0 { 0 } else { self.rank_ends[which - 1] };
3712                let end = self.rank_ends[which];
3713                let mut bytes = vec![0; (end - start) as usize];
3714                read_at(&self.file, self.rank_at + start, &mut bytes)?;
3715                if checksum(&bytes)
3716                    != *self
3717                        .rank_hashes
3718                        .get(rank / TEXT_RANK_BLOCK)
3719                        .ok_or_else(|| invalid("global dictionary rank block has no checksum"))?
3720                {
3721                    return Err(invalid("global dictionary rank checksum differs"));
3722                }
3723                Ok(bytes)
3724            })
3725            .as_ref()
3726            .map_err(Clone::clone)?;
3727        Ok((block.as_slice(), rank % TEXT_RANK_BLOCK))
3728    }
3729
3730    /// The first eight bytes of the value at `rank`, as the integer a comparison reads.
3731    fn head_at(&self, rank: usize) -> Result<u64> {
3732        let (block, within) = self.rank_parts(rank)?;
3733        let (base, width, packed) = rank_heads(block)?;
3734        let above = bitpack::tail_at(packed, width, within)
3735            .map_err(|_| invalid("global dictionary rank block is short of heads"))?;
3736        Ok(base.wrapping_add(above))
3737    }
3738
3739    /// The packed codes of one rank block, which follow the heads on the next byte boundary.
3740    fn rank_codes<'block>(&self, block: &'block [u8], count: usize) -> Result<&'block [u8]> {
3741        let (_, width, packed) = rank_heads(block)?;
3742        packed
3743            .get(bitpack::tail_len(count, width)..)
3744            .ok_or_else(|| invalid("global dictionary rank block is short of codes"))
3745    }
3746
3747    /// How many entries the block holding `rank` has, which is a full block except at the end.
3748    fn rank_block_len(&self, rank: usize) -> usize {
3749        let first = rank / TEXT_RANK_BLOCK * TEXT_RANK_BLOCK;
3750        TEXT_RANK_BLOCK.min(self.ranks - first)
3751    }
3752}
3753
3754/// The base, the width and the packed bytes of one rank block's heads.
3755fn rank_heads(block: &[u8]) -> Result<(u64, usize, &[u8])> {
3756    let header = block
3757        .get(..RANK_BLOCK_HEADER)
3758        .ok_or_else(|| invalid("global dictionary rank block is short"))?;
3759    let base = u64::from_le_bytes(header[..8].try_into().expect("eight bytes"));
3760    let width = header[8] as usize;
3761    if width > 64 {
3762        return Err(invalid("global dictionary rank block packs heads past a word"));
3763    }
3764    Ok((base, width, &block[RANK_BLOCK_HEADER..]))
3765}
3766
3767/// Bits one offset of a dictionary takes, which is what its widest payload block spans.
3768///
3769/// One width for the whole column rather than one a block. A block is 1,024 values of the same
3770/// column, so the blocks of a column are within a factor of two of each other on every ClickBench
3771/// string column, and a width a block would save a fraction of a bit and cost a byte a block plus
3772/// the arithmetic that finds where a block starts.
3773fn offset_width(ends: &[u32]) -> usize {
3774    // The ends are already relative to the block the value is in, so the last end of a block is that
3775    // block's total and the largest end anywhere is the widest block. There is no subtraction left
3776    // to do and no need to walk the blocks to find where one starts.
3777    let span = ends.iter().copied().max().unwrap_or(0);
3778    (u32::BITS - span.leading_zeros()) as usize
3779}
3780
3781/// How many bytes `values` offsets take at `bits`, which is what the reader has to know before it
3782/// has read any of them.
3783fn offset_bytes(values: usize, bits: usize) -> usize {
3784    let full = values / TEXT_OFFSET_RUN;
3785    let rest = values % TEXT_OFFSET_RUN;
3786    full * TEXT_OFFSET_RUN / 8 * bits + bitpack::tail_len(rest, bits)
3787}
3788
3789/// The end of every value within its payload block, packed a run at a time.
3790/// A run never straddles a block, because [`TEXT_OFFSET_RUN`] divides [`TEXT_PAYLOAD_VALUES`], which
3791/// is what lets this be a walk of the ends rather than arithmetic against a per block base.
3792fn encode_offsets(ends: &[u32], bits: usize, out: &mut Vec<u8>) -> Result<()> {
3793    let mut run = Vec::with_capacity(TEXT_OFFSET_RUN);
3794    for chunk in ends.chunks(TEXT_OFFSET_RUN) {
3795        run.clear();
3796        run.extend(chunk.iter().map(|&end| u64::from(end)));
3797        bitpack::pack_tail(&run, bits, out)
3798            .map_err(|_| invalid("global dictionary offsets do not pack"))?;
3799    }
3800    Ok(())
3801}
3802
3803/// How many bits a code of a dictionary of `values` entries takes.
3804fn code_width(values: usize) -> usize {
3805    match u64::try_from(values).unwrap_or(u64::MAX) {
3806        0 | 1 => 0,
3807        last => (u64::BITS - (last - 1).leading_zeros()) as usize,
3808    }
3809}
3810
3811impl TextSource for NativeText {
3812    fn len(&self) -> usize {
3813        self.values
3814    }
3815
3816    fn might_contain(&self, first: usize, literal: &[u8]) -> Result<bool> {
3817        let Some(grams) = &self.grams else { return Ok(true) };
3818        if literal.len() < 4 || first >= self.values {
3819            return Ok(true);
3820        }
3821        let bytes = grams
3822            .loaded
3823            .get_or_init(|| {
3824                let mut bytes = vec![0; grams.length];
3825                read_at(&self.file, grams.start, &mut bytes)?;
3826                if checksum(&bytes) != grams.hash {
3827                    return Err(invalid("global dictionary substring signatures checksum differs"));
3828                }
3829                Ok(bytes)
3830            })
3831            .as_ref()
3832            .map_err(Clone::clone)?;
3833        let block = first / TEXT_PAYLOAD_VALUES;
3834        let Some(bits) = bytes.get(block * TEXT_GRAM_BYTES..(block + 1) * TEXT_GRAM_BYTES) else {
3835            return Ok(true);
3836        };
3837        Ok(literal.windows(4).all(|gram| {
3838            gram_bits(gram).into_iter().all(|bit| bits[bit / 8] & (1 << (bit % 8)) != 0)
3839        }))
3840    }
3841
3842    fn bytes_at(&self, index: usize) -> Result<Option<&[u8]>> {
3843        if index >= self.values {
3844            return Ok(None);
3845        }
3846        let (start, end) = self.span_within(index)?;
3847        if start == end {
3848            return Ok(Some(&[]));
3849        }
3850        // A block holds a fixed number of values rather than a fixed number of bytes, so the value
3851        // is in one block and the offsets already say where in it.
3852        let block = index / TEXT_PAYLOAD_VALUES;
3853        let Some(bytes) = self.payload_block(block)? else { return Ok(None) };
3854        Ok(bytes.get(start as usize..end as usize))
3855    }
3856
3857    fn bytes_len_at(&self, index: usize) -> Result<Option<usize>> {
3858        if index >= self.values {
3859            return Ok(None);
3860        }
3861        let (start, end) = self.span_within(index)?;
3862        Ok(Some((end - start) as usize))
3863    }
3864
3865    /// Every length out of the unpacked ends in one loop, which is the point of having them.
3866    ///
3867    /// The whole run of positions counts towards [`Self::ends_worth_unpacking`] at once, because a
3868    /// caller asking for a vector of lengths has said how many it wants, and a vector of them is
3869    /// usually enough on its own. Until the table is worth building this is the row at a time read,
3870    /// the same as the default.
3871    fn bytes_lens_at(&self, indices: &[u32], into: &mut [i64]) -> Result<()> {
3872        self.ends_asked.fetch_add(indices.len(), Atomic::Relaxed);
3873        let Some(ends) = self.value_ends() else {
3874            for (slot, &index) in into.iter_mut().zip(indices) {
3875                *slot = self
3876                    .bytes_len_at(index as usize)?
3877                    .map_or(0, |len| i64::try_from(len).unwrap_or(i64::MAX));
3878            }
3879            return Ok(());
3880        };
3881        if let Some(lens) = self.value_lens.get_or_init(|| lengths_of(ends)) {
3882            for (slot, &index) in into.iter_mut().zip(indices) {
3883                // Past the end is no value and so no length, which is what a row at a time read
3884                // says.
3885                *slot = lens.get(index as usize).map_or(0, |&len| i64::from(len));
3886            }
3887            return Ok(());
3888        }
3889        for (slot, &index) in into.iter_mut().zip(indices) {
3890            let index = index as usize;
3891            // Past the end is no value and so no length, which is what a row at a time read says.
3892            let Some(&end) = ends.get(index) else {
3893                *slot = 0;
3894                continue;
3895            };
3896            let start = if index % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[index - 1] };
3897            if start > end {
3898                return Err(invalid("global dictionary value ends before it starts"));
3899            }
3900            *slot = i64::from(end - start);
3901        }
3902        Ok(())
3903    }
3904
3905    /// The rest of the block holding `first`, decoded into a buffer that may die with the call.
3906    ///
3907    /// A block is the unit this format decodes, so a walk that wants every value is going to decode
3908    /// every block whatever it does. The question is whether it keeps them, and both answers are
3909    /// wrong on their own. [`Self::payload_block`] keeps every block it is asked for, so a reader
3910    /// that walked the whole dictionary through `bytes_at` ended up holding the whole dictionary
3911    /// decoded, 4.2 GB on ClickBench `URL`. Keeping none of them makes the next statement asking
3912    /// the same question decode all of it again, which on the same column at a million rows is a
3913    /// `LIKE` going from 2.7 ms to 16.2 ms.
3914    ///
3915    /// So a sweep keeps what it decodes for the second time while the column is under
3916    /// [`TEXT_KEEP_BUDGET`] and drops it after that. A block already in hand is used where it is there and costs nothing either way.
3917    fn sweep(
3918        &self,
3919        first: usize,
3920        limit: usize,
3921        body: &mut dyn FnMut(usize, &[u8]) -> Result<()>,
3922    ) -> Result<usize> {
3923        let limit = limit.min(self.values);
3924        if first >= limit {
3925            return Ok(first);
3926        }
3927        let block = first / TEXT_PAYLOAD_VALUES;
3928        let last = ((block + 1) * TEXT_PAYLOAD_VALUES).min(limit);
3929        let decoded;
3930        let kept = self.blocks.get(block).and_then(OnceLock::get);
3931        let again = kept.is_none()
3932            && self.swept.get(block).is_some_and(|swept| swept.swap(true, Atomic::Relaxed));
3933        let bytes: &[u8] = match kept {
3934            Some(Ok(kept)) => kept,
3935            _ if again && self.payload_kept.load(Atomic::Relaxed) < self.keep_budget => {
3936                let kept = self
3937                    .payload_block(block)?
3938                    .ok_or_else(|| invalid("global dictionary block is past the payload"))?;
3939                self.payload_kept.fetch_add(kept.len(), Atomic::Relaxed);
3940                kept
3941            }
3942            _ => {
3943                decoded = self.decode_block(block)?;
3944                &decoded
3945            }
3946        };
3947        let ends = self.ends_within(first, last)?;
3948        if ends.len() != last - first {
3949            return Err(invalid("global dictionary offsets are short"));
3950        }
3951        let mut start = u64::from(self.start_within(first)?);
3952        // row at a time: the caller is handed one value after another, and what it does with one is
3953        // its own business, so there is no shape here for anything but a walk.
3954        for (index, &end) in (first..last).zip(&ends) {
3955            let value = usize::try_from(start)
3956                .ok()
3957                .zip(usize::try_from(end).ok())
3958                .and_then(|(from, to)| bytes.get(from..to))
3959                .ok_or_else(|| invalid("global dictionary value is past its block"))?;
3960            body(index, value)?;
3961            start = end;
3962        }
3963        Ok(last)
3964    }
3965
3966    /// Each block the indices land in, decoded once and dropped, or read where it is already kept.
3967    ///
3968    /// Never kept, unlike [`Self::sweep`] under its budget, because a scattered read is a one off:
3969    /// a synopsis turned into values is turned once and remembered by the reader as values, a few
3970    /// kilobytes, where the blocks it went through are megabytes nobody asks for again.
3971    fn visit(
3972        &self,
3973        indices: &[usize],
3974        body: &mut dyn FnMut(usize, &[u8]) -> Result<()>,
3975    ) -> Result<()> {
3976        let mut at = 0;
3977        while at < indices.len() {
3978            let block = indices[at] / TEXT_PAYLOAD_VALUES;
3979            let upto =
3980                at + indices[at..].partition_point(|&index| index / TEXT_PAYLOAD_VALUES == block);
3981            let wanted = &indices[at..upto];
3982            if wanted.iter().any(|&index| index >= self.values) {
3983                return Err(invalid("a visited value is past the global dictionary"));
3984            }
3985            let decoded;
3986            let bytes: &[u8] = match self.blocks.get(block).and_then(OnceLock::get) {
3987                Some(Ok(kept)) => kept,
3988                _ => {
3989                    decoded = self.decode_block(block)?;
3990                    &decoded
3991                }
3992            };
3993            for (offset, &index) in wanted.iter().enumerate() {
3994                let (start, end) = self.span_within(index)?;
3995                let value = bytes
3996                    .get(start as usize..end as usize)
3997                    .ok_or_else(|| invalid("global dictionary value is past its block"))?;
3998                body(at + offset, value)?;
3999            }
4000            at = upto;
4001        }
4002        Ok(())
4003    }
4004
4005    fn ranks(&self) -> Option<usize> {
4006        (self.ranks > 0).then_some(self.ranks)
4007    }
4008
4009    /// The boundary for `wanted`, out of [`Self::searched`] where it is there and put there where
4010    /// it is not.
4011    ///
4012    /// The lock is held over the search rather than dropped and taken again, so that two threads
4013    /// asking for the same value at the same time do the work once between them. That is the shape
4014    /// the scan actually arrives in: sixteen instances of a top N, all reading the same column, all
4015    /// improving their bound over the same early chunks.
4016    fn below(&self, ranks: usize, wanted: &[u8]) -> Result<(usize, bool)> {
4017        let mut memo = self.searched.lock().map_err(|_| invalid("a poisoned dictionary search"))?;
4018        if let Some(&answer) = memo.get(wanted) {
4019            return Ok(answer);
4020        }
4021        let answer = search_below(self, ranks, wanted)?;
4022        if memo.len() >= TEXT_SEARCH_MEMO {
4023            memo.clear();
4024        }
4025        memo.insert(wanted.to_vec(), answer);
4026        Ok(answer)
4027    }
4028
4029    fn compare_rank(&self, rank: usize, wanted: &[u8]) -> Result<Ordering> {
4030        // The head settles the probe unless the two values start with the same eight bytes, and
4031        // only then is a value read. On a column of URLs that is the difference between a search
4032        // that touches one block of the payload and a search that touches nineteen of them.
4033        let settled = self.head_at(rank)?.cmp(&head(wanted));
4034        if settled != Ordering::Equal {
4035            return Ok(settled);
4036        }
4037        let code = self.code_at_rank(rank)?;
4038        let bytes = self
4039            .bytes_at(code as usize)?
4040            .ok_or_else(|| invalid("global dictionary order names a code it does not have"))?;
4041        Ok(bytes.cmp(wanted))
4042    }
4043
4044    fn code_at_rank(&self, rank: usize) -> Result<u32> {
4045        let (block, within) = self.rank_parts(rank)?;
4046        let codes = self.rank_codes(block, self.rank_block_len(rank))?;
4047        let code = bitpack::tail_at(codes, self.code_bits, within)
4048            .map_err(|_| invalid("global dictionary rank block is short of codes"))?;
4049        let code = u32::try_from(code)
4050            .map_err(|_| invalid("global dictionary order names a code it does not have"))?;
4051        if code as usize >= self.len() {
4052            return Err(invalid("global dictionary order names a code it does not have"));
4053        }
4054        Ok(code)
4055    }
4056
4057    fn code_ranks(&self) -> Option<&[u32]> {
4058        // The order is a permutation of the positions, so inverting it needs every position to be
4059        // named exactly once. Anything else and the slice would have holes, and a caller indexing
4060        // it by a code would read a rank that belongs to nothing.
4061        if self.ranks == 0 || self.ranks != self.len() {
4062            return None;
4063        }
4064        self.code_ranks
4065            .get_or_init(|| {
4066                let mut ranks = vec![u32::MAX; self.ranks];
4067                // A block at a time rather than a rank at a time, because reading it per rank pays
4068                // for the bounds check, the division and the lock on every one of them.
4069                for first in (0..self.ranks).step_by(TEXT_RANK_BLOCK) {
4070                    let (block, _) = self.rank_parts(first).ok()?;
4071                    let count = self.rank_block_len(first);
4072                    let codes = self.rank_codes(block, count).ok()?;
4073                    for (within, code) in bitpack::unpack_tail(codes, self.code_bits, count)
4074                        .ok()?
4075                        .into_iter()
4076                        .enumerate()
4077                    {
4078                        let code = usize::try_from(code).ok()?;
4079                        *ranks.get_mut(code)? = u32::try_from(first + within).ok()?;
4080                    }
4081                }
4082                if ranks.contains(&u32::MAX) {
4083                    return None;
4084                }
4085                Some(ranks)
4086            })
4087            .as_deref()
4088    }
4089
4090    fn footprint(&self) -> usize {
4091        self.offsets.capacity()
4092            + self
4093                .value_ends
4094                .get()
4095                .and_then(Option::as_ref)
4096                .map_or(0, |ends| ends.capacity() * size_of::<u32>())
4097            + self
4098                .value_lens
4099                .get()
4100                .and_then(Option::as_ref)
4101                .map_or(0, |lens| lens.capacity() * size_of::<u32>())
4102            + self
4103                .code_ranks
4104                .get()
4105                .and_then(Option::as_ref)
4106                .map_or(0, |ranks| ranks.capacity() * size_of::<u32>())
4107            + self.rank_hashes.capacity() * size_of::<u64>()
4108            + self.rank_ends.capacity() * size_of::<u64>()
4109            + self.rank_blocks.capacity() * size_of::<OnceLock<Result<Vec<u8>>>>()
4110            + self
4111                .rank_blocks
4112                .iter()
4113                .filter_map(OnceLock::get)
4114                .filter_map(|result| result.as_ref().ok())
4115                .map(Vec::capacity)
4116                .sum::<usize>()
4117            + self.blocks.capacity() * size_of::<OnceLock<Result<Vec<u8>>>>()
4118            + self.hashes.capacity() * size_of::<u64>()
4119            + self.starts.capacity() * size_of::<u64>()
4120            + self.lengths.capacity() * size_of::<u64>()
4121            + self
4122                .grams
4123                .as_ref()
4124                .and_then(|grams| grams.loaded.get())
4125                .and_then(|result| result.as_ref().ok())
4126                .map_or(0, Vec::capacity)
4127            + self
4128                .blocks
4129                .iter()
4130                .filter_map(OnceLock::get)
4131                .filter_map(|result| result.as_ref().ok())
4132                .map(Vec::capacity)
4133                .sum::<usize>()
4134    }
4135}
4136
4137/// Every table wide part number in order, with the stripe it belongs to.
4138fn places(table: &Table) -> Result<Vec<Place>> {
4139    let mut places = Vec::with_capacity(table.stripes.len().saturating_mul(STRIPE_PARTS));
4140    for (at, stripe) in table.stripes.iter().enumerate() {
4141        let index = u32::try_from(at).map_err(|_| invalid("too many stripes"))?;
4142        for (part, &rows) in stripe.parts.iter().enumerate() {
4143            places.push(Place {
4144                stripe: index,
4145                part: u32::try_from(part).map_err(|_| invalid("too many parts in a stripe"))?,
4146                rows,
4147            });
4148        }
4149    }
4150    Ok(places)
4151}
4152
4153/// Reads one column's section of a stripe's index page.
4154///
4155/// The section carries its own checksum, so a reader that wants one column out of a hundred and
4156/// five preads a few hundred bytes and still knows that what it got is what was written.
4157fn read_index(file: &File, stripe: &Stripe, column: usize) -> Result<Vec<PartSpan>> {
4158    let parts = stripe.parts.len();
4159    let section = index_section(parts)?;
4160    let at = column.checked_mul(section).ok_or_else(|| invalid("index page offset overflow"))?;
4161    let end = at.checked_add(section).ok_or_else(|| invalid("index page offset overflow"))?;
4162    if end > stripe.index.length as usize {
4163        return Err(invalid("index page is shorter than its columns"));
4164    }
4165    let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
4166    let mut bytes = vec![0; section];
4167    let offset = stripe
4168        .index
4169        .offset
4170        .checked_add(at as u64)
4171        .ok_or_else(|| invalid("index page offset overflow"))?;
4172    read_at(file, offset, &mut bytes)?;
4173    let entries = section - size_of::<u64>();
4174    let stored = u64::from_le_bytes(bytes[entries..].try_into().expect("eight bytes"));
4175    if checksum(&bytes[..entries]) != stored {
4176        // With where it was read from, because the two ways this fires look identical from the
4177        // message alone: a file somebody damaged, and a file we wrote to the wrong offset.
4178        return Err(invalid(&format!(
4179            "index page section checksum differs, column {column} of {parts} parts at {offset}, \
4180             wanted {stored:016x} and got {:016x}",
4181            checksum(&bytes[..entries]),
4182        )));
4183    }
4184    let mut spans = Vec::with_capacity(parts);
4185    let mut start = 0_usize;
4186    for part in 0..parts {
4187        let at = part * INDEX_ENTRY;
4188        let length = u32::from_le_bytes(bytes[at..at + 4].try_into().expect("four bytes")) as usize;
4189        let hash = u64::from_le_bytes(bytes[at + 4..at + 12].try_into().expect("eight bytes"));
4190        spans.push(PartSpan { start, length, hash });
4191        start = start.checked_add(length).ok_or_else(|| invalid("column page length overflow"))?;
4192    }
4193    if start != page.length as usize {
4194        return Err(invalid("column page length differs from its index"));
4195    }
4196    Ok(spans)
4197}
4198
4199/// One part's bytes out of a whole column page.
4200fn part_bytes(page: &[u8], span: PartSpan) -> Result<&[u8]> {
4201    let end = span.start.checked_add(span.length).ok_or_else(|| invalid("part range overflow"))?;
4202    page.get(span.start..end).ok_or_else(|| invalid("part exceeds its column page"))
4203}
4204
4205/// Puts one stripe of one column in the cache, dropping the stripe that has been there longest.
4206///
4207/// The index goes in its own slot and stays. Only the page is under the budget, and `kept` is how
4208/// many pages that budget is.
4209fn remember(cached: &mut Cached, held: &CachedColumn, kept: usize) {
4210    if let Some(slot) = cached.index.get_mut(held.stripe) {
4211        if slot.is_none() {
4212            *slot = Some(Arc::clone(&held.index));
4213        }
4214    }
4215    let Some(page) = held.page.clone() else { return };
4216    let Some(slot) = cached.pages.get_mut(held.stripe) else { return };
4217    if slot.is_none() {
4218        cached.order.push_back(held.stripe);
4219    }
4220    *slot = Some(page);
4221    while cached.order.len() > kept.max(1) {
4222        let Some(oldest) = cached.order.pop_front() else { break };
4223        if let Some(slot) = cached.pages.get_mut(oldest) {
4224            *slot = None;
4225        }
4226    }
4227}
4228
4229/// Every table a native file holds, without the directory of any of them.
4230///
4231/// This is what opening a database reads. It is the small level of the directory, so the cost is
4232/// proportional to how many tables there are rather than to how much data they hold, and a session
4233/// that touches two tables of eight decodes two table directories.
4234///
4235/// The file handle is shared with every reader this hands out. Eight tables in one file is one open
4236/// file descriptor, not eight, which is the other thing one file buys over a file per table.
4237#[derive(Debug, Clone)]
4238pub struct Catalog {
4239    file: Arc<File>,
4240    size: u64,
4241    entries: Arc<Vec<Entry>>,
4242    /// The views the file holds, whole, since a view has no second level to read later.
4243    views: Arc<Vec<ViewEntry>>,
4244    opening: Opening,
4245}
4246
4247impl Catalog {
4248    /// Reads the highest valid catalog slot and nothing under it.
4249    ///
4250    /// # Errors
4251    ///
4252    /// If the file has no valid committed catalog or a catalog pointer is out of bounds.
4253    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
4254        let (file, size, _, bytes, opening) = slot_bytes(path)?;
4255        let (entries, views) = decode_catalog(&bytes, size)?;
4256        Ok(Self {
4257            file: Arc::new(file),
4258            size,
4259            entries: Arc::new(entries),
4260            views: Arc::new(views),
4261            opening,
4262        })
4263    }
4264
4265    /// The tables in the file, in the order they were written.
4266    pub fn names(&self) -> impl ExactSizeIterator<Item = &str> {
4267        self.entries.iter().map(|entry| entry.name.as_str())
4268    }
4269
4270    /// The same tables with how many rows each of them holds.
4271    ///
4272    /// The names alone answer which tables the file has, which is what a checkpoint needs to know.
4273    /// A load asks a second question: whether a table already in the file is really in the way of
4274    /// the one it wants to write. A table with no rows is not, because it has no pages the next
4275    /// generation would have to carry, so the count has to come out of the catalog beside the name.
4276    pub fn rows(&self) -> impl ExactSizeIterator<Item = (&str, usize)> {
4277        self.entries.iter().map(|entry| (entry.name.as_str(), entry.rows))
4278    }
4279
4280    /// The views in the file, in the order they were written.
4281    ///
4282    /// Whole, unlike [`Catalog::names`], which hands back names and makes the caller ask for a table
4283    /// by one. A view is a few strings and a column list and it was all read at open, so there is
4284    /// nothing left to go and fetch and no reason to make the caller ask twice.
4285    pub fn views(&self) -> impl ExactSizeIterator<Item = &ViewEntry> {
4286        self.views.iter()
4287    }
4288
4289    /// How many tables the file holds.
4290    #[must_use]
4291    pub fn len(&self) -> usize {
4292        self.entries.len()
4293    }
4294
4295    /// Whether the file holds no table at all, which is what [`Writer::empty`] writes and what a
4296    /// database somebody dropped the last table out of comes back as.
4297    #[must_use]
4298    pub fn is_empty(&self) -> bool {
4299        self.entries.is_empty()
4300    }
4301
4302    /// Opens one table by name, decoding its directory now.
4303    ///
4304    /// # Errors
4305    ///
4306    /// If there is no table by that name, or its directory is torn or points outside the file.
4307    pub fn table(&self, name: &str) -> Result<Reader> {
4308        let entry = self
4309            .entries
4310            .iter()
4311            .find(|entry| entry.name == name)
4312            .ok_or_else(|| invalid(&format!("the file holds no table called {name}")))?;
4313        // Checked and then decoded a window at a time, so that the directory's own bytes are never
4314        // all in memory beside the table they decode into. It is read twice, and the second read
4315        // comes out of the page cache the first one filled.
4316        let (offset, length) = (entry.directory.offset, entry.directory.length as usize);
4317        if file_checksum(&self.file, offset, length)? != entry.directory.hash {
4318            return Err(invalid(&format!("the directory of table {name} does not checksum")));
4319        }
4320        let mut opening = self.opening;
4321        opening.reads += 1;
4322        opening.bytes += u64::from(entry.directory.length);
4323        Reader::build(
4324            Arc::clone(&self.file),
4325            self.size,
4326            read_directory(Cursor::over(&self.file, offset, length), self.size, Some(offset))?,
4327            u64::from(entry.directory.length),
4328            opening,
4329        )
4330    }
4331}
4332
4333/// Where the slot naming `generation` goes, which is the one the generation before it did not use.
4334///
4335/// Generation 1 takes the slot at 16, so a file written once is byte for byte the file this wrote
4336/// before there was a second generation to write.
4337fn slot_offset(generation: u64) -> u64 {
4338    16 + (generation - 1) % 2 * SLOT_BYTES as u64
4339}
4340
4341/// The header and the bytes the highest valid slot points at.
4342///
4343/// Both levels of the directory are reached this way, so the magic check, the version check and the
4344/// choice between the two slots live here rather than being written out twice.
4345fn slot_bytes(path: impl AsRef<Path>) -> Result<(File, u64, Slot, Vec<u8>, Opening)> {
4346    let mut file = File::open(path).map_err(io)?;
4347    let size = file.metadata().map_err(io)?.len();
4348    if size < HEADER {
4349        return Err(invalid("file is shorter than its header"));
4350    }
4351    let mut header = [0; HEADER as usize];
4352    file.read_exact(&mut header).map_err(io)?;
4353    let mut opening = Opening { reads: 1, bytes: HEADER };
4354    let version = u32::from_le_bytes([header[8], header[9], header[10], header[11]]);
4355    // The two halves are worth telling apart. A wrong magic is a file that was never ours and
4356    // the answer is to look at the path. A wrong version is our own file from another build,
4357    // and the number this build wants is the only thing that tells the reader whether to
4358    // rebuild the file or to go back to the binary that wrote it.
4359    if &header[..8] != MAGIC {
4360        return Err(invalid("the header does not begin with a rudb native magic"));
4361    }
4362    if !READABLE.contains(&version) {
4363        return Err(invalid(&format!(
4364            "the file is format {version} and this build reads format {FORMAT}, so it has to \
4365                 be written again"
4366        )));
4367    }
4368    let mut selected = None;
4369    for start in [16, 16 + SLOT_BYTES] {
4370        let slot = Slot::read(&header[start..start + SLOT_BYTES]);
4371        if slot.generation == 0 || slot.length == 0 || slot.length as usize > MAX_DIRECTORY {
4372            continue;
4373        }
4374        let Some(end) = slot.offset.checked_add(u64::from(slot.length)) else { continue };
4375        if slot.offset < HEADER || end > size {
4376            continue;
4377        }
4378        let mut bytes = vec![0; slot.length as usize];
4379        file.seek(SeekFrom::Start(slot.offset)).map_err(io)?;
4380        file.read_exact(&mut bytes).map_err(io)?;
4381        opening.reads += 1;
4382        opening.bytes += u64::from(slot.length);
4383        if checksum(&bytes) == slot.hash
4384            && selected
4385                .as_ref()
4386                .is_none_or(|(old, _): &(Slot, Vec<u8>)| old.generation < slot.generation)
4387        {
4388            selected = Some((slot, bytes));
4389        }
4390    }
4391    let (slot, bytes) = selected.ok_or_else(|| invalid("no committed directory slot is valid"))?;
4392    Ok((file, size, slot, bytes, opening))
4393}
4394
4395impl Reader {
4396    /// Opens a file that holds exactly one table.
4397    ///
4398    /// # Errors
4399    ///
4400    /// If the file has no valid committed directory, a directory pointer is out of bounds, or the
4401    /// file holds more than one table, which is a file that has to be opened by name.
4402    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
4403        let catalog = Catalog::open(path)?;
4404        let mut names = catalog.names();
4405        let name = names.next().ok_or_else(|| invalid("the file holds no table"))?.to_string();
4406        if names.next().is_some() {
4407            return Err(invalid(
4408                "the file holds more than one table, so it has to be opened by name",
4409            ));
4410        }
4411        catalog.table(&name)
4412    }
4413
4414    /// Builds a reader over one decoded table directory.
4415    fn build(
4416        file: Arc<File>,
4417        size: u64,
4418        table: Table,
4419        directory: u64,
4420        opening: Opening,
4421    ) -> Result<Self> {
4422        let places = places(&table)?;
4423        let dictionaries = (0..table.fields.len()).map(|_| OnceLock::new()).collect();
4424        let table_fields = table.fields.len();
4425        let stripes = table.stripes.len();
4426        let cache = (0..table.fields.len())
4427            .map(|_| {
4428                Mutex::new(Cached {
4429                    pages: (0..stripes).map(|_| None).collect(),
4430                    index: (0..stripes).map(|_| None).collect(),
4431                    ..Cached::default()
4432                })
4433            })
4434            .collect::<Vec<_>>();
4435        let sieves: Vec<Vec<SieveSlot>> = (0..table.fields.len())
4436            .map(|_| table.stripes.iter().map(|_| OnceLock::new()).collect())
4437            .collect();
4438        let part_ranges: Vec<Vec<RangeSlot>> = (0..table.fields.len())
4439            .map(|_| table.stripes.iter().map(|_| OnceLock::new()).collect())
4440            .collect();
4441        Ok(Self {
4442            file,
4443            table: Arc::new(table),
4444            dictionaries: Arc::new(dictionaries),
4445            loading: Arc::new((0..table_fields).map(|_| Mutex::new(())).collect()),
4446            frequency_values: Arc::new((0..table_fields).map(|_| OnceLock::new()).collect()),
4447            frequency_summaries: Arc::new((0..table_fields).map(|_| OnceLock::new()).collect()),
4448            opened: Arc::new(AtomicUsize::new(0)),
4449            sieves: Arc::new(sieves),
4450            part_ranges: Arc::new(part_ranges),
4451            places: Arc::new(places),
4452            cache: Arc::new(cache),
4453            pages: Arc::new(AtomicUsize::new(0)),
4454            indexes: Arc::new(AtomicUsize::new(0)),
4455            kept: Arc::new(AtomicUsize::new(CACHED_STRIPES_PER_COLUMN)),
4456            size,
4457            directory,
4458            opening,
4459        })
4460    }
4461
4462    /// What this reader has read so far, and what opening it cost.
4463    ///
4464    /// Public because the claim of `spec/stats/04-in-memory.md` section 4.2 is about this number
4465    /// and a claim nobody can check is a comment. A caller that wants to know whether opening a
4466    /// file touched the data asks here, and gets an answer that does not depend on what the page
4467    /// cache happened to hold.
4468    #[must_use]
4469    pub fn reads(&self) -> Reads {
4470        Reads {
4471            opening: self.opening,
4472            pages: self.pages.load(Atomic::Relaxed),
4473            indexes: self.indexes.load(Atomic::Relaxed),
4474            dictionaries: self.opened.load(Atomic::Relaxed),
4475        }
4476    }
4477
4478    /// Where the file's bytes went, from the directory alone.
4479    ///
4480    /// No page is read, so this costs the same on a 45 GB table as on an empty one. See [`Layout`]
4481    /// for what is charged where and for why the three things that are not columns stay separate.
4482    #[must_use]
4483    pub fn layout(&self) -> Layout {
4484        let table = &self.table;
4485        let stripes = table.stripes.as_slice();
4486        let columns = table
4487            .fields
4488            .iter()
4489            .enumerate()
4490            .map(|(at, field)| ColumnLayout {
4491                name: field.name.clone(),
4492                kind: field.ty.to_string(),
4493                pages: sum(stripes.iter().map(|stripe| span_bytes(&stripe.pages, at))),
4494                memberships: sum(stripes.iter().map(|stripe| stripe.memberships.bytes(at))),
4495                sieves: sum(stripes.iter().map(|stripe| stripe.sieves.bytes(at))),
4496                part_ranges: sum(stripes.iter().map(|stripe| stripe.part_ranges.bytes(at))),
4497                dictionary: dictionary_bytes(table, at),
4498            })
4499            .collect();
4500        Layout {
4501            file: self.size,
4502            rows: table.rows,
4503            stripes: stripes.len(),
4504            parts: self.places.len(),
4505            columns,
4506            indexes: sum(stripes.iter().map(|stripe| u64::from(stripe.index.length))),
4507            directory: self.directory,
4508            header: HEADER,
4509        }
4510    }
4511
4512    /// What every part of one column is stored as, which is what `pragma_storage_info` reports.
4513    ///
4514    /// Unlike [`Self::layout`] this reads the data, because the encoder's choice is in the page and
4515    /// nowhere else. The directory says how many bytes a column took and says nothing about what
4516    /// shape they are in, and the shape is the question worth asking: the same rows in a different
4517    /// order come back bit packed on one file and plain on another, and that is the difference a
4518    /// clustered load makes to a scan.
4519    ///
4520    /// One read per stripe rather than one per part. A part is a few kilobytes out of a page that
4521    /// is a quarter of a megabyte, so asking part by part would read the same page sixty four
4522    /// times. Nothing is put in the page cache, because a caller asking what a file looks like is
4523    /// not about to scan it and evicting the pages a real query wants would be a poor trade.
4524    ///
4525    /// # Errors
4526    ///
4527    /// If the column is outside the schema, or a page, index section or checksum is invalid.
4528    pub fn stored(&self, column: usize) -> Result<Vec<StoredPart>> {
4529        let field = self
4530            .table
4531            .fields
4532            .get(column)
4533            .ok_or_else(|| invalid("stored column index out of range"))?;
4534        let mut stored = Vec::with_capacity(self.places.len());
4535        let mut row = 0;
4536        for (at, stripe) in self.table.stripes.iter().enumerate() {
4537            let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
4538            let index = read_index(&self.file, stripe, column)?;
4539            let mut bytes = vec![0; page.length as usize];
4540            read_at(&self.file, page.offset, &mut bytes)?;
4541            let ranges = self.stripe_part_ranges(at, column);
4542            for (part, &rows) in stripe.parts.iter().enumerate() {
4543                let span = *index.get(part).ok_or_else(|| invalid("part index out of range"))?;
4544                let held = part_bytes(&bytes, span)?;
4545                let range = ranges.and_then(|held| held.get(part));
4546                stored.push(StoredPart {
4547                    stripe: at,
4548                    part,
4549                    row,
4550                    rows: rows as usize,
4551                    encoding: page_encoding(&field.ty, rows as usize, held),
4552                    bytes: span.length as u64,
4553                    page: page.offset,
4554                    offset: span.start as u64,
4555                    low: range
4556                        .and_then(|range| range.low.clone())
4557                        .and_then(|bound| bound.into_value(&field.ty)),
4558                    high: range
4559                        .and_then(|range| range.high.clone())
4560                        .and_then(|bound| bound.into_value(&field.ty)),
4561                    nulls: range.map(|range| range.nulls),
4562                });
4563                row += rows as usize;
4564            }
4565        }
4566        Ok(stored)
4567    }
4568
4569    /// How many parts the table has, which is how many chunks a scan of it reads.
4570    #[must_use]
4571    pub fn parts(&self) -> usize {
4572        self.places.len()
4573    }
4574
4575    /// The parts of each stripe, in table wide part numbers.
4576    ///
4577    /// A scan that wants one worker to own the page it reads hands work out in these runs. The
4578    /// stripes are contiguous in part numbering and all but the last hold sixty four parts, but a
4579    /// stripe can be flushed early when rows arrive out of order, so the runs are read off the
4580    /// directory rather than worked out from a constant.
4581    #[must_use]
4582    pub fn stripe_parts(&self) -> Vec<std::ops::Range<usize>> {
4583        let mut runs = Vec::with_capacity(self.table.stripes.len());
4584        let mut start = 0;
4585        for stripe in &self.table.stripes {
4586            let end = start + stripe.parts.len();
4587            runs.push(start..end);
4588            start = end;
4589        }
4590        runs
4591    }
4592
4593    /// How many rows one stripe holds, in the numbering [`Self::stripe_parts`] hands back.
4594    ///
4595    /// Off the directory, which is already in memory, rather than by the caller asking for each
4596    /// part in turn through the catalog. Nothing past the end holds any rows.
4597    #[must_use]
4598    pub fn stripe_rows(&self, stripe: usize) -> usize {
4599        self.table.stripes.get(stripe).map_or(0, |held| held.rows)
4600    }
4601
4602    /// Asks the page cache to keep `stripes` stripes of every column instead of the default.
4603    ///
4604    /// This only ever raises the number. A scan that gives each worker a whole stripe has one page
4605    /// per column per worker open at once, and a cache smaller than that is worse than no cache at
4606    /// all: every worker's page is evicted by the others before it has finished its stripe, so it
4607    /// reads a quarter of a megabyte for every part it takes out of it.
4608    pub fn keep_stripes(&self, stripes: usize) {
4609        self.kept.fetch_max(stripes, Atomic::Relaxed);
4610    }
4611
4612    /// Rows in one part, or zero when the part number is past the table.
4613    #[must_use]
4614    pub fn part_rows(&self, at: usize) -> usize {
4615        self.places.get(at).map_or(0, |place| place.rows as usize)
4616    }
4617
4618    /// The committed table directory.
4619    #[must_use]
4620    pub fn table(&self) -> &Table {
4621        &self.table
4622    }
4623
4624    /// Exact leading frequencies when the stored synopsis proves a count-descending prefix.
4625    ///
4626    /// The returned list can be longer than `top`. Keeping the stored tail lets a later TopN apply
4627    /// additional ordering keys without losing a value tied with the requested boundary.
4628    ///
4629    /// # Errors
4630    ///
4631    /// If the column is outside the schema or a stored value does not fit its declared type.
4632    pub fn top_frequencies(&self, column: usize, top: usize) -> Result<Option<Vec<(Value, u64)>>> {
4633        let field = self
4634            .table
4635            .fields
4636            .get(column)
4637            .ok_or_else(|| invalid("frequency column index out of range"))?;
4638        let Some(summary) = self.frequency_summary(column)? else {
4639            return Ok(None);
4640        };
4641        if top == 0 || summary.entries.len() < top {
4642            return Ok(None);
4643        }
4644        let boundary = summary.entries[top - 1].count;
4645        if boundary <= summary.omitted_max {
4646            return Ok(None);
4647        }
4648        self.decode_frequencies(column, &field.ty, &summary.entries).map(Some)
4649    }
4650
4651    /// Exact leading counts for a numeric key paired with a stable-dictionary string key.
4652    ///
4653    /// The stored prefix is returned only when its requested boundary strictly beats the bound on
4654    /// every pair omitted at load time. The returned tail may be longer than `top`, as with
4655    /// [`Self::top_frequencies`], so downstream ordering can settle ties without reading rows.
4656    ///
4657    /// # Errors
4658    ///
4659    /// If either column is outside the schema or persisted pair metadata is inconsistent with the
4660    /// frequency synopsis or dictionary it names.
4661    pub fn top_pair_frequencies(
4662        &self,
4663        first: usize,
4664        second: usize,
4665        top: usize,
4666    ) -> Result<Option<PairFrequencyCounts>> {
4667        if first >= self.table.fields.len() || second >= self.table.fields.len() {
4668            return Err(invalid("pair frequency column index out of range"));
4669        }
4670        let Some(summary) =
4671            self.table.pair_frequencies.iter().find(|summary| {
4672                summary.first as usize == first && summary.second as usize == second
4673            })
4674        else {
4675            return Ok(None);
4676        };
4677        if top == 0 || summary.entries.len() < top {
4678            return Ok(None);
4679        }
4680        let boundary = summary.entries[top - 1].count;
4681        if boundary <= summary.omitted_max {
4682            return Ok(None);
4683        }
4684        let first_summary = self
4685            .frequency_summary(first)?
4686            .ok_or_else(|| invalid("pair frequency first column has no synopsis"))?;
4687        let anchors = self
4688            .decode_frequencies(first, &self.table.fields[first].ty, &first_summary.entries)?
4689            .into_iter()
4690            .map(|(value, _)| value)
4691            .collect::<Vec<_>>();
4692        let dictionary = self
4693            .dictionary(second)?
4694            .ok_or_else(|| invalid("pair frequency second column has no dictionary"))?;
4695        let mut codes = summary.entries.iter().filter_map(|entry| entry.second).collect::<Vec<_>>();
4696        codes.sort_unstable();
4697        codes.dedup();
4698        let texts = dictionary
4699            .try_values_visited(&codes.iter().map(|&code| code as usize).collect::<Vec<_>>())?;
4700        let mut out = Vec::with_capacity(summary.entries.len());
4701        for entry in &summary.entries {
4702            if entry.count < boundary {
4703                break;
4704            }
4705            let first = anchors
4706                .get(entry.first_entry as usize)
4707                .cloned()
4708                .ok_or_else(|| invalid("pair frequency anchor is outside its values"))?;
4709            let second = match entry.second {
4710                None => Value::Null,
4711                Some(code) => {
4712                    let at = codes
4713                        .binary_search(&code)
4714                        .map_err(|_| invalid("pair frequency code was not among the codes read"))?;
4715                    texts[at].clone()
4716                }
4717            };
4718            out.push((vec![first, second], entry.count));
4719        }
4720        Ok(Some(out))
4721    }
4722
4723    /// Every value of one column with the number of rows holding it, when the synopsis is complete.
4724    ///
4725    /// The heavy hitter pass keeps a bounded set of candidates and decrements them all when it runs
4726    /// out of room, so what it usually ends with is the leading values and a bound on everything it
4727    /// dropped. `omitted_max` of zero says that never happened: no candidate was ever decremented and
4728    /// the entries did not overflow the stored budget, so the list is every distinct value of the
4729    /// column with an exact count, and a null counts as a value of its own rather than being skipped.
4730    ///
4731    /// That makes a whole class of question answerable without reading a row. How many rows hold a
4732    /// value, how many do not, and what a `GROUP BY` of that column with a count over it produces are
4733    /// all in here. It is only ever true of a column with few enough distinct values, which is the
4734    /// case worth having, because that is exactly the column a grouping or an equality filter would
4735    /// otherwise walk every row to answer.
4736    ///
4737    /// `None` when the column has no synopsis, or has one that dropped anything.
4738    ///
4739    /// # Errors
4740    ///
4741    /// If the column is outside the schema or a stored value does not fit its declared type.
4742    pub fn exact_frequencies(&self, column: usize) -> Result<Option<Vec<(Value, u64)>>> {
4743        let Some(prefix) = self.frequency_prefix(column)? else {
4744            return Ok(None);
4745        };
4746        Ok((prefix.omitted_max == 0).then_some(prefix.entries))
4747    }
4748
4749    /// Every value the synopsis lists with the number of rows holding it, and a bound on the rest.
4750    ///
4751    /// The counts are exact whether or not the list is complete. The heavy hitter pass keeps a
4752    /// bounded candidate set and then recounts only the candidates that survived it, so a value that
4753    /// made it into the list carries the number of rows that really hold it rather than whatever the
4754    /// pass had left over. What the pass loses is values, not counts.
4755    ///
4756    /// `omitted_max` is how many rows the most common value left out can hold, and zero says nothing
4757    /// was left out at all, which is what [`exact_frequencies`] asks for. Above zero the list is the
4758    /// leading values of the column and everything else is somewhere between no rows and that bound.
4759    ///
4760    /// That prefix is worth reading on its own. A column with a value in half its rows and a long
4761    /// tail behind it has no complete synopsis and never will, and it is the column where dividing
4762    /// the rows by the distinct count is furthest from the truth.
4763    ///
4764    /// `None` when the column has no synopsis.
4765    ///
4766    /// # Errors
4767    ///
4768    /// If the column is outside the schema or a stored value does not fit its declared type.
4769    ///
4770    /// [`exact_frequencies`]: Self::exact_frequencies
4771    pub fn frequency_prefix(&self, column: usize) -> Result<Option<FrequencyPrefix>> {
4772        let field = self
4773            .table
4774            .fields
4775            .get(column)
4776            .ok_or_else(|| invalid("frequency column index out of range"))?;
4777        let Some(summary) = self.frequency_summary(column)? else {
4778            return Ok(None);
4779        };
4780        let entries = self.decode_frequencies(column, &field.ty, &summary.entries)?;
4781        Ok(Some(FrequencyPrefix { entries, omitted_max: summary.omitted_max }))
4782    }
4783
4784    /// One column's synopsis, read back from the file when the directory left it there.
4785    fn frequency_summary(&self, column: usize) -> Result<Option<Cow<'_, FrequencySummary>>> {
4786        Ok(match self.table.frequencies.get(column) {
4787            None | Some(None) => None,
4788            Some(Some(Frequencies::Held(summary))) => Some(Cow::Borrowed(summary)),
4789            Some(Some(Frequencies::Stored { span, values })) => {
4790                let slot = self
4791                    .frequency_summaries
4792                    .get(column)
4793                    .ok_or_else(|| invalid("frequency column index out of range"))?;
4794                if let Some(summary) = slot.get() {
4795                    return Ok(Some(Cow::Borrowed(summary.as_ref())));
4796                }
4797                let field = self
4798                    .table
4799                    .fields
4800                    .get(column)
4801                    .ok_or_else(|| invalid("frequency column index out of range"))?;
4802                let mut bytes = vec![0; span.length as usize];
4803                read_at(&self.file, span.offset, &mut bytes)?;
4804                let summary =
4805                    decode_summary(&mut Cursor::new(&bytes), field, self.table.rows, *values)?;
4806                let summary = summary.ok_or_else(|| invalid("a stored synopsis is missing"))?;
4807                let _ = slot.set(Arc::new(summary));
4808                Some(Cow::Borrowed(slot.get().expect("the decoded summary was stored").as_ref()))
4809            }
4810        })
4811    }
4812
4813    /// Turns stored frequency entries into values of the column's own type.
4814    ///
4815    /// Remembered per column, because the planner asks once for every estimate that touches the
4816    /// column and the executor asks again, and the answer is a few hundred values. The codes of a
4817    /// string column are read through [`Vector::try_values_visited`], which does not keep the blocks
4818    /// it decodes, so what a query answered out of the synopsis holds is those values and not the
4819    /// hundred or so dictionary blocks they are scattered over.
4820    fn decode_frequencies(
4821        &self,
4822        column: usize,
4823        ty: &LogicalType,
4824        entries: &[FrequencyEntry],
4825    ) -> Result<Vec<(Value, u64)>> {
4826        if let Some(values) = self.frequency_values.get(column).and_then(OnceLock::get) {
4827            return Ok(values.as_ref().clone());
4828        }
4829        let values = self.decode_frequencies_once(column, ty, entries)?;
4830        if let Some(slot) = self.frequency_values.get(column) {
4831            let _ = slot.set(Arc::new(values.clone()));
4832        }
4833        Ok(values)
4834    }
4835
4836    fn decode_frequencies_once(
4837        &self,
4838        column: usize,
4839        ty: &LogicalType,
4840        entries: &[FrequencyEntry],
4841    ) -> Result<Vec<(Value, u64)>> {
4842        let stored_texts = self.table.frequency_texts.get(column).filter(|texts| !texts.is_empty());
4843        if stored_texts.is_some_and(|texts| texts.len() != entries.len()) {
4844            return Err(invalid("frequency text count differs from its synopsis"));
4845        }
4846        let dictionary = if *ty == LogicalType::Varchar && stored_texts.is_none() {
4847            self.dictionary(column)?
4848        } else {
4849            None
4850        };
4851        let mut codes = entries
4852            .iter()
4853            .filter_map(|entry| match entry.value {
4854                FrequencyValue::Code(code) => Some(code as usize),
4855                _ => None,
4856            })
4857            .collect::<Vec<_>>();
4858        codes.sort_unstable();
4859        codes.dedup();
4860        let texts = match &dictionary {
4861            Some(dictionary) if !codes.is_empty() => dictionary.try_values_visited(&codes)?,
4862            _ => Vec::new(),
4863        };
4864        let mut out = Vec::with_capacity(entries.len());
4865        for (entry_at, entry) in entries.iter().enumerate() {
4866            let value = match entry.value {
4867                FrequencyValue::Null => {
4868                    if stored_texts.and_then(|texts| texts[entry_at].as_ref()).is_some() {
4869                        return Err(invalid("a null frequency entry has text"));
4870                    }
4871                    Value::Null
4872                }
4873                FrequencyValue::Integer(value) => match *ty {
4874                    LogicalType::TinyInt => Value::TinyInt(
4875                        i8::try_from(value)
4876                            .map_err(|_| invalid("frequency TINYINT is out of range"))?,
4877                    ),
4878                    LogicalType::UTinyInt => Value::UTinyInt(
4879                        u8::try_from(value)
4880                            .map_err(|_| invalid("frequency UTINYINT is out of range"))?,
4881                    ),
4882                    LogicalType::USmallInt => Value::USmallInt(
4883                        u16::try_from(value)
4884                            .map_err(|_| invalid("frequency USMALLINT is out of range"))?,
4885                    ),
4886                    LogicalType::UInteger => Value::UInteger(
4887                        u32::try_from(value)
4888                            .map_err(|_| invalid("frequency UINTEGER is out of range"))?,
4889                    ),
4890                    LogicalType::UBigInt => Value::UBigInt(
4891                        u64::try_from(value)
4892                            .map_err(|_| invalid("frequency UBIGINT is out of range"))?,
4893                    ),
4894                    LogicalType::SmallInt => Value::SmallInt(
4895                        i16::try_from(value)
4896                            .map_err(|_| invalid("frequency SMALLINT is out of range"))?,
4897                    ),
4898                    LogicalType::Integer => Value::Integer(
4899                        i32::try_from(value)
4900                            .map_err(|_| invalid("frequency INTEGER is out of range"))?,
4901                    ),
4902                    LogicalType::BigInt => Value::BigInt(
4903                        i64::try_from(value)
4904                            .map_err(|_| invalid("frequency BIGINT is out of range"))?,
4905                    ),
4906                    LogicalType::Date => Value::Date(
4907                        i32::try_from(value)
4908                            .map_err(|_| invalid("frequency DATE is out of range"))?,
4909                    ),
4910                    LogicalType::Timestamp => Value::Timestamp(
4911                        i64::try_from(value)
4912                            .map_err(|_| invalid("frequency TIMESTAMP is out of range"))?,
4913                    ),
4914                    _ => return Err(invalid("integer frequency belongs to another type")),
4915                },
4916                FrequencyValue::Code(code) => {
4917                    if let Some(text) = stored_texts.and_then(|texts| texts[entry_at].as_ref()) {
4918                        Value::Varchar(
4919                            String::from_utf8(text.clone())
4920                                .map_err(|_| invalid("frequency text is not UTF-8"))?,
4921                        )
4922                    } else {
4923                        if dictionary.is_none() {
4924                            return Err(invalid("frequency code has no dictionary or stored text"));
4925                        }
4926                        let at = codes
4927                            .binary_search(&(code as usize))
4928                            .map_err(|_| invalid("frequency code was not among the codes read"))?;
4929                        texts[at].clone()
4930                    }
4931                }
4932            };
4933            out.push((value, entry.count));
4934        }
4935        Ok(out)
4936    }
4937
4938    /// Sparse rows belonging to the bounded numeric frequency candidate set.
4939    ///
4940    /// The list is omitted when collecting it would exceed the fixed storage budget. A composite
4941    /// aggregate may accept a result over these rows only when its requested boundary is strictly
4942    /// greater than `omitted_max`.
4943    ///
4944    /// # Errors
4945    ///
4946    /// If the column is outside the schema.
4947    pub fn frequency_occurrences(&self, column: usize) -> Result<Option<FrequencyOccurrences>> {
4948        let field = self
4949            .table
4950            .fields
4951            .get(column)
4952            .ok_or_else(|| invalid("frequency column index out of range"))?;
4953        let Some(summary) = self.frequency_summary(column)? else {
4954            return Ok(None);
4955        };
4956        if summary.ordinals.is_empty() {
4957            return Ok(None);
4958        }
4959        let (anchors, anchor_indices) = if summary.ordinal_entries.len() == summary.ordinals.len() {
4960            let entries = self.decode_frequencies(column, &field.ty, &summary.entries)?;
4961            (entries.into_iter().map(|(value, _)| value).collect(), summary.ordinal_entries.clone())
4962        } else {
4963            (Vec::new(), Vec::new())
4964        };
4965        Ok(Some(FrequencyOccurrences {
4966            omitted_max: summary.omitted_max,
4967            ordinals: summary.ordinals.clone(),
4968            anchors,
4969            anchor_indices,
4970        }))
4971    }
4972
4973    /// How many distinct values one column holds, counting a null as no value.
4974    ///
4975    /// A string column of this format is written against one dictionary that covers the whole table.
4976    /// A code is handed out the first time a value is seen and nothing ever removes one, so the
4977    /// number of codes is the number of distinct values exactly rather than an estimate. That makes
4978    /// `COUNT(DISTINCT column)` over a whole table a question the directory already knows the answer
4979    /// to, and the alternative is a hash table with a row per distinct value built from a pass over
4980    /// every row.
4981    ///
4982    /// A null in the column used to make this `None` and no longer does. A null row is written as
4983    /// the code for the empty string, so a nullable column's dictionary can hold an empty string
4984    /// that no row of it actually has, and the dictionary on its own does not say which case it is.
4985    /// The writer does know, because it counts the non-null rows that use each code on its way to
4986    /// the frequency summary, so it records how many codes any row holds and the directory carries
4987    /// that number. This reads it rather than the size of the dictionary, which also means the
4988    /// dictionary page is not opened to answer.
4989    ///
4990    /// An integer column has no dictionary, and its count comes from the set the writer keeps on its
4991    /// numeric frequency pass instead, which is exact up to a cap. `None` for a column past that cap
4992    /// and for every column that is neither, where a sketch would answer approximately and SQL asked
4993    /// for the exact number.
4994    ///
4995    /// # Errors
4996    ///
4997    /// If the column is outside the schema.
4998    pub fn distinct_values(&self, column: usize) -> Result<Option<u64>> {
4999        self.table
5000            .distincts
5001            .get(column)
5002            .copied()
5003            .ok_or_else(|| invalid("distinct column index out of range"))
5004    }
5005
5006    /// How many rows of one column are null, added up over the stripes.
5007    ///
5008    /// Every stripe records this exactly when it is written, because a null count is not a bound
5009    /// that is allowed to be wide the way a minimum and a maximum are: a filter that reads one too
5010    /// many is slow and a `COUNT` that reads one too many is wrong. Adding up a few hundred numbers
5011    /// already in memory is what makes `COUNT(column)` over a whole table free.
5012    ///
5013    /// # Errors
5014    ///
5015    /// If the column is outside the schema.
5016    pub fn null_count(&self, column: usize) -> Result<u64> {
5017        if column >= self.table.fields.len() {
5018            return Err(invalid("null count column index out of range"));
5019        }
5020        let mut nulls = 0_u64;
5021        for stripe in &self.table.stripes {
5022            let range = stripe
5023                .zone
5024                .column(column)
5025                .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5026            nulls = nulls
5027                .checked_add(range.nulls as u64)
5028                .ok_or_else(|| invalid("null count overflow"))?;
5029        }
5030        Ok(nulls)
5031    }
5032
5033    /// The smallest and the largest value of one string column, from the order beside its values.
5034    ///
5035    /// The dictionary holds exactly the values the column holds, so the first and the last of them
5036    /// in sorted order are the column's minimum and maximum. Two reads of a rank block settle what
5037    /// otherwise walks a million rows.
5038    ///
5039    /// `None` when the column is not a string, when the file was written before version 9 and so has
5040    /// no order, when the column has no values at all, or when it has a null in it, which is the
5041    /// placeholder again: the empty string a null is written as would sort ahead of every real
5042    /// value and be reported as the minimum.
5043    ///
5044    /// # Errors
5045    ///
5046    /// If the column is outside the schema, or a rank names a code the dictionary does not have.
5047    pub fn text_extremes(&self, column: usize) -> Result<Option<(Value, Value)>> {
5048        if self.null_count(column)? > 0 {
5049            return Ok(None);
5050        }
5051        let Some(dictionary) = self.dictionary(column)? else { return Ok(None) };
5052        let Some(ranks) = dictionary.ranks() else { return Ok(None) };
5053        if ranks == 0 {
5054            return Ok(None);
5055        }
5056        let low = text_at_rank(&dictionary, 0)?;
5057        let high = text_at_rank(&dictionary, ranks - 1)?;
5058        Ok(Some((low, high)))
5059    }
5060
5061    /// The smallest and the largest value of one column, when every stripe wrote exact ends.
5062    ///
5063    /// A stripe's ends are allowed to be wider than the truth, because a bound that rules out a
5064    /// chunk that could not match is still correct when it rules out nothing. That is what makes
5065    /// them cheap to write for a bit packed or a dictionary column, and it is also what stops them
5066    /// answering a `MIN`. So each stripe says which of the two it wrote, and this answers only when
5067    /// all of them walked their rows.
5068    ///
5069    /// `None` for a column with no ends, for an empty table, and for a column any stripe of which
5070    /// guessed. Nulls need no special case, because the ends skip them the same way `MIN` does.
5071    ///
5072    /// One case is given up on that did not have to be. A stripe merges the ends of its sixty four
5073    /// parts, and a part with no ends at all erases the merged ones, because a part whose rows are
5074    /// not covered by the stripe's ends is a stripe that would skip rows it should keep. A part of
5075    /// nothing but nulls has no rows to cover and so did not need to erase anything, but the merge
5076    /// cannot tell that part from a part whose layout it could not read. So a column with a chunk
5077    /// of nothing but nulls in the middle of it goes and reads the rows. That is slow and right,
5078    /// and the fix is a row count per part rather than anything here.
5079    ///
5080    /// # Errors
5081    ///
5082    /// If the column is outside the schema.
5083    pub fn exact_extremes(&self, column: usize) -> Result<Option<(Bound, Bound)>> {
5084        if column >= self.table.fields.len() {
5085            return Err(invalid("extremes column index out of range"));
5086        }
5087        let mut low: Option<Bound> = None;
5088        let mut high: Option<Bound> = None;
5089        for stripe in &self.table.stripes {
5090            let range = stripe
5091                .zone
5092                .column(column)
5093                .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5094            if !range.exact {
5095                return Ok(None);
5096            }
5097            // A stripe of nothing but nulls has no ends and says nothing about the column's, which
5098            // is why this skips it rather than giving up on the whole column. A stripe that has
5099            // rows and still has no end is a layout whose values this cannot see, and skipping that
5100            // one would answer with an end taken from the other stripes, so it gives up instead.
5101            let (Some(small), Some(large)) = (range.low.as_ref(), range.high.as_ref()) else {
5102                if stripe.rows > range.nulls {
5103                    return Ok(None);
5104                }
5105                continue;
5106            };
5107            low = Some(low.map_or_else(|| small.clone(), |held| held.smaller(small.clone())));
5108            high = Some(high.map_or_else(|| large.clone(), |held| held.larger(large.clone())));
5109        }
5110        Ok(low.zip(high))
5111    }
5112
5113    /// The sum of one integer column and how many rows went into it, when every stripe wrote one.
5114    ///
5115    /// The count beside the sum is the non-null rows, because that is what a `SUM` adds up and what
5116    /// an `AVG` divides by, and a caller that had to work it out from the row count and the null
5117    /// count would be doing the same walk twice.
5118    ///
5119    /// `None` for anything that is not an integer column, for a file written by something that did
5120    /// not record it, and when adding the stripes together would overflow.
5121    ///
5122    /// # Errors
5123    ///
5124    /// If the column is outside the schema.
5125    pub fn exact_sum(&self, column: usize) -> Result<Option<(i128, u64)>> {
5126        if column >= self.table.fields.len() {
5127            return Err(invalid("sum column index out of range"));
5128        }
5129        let mut total = 0_i128;
5130        let mut rows = 0_u64;
5131        for stripe in &self.table.stripes {
5132            let range = stripe
5133                .zone
5134                .column(column)
5135                .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5136            let Some(part) = range.sum else { return Ok(None) };
5137            let Some(sum) = total.checked_add(part) else { return Ok(None) };
5138            total = sum;
5139            rows = rows.saturating_add(stripe.rows as u64 - range.nulls as u64);
5140        }
5141        Ok(Some((total, rows)))
5142    }
5143
5144    /// Certified host groups over a string column, when the caller's inclusive row-count bound
5145    /// excludes every host the synopsis omitted.
5146    pub fn host_groups(
5147        &self,
5148        column: usize,
5149        minimum_count: u64,
5150    ) -> Result<Option<Vec<host::HostEntry>>> {
5151        if column >= self.table.fields.len() {
5152            return Err(invalid("host group column index out of range"));
5153        }
5154        let Some(summary) = &self.table.host_groups else { return Ok(None) };
5155        if summary.column != column || minimum_count <= summary.omitted_max {
5156            return Ok(None);
5157        }
5158        Ok(Some(summary.entries.clone()))
5159    }
5160
5161    /// The global dictionary of a column, opened once however many workers ask for it at once.
5162    ///
5163    /// The unlocked look is first because it is the answer every time after the first and it costs a
5164    /// load. Everybody who misses it queues on [`Self::loading`] and looks again on the way in, so
5165    /// the one who arrived first does the reading and the rest take what it left. Waiting is the
5166    /// cheaper thing to do: the work behind the lock is a page read, a checksum and the decode of a
5167    /// dictionary that can hold half a million entries, and the alternative is every worker of the
5168    /// scan doing all of it and all but one dropping the result on the floor.
5169    fn dictionary(&self, column: usize) -> Result<Option<Arc<Vector>>> {
5170        let Some(page) = self.table.dictionaries[column] else { return Ok(None) };
5171        if let Some(dictionary) = self.dictionaries[column].get() {
5172            return Ok(Some(Arc::clone(dictionary)));
5173        }
5174        let _queued = self.loading[column].lock().map_err(|_| invalid("a poisoned dictionary"))?;
5175        if let Some(dictionary) = self.dictionaries[column].get() {
5176            return Ok(Some(Arc::clone(dictionary)));
5177        }
5178        self.opened.fetch_add(1, Atomic::Relaxed);
5179        let dictionary = Arc::new(open_global_dictionary(
5180            Arc::clone(&self.file),
5181            page,
5182            &self.table.fields[column].ty,
5183            TEXT_KEEP_BUDGET,
5184        )?);
5185        let _ = self.dictionaries[column].set(Arc::clone(&dictionary));
5186        Ok(Some(dictionary))
5187    }
5188
5189    /// Reads one section's extent table and checks it against the entry that names it.
5190    ///
5191    /// # Errors
5192    ///
5193    /// If the entry points outside the file, the table does not checksum, or it does not decode as
5194    /// a run of extents in element order.
5195    pub fn extents(&self, of: &Section) -> Result<Vec<section::Extent>> {
5196        if of.extent_bytes == 0 {
5197            return Ok(Vec::new());
5198        }
5199        let mut bytes = vec![0; of.extent_bytes as usize];
5200        read_at(&self.file, of.extent_page, &mut bytes)?;
5201        if checksum(&bytes) != of.hash {
5202            return Err(invalid("a section's extent table does not checksum"));
5203        }
5204        let extents = section::decode_extents(&bytes)?;
5205        if extents.len() != of.extents as usize {
5206            return Err(invalid("a section's extent table is not the length the entry says"));
5207        }
5208        Ok(extents)
5209    }
5210
5211    /// Reads and verifies one extent of a section.
5212    ///
5213    /// This is what section 3.2's second rule is for. A reduction that needs one extent of a two
5214    /// gigabyte forward link reads and checksums that extent and nothing else, which is the whole
5215    /// difference between a structure that works at SF100 and issue #745.
5216    ///
5217    /// # Errors
5218    ///
5219    /// If the extent points outside the file, or its bytes do not checksum.
5220    pub fn extent(&self, of: &section::Extent) -> Result<Vec<u8>> {
5221        let end = of
5222            .offset
5223            .checked_add(u64::from(of.length))
5224            .ok_or_else(|| invalid("an extent overflows the file"))?;
5225        if of.offset < HEADER || end > self.size {
5226            return Err(invalid("an extent is outside the file"));
5227        }
5228        let mut bytes = vec![0; of.length as usize];
5229        read_at(&self.file, of.offset, &mut bytes)?;
5230        if checksum(&bytes) != of.hash {
5231            return Err(invalid("an extent does not checksum"));
5232        }
5233        Ok(bytes)
5234    }
5235
5236    /// Reads a whole section's payload, every extent of it, in order.
5237    ///
5238    /// For a structure that is resident anyway, which a key map is. Anything large enough that the
5239    /// split matters should be walking [`Reader::extents`] and taking the one it needs.
5240    ///
5241    /// # Errors
5242    ///
5243    /// If the extent table or any extent fails its check.
5244    pub fn payload(&self, of: &Section) -> Result<Vec<u8>> {
5245        let extents = self.extents(of)?;
5246        let mut bytes =
5247            Vec::with_capacity(sum(extents.iter().map(|one| u64::from(one.length))) as usize);
5248        for one in &extents {
5249            if one.first != bytes.len() as u64 {
5250                return Err(invalid("a section's extents do not join up"));
5251            }
5252            bytes.extend_from_slice(&self.extent(one)?);
5253        }
5254        // The same exception `write_section` makes: a budget record has no bytes, so its
5255        // `header_bytes` is a size rather than a header and there is nothing for it to run past.
5256        if !bytes.is_empty() && of.header_bytes as usize > bytes.len() {
5257            return Err(invalid("a section's header is longer than its payload"));
5258        }
5259        Ok(bytes)
5260    }
5261
5262    /// Reads only the named columns from one part.
5263    ///
5264    /// The whole stripe page each column lives in is read and kept, because a scan asks for the
5265    /// parts of a stripe one after another and this is what turns sixty four reads into one.
5266    ///
5267    /// # Errors
5268    ///
5269    /// If a part, column, page, or checksum is invalid.
5270    pub fn read(&self, part: usize, columns: &[usize]) -> Result<Chunk> {
5271        self.read_impl(part, columns, true)
5272    }
5273
5274    /// Reads named columns from one part without keeping the stripe page it came out of.
5275    ///
5276    /// This is for sparse row fetches after a selective TopN or filter, which reach a few parts of
5277    /// a stripe rather than all of them. A caller that will read most of a stripe should use
5278    /// [`Self::read`] instead, because this reads and discards the page index every time.
5279    ///
5280    /// # Errors
5281    ///
5282    /// If a part, column, page, or checksum is invalid.
5283    pub fn read_sparse(&self, part: usize, columns: &[usize]) -> Result<Chunk> {
5284        self.read_impl(part, columns, false)
5285    }
5286
5287    /// Whether an exact global-code membership index proves that the stripe holding a part cannot
5288    /// contain any of the sorted candidate codes.
5289    ///
5290    /// # Errors
5291    ///
5292    /// If the part, column, index page, checksum, or delta stream is invalid.
5293    pub fn skips_codes(&self, part: usize, column: usize, candidates: &[u32]) -> Result<bool> {
5294        if candidates.is_empty() {
5295            return Ok(true);
5296        }
5297        if candidates.windows(2).any(|pair| pair[0] >= pair[1]) {
5298            return Err(Error::internal("native code candidates are not sorted and unique"));
5299        }
5300        let stripe = self.stripe_of(part)?;
5301        let Some(page) = stripe.memberships.get(column) else {
5302            return Ok(false);
5303        };
5304        let mut bytes = vec![0; page.length as usize];
5305        read_at(&self.file, page.offset, &mut bytes)?;
5306        if checksum(&bytes) != page.hash {
5307            return Err(invalid("membership page checksum differs"));
5308        }
5309        let codes = decode_membership(&bytes)?;
5310        let mut left = 0;
5311        let mut right = 0;
5312        while left < codes.len() && right < candidates.len() {
5313            match codes[left].cmp(&candidates[right]) {
5314                Ordering::Less => left += 1,
5315                Ordering::Greater => right += 1,
5316                Ordering::Equal => return Ok(false),
5317            }
5318        }
5319        Ok(true)
5320    }
5321
5322    fn stripe_of(&self, part: usize) -> Result<&Stripe> {
5323        let place = self.places.get(part).ok_or_else(|| invalid("part index out of range"))?;
5324        self.table
5325            .stripes
5326            .get(place.stripe as usize)
5327            .ok_or_else(|| invalid("stripe index out of range"))
5328    }
5329
5330    /// The page index of one column of one stripe, and its page when the caller wants all of it.
5331    ///
5332    /// A scan hands parts out in order, so every worker on a column crosses into a new stripe within
5333    /// a few parts of the others and they all want the same page at the same moment. This used to
5334    /// let all of them read it, which cost the scan as many copies of every page as it had workers.
5335    /// On the full ClickBench file a `MIN(EventDate), MAX(EventDate)` moved 3.2 GB off the disk to
5336    /// look at 400 MB of column.
5337    ///
5338    /// A worker that finds the page it wants already being read neither waits for it nor reads it
5339    /// again. It comes back with the index alone, which sends [`Reader::read_impl`] down the path
5340    /// that reads the one part it came for, a few kilobytes against a quarter of a megabyte, and it
5341    /// picks the page up from the cache on its next part. Waiting would be the other way to avoid
5342    /// the duplicate read and it is worse: the pages that matter are the wide string ones, they take
5343    /// milliseconds to copy even warm, and every other worker would be stopped for all of it.
5344    ///
5345    /// The file is never read under the lock.
5346    fn held(&self, at: usize, stripe: &Stripe, column: usize, whole: bool) -> Result<CachedColumn> {
5347        let cache = self.cache.get(column).ok_or_else(|| invalid("column index out of range"))?;
5348        let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5349        let known = cached.index.get(at).and_then(Clone::clone);
5350        let page = cached.pages.get(at).and_then(Clone::clone);
5351        if let Some(index) = known.clone() {
5352            if !whole || page.is_some() {
5353                return Ok(CachedColumn { stripe: at, index, page });
5354            }
5355        }
5356        if cached.loading.contains(&at) {
5357            drop(cached);
5358            // The index is almost always already here, because somebody read this stripe to get
5359            // into the loading list in the first place, so this branch usually costs no read at
5360            // all and the one part read in `read_impl` is all the losing worker pays for.
5361            if let Some(index) = known {
5362                return Ok(CachedColumn { stripe: at, index, page: None });
5363            }
5364            let held = self.page_of(stripe, column, at, false, None)?;
5365            let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5366            remember(&mut cached, &held, self.kept.load(Atomic::Relaxed));
5367            return Ok(held);
5368        }
5369        cached.loading.push(at);
5370        drop(cached);
5371
5372        let read = self.page_of(stripe, column, at, whole, known);
5373
5374        // The stripe leaves the loading list and its page enters the cache under one lock. Doing
5375        // them separately would leave a moment where another worker sees neither and reads the
5376        // page a second time, which is the whole thing this is here to stop.
5377        let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5378        if let Some(position) = cached.loading.iter().position(|loading| *loading == at) {
5379            cached.loading.remove(position);
5380        }
5381        let held = read?;
5382        remember(&mut cached, &held, self.kept.load(Atomic::Relaxed));
5383        Ok(held)
5384    }
5385
5386    /// Reads one stripe's index for a column, and its page when the caller wants all of it.
5387    ///
5388    /// `known` is the index when the reader has already read it, which after the first worker
5389    /// through a stripe it always has, because [`remember`] keeps every index for the life of the
5390    /// reader. Without that a scan reads the index again on every part that misses the page cache.
5391    fn page_of(
5392        &self,
5393        stripe: &Stripe,
5394        column: usize,
5395        at: usize,
5396        whole: bool,
5397        known: Option<Arc<Vec<PartSpan>>>,
5398    ) -> Result<CachedColumn> {
5399        let index = match known {
5400            Some(index) => index,
5401            None => {
5402                self.indexes.fetch_add(1, Atomic::Relaxed);
5403                Arc::new(read_index(&self.file, stripe, column)?)
5404            }
5405        };
5406        let page = if whole {
5407            self.pages.fetch_add(1, Atomic::Relaxed);
5408            let span = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
5409            let mut bytes = vec![0; span.length as usize];
5410            read_at(&self.file, span.offset, &mut bytes)?;
5411            Some(Arc::new(bytes))
5412        } else {
5413            None
5414        };
5415        Ok(CachedColumn { stripe: at, index, page })
5416    }
5417
5418    fn read_impl(&self, at: usize, columns: &[usize], whole: bool) -> Result<Chunk> {
5419        let place = *self.places.get(at).ok_or_else(|| invalid("part index out of range"))?;
5420        let index = place.stripe as usize;
5421        let stripe =
5422            self.table.stripes.get(index).ok_or_else(|| invalid("stripe index out of range"))?;
5423        let rows = place.rows as usize;
5424        let mut picked = Vec::with_capacity(columns.len());
5425        for &column in columns {
5426            let field = self
5427                .table
5428                .fields
5429                .get(column)
5430                .ok_or_else(|| invalid("column index out of range"))?;
5431            let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
5432            let held = self.held(index, stripe, column, whole)?;
5433            let span = *held
5434                .index
5435                .get(place.part as usize)
5436                .ok_or_else(|| invalid("part index out of range"))?;
5437            let owned;
5438            let bytes = match &held.page {
5439                Some(held) => part_bytes(held, span)?,
5440                None => {
5441                    let offset = page
5442                        .offset
5443                        .checked_add(span.start as u64)
5444                        .ok_or_else(|| invalid("part range overflow"))?;
5445                    let mut bytes = vec![0; span.length];
5446                    read_at(&self.file, offset, &mut bytes)?;
5447                    owned = bytes;
5448                    &owned
5449                }
5450            };
5451            if checksum(bytes) != span.hash {
5452                return Err(invalid(&format!(
5453                    "column page checksum differs, column {column} part {} at {}+{} of {} bytes, \
5454                     wanted {:016x} and got {:016x}",
5455                    place.part,
5456                    page.offset,
5457                    span.start,
5458                    span.length,
5459                    span.hash,
5460                    checksum(bytes),
5461                )));
5462            }
5463            let dictionary = self.dictionary(column)?;
5464            // Held as a page, because a column that came out of a file is handed out more than
5465            // once. A group by clones its key columns out of the chunk so the keys outlive it, a
5466            // projection of a bare column name does the same, and a cut of a flat run copies unless
5467            // the run is a page. One `Arc` per column per part buys all of those, and it moves the
5468            // run into the `Arc` without touching a value.
5469            picked.push(decode(&field.ty, rows, bytes, dictionary)?.into_pages());
5470        }
5471        Chunk::with_rows(picked, rows)
5472    }
5473
5474    /// Whether persisted statistics prove that a part cannot match the predicates.
5475    ///
5476    /// Three of them, asked cheapest first.
5477    ///
5478    /// The stripe's bounds are in memory already, so they are free, and they are also the coarsest:
5479    /// every part of a stripe gets the same answer and a scan that skips one part that way skips all
5480    /// sixty four. Then the part's own bounds, which are a read of one page per column per stripe
5481    /// and are sixty four times finer. Then the sieves, which are per part and answer equality, the
5482    /// test bounds are worst at: a column of identifiers has every stripe and nearly every part
5483    /// covering the whole of its type, so bounds keep them all and the sieve keeps the ones that
5484    /// really hold the value.
5485    ///
5486    /// The middle one is what an ordered comparison on a column the rows are not sorted by needs. On
5487    /// ClickBench 24 the stripe bounds leave eight stripes of sixteen alive, which is half the file,
5488    /// and the part bounds leave thirty parts of nine hundred and seventy four.
5489    #[must_use]
5490    pub fn skips(&self, part: usize, probes: &[Probe]) -> bool {
5491        let Some(place) = self.places.get(part).copied() else { return false };
5492        let Some(stripe) = self.table.stripes.get(place.stripe as usize) else { return false };
5493        if stripe.zone.skips(probes) {
5494            return true;
5495        }
5496        probes.iter().any(|probe| self.outside(place, probe) || self.sifted(place, probe))
5497    }
5498
5499    /// Whether the bounds of one part rule out one probe.
5500    ///
5501    /// The part's own two ends, which are narrower than the stripe's and cost a page read the first
5502    /// time this is asked about a column. A column with no page here answers `false`, which is the
5503    /// answer a caller got before there were any.
5504    fn outside(&self, place: Place, probe: &Probe) -> bool {
5505        match self.stripe_part_ranges(place.stripe as usize, probe.column) {
5506            Some(ranges) => ranges
5507                .get(place.part as usize)
5508                .is_some_and(|range| range.excludes(probe.op, &probe.value)),
5509            None => false,
5510        }
5511    }
5512
5513    /// The per part ranges of one stripe of one column, read once and kept.
5514    ///
5515    /// `None` when the column has no page in that stripe and when the page is damaged, on the same
5516    /// reasoning as the sieves: this is an index over data that is still there, so a caller that
5517    /// cannot read one reads the rows and gets the right answer slowly.
5518    fn stripe_part_ranges(&self, stripe: usize, column: usize) -> Option<&[Range]> {
5519        let slot = self.part_ranges.get(column)?.get(stripe)?;
5520        if let Some(held) = slot.get() {
5521            return Some(held);
5522        }
5523        let page = self.table.stripes.get(stripe)?.part_ranges.get(column)?;
5524        let mut bytes = vec![0; page.length as usize];
5525        read_at(&self.file, page.offset, &mut bytes).ok()?;
5526        if checksum(&bytes) != page.hash {
5527            return None;
5528        }
5529        let ranges = Arc::new(decode_part_ranges(&bytes).ok()?);
5530        let _ = slot.set(ranges);
5531        slot.get().map(|held| held.as_slice())
5532    }
5533
5534    /// Whether persisted statistics prove that every row of a part matches the predicates.
5535    ///
5536    /// Only the bounds. The sieves say nothing here, because a sieve that holds a value is a sieve
5537    /// that may be holding somebody else's hash, so it can rule a part out and can never wave one
5538    /// through.
5539    ///
5540    /// The stripe first and the part after it, the same two steps and in the same order as
5541    /// [`Self::skips`]. The stripe's bounds are in memory already and its null count covers sixty
5542    /// four parts rather than one, so a stripe that answers is an answer for nothing, and the part's
5543    /// own bounds are only read for the probes it could not settle. Both directions are safe: a
5544    /// stretch where everything passes contains no narrower stretch where something fails, and a
5545    /// stripe with no nulls has no nulls in any of its parts.
5546    ///
5547    /// A string end a part recorded is cut down to its first few bytes, so a part's stretch can be
5548    /// wider than its rows really are as well. That is the same safe direction for the same reason,
5549    /// and it is why this asks the two ends rather than anything `exact` says.
5550    #[must_use]
5551    pub fn certain(&self, part: usize, probes: &[Probe]) -> bool {
5552        let Some(place) = self.places.get(part).copied() else { return false };
5553        let Some(stripe) = self.table.stripes.get(place.stripe as usize) else { return false };
5554        if stripe.zone.certain(probes) {
5555            return true;
5556        }
5557        probes
5558            .iter()
5559            .all(|probe| stripe.zone.certain(slice::from_ref(probe)) || self.inside(place, probe))
5560    }
5561
5562    /// Whether one part's own two ends prove that every row of it passes `probe`.
5563    ///
5564    /// The mirror of [`Self::outside`], reading the same page. `false` for a part whose stripe wrote
5565    /// no range page, which is a stripe of one part, because there the stripe's own bounds are the
5566    /// part's and the caller has already asked them.
5567    fn inside(&self, place: Place, probe: &Probe) -> bool {
5568        match self.stripe_part_ranges(place.stripe as usize, probe.column) {
5569            Some(ranges) => ranges
5570                .get(place.part as usize)
5571                .is_some_and(|range| range.certain(probe.op, &probe.value)),
5572            None => false,
5573        }
5574    }
5575
5576    /// Whether the bounds of one stripe prove that none of its parts can match the predicates.
5577    ///
5578    /// The cheap half of [`Self::skips`], asked about a whole stripe at once. The bounds live in the
5579    /// directory and are already in memory, so this answers without touching the file, and that is
5580    /// the reason it is worth having on its own: a caller that wants to know roughly where the work
5581    /// is before it starts any workers can ask this about sixteen stripes for nothing, where asking
5582    /// [`Self::skips`] about nine hundred parts would read and decode a sieve page per stripe first.
5583    ///
5584    /// It keeps stripes that [`Self::skips`] would rule out part by part, which is the right way for
5585    /// it to be wrong: the parts are still checked when they are read.
5586    #[must_use]
5587    pub fn stripe_skips(&self, stripe: usize, probes: &[Probe]) -> bool {
5588        self.table.stripes.get(stripe).is_some_and(|held| held.zone.skips(probes))
5589    }
5590
5591    /// Whether the sieve of one part rules out one probe.
5592    ///
5593    /// Only equality. An ordered comparison is what the bounds are for and a sieve says nothing
5594    /// about it, and a read that cannot answer keeps the part, which is the answer a caller with no
5595    /// sieve gets anyway.
5596    fn sifted(&self, place: Place, probe: &Probe) -> bool {
5597        if probe.op != Op::Equal {
5598            return false;
5599        }
5600        match self.stripe_sieves(place.stripe as usize, probe.column) {
5601            Some(sieves) => sieves
5602                .get(place.part as usize)
5603                .and_then(Option::as_ref)
5604                .is_some_and(|sieve| sieve.excludes(&probe.value)),
5605            None => false,
5606        }
5607    }
5608
5609    /// The sieves of one stripe of one column, read once and kept.
5610    ///
5611    /// `None` when the column has no sieves in that stripe, when the page is damaged, and when the
5612    /// bytes are not a page this version can read. A sieve is an index over data that is still there
5613    /// and a caller that cannot read one reads the rows, so this is the one place in the file where
5614    /// a bad checksum is a slow query rather than an error.
5615    fn stripe_sieves(&self, stripe: usize, column: usize) -> Option<&[Option<Sieve>]> {
5616        let slot = self.sieves.get(column)?.get(stripe)?;
5617        if let Some(held) = slot.get() {
5618            return Some(held);
5619        }
5620        let page = self.table.stripes.get(stripe)?.sieves.get(column)?;
5621        let mut bytes = vec![0; page.length as usize];
5622        read_at(&self.file, page.offset, &mut bytes).ok()?;
5623        if checksum(&bytes) != page.hash {
5624            return None;
5625        }
5626        let sieves = Arc::new(decode_sieves(&bytes).ok()?);
5627        let _ = slot.set(sieves);
5628        slot.get().map(|held| held.as_slice())
5629    }
5630}
5631
5632/// The value sitting at one position of a dictionary's sorted order.
5633fn text_at_rank(dictionary: &Vector, rank: usize) -> Result<Value> {
5634    let code = dictionary.code_at_rank(rank)? as usize;
5635    let text = dictionary
5636        .try_text_at(code)?
5637        .ok_or_else(|| invalid("global dictionary order names a code it does not have"))?;
5638    Ok(Value::Varchar(text.into()))
5639}
5640
5641/// Writes one span of a file at an offset, without depending on where the cursor is.
5642///
5643/// The writer owns an offset of its own and passes it in here, so that nothing it writes depends on
5644/// a cursor that a read is entitled to move. Both of these can come back short and both loop.
5645#[cfg(unix)]
5646fn write_at(file: &File, mut offset: u64, mut bytes: &[u8]) -> Result<()> {
5647    use std::os::unix::fs::FileExt;
5648    while !bytes.is_empty() {
5649        let written = file.write_at(bytes, offset).map_err(io)?;
5650        if written == 0 {
5651            return Err(invalid("a write to the native file wrote nothing"));
5652        }
5653        offset += written as u64;
5654        bytes = &bytes[written..];
5655    }
5656    Ok(())
5657}
5658
5659/// The same write, on the call Windows spells differently.
5660#[cfg(windows)]
5661fn write_at(file: &File, mut offset: u64, mut bytes: &[u8]) -> Result<()> {
5662    use std::os::windows::fs::FileExt;
5663    while !bytes.is_empty() {
5664        let written = file.seek_write(bytes, offset).map_err(io)?;
5665        if written == 0 {
5666            return Err(invalid("a write to the native file wrote nothing"));
5667        }
5668        offset += written as u64;
5669        bytes = &bytes[written..];
5670    }
5671    Ok(())
5672}
5673
5674/// Somewhere that is neither, where the cursor is all there is.
5675#[cfg(not(any(unix, windows)))]
5676fn write_at(file: &File, offset: u64, bytes: &[u8]) -> Result<()> {
5677    use std::io::Write;
5678    let mut file = file.try_clone().map_err(io)?;
5679    file.seek(SeekFrom::Start(offset)).map_err(io)?;
5680    file.write_all(bytes).map_err(io)
5681}
5682
5683/// Reads one span of a file at an offset, without moving a cursor anybody else can see.
5684///
5685/// Every reader of a table shares one [`File`] behind an [`Arc`], and a grouped aggregate reads its
5686/// pages from several threads at once, so this has to be positional. Seeking and then reading is
5687/// two calls with a gap in the middle, and in that gap another thread's seek lands and the read
5688/// comes back with somebody else's bytes.
5689///
5690/// Both of these can come back short, so both loop. A read of zero bytes before the span is filled
5691/// means the file stops earlier than the directory said it does.
5692#[cfg(unix)]
5693fn read_at(file: &File, mut offset: u64, mut bytes: &mut [u8]) -> Result<()> {
5694    use std::os::unix::fs::FileExt;
5695    while !bytes.is_empty() {
5696        let read = file.read_at(bytes, offset).map_err(io)?;
5697        if read == 0 {
5698            return Err(invalid("column page ends before its declared length"));
5699        }
5700        offset += read as u64;
5701        bytes = &mut bytes[read..];
5702    }
5703    Ok(())
5704}
5705
5706/// The same read, on the call Windows spells differently.
5707///
5708/// `seek_read` is one `ReadFile` carrying the offset with it, so two of them cannot interleave the
5709/// way a seek and a read can. It does leave the shared cursor somewhere afterwards, which is why
5710/// nothing in this file may read that cursor.
5711#[cfg(windows)]
5712fn read_at(file: &File, mut offset: u64, mut bytes: &mut [u8]) -> Result<()> {
5713    use std::os::windows::fs::FileExt;
5714    while !bytes.is_empty() {
5715        let read = file.seek_read(bytes, offset).map_err(io)?;
5716        if read == 0 {
5717            return Err(invalid("column page ends before its declared length"));
5718        }
5719        offset += read as u64;
5720        bytes = &mut bytes[read..];
5721    }
5722    Ok(())
5723}
5724
5725/// Somewhere that is neither, where the cursor is all there is.
5726///
5727/// This one does race, and there is no way to write it so it does not. Nothing we build for runs
5728/// here, so it exists to keep the crate compiling rather than to be correct under threads.
5729#[cfg(not(any(unix, windows)))]
5730fn read_at(file: &File, offset: u64, bytes: &mut [u8]) -> Result<()> {
5731    let mut file = file.try_clone().map_err(io)?;
5732    file.seek(SeekFrom::Start(offset)).map_err(io)?;
5733    file.read_exact(bytes).map_err(io)
5734}
5735
5736/// What a column type is called in the directory.
5737///
5738/// A tag is a number in a file somebody else wrote, so a tag that has been used is used forever and
5739/// the only thing that may happen to this list is that it grows. 1 to 13 are the tags the format
5740/// had when it could store thirteen types, and 14 to 27 are the rest, in the order they were added
5741/// rather than in an order that means anything.
5742fn type_tag(ty: &LogicalType) -> Result<u8> {
5743    match ty {
5744        LogicalType::SmallInt => Ok(1),
5745        LogicalType::Integer => Ok(2),
5746        LogicalType::BigInt => Ok(3),
5747        LogicalType::Varchar => Ok(4),
5748        LogicalType::Date => Ok(5),
5749        LogicalType::Timestamp => Ok(6),
5750        LogicalType::Boolean => Ok(7),
5751        LogicalType::TinyInt => Ok(8),
5752        LogicalType::UTinyInt => Ok(9),
5753        LogicalType::USmallInt => Ok(10),
5754        LogicalType::UInteger => Ok(11),
5755        LogicalType::UBigInt => Ok(12),
5756        LogicalType::Decimal { .. } => Ok(13),
5757        LogicalType::Float => Ok(14),
5758        LogicalType::Double => Ok(15),
5759        LogicalType::HugeInt => Ok(16),
5760        LogicalType::UHugeInt => Ok(17),
5761        LogicalType::Time => Ok(18),
5762        LogicalType::TimeTz => Ok(19),
5763        LogicalType::TimestampTz => Ok(20),
5764        LogicalType::Interval => Ok(21),
5765        LogicalType::Uuid => Ok(22),
5766        LogicalType::Blob => Ok(23),
5767        LogicalType::Bit => Ok(24),
5768        LogicalType::TimestampS => Ok(25),
5769        LogicalType::TimestampMs => Ok(26),
5770        LogicalType::TimestampNs => Ok(27),
5771        _ => Err(Error::not_implemented(format!("native storage for {ty}"))),
5772    }
5773}
5774
5775/// The tag of a column type, and the parameters of the ones that have any.
5776///
5777/// Only `DECIMAL` has parameters today. Width and scale go after the tag rather than into it
5778/// because they are what says how wide a value is on disk, and a reader that guessed would read the
5779/// wrong number of bytes per row rather than the wrong number of digits.
5780fn put_type(out: &mut Vec<u8>, ty: &LogicalType) -> Result<()> {
5781    out.push(type_tag(ty)?);
5782    if let LogicalType::Decimal { width, scale } = ty {
5783        out.push(*width);
5784        out.push(*scale);
5785    }
5786    Ok(())
5787}
5788
5789/// The other half of [`put_type`], reading the parameters the tag says are there.
5790fn read_type(cur: &mut Cursor<'_>) -> Result<LogicalType> {
5791    let tag = cur.u8()?;
5792    if tag == 13 {
5793        let width = cur.u8()?;
5794        let scale = cur.u8()?;
5795        return LogicalType::decimal(width, scale)
5796            .map_err(|_| invalid("decimal column width and scale are not a decimal"));
5797    }
5798    tag_type(tag)
5799}
5800
5801fn tag_type(tag: u8) -> Result<LogicalType> {
5802    match tag {
5803        1 => Ok(LogicalType::SmallInt),
5804        2 => Ok(LogicalType::Integer),
5805        3 => Ok(LogicalType::BigInt),
5806        4 => Ok(LogicalType::Varchar),
5807        5 => Ok(LogicalType::Date),
5808        6 => Ok(LogicalType::Timestamp),
5809        7 => Ok(LogicalType::Boolean),
5810        8 => Ok(LogicalType::TinyInt),
5811        9 => Ok(LogicalType::UTinyInt),
5812        10 => Ok(LogicalType::USmallInt),
5813        11 => Ok(LogicalType::UInteger),
5814        12 => Ok(LogicalType::UBigInt),
5815        14 => Ok(LogicalType::Float),
5816        15 => Ok(LogicalType::Double),
5817        16 => Ok(LogicalType::HugeInt),
5818        17 => Ok(LogicalType::UHugeInt),
5819        18 => Ok(LogicalType::Time),
5820        19 => Ok(LogicalType::TimeTz),
5821        20 => Ok(LogicalType::TimestampTz),
5822        21 => Ok(LogicalType::Interval),
5823        22 => Ok(LogicalType::Uuid),
5824        23 => Ok(LogicalType::Blob),
5825        24 => Ok(LogicalType::Bit),
5826        25 => Ok(LogicalType::TimestampS),
5827        26 => Ok(LogicalType::TimestampMs),
5828        27 => Ok(LogicalType::TimestampNs),
5829        _ => Err(invalid("column type tag is unknown")),
5830    }
5831}
5832
5833fn put_u16(out: &mut Vec<u8>, value: u16) {
5834    out.extend_from_slice(&value.to_le_bytes());
5835}
5836fn put_u32(out: &mut Vec<u8>, value: u32) {
5837    out.extend_from_slice(&value.to_le_bytes());
5838}
5839fn put_u64(out: &mut Vec<u8>, value: u64) {
5840    out.extend_from_slice(&value.to_le_bytes());
5841}
5842fn put_var_u64(out: &mut Vec<u8>, mut value: u64) {
5843    while value >= 0x80 {
5844        out.push((value as u8 & 0x7f) | 0x80);
5845        value >>= 7;
5846    }
5847    out.push(value as u8);
5848}
5849
5850fn frequency_order(left: FrequencyValue, right: FrequencyValue) -> Ordering {
5851    match (left, right) {
5852        (FrequencyValue::Null, FrequencyValue::Null) => Ordering::Equal,
5853        (FrequencyValue::Null, _) => Ordering::Less,
5854        (_, FrequencyValue::Null) => Ordering::Greater,
5855        (FrequencyValue::Integer(left), FrequencyValue::Integer(right)) => left.cmp(&right),
5856        (FrequencyValue::Code(left), FrequencyValue::Code(right)) => left.cmp(&right),
5857        (FrequencyValue::Integer(_), FrequencyValue::Code(_)) => Ordering::Less,
5858        (FrequencyValue::Code(_), FrequencyValue::Integer(_)) => Ordering::Greater,
5859    }
5860}
5861
5862/// Leaves the [`FREQUENCY_ENTRIES`] commonest entries in order and says what the next one counted.
5863///
5864/// There is one entry a distinct value, so on `URL` this is handed two and a quarter million of
5865/// them and keeps five hundred and twelve. Sorting all of them to throw almost all of them away is
5866/// the whole of what counting a dictionary column used to cost, 2.13 seconds of it on `URL` at eight
5867/// million rows against 11.93 for compressing the same column's values.
5868///
5869/// Partitioning answers both questions instead. It puts the five hundred and thirteenth entry where
5870/// it belongs and everything commoner in front of it, which is the entries to keep and the count to
5871/// report as the largest one omitted, and then only the part that survives is sorted. The order that
5872/// comes out is the order the sort gave, because the tie break makes the comparison total: two
5873/// entries never hold the same value.
5874fn keep_most_frequent(entries: &mut Vec<FrequencyEntry>) -> u64 {
5875    let order = |left: &FrequencyEntry, right: &FrequencyEntry| {
5876        right.count.cmp(&left.count).then_with(|| frequency_order(left.value, right.value))
5877    };
5878    let omitted_max = if entries.len() > FREQUENCY_ENTRIES {
5879        let (_, next, _) = entries.select_nth_unstable_by(FREQUENCY_ENTRIES, order);
5880        let omitted_max = next.count;
5881        entries.truncate(FREQUENCY_ENTRIES);
5882        omitted_max
5883    } else {
5884        0
5885    };
5886    entries.sort_unstable_by(order);
5887    omitted_max
5888}
5889
5890fn code_frequency(
5891    dictionary: &GlobalDictionary,
5892    flat: &[u8],
5893    bases: &[u64],
5894) -> Result<(FrequencySummary, Vec<Option<Vec<u8>>>)> {
5895    let mut entries = dictionary
5896        .counts
5897        .iter()
5898        .enumerate()
5899        .filter(|(_, count)| **count != 0)
5900        .map(|(code, &count)| FrequencyEntry { value: FrequencyValue::Code(code as u32), count })
5901        .collect::<Vec<_>>();
5902    if dictionary.nulls != 0 {
5903        entries.push(FrequencyEntry { value: FrequencyValue::Null, count: dictionary.nulls });
5904    }
5905    let omitted_max = keep_most_frequent(&mut entries);
5906    let mut spans = Vec::with_capacity(entries.len());
5907    let mut text_bytes = 0_usize;
5908    for entry in &entries {
5909        let span = match entry.value {
5910            FrequencyValue::Code(code) => {
5911                let span = GlobalDictionary::value_span(&dictionary.ends, bases, code as usize);
5912                let bytes = flat
5913                    .get(span.0..span.1)
5914                    .ok_or_else(|| invalid("a frequency code is outside its dictionary"))?;
5915                text_bytes = text_bytes.saturating_add(bytes.len());
5916                Some(span)
5917            }
5918            FrequencyValue::Null | FrequencyValue::Integer(_) => None,
5919        };
5920        spans.push(span);
5921    }
5922    let texts = if text_bytes > FREQUENCY_TEXT_BUDGET {
5923        Vec::new()
5924    } else {
5925        spans.into_iter().map(|span| span.map(|(from, to)| flat[from..to].to_vec())).collect()
5926    };
5927    Ok((
5928        FrequencySummary {
5929            entries,
5930            omitted_max,
5931            ordinals: Vec::new(),
5932            ordinal_entries: Vec::new(),
5933        },
5934        texts,
5935    ))
5936}
5937
5938fn encode_directory(table: &Table) -> Result<Vec<u8>> {
5939    let mut out = DIRECTORY.to_vec();
5940    let name = table.name.as_bytes();
5941    put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("table name too long"))?);
5942    out.extend_from_slice(name);
5943    put_u16(&mut out, u16::try_from(table.fields.len()).map_err(|_| invalid("too many columns"))?);
5944    for field in &table.fields {
5945        let name = field.name.as_bytes();
5946        put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?);
5947        out.extend_from_slice(name);
5948        put_type(&mut out, &field.ty)?;
5949        out.push(u8::from(field.not_null));
5950    }
5951    for dictionary in &table.dictionaries {
5952        match dictionary {
5953            None => out.push(0),
5954            Some(page) => {
5955                out.push(1);
5956                put_u64(&mut out, page.offset);
5957                put_u32(&mut out, page.length);
5958                put_u64(&mut out, page.hash);
5959            }
5960        }
5961    }
5962    for distinct in &table.distincts {
5963        match distinct {
5964            None => out.push(0),
5965            Some(count) => {
5966                out.push(1);
5967                put_u64(&mut out, *count);
5968            }
5969        }
5970    }
5971    put_u64(&mut out, u64::try_from(table.rows).map_err(|_| invalid("row count overflow"))?);
5972    put_u32(&mut out, u32::try_from(table.stripes.len()).map_err(|_| invalid("too many stripes"))?);
5973    for stripe in &table.stripes {
5974        put_u32(
5975            &mut out,
5976            u32::try_from(stripe.parts.len()).map_err(|_| invalid("too many parts in a stripe"))?,
5977        );
5978        for &rows in &stripe.parts {
5979            put_u32(&mut out, rows);
5980        }
5981        put_u64(&mut out, stripe.index.offset);
5982        put_u32(&mut out, stripe.index.length);
5983        for page in &stripe.pages {
5984            put_u64(&mut out, page.offset);
5985            put_u32(&mut out, page.length);
5986        }
5987        // A membership index says which of a dictionary's codes a part holds, so a column the writer
5988        // decided against giving a dictionary has nothing for it to be about and writes none. Every
5989        // file written before that decision existed has a dictionary on every varchar column, so
5990        // this reads those files byte for byte the way it always did.
5991        for ((field, dictionary), membership) in
5992            table.fields.iter().zip(&table.dictionaries).zip(stripe.memberships.slots())
5993        {
5994            if field.ty != LogicalType::Varchar || dictionary.is_none() {
5995                continue;
5996            }
5997            let page =
5998                membership.ok_or_else(|| invalid("string page has no code membership index"))?;
5999            put_u64(&mut out, page.offset);
6000            put_u32(&mut out, page.length);
6001            put_u64(&mut out, page.hash);
6002        }
6003        for sieve in stripe.sieves.slots() {
6004            match sieve {
6005                None => out.push(0),
6006                Some(page) => {
6007                    out.push(1);
6008                    put_u64(&mut out, page.offset);
6009                    put_u32(&mut out, page.length);
6010                    put_u64(&mut out, page.hash);
6011                }
6012            }
6013        }
6014        for held in stripe.part_ranges.slots() {
6015            match held {
6016                None => out.push(0),
6017                Some(page) => {
6018                    out.push(1);
6019                    put_u64(&mut out, page.offset);
6020                    put_u32(&mut out, page.length);
6021                    put_u64(&mut out, page.hash);
6022                }
6023            }
6024        }
6025        for range in stripe.zone.columns() {
6026            put_bound(&mut out, range.low.as_ref())?;
6027            put_bound(&mut out, range.high.as_ref())?;
6028            put_u32(
6029                &mut out,
6030                u32::try_from(range.nulls).map_err(|_| invalid("null count overflow"))?,
6031            );
6032            out.push(u8::from(range.exact));
6033            match range.sum {
6034                None => out.push(0),
6035                Some(total) => {
6036                    out.push(1);
6037                    out.extend_from_slice(&total.to_le_bytes());
6038                }
6039            }
6040        }
6041    }
6042    out.extend_from_slice(FREQUENCIES);
6043    put_u16(
6044        &mut out,
6045        u16::try_from(table.frequencies.len())
6046            .map_err(|_| invalid("too many frequency columns"))?,
6047    );
6048    for summary in &table.frequencies {
6049        let summary = match summary {
6050            None => {
6051                out.push(0);
6052                continue;
6053            }
6054            Some(Frequencies::Held(summary)) => summary,
6055            // Only a reader leaves a synopsis in the file, and nothing writes a reader's table back.
6056            Some(Frequencies::Stored { .. }) => {
6057                return Err(invalid("a synopsis left in the file cannot be written back"));
6058            }
6059        };
6060        out.push(1);
6061        put_u64(&mut out, summary.omitted_max);
6062        put_u32(
6063            &mut out,
6064            u32::try_from(summary.entries.len())
6065                .map_err(|_| invalid("too many frequency entries"))?,
6066        );
6067        for entry in &summary.entries {
6068            match entry.value {
6069                FrequencyValue::Null => out.push(0),
6070                FrequencyValue::Integer(value) => {
6071                    out.push(1);
6072                    out.extend_from_slice(&value.to_le_bytes());
6073                }
6074                FrequencyValue::Code(value) => {
6075                    out.push(2);
6076                    put_u32(&mut out, value);
6077                }
6078            }
6079            put_u64(&mut out, entry.count);
6080        }
6081        put_u32(
6082            &mut out,
6083            u32::try_from(summary.ordinals.len())
6084                .map_err(|_| invalid("too many frequency ordinals"))?,
6085        );
6086        let mut previous = 0_u64;
6087        for (at, &ordinal) in summary.ordinals.iter().enumerate() {
6088            let delta = if at == 0 {
6089                ordinal
6090            } else {
6091                ordinal
6092                    .checked_sub(previous)
6093                    .ok_or_else(|| invalid("frequency ordinals are not ordered"))?
6094            };
6095            if at != 0 && delta == 0 {
6096                return Err(invalid("frequency ordinals are not unique"));
6097            }
6098            put_var_u64(&mut out, delta);
6099            previous = ordinal;
6100        }
6101        if summary.ordinal_entries.len() != summary.ordinals.len() {
6102            return Err(invalid("frequency ordinal values have a different length"));
6103        }
6104        for &entry in &summary.ordinal_entries {
6105            if entry as usize >= summary.entries.len() {
6106                return Err(invalid("frequency ordinal value is outside its entries"));
6107            }
6108            put_u16(&mut out, entry);
6109        }
6110    }
6111    if !table.pair_frequencies.is_empty() {
6112        out.extend_from_slice(PAIR_FREQUENCIES);
6113        put_u16(
6114            &mut out,
6115            u16::try_from(table.pair_frequencies.len())
6116                .map_err(|_| invalid("too many pair frequency summaries"))?,
6117        );
6118        for summary in &table.pair_frequencies {
6119            put_u16(&mut out, summary.first);
6120            put_u16(&mut out, summary.second);
6121            put_u64(&mut out, summary.omitted_max);
6122            put_u16(
6123                &mut out,
6124                u16::try_from(summary.entries.len())
6125                    .map_err(|_| invalid("too many pair frequency entries"))?,
6126            );
6127            for entry in &summary.entries {
6128                put_u16(&mut out, entry.first_entry);
6129                match entry.second {
6130                    None => out.push(0),
6131                    Some(code) => {
6132                        out.push(1);
6133                        put_u32(&mut out, code);
6134                    }
6135                }
6136                put_u64(&mut out, entry.count);
6137            }
6138        }
6139    }
6140    let text_columns = table.frequency_texts.iter().filter(|texts| !texts.is_empty()).count();
6141    if text_columns != 0 {
6142        out.extend_from_slice(FREQUENCY_TEXTS);
6143        put_u16(
6144            &mut out,
6145            u16::try_from(text_columns)
6146                .map_err(|_| invalid("too many string frequency columns"))?,
6147        );
6148        for (column, texts) in table.frequency_texts.iter().enumerate() {
6149            if texts.is_empty() {
6150                continue;
6151            }
6152            put_u16(
6153                &mut out,
6154                u16::try_from(column).map_err(|_| invalid("frequency text column overflows"))?,
6155            );
6156            put_u16(
6157                &mut out,
6158                u16::try_from(texts.len())
6159                    .map_err(|_| invalid("too many frequency text entries"))?,
6160            );
6161            for text in texts {
6162                match text {
6163                    None => out.push(0),
6164                    Some(text) => {
6165                        out.push(1);
6166                        put_u32(
6167                            &mut out,
6168                            u32::try_from(text.len())
6169                                .map_err(|_| invalid("frequency text is too long"))?,
6170                        );
6171                        out.extend_from_slice(text);
6172                    }
6173                }
6174            }
6175        }
6176    }
6177    if let Some(summary) = &table.host_groups {
6178        out.extend_from_slice(HOST_GROUPS);
6179        put_u16(
6180            &mut out,
6181            u16::try_from(summary.column).map_err(|_| invalid("host column overflows"))?,
6182        );
6183        put_u64(&mut out, summary.omitted_max);
6184        put_u16(
6185            &mut out,
6186            u16::try_from(summary.entries.len()).map_err(|_| invalid("too many host groups"))?,
6187        );
6188        for entry in &summary.entries {
6189            put_u32(
6190                &mut out,
6191                u32::try_from(entry.host.len()).map_err(|_| invalid("host name is too long"))?,
6192            );
6193            out.extend_from_slice(entry.host.as_bytes());
6194            put_u64(&mut out, entry.count);
6195            out.extend_from_slice(&entry.bytes_sum.to_le_bytes());
6196            put_u32(
6197                &mut out,
6198                u32::try_from(entry.minimum.len())
6199                    .map_err(|_| invalid("host minimum is too long"))?,
6200            );
6201            out.extend_from_slice(entry.minimum.as_bytes());
6202        }
6203    }
6204    // Written only when there is a declaration, so that the common file is the same bytes it was
6205    // and the section is not a byte of zero on every table in the world that never asked for one.
6206    if let Some(clustering) = &table.clustering {
6207        out.extend_from_slice(CLUSTERING);
6208        out.push(clustering.width().tag());
6209        put_u16(
6210            &mut out,
6211            u16::try_from(clustering.columns().len())
6212                .map_err(|_| invalid("too many clustering columns"))?,
6213        );
6214        for &column in clustering.columns() {
6215            put_u16(
6216                &mut out,
6217                u16::try_from(column).map_err(|_| invalid("clustering column index overflow"))?,
6218            );
6219        }
6220    }
6221    // The section table, last, behind its own magic, for the same reason the frequency block is
6222    // behind its own: a reader that stops before it gets a table with no sections, and a table with
6223    // no sections is a correct table. The one difference from the blocks before it is that this one
6224    // is written even when it is empty, so that a file written by this build always says which
6225    // sections it has rather than leaving a reader to infer it from where the bytes ran out.
6226    out.extend_from_slice(SECTIONS);
6227    put_u64(&mut out, table.generation);
6228    put_u16(
6229        &mut out,
6230        u16::try_from(table.sections.len()).map_err(|_| invalid("too many sections"))?,
6231    );
6232    for held in &table.sections {
6233        held.encode(&mut out)?;
6234    }
6235    if table.dictionary_payloads.iter().any(|&bytes| bytes != 0) {
6236        out.extend_from_slice(DICTIONARY_PAYLOADS);
6237        put_u16(
6238            &mut out,
6239            u16::try_from(table.fields.len()).map_err(|_| invalid("too many columns"))?,
6240        );
6241        for at in 0..table.fields.len() {
6242            put_u64(&mut out, table.dictionary_payloads.get(at).copied().unwrap_or(0));
6243        }
6244    }
6245    Ok(out)
6246}
6247
6248/// The small level of the directory, naming every table in the file.
6249///
6250/// This is what a footer slot points at. Each entry carries its own checksum over its table
6251/// directory, so a table whose directory is torn is found when that table is first touched rather
6252/// than being trusted because the catalog around it checksummed.
6253///
6254/// The views go after the tables and are whole here, since a view is text and a column list and has
6255/// no pages for a second level to point at.
6256fn encode_catalog(entries: &[Entry], views: &[ViewEntry]) -> Result<Vec<u8>> {
6257    let mut out = CATALOG.to_vec();
6258    put_u32(&mut out, u32::try_from(entries.len()).map_err(|_| invalid("too many tables"))?);
6259    for entry in entries {
6260        let name = entry.name.as_bytes();
6261        put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("table name too long"))?);
6262        out.extend_from_slice(name);
6263        put_u64(&mut out, u64::try_from(entry.rows).map_err(|_| invalid("row count overflow"))?);
6264        put_u16(
6265            &mut out,
6266            u16::try_from(entry.fields.len()).map_err(|_| invalid("too many columns"))?,
6267        );
6268        for field in &entry.fields {
6269            let name = field.name.as_bytes();
6270            put_u16(
6271                &mut out,
6272                u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?,
6273            );
6274            out.extend_from_slice(name);
6275            put_type(&mut out, &field.ty)?;
6276            out.push(u8::from(field.not_null));
6277        }
6278        put_u64(&mut out, entry.directory.offset);
6279        put_u32(&mut out, entry.directory.length);
6280        put_u64(&mut out, entry.directory.hash);
6281    }
6282    put_u32(&mut out, u32::try_from(views.len()).map_err(|_| invalid("too many views"))?);
6283    for view in views {
6284        let name = view.name.as_bytes();
6285        put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("view name too long"))?);
6286        out.extend_from_slice(name);
6287        put_long_text(&mut out, &view.sql, "view body")?;
6288        put_long_text(&mut out, &view.statement, "view statement")?;
6289        put_u16(
6290            &mut out,
6291            u16::try_from(view.aliases.len()).map_err(|_| invalid("too many aliases"))?,
6292        );
6293        for alias in &view.aliases {
6294            let alias = alias.as_bytes();
6295            put_u16(
6296                &mut out,
6297                u16::try_from(alias.len()).map_err(|_| invalid("alias name too long"))?,
6298            );
6299            out.extend_from_slice(alias);
6300        }
6301        put_u16(
6302            &mut out,
6303            u16::try_from(view.columns.len()).map_err(|_| invalid("too many columns"))?,
6304        );
6305        for field in &view.columns {
6306            let name = field.name.as_bytes();
6307            put_u16(
6308                &mut out,
6309                u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?,
6310            );
6311            out.extend_from_slice(name);
6312            put_type(&mut out, &field.ty)?;
6313            out.push(u8::from(field.not_null));
6314        }
6315    }
6316    Ok(out)
6317}
6318
6319/// A length and that many bytes, for text that is allowed to be longer than a name.
6320fn put_long_text(out: &mut Vec<u8>, text: &str, what: &str) -> Result<()> {
6321    let bytes = text.as_bytes();
6322    put_u32(out, u32::try_from(bytes.len()).map_err(|_| invalid(&format!("{what} too long")))?);
6323    out.extend_from_slice(bytes);
6324    Ok(())
6325}
6326
6327/// Reads the catalog directory back, checking every span against the file before anything is
6328/// allocated for it.
6329fn decode_catalog(bytes: &[u8], size: u64) -> Result<(Vec<Entry>, Vec<ViewEntry>)> {
6330    let mut cur = Cursor::new(bytes);
6331    if cur.take(8)? != CATALOG {
6332        return Err(invalid("catalog magic differs"));
6333    }
6334    let count = cur.u32()? as usize;
6335    let mut entries: Vec<Entry> = Vec::with_capacity(count.min(1024));
6336    for _ in 0..count {
6337        let name = cur.text()?;
6338        let rows = usize::try_from(cur.u64()?).map_err(|_| invalid("row count does not fit"))?;
6339        let width = cur.u16()? as usize;
6340        let mut fields = Vec::with_capacity(width);
6341        for _ in 0..width {
6342            let name = cur.text()?;
6343            let ty = read_type(&mut cur)?;
6344            let not_null = match cur.u8()? {
6345                0 => false,
6346                1 => true,
6347                _ => return Err(invalid("nullability flag differs")),
6348            };
6349            fields.push(Field { name, ty, not_null });
6350        }
6351        let directory = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6352        let end = directory
6353            .offset
6354            .checked_add(u64::from(directory.length))
6355            .ok_or_else(|| invalid("table directory offset overflow"))?;
6356        if directory.offset < HEADER
6357            || end > size
6358            || directory.length as usize > MAX_DIRECTORY
6359            || directory.length == 0
6360        {
6361            return Err(invalid("table directory range is outside the file"));
6362        }
6363        if entries.iter().any(|held| held.name == name) {
6364            return Err(invalid("two tables in the catalog have the same name"));
6365        }
6366        entries.push(Entry { name, fields, rows, directory });
6367    }
6368    // A catalog that ends where the tables end is a catalog with no views in it, which is every
6369    // file written before format 25. That is why the count is allowed to be missing rather than
6370    // read as a zero that has to be there: an older file has nothing after the last table entry at
6371    // all, and [`READABLE`] says those files still open.
6372    let count = if cur.done() { 0 } else { cur.u32()? as usize };
6373    let mut views: Vec<ViewEntry> = Vec::with_capacity(count.min(1024));
6374    for _ in 0..count {
6375        let name = cur.text()?;
6376        let sql = cur.long_text()?;
6377        let statement = cur.long_text()?;
6378        let width = cur.u16()? as usize;
6379        let mut aliases = Vec::with_capacity(width);
6380        for _ in 0..width {
6381            aliases.push(cur.text()?);
6382        }
6383        let width = cur.u16()? as usize;
6384        let mut columns = Vec::with_capacity(width);
6385        for _ in 0..width {
6386            let name = cur.text()?;
6387            let ty = read_type(&mut cur)?;
6388            let not_null = match cur.u8()? {
6389                0 => false,
6390                1 => true,
6391                _ => return Err(invalid("nullability flag differs")),
6392            };
6393            columns.push(Field { name, ty, not_null });
6394        }
6395        // The same rule the tables above get, and for the same reason. Two entries under one name
6396        // is a catalog nothing can answer a lookup from, and finding that out here is better than
6397        // finding it out from whichever of the two a search happened to reach first.
6398        if views.iter().any(|held| held.name == name) {
6399            return Err(invalid("two views in the catalog have the same name"));
6400        }
6401        if entries.iter().any(|held| held.name == name) {
6402            return Err(invalid("a table and a view in the catalog have the same name"));
6403        }
6404        views.push(ViewEntry { name, sql, statement, aliases, columns });
6405    }
6406    Ok((entries, views))
6407}
6408
6409/// Reads the fields of a directory or a catalog in order, off bytes in memory or out of the file.
6410///
6411/// A catalog is small and is read whole. A table directory is not: at ten million rows of `hits` it
6412/// is nearly a megabyte, and holding that buffer while the table it describes is built out of it
6413/// put both at the peak of every query. Out of the file, the cursor holds one window of
6414/// [`DIRECTORY_WINDOW`] bytes and moves it forward as the fields are read, so what a directory
6415/// costs at open is what it decodes into and not that plus its own bytes.
6416struct Cursor<'a> {
6417    bytes: &'a [u8],
6418    at: usize,
6419    window: Option<Window<'a>>,
6420}
6421
6422/// The part of a directory in the file that a [`Cursor`] has read in.
6423struct Window<'a> {
6424    file: &'a File,
6425    offset: u64,
6426    length: usize,
6427    /// Where `held` starts, counted from the start of the directory.
6428    start: usize,
6429    held: Vec<u8>,
6430    /// How much to read at once, which is [`DIRECTORY_WINDOW`] outside the tests.
6431    size: usize,
6432}
6433
6434/// How much of a directory a cursor reading one out of the file holds at once.
6435const DIRECTORY_WINDOW: usize = 64 << 10;
6436
6437impl<'a> Cursor<'a> {
6438    fn new(bytes: &'a [u8]) -> Self {
6439        Self { bytes, at: 0, window: None }
6440    }
6441
6442    /// A cursor over `length` bytes of `file` from `offset`, which it reads a window at a time.
6443    fn over(file: &'a File, offset: u64, length: usize) -> Self {
6444        let window =
6445            Window { file, offset, length, start: 0, held: Vec::new(), size: DIRECTORY_WINDOW };
6446        Self { bytes: &[], at: 0, window: Some(window) }
6447    }
6448
6449    /// How many bytes the cursor walks in all.
6450    fn len(&self) -> usize {
6451        self.window.as_ref().map_or(self.bytes.len(), |window| window.length)
6452    }
6453
6454    /// Makes sure the next `len` bytes are in memory.
6455    fn ensure(&mut self, len: usize) -> Result<()> {
6456        let end = self.at.checked_add(len).ok_or_else(|| invalid("directory offset overflow"))?;
6457        if end > self.len() {
6458            return Err(invalid("directory is truncated"));
6459        }
6460        let Some(window) = &mut self.window else { return Ok(()) };
6461        if self.at < window.start || end > window.start + window.held.len() {
6462            let want = len.max(window.size).min(window.length - self.at);
6463            window.start = self.at;
6464            window.held.resize(want, 0);
6465            read_at(window.file, window.offset + self.at as u64, &mut window.held)?;
6466        }
6467        Ok(())
6468    }
6469
6470    /// `len` bytes from `at`, which [`Self::ensure`] has already brought in.
6471    fn held(&self, at: usize, len: usize) -> &[u8] {
6472        match &self.window {
6473            Some(window) => &window.held[at - window.start..at - window.start + len],
6474            None => &self.bytes[at..at + len],
6475        }
6476    }
6477
6478    /// The next `len` bytes, without moving past them.
6479    #[inline]
6480    fn peek(&mut self, len: usize) -> Result<&[u8]> {
6481        if self.window.is_none() {
6482            let bytes = self.bytes;
6483            return Ok(&bytes[self.at..self.end(len)?]);
6484        }
6485        self.ensure(len)?;
6486        Ok(self.held(self.at, len))
6487    }
6488
6489    /// The next `len` bytes, moving past them.
6490    ///
6491    /// Every data page is decoded through this, a byte or a word at a time, so a cursor over bytes
6492    /// already in memory takes them here and never reaches [`Self::ensure`]. With the window check
6493    /// on every call, q06 on TPC-H spent a seventh of its instructions in it.
6494    #[inline]
6495    fn take(&mut self, len: usize) -> Result<&[u8]> {
6496        if self.window.is_none() {
6497            let bytes = self.bytes;
6498            let (at, end) = (self.at, self.end(len)?);
6499            self.at = end;
6500            return Ok(&bytes[at..end]);
6501        }
6502        self.take_windowed(len)
6503    }
6504
6505    /// Where `len` bytes from here end, when they end inside the bytes.
6506    #[inline]
6507    fn end(&self, len: usize) -> Result<usize> {
6508        let end = self.at.checked_add(len).ok_or_else(|| invalid("directory offset overflow"))?;
6509        if end > self.bytes.len() {
6510            return Err(invalid("directory is truncated"));
6511        }
6512        Ok(end)
6513    }
6514
6515    /// [`Self::take`] out of the file, a window at a time.
6516    #[inline(never)]
6517    fn take_windowed(&mut self, len: usize) -> Result<&[u8]> {
6518        self.ensure(len)?;
6519        self.at += len;
6520        Ok(self.held(self.at - len, len))
6521    }
6522    #[inline]
6523    fn u8(&mut self) -> Result<u8> {
6524        Ok(self.take(1)?[0])
6525    }
6526    #[inline]
6527    fn u16(&mut self) -> Result<u16> {
6528        Ok(u16::from_le_bytes(self.take(2)?.try_into().expect("two bytes")))
6529    }
6530    #[inline]
6531    fn u32(&mut self) -> Result<u32> {
6532        Ok(u32::from_le_bytes(self.take(4)?.try_into().expect("four bytes")))
6533    }
6534    #[inline]
6535    fn u64(&mut self) -> Result<u64> {
6536        Ok(u64::from_le_bytes(self.take(8)?.try_into().expect("eight bytes")))
6537    }
6538    fn var_u64(&mut self) -> Result<u64> {
6539        let mut value = 0_u64;
6540        for shift in (0..=63).step_by(7) {
6541            let byte = self.u8()?;
6542            let part = u64::from(byte & 0x7f);
6543            if shift == 63 && part > 1 {
6544                return Err(invalid("frequency ordinal varint overflows"));
6545            }
6546            value |= part << shift;
6547            if byte & 0x80 == 0 {
6548                return Ok(value);
6549            }
6550        }
6551        Err(invalid("frequency ordinal varint is too long"))
6552    }
6553    /// A zone map's end, in the layout `rudb_common::bounds` defines.
6554    ///
6555    /// The bytes are the ones this directory has written since format 10 and the codec moved to
6556    /// rank zero rather than being copied, because a column summary now writes the same two ends
6557    /// and two encodings of one type is how the two quietly stop agreeing.
6558    ///
6559    /// A bound's length is in the bound, so out of the file the cursor offers the codec a few bytes
6560    /// and offers it twice as many whenever it runs out before the directory does.
6561    fn bound(&mut self) -> Result<Option<Bound>> {
6562        let rest = self.len().saturating_sub(self.at);
6563        let mut want = 32;
6564        loop {
6565            let offered = self.peek(want.min(rest))?;
6566            let mut used = 0;
6567            match bounds::get(offered, &mut used) {
6568                Ok(bound) => {
6569                    self.at += used;
6570                    return Ok(bound);
6571                }
6572                Err(_) if want < rest => want *= 2,
6573                Err(error) => return Err(error),
6574            }
6575        }
6576    }
6577    fn text(&mut self) -> Result<String> {
6578        let len = self.u16()? as usize;
6579        String::from_utf8(self.take(len)?.to_vec()).map_err(|_| invalid("name is not UTF-8"))
6580    }
6581    /// Whether everything has been read, which is how a section that an older file does not have at
6582    /// all is told from one that is there and empty.
6583    fn done(&self) -> bool {
6584        self.at >= self.len()
6585    }
6586    /// The same, for text that is a query rather than a name.
6587    ///
6588    /// A name fits in sixteen bits of length and a view body does not have to. Nobody writes a 64
6589    /// kilobyte identifier by accident and people do write generated queries that long, and a view
6590    /// that could not be written down because its body was too big would be a limit invented here
6591    /// rather than one anything else in the engine has.
6592    fn long_text(&mut self) -> Result<String> {
6593        let len = self.u32()? as usize;
6594        String::from_utf8(self.take(len)?.to_vec()).map_err(|_| invalid("text is not UTF-8"))
6595    }
6596}
6597
6598/// One column's frequency synopsis, or `None` for a column that has none, checked against the column.
6599fn decode_summary(
6600    cur: &mut Cursor<'_>,
6601    field: &Field,
6602    rows: usize,
6603    values: bool,
6604) -> Result<Option<FrequencySummary>> {
6605    Ok(match cur.u8()? {
6606        0 => None,
6607        1 => {
6608            let omitted_max = cur.u64()?;
6609            let count = cur.u32()? as usize;
6610            if count > FREQUENCY_ENTRIES {
6611                return Err(invalid("frequency entry count exceeds its bound"));
6612            }
6613            let mut entries = Vec::with_capacity(count);
6614            // row at a time: directory decoding validates each persisted bounded frequency entry.
6615            for _ in 0..count {
6616                let value = match cur.u8()? {
6617                    0 => FrequencyValue::Null,
6618                    1 => FrequencyValue::Integer(i128::from_le_bytes(
6619                        cur.take(16)?.try_into().expect("sixteen bytes"),
6620                    )),
6621                    2 => FrequencyValue::Code(cur.u32()?),
6622                    _ => return Err(invalid("frequency value tag differs")),
6623                };
6624                let valid = matches!(
6625                    (&field.ty, value),
6626                    (_, FrequencyValue::Null)
6627                        | (LogicalType::Varchar, FrequencyValue::Code(_))
6628                        | (
6629                            LogicalType::TinyInt
6630                                | LogicalType::SmallInt
6631                                | LogicalType::Integer
6632                                | LogicalType::BigInt
6633                                | LogicalType::UTinyInt
6634                                | LogicalType::USmallInt
6635                                | LogicalType::UInteger
6636                                | LogicalType::UBigInt
6637                                | LogicalType::Date
6638                                | LogicalType::Timestamp,
6639                            FrequencyValue::Integer(_),
6640                        )
6641                );
6642                if !valid {
6643                    return Err(invalid("frequency value does not match its column"));
6644                }
6645                let count = cur.u64()?;
6646                if count == 0 || count > rows as u64 {
6647                    return Err(invalid("frequency count is outside the table"));
6648                }
6649                entries.push(FrequencyEntry { value, count });
6650            }
6651            if entries.windows(2).any(|pair| pair[0].count < pair[1].count) {
6652                return Err(invalid("frequency entries are not descending"));
6653            }
6654            let ordinals = {
6655                let ordinal_count = cur.u32()? as usize;
6656                if ordinal_count > FREQUENCY_ORDINALS || ordinal_count > rows {
6657                    return Err(invalid("frequency ordinal count exceeds its bound"));
6658                }
6659                let mut ordinals = Vec::with_capacity(ordinal_count);
6660                let mut previous = 0_u64;
6661                for at in 0..ordinal_count {
6662                    let delta = cur.var_u64()?;
6663                    if at != 0 && delta == 0 {
6664                        return Err(invalid("frequency ordinals are not increasing"));
6665                    }
6666                    let ordinal = if at == 0 {
6667                        delta
6668                    } else {
6669                        previous
6670                            .checked_add(delta)
6671                            .ok_or_else(|| invalid("frequency ordinal overflows"))?
6672                    };
6673                    if ordinal >= rows as u64 {
6674                        return Err(invalid("frequency ordinal is outside the table"));
6675                    }
6676                    ordinals.push(ordinal);
6677                    previous = ordinal;
6678                }
6679                ordinals
6680            };
6681            let ordinal_entries = if values {
6682                let mut ordinal_entries = Vec::with_capacity(ordinals.len());
6683                for _ in 0..ordinals.len() {
6684                    let entry = cur.u16()?;
6685                    if entry as usize >= entries.len() {
6686                        return Err(invalid("frequency ordinal value is outside its entries"));
6687                    }
6688                    ordinal_entries.push(entry);
6689                }
6690                ordinal_entries
6691            } else {
6692                Vec::new()
6693            };
6694            Some(FrequencySummary { entries, omitted_max, ordinals, ordinal_entries })
6695        }
6696        _ => return Err(invalid("frequency summary tag differs")),
6697    })
6698}
6699
6700fn decode_directory(bytes: &[u8], size: u64) -> Result<Table> {
6701    read_directory(Cursor::new(bytes), size, None)
6702}
6703
6704/// A directory out of `cur`, which is a whole one in memory or one being read out of the file.
6705///
6706/// `stored_at` is where the directory starts in the file when it is being read out of it, and then
6707/// every frequency synopsis is checked and left there, as [`Frequencies::Stored`].
6708fn read_directory(mut cur: Cursor<'_>, size: u64, stored_at: Option<u64>) -> Result<Table> {
6709    if cur.take(8)? != DIRECTORY {
6710        return Err(invalid("directory magic differs"));
6711    }
6712    let name = cur.text()?;
6713    let width = cur.u16()? as usize;
6714    let mut fields = Vec::with_capacity(width);
6715    for _ in 0..width {
6716        let name = cur.text()?;
6717        let ty = read_type(&mut cur)?;
6718        let not_null = match cur.u8()? {
6719            0 => false,
6720            1 => true,
6721            _ => return Err(invalid("nullability flag differs")),
6722        };
6723        fields.push(Field { name, ty, not_null });
6724    }
6725    let mut dictionaries = Vec::with_capacity(width);
6726    for _ in 0..width {
6727        dictionaries.push(match cur.u8()? {
6728            0 => None,
6729            1 => {
6730                let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6731                let end = page
6732                    .offset
6733                    .checked_add(u64::from(page.length))
6734                    .ok_or_else(|| invalid("dictionary page offset overflow"))?;
6735                // A global dictionary covers a whole column, not one bounded stripe. Its lazy
6736                // payload is intentionally allowed to grow past `MAX_PAGE`; only ordinary column
6737                // pages are capped there. `Writer::finish` has already bounded this length by the
6738                // on-disk `u32`, and the range check below keeps it inside the file.
6739                if page.offset < HEADER || end > size {
6740                    return Err(invalid("dictionary page range is outside the file"));
6741                }
6742                Some(page)
6743            }
6744            _ => return Err(invalid("dictionary page tag differs")),
6745        });
6746    }
6747    let mut distincts = Vec::with_capacity(width);
6748    for _ in 0..width {
6749        distincts.push(match cur.u8()? {
6750            0 => None,
6751            1 => Some(cur.u64()?),
6752            _ => return Err(invalid("distinct count tag differs")),
6753        });
6754    }
6755    let rows = usize::try_from(cur.u64()?).map_err(|_| invalid("row count does not fit"))?;
6756    let count = cur.u32()? as usize;
6757    let mut stripes = Vec::with_capacity(count);
6758    let mut total = 0_usize;
6759    for _ in 0..count {
6760        let count = cur.u32()? as usize;
6761        if count == 0 || count > STRIPE_PARTS {
6762            return Err(invalid("stripe part count is outside its bound"));
6763        }
6764        let mut parts = Vec::with_capacity(count);
6765        let mut stripe_rows = 0_usize;
6766        for _ in 0..count {
6767            let rows = cur.u32()?;
6768            if rows == 0 {
6769                return Err(invalid("empty part"));
6770            }
6771            parts.push(rows);
6772            stripe_rows = stripe_rows
6773                .checked_add(rows as usize)
6774                .ok_or_else(|| invalid("stripe row count overflow"))?;
6775        }
6776        total =
6777            total.checked_add(stripe_rows).ok_or_else(|| invalid("stripe row count overflow"))?;
6778        let index = Span { offset: cur.u64()?, length: cur.u32()? };
6779        let section = index_section(count)?;
6780        let wanted = section
6781            .checked_mul(width)
6782            .and_then(|bytes| u32::try_from(bytes).ok())
6783            .ok_or_else(|| invalid("index page length overflow"))?;
6784        let end = index
6785            .offset
6786            .checked_add(u64::from(index.length))
6787            .ok_or_else(|| invalid("index page offset overflow"))?;
6788        if index.offset < HEADER || end > size || index.length != wanted {
6789            return Err(invalid("index page range is outside the file"));
6790        }
6791        let mut pages = Vec::with_capacity(width);
6792        for _ in 0..width {
6793            let offset = cur.u64()?;
6794            let length = cur.u32()?;
6795            let end = offset
6796                .checked_add(u64::from(length))
6797                .ok_or_else(|| invalid("page offset overflow"))?;
6798            if offset < HEADER || end > size || length as usize > MAX_PAGE {
6799                return Err(invalid("page range is outside the file"));
6800            }
6801            pages.push(Span { offset, length });
6802        }
6803        let mut memberships = vec![None; width];
6804        for (column, field) in fields.iter().enumerate() {
6805            if field.ty != LogicalType::Varchar || dictionaries[column].is_none() {
6806                continue;
6807            }
6808            let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6809            let end = page
6810                .offset
6811                .checked_add(u64::from(page.length))
6812                .ok_or_else(|| invalid("membership page offset overflow"))?;
6813            if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6814                return Err(invalid("membership page range is outside the file"));
6815            }
6816            memberships[column] = Some(page);
6817        }
6818        let mut sieves = vec![None; width];
6819        for sieve in sieves.iter_mut().take(width) {
6820            match cur.u8()? {
6821                0 => continue,
6822                1 => {}
6823                _ => return Err(invalid("a sieve page has an unknown tag")),
6824            }
6825            let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6826            let end = page
6827                .offset
6828                .checked_add(u64::from(page.length))
6829                .ok_or_else(|| invalid("sieve page offset overflow"))?;
6830            if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6831                return Err(invalid("sieve page range is outside the file"));
6832            }
6833            *sieve = Some(page);
6834        }
6835        let mut part_ranges = vec![None; width];
6836        for held in part_ranges.iter_mut().take(width) {
6837            match cur.u8()? {
6838                0 => continue,
6839                1 => {}
6840                _ => return Err(invalid("a part range page has an unknown tag")),
6841            }
6842            let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6843            let end = page
6844                .offset
6845                .checked_add(u64::from(page.length))
6846                .ok_or_else(|| invalid("part range page offset overflow"))?;
6847            if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6848                return Err(invalid("part range page range is outside the file"));
6849            }
6850            *held = Some(page);
6851        }
6852        let mut ranges = Vec::with_capacity(width);
6853        for column in 0..width {
6854            let low = cur.bound()?;
6855            let high = cur.bound()?;
6856            let nulls = cur.u32()? as usize;
6857            if nulls > stripe_rows {
6858                return Err(invalid("null count exceeds stripe rows"));
6859            }
6860            let exact = cur.u8()? != 0;
6861            let sum = match cur.u8()? {
6862                0 => None,
6863                1 => Some(i128::from_le_bytes(
6864                    cur.take(16)?.try_into().map_err(|_| invalid("a stripe sum is truncated"))?,
6865                )),
6866                _ => return Err(invalid("a stripe sum has an unknown tag")),
6867            };
6868            // Files written before the ends of a decimal or a timestamp column carried their power
6869            // of ten hold a bare integer here, and that integer is the one the column holds, which
6870            // is what the power is over. So the type puts it back on the way in and an old file
6871            // prunes as well as a new one. A file that already wrote the power keeps it, because
6872            // this leaves anything that is not an integer alone.
6873            let ty = &fields.get(column).ok_or_else(|| invalid("a stripe range has no column"))?.ty;
6874            let low = low.map(|bound| scaled_as(bound, ty));
6875            let high = high.map(|bound| scaled_as(bound, ty));
6876            ranges.push(Range { low, high, nulls, exact, sum });
6877        }
6878        stripes.push(Stripe {
6879            rows: stripe_rows,
6880            parts,
6881            index,
6882            pages,
6883            memberships: Pages::from_slots(memberships)?,
6884            sieves: Pages::from_slots(sieves)?,
6885            part_ranges: Pages::from_slots(part_ranges)?,
6886            zone: Zone::from_ranges(ranges),
6887        });
6888    }
6889    if total != rows {
6890        return Err(invalid("table row count differs from stripes"));
6891    }
6892    // How many entries each column's synopsis lists, which is all a pair summary is checked against,
6893    // kept apart because the synopses themselves may be left in the file.
6894    let mut entry_counts = vec![0; width];
6895    let frequencies = if cur.done() {
6896        vec![None; width]
6897    } else {
6898        let frequency_magic = cur.take(8)?;
6899        let frequency_values = frequency_magic == FREQUENCIES;
6900        if !frequency_values && frequency_magic != FREQUENCIES_V2 {
6901            return Err(invalid("directory extension magic differs"));
6902        }
6903        if cur.u16()? as usize != width {
6904            return Err(invalid("frequency column count differs"));
6905        }
6906        let mut frequencies = Vec::with_capacity(width);
6907        for (field, entry_count) in fields.iter().zip(&mut entry_counts) {
6908            let start = cur.at;
6909            let summary = decode_summary(&mut cur, field, rows, frequency_values)?;
6910            *entry_count = summary.as_ref().map_or(0, |summary| summary.entries.len());
6911            frequencies.push(match (summary, stored_at) {
6912                (None, _) => None,
6913                (Some(summary), None) => Some(Frequencies::Held(summary)),
6914                (Some(_), Some(offset)) => Some(Frequencies::Stored {
6915                    span: Span {
6916                        offset: offset + start as u64,
6917                        length: u32::try_from(cur.at - start)
6918                            .map_err(|_| invalid("a frequency synopsis is too long"))?,
6919                    },
6920                    values: frequency_values,
6921                }),
6922            });
6923        }
6924        frequencies
6925    };
6926    // There are two optional trailing blocks now rather than one, so the reader dispatches on the
6927    // magic it finds rather than on where the bytes ran out. That is what lets the two arrive
6928    // independently: a format 22 directory ends here and has neither, a directory written before
6929    // the section table has only the clustering declaration, and each one still opens without a
6930    // rewrite. It is the G1 exit criterion, which is that a reader that knows about sections opens
6931    // a file that predates them and answers every query, only without the graph path.
6932    //
6933    // A repeated block is refused rather than allowed to win, because two clustering declarations
6934    // in one directory is a torn directory and the only question is which of them is the lie.
6935    let mut clustering = None;
6936    let mut sections = Vec::new();
6937    let mut pair_frequencies = Vec::new();
6938    let mut seen_pair_frequencies = false;
6939    let mut frequency_texts = vec![Vec::new(); width];
6940    let mut seen_frequency_texts = false;
6941    let mut host_groups = None;
6942    let mut seen_sections = false;
6943    let mut dictionary_payloads = Vec::new();
6944    let mut seen_payloads = false;
6945    // Zero until a section table says otherwise, which is what a format 22 table gets and what
6946    // makes every section stamp fail to match on one, because real generations start at one.
6947    let mut generation = 0;
6948    while !cur.done() {
6949        let mut tag = [0u8; 8];
6950        tag.copy_from_slice(cur.take(8)?);
6951        if &tag == PAIR_FREQUENCIES {
6952            if seen_pair_frequencies {
6953                return Err(invalid("directory names two pair frequency blocks"));
6954            }
6955            seen_pair_frequencies = true;
6956            let count = cur.u16()? as usize;
6957            if count > MAX_PAIR_FREQUENCIES {
6958                return Err(invalid("pair frequency count exceeds its bound"));
6959            }
6960            pair_frequencies = Vec::with_capacity(count);
6961            for _ in 0..count {
6962                let first = cur.u16()?;
6963                let second = cur.u16()?;
6964                let first_at = first as usize;
6965                let second_at = second as usize;
6966                if frequencies.get(first_at).and_then(Option::as_ref).is_none() {
6967                    return Err(invalid("pair frequency first column has no synopsis"));
6968                }
6969                let first_entries = entry_counts[first_at];
6970                if !matches!(fields.get(second_at), Some(field) if field.ty == LogicalType::Varchar)
6971                    || dictionaries.get(second_at).copied().flatten().is_none()
6972                {
6973                    return Err(invalid("pair frequency second column has no stable dictionary"));
6974                }
6975                if pair_frequencies
6976                    .iter()
6977                    .any(|held: &PairFrequencySummary| held.first == first && held.second == second)
6978                {
6979                    return Err(invalid("directory repeats a pair frequency summary"));
6980                }
6981                let omitted_max = cur.u64()?;
6982                if omitted_max > rows as u64 {
6983                    return Err(invalid("pair frequency omitted count exceeds the table"));
6984                }
6985                let entries_count = cur.u16()? as usize;
6986                if entries_count > FREQUENCY_ENTRIES {
6987                    return Err(invalid("pair frequency entry count exceeds its bound"));
6988                }
6989                let mut entries = Vec::with_capacity(entries_count);
6990                for _ in 0..entries_count {
6991                    let first_entry = cur.u16()?;
6992                    if first_entry as usize >= first_entries {
6993                        return Err(invalid("pair frequency anchor is outside its synopsis"));
6994                    }
6995                    let second = match cur.u8()? {
6996                        0 => None,
6997                        1 => Some(cur.u32()?),
6998                        _ => return Err(invalid("pair frequency string tag differs")),
6999                    };
7000                    let count = cur.u64()?;
7001                    if count == 0 || count > rows as u64 {
7002                        return Err(invalid("pair frequency count is outside the table"));
7003                    }
7004                    entries.push(PairFrequencyEntry { first_entry, second, count });
7005                }
7006                if entries.windows(2).any(|pair| pair[0].count < pair[1].count) {
7007                    return Err(invalid("pair frequency entries are not descending"));
7008                }
7009                pair_frequencies.push(PairFrequencySummary { first, second, entries, omitted_max });
7010            }
7011        } else if &tag == FREQUENCY_TEXTS {
7012            if seen_frequency_texts {
7013                return Err(invalid("directory names two frequency text blocks"));
7014            }
7015            seen_frequency_texts = true;
7016            let columns = cur.u16()? as usize;
7017            if columns > width {
7018                return Err(invalid("frequency text column count exceeds the schema"));
7019            }
7020            for _ in 0..columns {
7021                let column = cur.u16()? as usize;
7022                if !frequency_texts.get(column).is_some_and(Vec::is_empty) {
7023                    return Err(invalid("frequency text column is repeated or out of range"));
7024                }
7025                if !matches!(fields.get(column), Some(field) if field.ty == LogicalType::Varchar)
7026                    || dictionaries.get(column).copied().flatten().is_none()
7027                    || frequencies.get(column).and_then(Option::as_ref).is_none()
7028                {
7029                    return Err(invalid("frequency texts belong to a non-string synopsis"));
7030                }
7031                let count = cur.u16()? as usize;
7032                if count == 0 || count != entry_counts[column] {
7033                    return Err(invalid("frequency text count differs from its synopsis"));
7034                }
7035                let mut texts = Vec::with_capacity(count);
7036                for _ in 0..count {
7037                    texts.push(match cur.u8()? {
7038                        0 => None,
7039                        1 => {
7040                            let length = cur.u32()? as usize;
7041                            let bytes = cur.take(length)?.to_vec();
7042                            std::str::from_utf8(&bytes)
7043                                .map_err(|_| invalid("frequency text is not UTF-8"))?;
7044                            Some(bytes)
7045                        }
7046                        _ => return Err(invalid("frequency text tag differs")),
7047                    });
7048                }
7049                frequency_texts[column] = texts;
7050            }
7051        } else if &tag == HOST_GROUPS {
7052            if host_groups.is_some() {
7053                return Err(invalid("directory names two host group blocks"));
7054            }
7055            let column = cur.u16()? as usize;
7056            if !matches!(fields.get(column), Some(field) if field.ty == LogicalType::Varchar)
7057                || dictionaries.get(column).copied().flatten().is_none()
7058            {
7059                return Err(invalid("host groups belong to a non-string dictionary"));
7060            }
7061            let omitted_max = cur.u64()?;
7062            if omitted_max > rows as u64 {
7063                return Err(invalid("host group bound exceeds the table"));
7064            }
7065            let count = cur.u16()? as usize;
7066            if count > host::CAPACITY {
7067                return Err(invalid("host group count exceeds its bound"));
7068            }
7069            let mut entries = Vec::with_capacity(count);
7070            let mut bytes = 0_usize;
7071            for _ in 0..count {
7072                let host_len = cur.u32()? as usize;
7073                bytes =
7074                    bytes.checked_add(host_len).ok_or_else(|| invalid("host bytes overflow"))?;
7075                if bytes > host::BYTE_BUDGET {
7076                    return Err(invalid("host groups exceed their byte budget"));
7077                }
7078                let host = std::str::from_utf8(cur.take(host_len)?)
7079                    .map_err(|_| invalid("host is not UTF-8"))?
7080                    .to_owned();
7081                let count = cur.u64()?;
7082                if count == 0 || count > rows as u64 {
7083                    return Err(invalid("host group count exceeds the table"));
7084                }
7085                let bytes_sum = i128::from_le_bytes(
7086                    cur.take(16)?
7087                        .try_into()
7088                        .map_err(|_| invalid("host length sum is truncated"))?,
7089                );
7090                if bytes_sum < 0 {
7091                    return Err(invalid("host length sum is negative"));
7092                }
7093                let minimum_len = cur.u32()? as usize;
7094                bytes =
7095                    bytes.checked_add(minimum_len).ok_or_else(|| invalid("host bytes overflow"))?;
7096                if bytes > host::BYTE_BUDGET {
7097                    return Err(invalid("host groups exceed their byte budget"));
7098                }
7099                let minimum = std::str::from_utf8(cur.take(minimum_len)?)
7100                    .map_err(|_| invalid("host minimum is not UTF-8"))?
7101                    .to_owned();
7102                entries.push(host::HostEntry { host, count, bytes_sum, minimum });
7103            }
7104            if entries.windows(2).any(|pair| pair[0].count < pair[1].count)
7105                || entries.iter().any(|entry| entry.host.is_empty() || entry.minimum.is_empty())
7106            {
7107                return Err(invalid("host groups are not in certified order"));
7108            }
7109            host_groups = Some(host::HostSummary { column, omitted_max, entries });
7110        } else if &tag == CLUSTERING {
7111            if clustering.is_some() {
7112                return Err(invalid("directory names two clustering declarations"));
7113            }
7114            let bucket = Width::from_tag(cur.u8()?)
7115                .ok_or_else(|| invalid("clustering width tag differs"))?;
7116            let count = cur.u16()? as usize;
7117            let mut columns = Vec::with_capacity(count.min(fields.len()));
7118            for _ in 0..count {
7119                columns.push(u32::from(cur.u16()?));
7120            }
7121            // Through the constructor and not built by hand, so that a file claiming a column the
7122            // table does not have is caught at open rather than at the first scan that trusted it.
7123            clustering = Some(Clustering::new(columns, bucket, &fields).map_err(|_| {
7124                invalid("stored clustering declaration does not match the table it is on")
7125            })?);
7126        } else if &tag == SECTIONS {
7127            if seen_sections {
7128                return Err(invalid("directory names two section tables"));
7129            }
7130            seen_sections = true;
7131            generation = cur.u64()?;
7132            let count = cur.u16()? as usize;
7133            if count > MAX_SECTIONS {
7134                return Err(invalid("section count exceeds its bound"));
7135            }
7136            sections = Vec::with_capacity(count);
7137            // entry at a time: a malformed section entry is refused rather than turned into an
7138            // offset.
7139            for _ in 0..count {
7140                sections.push(Section::decode(cur.take(section::ENTRY_BYTES)?)?);
7141            }
7142            for held in &sections {
7143                let Some(end) = held.extent_page.checked_add(u64::from(held.extent_bytes)) else {
7144                    return Err(invalid("a section's extent table overflows the file"));
7145                };
7146                // The bound check is here and not in `section`, because only the caller knows how
7147                // big the file is. A section pointing past the end is a torn directory, and reading
7148                // the payload it names would be reading whatever else is at that offset.
7149                if held.extent_bytes != 0 && (held.extent_page < HEADER || end > size) {
7150                    return Err(invalid("a section's extent table is outside the file"));
7151                }
7152                if held.extents == 0 && held.extent_bytes != 0 {
7153                    return Err(invalid("a section with no extents names an extent table"));
7154                }
7155            }
7156        } else if &tag == DICTIONARY_PAYLOADS {
7157            if seen_payloads {
7158                return Err(invalid("directory names two dictionary payload blocks"));
7159            }
7160            seen_payloads = true;
7161            let count = cur.u16()? as usize;
7162            if count != fields.len() {
7163                return Err(invalid("dictionary payload block does not match the table's columns"));
7164            }
7165            dictionary_payloads = Vec::with_capacity(count);
7166            for _ in 0..count {
7167                let bytes = cur.u64()?;
7168                if bytes > size {
7169                    return Err(invalid("a dictionary payload is larger than the file"));
7170                }
7171                dictionary_payloads.push(bytes);
7172            }
7173        } else {
7174            return Err(invalid("directory extension magic differs"));
7175        }
7176    }
7177    if !cur.done() {
7178        return Err(invalid("directory has trailing bytes"));
7179    }
7180    Ok(Table {
7181        name,
7182        fields,
7183        stripes,
7184        rows,
7185        dictionaries,
7186        dictionary_payloads,
7187        distincts,
7188        frequencies,
7189        pair_frequencies,
7190        frequency_texts,
7191        host_groups,
7192        clustering,
7193        generation,
7194        sections,
7195    })
7196}
7197
7198/// A zone map's end, in the layout `rudb_common::bounds` defines. See [`Cursor::bound`].
7199fn put_bound(out: &mut Vec<u8>, bound: Option<&Bound>) -> Result<()> {
7200    bounds::put(out, bound)
7201}
7202
7203/// Which cascades are worth trying on a run of dictionary codes.
7204///
7205/// The exhaustive chooser encodes every candidate at every level of a cascade three deep and keeps
7206/// the smallest, which on a part of 1024 codes is around a hundred full encodes to decide something
7207/// three candidates were always going to win. It is the right default for a crate that does not
7208/// know what it is looking at. Here we do know. Codes are counted from zero in the order the values
7209/// were first seen, so a part of them is one value, or a narrow band, or a few long runs, and those
7210/// are constant, frame of reference and run length. Nothing else has ever come first on this data.
7211///
7212/// A dictionary of dictionary codes is the one candidate that can never pay, because the codes are
7213/// already the dictionary, and it is also the most expensive one to try. Below the top level the
7214/// streams are an RLE's run values and run lengths, which are integers in their own right with no
7215/// runs left in them, so only the two flat candidates go down there.
7216///
7217/// This is size given up for time on purpose, and the ablation is this chooser against
7218/// [`chooser::EXHAUSTIVE`] on the same file.
7219#[derive(Debug)]
7220struct Codes;
7221
7222impl chooser::Chooser for Codes {
7223    fn name(&self) -> &'static str {
7224        "codes"
7225    }
7226
7227    fn narrow_strings(
7228        &self,
7229        _values: &[&[u8]],
7230        offered: &[string::Kind],
7231        _depth: u8,
7232    ) -> Vec<string::Kind> {
7233        // Never reached, because nothing here encodes strings through the cascade. The trait asks
7234        // for it and the honest answer to a question we have no opinion on is the whole list.
7235        offered.to_vec()
7236    }
7237
7238    fn narrow_integers(
7239        &self,
7240        _values: &[i64],
7241        offered: &[integer::Kind],
7242        depth: u8,
7243    ) -> Vec<integer::Kind> {
7244        // The contract is a non empty subset, and a chunk that offers none of the three is a chunk
7245        // this has no opinion about rather than one that cannot be written.
7246        narrowed_to(Codes::keep(depth), offered)
7247    }
7248
7249    fn considers_integer(&self, kind: integer::Kind, depth: u8) -> bool {
7250        Codes::keep(depth).contains(&kind)
7251    }
7252}
7253
7254impl Codes {
7255    fn keep(depth: u8) -> &'static [integer::Kind] {
7256        if depth == 0 {
7257            &[integer::Kind::Constant, integer::Kind::Packed, integer::Kind::Rle]
7258        } else {
7259            &[integer::Kind::Constant, integer::Kind::Packed]
7260        }
7261    }
7262}
7263
7264/// The kinds of `offered` that are in `keep`, or all of `offered` when none of them are.
7265///
7266/// `Packed` applies to every chunk and both choosers keep it, so the fallback is never taken on a
7267/// chunk the cascade offers. It is there because the contract is a non empty subset and a chooser
7268/// that returned nothing would be a chunk that cannot be written. It is also why saying no to a kind
7269/// in `considers_integer` is safe: a kind that is never offered could only have been kept through
7270/// this fallback, and the fallback is never reached.
7271fn narrowed_to(keep: &[integer::Kind], offered: &[integer::Kind]) -> Vec<integer::Kind> {
7272    let narrowed: Vec<integer::Kind> =
7273        offered.iter().copied().filter(|kind| keep.contains(kind)).collect();
7274    if narrowed.is_empty() { offered.to_vec() } else { narrowed }
7275}
7276
7277/// Which cascades are worth trying on a part of plain integers.
7278///
7279/// Wider than [`Codes`] because the values are not codes and carry whatever shape the column has.
7280/// A timestamp column climbs, so delta is the one that matters and is the reason this exists at
7281/// all: three timestamp columns in ClickBench were coming out at exactly eight bytes a row with
7282/// nothing asked of them. The same three columns are why the stride is here, since a timestamp
7283/// loaded from a source that recorded whole seconds is microseconds with twenty zero bits under
7284/// every value. A column that is one value with a handful of exceptions is sparse. What is still
7285/// left out is the dictionary, for the same reason as in [`Codes`]: it is the most
7286/// expensive candidate to try and this file already puts the columns that want one through a
7287/// dictionary of their own before they ever reach here.
7288#[derive(Debug)]
7289struct Fixed;
7290
7291impl chooser::Chooser for Fixed {
7292    fn name(&self) -> &'static str {
7293        "fixed"
7294    }
7295
7296    fn narrow_strings(
7297        &self,
7298        _values: &[&[u8]],
7299        offered: &[string::Kind],
7300        _depth: u8,
7301    ) -> Vec<string::Kind> {
7302        offered.to_vec()
7303    }
7304
7305    fn narrow_integers(
7306        &self,
7307        _values: &[i64],
7308        offered: &[integer::Kind],
7309        depth: u8,
7310    ) -> Vec<integer::Kind> {
7311        narrowed_to(Fixed::keep(depth), offered)
7312    }
7313
7314    fn considers_integer(&self, kind: integer::Kind, depth: u8) -> bool {
7315        Fixed::keep(depth).contains(&kind)
7316    }
7317}
7318
7319impl Fixed {
7320    fn keep(depth: u8) -> &'static [integer::Kind] {
7321        if depth == 0 {
7322            &[
7323                integer::Kind::Constant,
7324                integer::Kind::Packed,
7325                integer::Kind::Delta,
7326                integer::Kind::Rle,
7327                integer::Kind::Sparse,
7328                integer::Kind::Strided,
7329            ]
7330        } else {
7331            &[integer::Kind::Constant, integer::Kind::Packed, integer::Kind::Delta]
7332        }
7333    }
7334}
7335
7336/// Every value of an integer part as an `i64`, or `None` for a part this cannot widen without
7337/// losing one.
7338///
7339/// `UBIGINT` is the only integer type left out, because half its range does not fit and a page that
7340/// silently wrapped would be worse than a page that stays plain. Booleans and strings are not
7341/// integers and have their own ways of being small.
7342fn widened(data: &Data) -> Option<Vec<i64>> {
7343    match data {
7344        Data::Int8(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7345        Data::UInt8(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7346        Data::Int16(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7347        Data::UInt16(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7348        Data::Int32(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7349        Data::UInt32(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7350        Data::Int64(values) => Some(values.to_vec()),
7351        _ => None,
7352    }
7353}
7354
7355/// An integer type a cascaded page can be read back into, and how to tell whether a value fits.
7356///
7357/// This exists so that the check and the conversion can be two loops instead of one. `TryFrom` puts
7358/// them together, which is the right shape for one value and the wrong one for a page: a fallible
7359/// conversion a value at a time is a branch a value at a time, the branch decides whether the loop
7360/// keeps going, and a loop like that is one no compiler will widen.
7361trait Narrow: Copy {
7362    /// How wide this type is, and what to add to a value to put its range at the bottom of a `u64`.
7363    ///
7364    /// Half the width for a signed type, which is what moves its smallest value to zero, and nothing
7365    /// for an unsigned one, whose smallest value is already there.
7366    const BIASED: (u32, u64);
7367
7368    /// The value narrowed, which the caller has already shown fits.
7369    fn narrow(value: i64) -> Self;
7370}
7371
7372/// The bits of `value` a `T` cannot hold, and zero when the value fits.
7373///
7374/// The question is asked this way round because the answers or together. A page fits when every
7375/// residue in it is zero, so the loop is an or into an accumulator and the decision is one test
7376/// after it, where asking whether each value is between a floor and a ceiling gives an answer that
7377/// does not combine and turns into a running minimum and maximum.
7378///
7379/// Biasing and shifting is what the answer is made of, rather than anything that reads more like the
7380/// question, because those are the operations a machine has four of. A 64 bit integer minimum is
7381/// AVX-512. So is a 64 bit arithmetic shift right, which is how the sign extension this could be
7382/// written as would have to be done. An add and a logical shift right are AVX2 and are on every
7383/// machine this runs on, so this is the form that gets four values a cycle instead of one.
7384///
7385/// Adding the bias moves the type's range to `0..=2^bits`, wrapping, so everything in range shifts
7386/// away to nothing and everything outside it leaves something behind. A negative value under an
7387/// unsigned type is caught by the same shift, because a negative `i64` read as a `u64` is enormous.
7388#[allow(clippy::cast_sign_loss, reason = "a residue is a bit pattern and not a number")]
7389fn residue<T: Narrow>(value: i64) -> u64 {
7390    let (bits, bias) = T::BIASED;
7391    (value as u64).wrapping_add(bias) >> bits
7392}
7393
7394/// Says a primitive integer narrows with `as`, and where the bottom of its range is.
7395///
7396/// `as` is a truncation and is the right operation here only because [`fit`] has already found every
7397/// residue zero, and it is what makes the second loop a narrowing store with no branch in it.
7398macro_rules! narrows {
7399    ($($ty:ty => $bias:expr),* $(,)?) => {$(
7400        impl Narrow for $ty {
7401            const BIASED: (u32, u64) = (<$ty>::BITS, $bias);
7402
7403            #[allow(
7404                clippy::cast_possible_truncation,
7405                clippy::cast_sign_loss,
7406                reason = "the caller has checked the bits this truncates away"
7407            )]
7408            fn narrow(value: i64) -> Self {
7409                value as Self
7410            }
7411        }
7412    )*};
7413}
7414
7415narrows! {
7416    i8 => 1 << 7,
7417    u8 => 0,
7418    i16 => 1 << 15,
7419    u16 => 0,
7420    i32 => 1 << 31,
7421    u32 => 0,
7422}
7423
7424/// Narrows a page's values, refusing the page if any of them does not fit.
7425///
7426/// The check first and the conversion second, rather than a fallible conversion a value at a time.
7427/// Both loops here are ones a compiler widens: [`residue`] is three instructions a lane and a
7428/// narrowing store is one. The version before this was a `TryFrom` and a `collect` into a `Result`,
7429/// which is a compare, a branch and a short circuit a value at a time, and on ClickBench 39 it was
7430/// seven percent of the query. The version after that kept a running minimum and maximum, which is
7431/// the obvious way to ask and needs a 64 bit integer minimum that AVX2 does not have, so it stayed
7432/// a value at a time and was still ten percent of the same query.
7433///
7434/// An empty page has nothing to refuse, which falls out of the accumulator starting at zero rather
7435/// than needing a case of its own.
7436fn fit<T: Narrow>(values: &[i64]) -> Result<Vec<T>> {
7437    let mut spilled = 0u64;
7438    for value in values {
7439        spilled |= residue::<T>(*value);
7440    }
7441    if spilled != 0 {
7442        return Err(invalid("page value is not of its type"));
7443    }
7444    Ok(values.iter().map(|value| T::narrow(*value)).collect())
7445}
7446
7447/// The same values back in the width the column is declared at.
7448///
7449/// A value that does not fit is a page that disagrees with the directory about what the column is,
7450/// which is a damaged file rather than a caller error, so it is refused rather than truncated.
7451fn narrowed(ty: &LogicalType, values: Vec<i64>) -> Result<Data> {
7452    Ok(match ty {
7453        LogicalType::TinyInt => Data::Int8(fit::<i8>(&values)?.into()),
7454        LogicalType::UTinyInt => Data::UInt8(fit::<u8>(&values)?.into()),
7455        LogicalType::SmallInt => Data::Int16(fit::<i16>(&values)?.into()),
7456        LogicalType::USmallInt => Data::UInt16(fit::<u16>(&values)?.into()),
7457        LogicalType::Integer | LogicalType::Date => Data::Int32(fit::<i32>(&values)?.into()),
7458        LogicalType::UInteger => Data::UInt32(fit::<u32>(&values)?.into()),
7459        LogicalType::BigInt
7460        | LogicalType::Timestamp
7461        | LogicalType::Time
7462        | LogicalType::TimeTz
7463        | LogicalType::TimestampTz
7464        | LogicalType::TimestampS
7465        | LogicalType::TimestampMs
7466        | LogicalType::TimestampNs => Data::Int64(values.into()),
7467        // A decimal is an integer of unscaled units, so the cascade reads back into whichever
7468        // integer the declared width says the column is stored as.
7469        LogicalType::Decimal { .. } => match ty.physical() {
7470            PhysicalType::Int16 => Data::Int16(fit::<i16>(&values)?.into()),
7471            PhysicalType::Int32 => Data::Int32(fit::<i32>(&values)?.into()),
7472            PhysicalType::Int64 => Data::Int64(values.into()),
7473            _ => return Err(invalid("cascade codec belongs to a decimal that is not an integer")),
7474        },
7475        _ => return Err(invalid("cascade codec belongs to a page that is not integers")),
7476    })
7477}
7478
7479/// How many bytes a part of this type costs written out plainly, which is what the cascade has to
7480/// beat before it is worth the decode.
7481fn plain_width(ty: &LogicalType) -> Option<usize> {
7482    Some(match ty {
7483        LogicalType::TinyInt | LogicalType::UTinyInt => 1,
7484        LogicalType::SmallInt | LogicalType::USmallInt => 2,
7485        LogicalType::Integer | LogicalType::UInteger | LogicalType::Date => 4,
7486        LogicalType::BigInt
7487        | LogicalType::Timestamp
7488        | LogicalType::Time
7489        | LogicalType::TimeTz
7490        | LogicalType::TimestampTz
7491        | LogicalType::TimestampS
7492        | LogicalType::TimestampMs
7493        | LogicalType::TimestampNs => 8,
7494        LogicalType::Decimal { .. } => match ty.physical() {
7495            PhysicalType::Int16 => 2,
7496            PhysicalType::Int32 => 4,
7497            PhysicalType::Int64 => 8,
7498            // The widest decimals are stored as `i128`, which the cascade does not widen into, so
7499            // they take the plain path and there is nothing here to compare against.
7500            _ => return None,
7501        },
7502        _ => return None,
7503    })
7504}
7505
7506/// A part's plain integers through the cascade, or `None` when nothing it offers is worth it.
7507///
7508/// What it has to beat is whatever the page would otherwise have cost, which is the bit packed form
7509/// where there is one and the plain width where there is not. Both are cheaper to decode than a
7510/// cascade, so a tie goes to them.
7511fn cascaded(
7512    flat: &Vector,
7513    ty: &LogicalType,
7514    packed: Option<&Packed<'_>>,
7515) -> Result<Option<Vec<u8>>> {
7516    let (Some(width), Some(data)) = (plain_width(ty), flat.data()) else { return Ok(None) };
7517    let Some(values) = widened(data) else { return Ok(None) };
7518    let plain = values.len().saturating_mul(width);
7519    let best = match packed {
7520        // The tag, the base, the word count and the words, which is what the codec 2 branch writes.
7521        Some(packed) => plain.min(21 + size_of_val(packed.words())),
7522        None => plain,
7523    };
7524    let out = integer::encode_with(&values, &Fixed)?;
7525    Ok((out.len() < best).then_some(out))
7526}
7527
7528/// A part's dictionary codes through the integer cascade, or `None` when the cascade did not pay.
7529///
7530/// Until now this stream was a `u32` a row with nothing asked of it, and on ClickBench that was
7531/// 400,185,326 bytes for every one of the 28 varchar columns, the same count for `URL` as for a
7532/// column holding the empty string in nearly every row. Codes are dense integers counted from zero
7533/// and a part holds 1024 of them, which is the shape frame of reference is best at, and a column
7534/// with one value everywhere comes back a constant costing nothing per row rather than four bytes.
7535///
7536/// The result is taken only when it is smaller than the plain form. A cascade is allowed to come
7537/// out larger on a part whose codes are genuinely wide, `URL` has about sixty million distinct
7538/// values, and there is no reason to pay for the decode when it does.
7539/// A varchar page as one FSST layer, or `None` when it did not pay.
7540///
7541/// Until now a varchar page that neither the global dictionary nor the per page dictionary claimed
7542/// was written out raw: four bytes of offset a row and then the bytes. That is the right answer for
7543/// a page of values with nothing in common and the wrong one for a page of English, and a column of
7544/// comments is the case this exists for.
7545///
7546/// One layer and not the full string cascade, which is what the payload blocks of a global
7547/// dictionary go through. The cascade is a search: it encodes the page under every candidate it has
7548/// and recurses into the integer cascade for the lengths of each one, and on TPC-H `orders` that
7549/// took the write from 6.9 s to 48.3 s. It reads back no faster than the dictionary it replaced
7550/// either, 1.807 G instructions against 1.810 G for `select o_comment from orders`, because
7551/// unpicking a nest of layers a value at a time costs what the dictionary's payload block decode
7552/// cost. Raw pages of the same column read in 0.686 G, which says the whole of the difference is
7553/// what the page has to be put back together from.
7554///
7555/// FSST alone keeps most of what the cascade found and gives all of that back. Decoding it is one
7556/// pass over the payload into one buffer, the values are laid end to end in it the way the raw form
7557/// already lays them out, and what the reader hands a chunk is views over that buffer.
7558///
7559/// The page dictionary gets first refusal because it is cheaper still, and it wins on a page whose
7560/// values repeat. What is left for this is the page whose values mostly do not, which is exactly the
7561/// page that was being written raw.
7562///
7563/// Taken only when it comes out smaller than the raw form, so a page of incompressible values pays
7564/// nothing at read time for having been offered.
7565fn text_compressed(flat: &Vector) -> Result<Option<Vec<u8>>> {
7566    let mut values: Vec<&[u8]> = Vec::with_capacity(flat.len());
7567    let mut payload = 0_usize;
7568    for row in 0..flat.len() {
7569        let text = flat.text_at(row).unwrap_or("").as_bytes();
7570        payload = payload.saturating_add(text.len());
7571        values.push(text);
7572    }
7573    // What codec 0 writes for a varchar page: an offset a row and one more, then the payload.
7574    let plain = (flat.len() + 1).saturating_mul(4).saturating_add(payload);
7575    let Some(out) = string::encode_only(string::Kind::Fsst, &values)? else {
7576        return Ok(None);
7577    };
7578    Ok((out.len() < plain).then_some(out))
7579}
7580
7581fn encoded_codes(codes: &[u32]) -> Result<Option<Vec<u8>>> {
7582    let wide: Vec<i64> = codes.iter().map(|code| i64::from(*code)).collect();
7583    let coded = integer::encode_with(&wide, &Codes)?;
7584    let plain = codes.len().saturating_mul(size_of::<u32>());
7585    Ok((coded.len() < plain).then_some(coded))
7586}
7587
7588/// The validity of a page, which is a flag and then, when some rows are null and some are not, a
7589/// bit a row with the valid ones set.
7590fn push_validity(out: &mut Vec<u8>, flat: &Vector) {
7591    let flag = match flat.validity() {
7592        Validity::AllValid => 0,
7593        Validity::AllInvalid => 1,
7594        Validity::Mask(_) => 2,
7595    };
7596    out.push(flag);
7597    if flag == 2 {
7598        for group in (0..flat.len()).step_by(8) {
7599            let mut bits = 0_u8;
7600            for bit in 0..8 {
7601                if group + bit < flat.len() && !flat.is_null_at(group + bit) {
7602                    bits |= 1 << bit;
7603                }
7604            }
7605            out.push(bits);
7606        }
7607    }
7608}
7609
7610/// One part of a column coded against its global dictionary as a page, from the codes and the
7611/// validity [`push_validity`] wrote for it.
7612///
7613/// The codes go through the integer cascade when that comes out smaller than four bytes a code,
7614/// which on a column that repeats itself it nearly always does, and are written as they are when it
7615/// does not.
7616fn coded_page(codes: &[u32], validity: &[u8]) -> Result<Vec<u8>> {
7617    let coded = encoded_codes(codes)?;
7618    let mut out = Vec::with_capacity(
7619        1 + validity.len() + coded.as_ref().map_or(size_of_val(codes), Vec::len),
7620    );
7621    out.push(if coded.is_some() { 4 } else { 3 });
7622    out.extend_from_slice(validity);
7623    match coded {
7624        Some(coded) => out.extend_from_slice(&coded),
7625        None => {
7626            for &code in codes {
7627                put_u32(&mut out, code);
7628            }
7629        }
7630    }
7631    Ok(out)
7632}
7633
7634/// One part of one column as a page, for every column that is not coded against a global
7635/// dictionary. Those are built by [`coded_page`] from codes [`prepare`] handed out.
7636fn encode(vector: &Vector) -> Result<Vec<u8>> {
7637    let ty = vector.logical_type();
7638    // flatten: the file writer needs a uniform scalar page and does it once per loaded chunk.
7639    let flat = vector.flatten()?;
7640    let mut out = Vec::new();
7641    let dictionary = if ty == &LogicalType::Varchar { string_dictionary(&flat)? } else { None };
7642    let compressed_text = if dictionary.is_none() && ty == &LogicalType::Varchar {
7643        text_compressed(&flat)?
7644    } else {
7645        None
7646    };
7647    let packed_vector = if dictionary.is_none() { Some(flat.bit_packed()?) } else { None };
7648    let packed = packed_vector.as_ref().and_then(Vector::packed_parts);
7649    // Only where nothing else has claimed the page, which is the plain integer case. A packed part
7650    // is still on the table because the cascade has to beat it too: the bit pack takes a part only
7651    // when it halves it, so a column that shrinks by a third was coming out whole.
7652    let cascade = if dictionary.is_none() { cascaded(&flat, ty, packed.as_ref())? } else { None };
7653    out.push(if cascade.is_some() {
7654        5
7655    } else if dictionary.is_some() {
7656        1
7657    } else if compressed_text.is_some() {
7658        6
7659    } else if packed.is_some() {
7660        2
7661    } else {
7662        0
7663    });
7664    push_validity(&mut out, &flat);
7665    if let Some(cascade) = cascade {
7666        out.extend_from_slice(&cascade);
7667        return Ok(out);
7668    }
7669    if let Some(dictionary) = dictionary {
7670        out.extend_from_slice(&dictionary);
7671        return Ok(out);
7672    }
7673    if let Some(compressed_text) = compressed_text {
7674        out.extend_from_slice(&compressed_text);
7675        return Ok(out);
7676    }
7677    if let Some(packed) = packed {
7678        if packed.offset() != 0 {
7679            return Err(invalid("writer received a sliced packed vector"));
7680        }
7681        out.push(u8::try_from(packed.width()).map_err(|_| invalid("packed width overflow"))?);
7682        out.extend_from_slice(&packed.base().to_le_bytes());
7683        put_u32(
7684            &mut out,
7685            u32::try_from(packed.words().len()).map_err(|_| invalid("too many packed words"))?,
7686        );
7687        for word in packed.words() {
7688            put_u64(&mut out, *word);
7689        }
7690        return Ok(out);
7691    }
7692    let data = flat.data().ok_or_else(|| invalid("scalar column did not flatten"))?;
7693    match (ty, data) {
7694        (LogicalType::TinyInt, Data::Int8(values)) => {
7695            for value in &**values {
7696                out.extend_from_slice(&value.to_le_bytes());
7697            }
7698        }
7699        (LogicalType::UTinyInt, Data::UInt8(values)) => {
7700            for value in &**values {
7701                out.extend_from_slice(&value.to_le_bytes());
7702            }
7703        }
7704        (LogicalType::SmallInt, Data::Int16(values)) => {
7705            for value in &**values {
7706                out.extend_from_slice(&value.to_le_bytes());
7707            }
7708        }
7709        (LogicalType::USmallInt, Data::UInt16(values)) => {
7710            for value in &**values {
7711                out.extend_from_slice(&value.to_le_bytes());
7712            }
7713        }
7714        (LogicalType::UInteger, Data::UInt32(values)) => {
7715            for value in &**values {
7716                out.extend_from_slice(&value.to_le_bytes());
7717            }
7718        }
7719        (LogicalType::UBigInt, Data::UInt64(values)) => {
7720            for value in &**values {
7721                out.extend_from_slice(&value.to_le_bytes());
7722            }
7723        }
7724        (LogicalType::Integer | LogicalType::Date, Data::Int32(values)) => {
7725            for value in &**values {
7726                out.extend_from_slice(&value.to_le_bytes());
7727            }
7728        }
7729        (
7730            LogicalType::BigInt
7731            | LogicalType::Timestamp
7732            | LogicalType::Time
7733            | LogicalType::TimeTz
7734            | LogicalType::TimestampTz
7735            | LogicalType::TimestampS
7736            | LogicalType::TimestampMs
7737            | LogicalType::TimestampNs,
7738            Data::Int64(values),
7739        ) => {
7740            for value in &**values {
7741                out.extend_from_slice(&value.to_le_bytes());
7742            }
7743        }
7744        // A hugeint and a uuid are both the 128 bit lane, and a uuid's bits are the ones the rest of
7745        // the engine already carries it in, so nothing about the value changes on the way down.
7746        (LogicalType::HugeInt | LogicalType::Uuid, Data::Int128(values)) => {
7747            for value in &**values {
7748                out.extend_from_slice(&value.to_le_bytes());
7749            }
7750        }
7751        (LogicalType::UHugeInt, Data::UInt128(values)) => {
7752            for value in &**values {
7753                out.extend_from_slice(&value.to_le_bytes());
7754            }
7755        }
7756        // Plainly, in the IEEE bytes. The integer encodings do not apply to a float and none of the
7757        // float codecs is worth having before somebody has measured a corpus of them.
7758        (LogicalType::Float, Data::Float32(values)) => {
7759            for value in &**values {
7760                out.extend_from_slice(&value.to_le_bytes());
7761            }
7762        }
7763        (LogicalType::Double, Data::Float64(values)) => {
7764            for value in &**values {
7765                out.extend_from_slice(&value.to_le_bytes());
7766            }
7767        }
7768        // Three counts and not one number. Months, days and microseconds stay apart on disk because
7769        // they are apart in the value: a month is not a fixed number of days and a day is not a
7770        // fixed number of microseconds, which is the whole reason the type has three fields.
7771        (LogicalType::Interval, Data::Interval(values)) => {
7772            for (months, days, micros) in &**values {
7773                out.extend_from_slice(&months.to_le_bytes());
7774                out.extend_from_slice(&days.to_le_bytes());
7775                out.extend_from_slice(&micros.to_le_bytes());
7776            }
7777        }
7778        (LogicalType::Boolean, Data::Bool(values)) => {
7779            for value in &**values {
7780                out.push(u8::from(*value));
7781            }
7782        }
7783        // The unscaled integer and nothing else. Scale is a property of the column and it is in the
7784        // directory already, so writing it a value at a time would be paying for it twice.
7785        (LogicalType::Decimal { .. }, Data::Int16(values)) => {
7786            for value in &**values {
7787                out.extend_from_slice(&value.to_le_bytes());
7788            }
7789        }
7790        (LogicalType::Decimal { .. }, Data::Int32(values)) => {
7791            for value in &**values {
7792                out.extend_from_slice(&value.to_le_bytes());
7793            }
7794        }
7795        (LogicalType::Decimal { .. }, Data::Int64(values)) => {
7796            for value in &**values {
7797                out.extend_from_slice(&value.to_le_bytes());
7798            }
7799        }
7800        (LogicalType::Decimal { .. }, Data::Int128(values)) => {
7801            for value in &**values {
7802                out.extend_from_slice(&value.to_le_bytes());
7803            }
7804        }
7805        // A blob and a bit string go down the way a varchar does, because the layout is the same
7806        // one: an offset a value and then the bytes. What is not the same is that nothing here may
7807        // read the payload as text, which is why this arm asks the column for bytes rather than for
7808        // a string, and why the codecs above that do read text are all asked of a varchar by name.
7809        (LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit, Data::Varlen(values)) => {
7810            let mut bytes = Vec::new();
7811            put_u32(&mut out, 0);
7812            for row in 0..vector.len() {
7813                let value = values.bytes(row).ok_or_else(|| invalid("string view is invalid"))?;
7814                bytes.extend_from_slice(value);
7815                put_u32(
7816                    &mut out,
7817                    u32::try_from(bytes.len())
7818                        .map_err(|_| invalid("string payload exceeds 4GiB"))?,
7819                );
7820            }
7821            out.extend_from_slice(&bytes);
7822        }
7823        _ => return Err(Error::not_implemented(format!("native page for {ty}"))),
7824    }
7825    Ok(out)
7826}
7827
7828fn put_varint(out: &mut Vec<u8>, mut value: u32) {
7829    while value >= 0x80 {
7830        out.push((value as u8 & 0x7f) | 0x80);
7831        value >>= 7;
7832    }
7833    out.push(value as u8);
7834}
7835
7836/// The distinct codes of one part, which is what a stripe's membership index is merged from.
7837fn unique_codes(codes: &[u32]) -> Vec<u32> {
7838    let mut unique = codes.to_vec();
7839    unique.sort_unstable();
7840    unique.dedup();
7841    unique
7842}
7843
7844/// The union of the sorted distinct codes of every part in a stripe.
7845///
7846/// Pairwise up a tree rather than one long list concatenated and sorted. Both are the same order of
7847/// work on paper and the tree is the one that does not sort what is already in order: sixty four
7848/// sorted lists become one in six passes over the values.
7849fn merged_codes(lists: Vec<Vec<u32>>) -> Vec<u32> {
7850    let mut lists = lists;
7851    while lists.len() > 1 {
7852        let mut next = Vec::with_capacity(lists.len().div_ceil(2));
7853        for pair in lists.chunks(2) {
7854            match pair {
7855                [left, right] => next.push(merged_pair(left, right)),
7856                [only] => next.push(only.clone()),
7857                _ => {}
7858            }
7859        }
7860        lists = next;
7861    }
7862    lists.pop().unwrap_or_default()
7863}
7864
7865fn merged_pair(left: &[u32], right: &[u32]) -> Vec<u32> {
7866    let mut out = Vec::with_capacity(left.len().saturating_add(right.len()));
7867    let mut at = 0;
7868    let mut to = 0;
7869    while at < left.len() && to < right.len() {
7870        match left[at].cmp(&right[to]) {
7871            Ordering::Less => {
7872                out.push(left[at]);
7873                at += 1;
7874            }
7875            Ordering::Greater => {
7876                out.push(right[to]);
7877                to += 1;
7878            }
7879            Ordering::Equal => {
7880                out.push(left[at]);
7881                at += 1;
7882                to += 1;
7883            }
7884        }
7885    }
7886    out.extend_from_slice(&left[at..]);
7887    out.extend_from_slice(&right[to..]);
7888    out
7889}
7890
7891/// The widest bounds and the total null count of a stripe, from the bounds of its parts.
7892///
7893/// A bound that is missing from any part is missing from the stripe, because a missing bound means
7894/// nothing is known and a stripe that holds an unknown cannot claim one.
7895fn merged_range(ranges: impl Iterator<Item = Range>) -> Range {
7896    let mut merged = Range::default();
7897    let mut first = true;
7898    for range in ranges {
7899        merged.nulls = merged.nulls.saturating_add(range.nulls);
7900        // Both of these have to survive every part, so one part that could not say anything makes
7901        // the stripe unable to say it either. A sum is dropped on overflow rather than wrapped,
7902        // which leaves the stripe with exact ends and no total, which is a true thing to say.
7903        merged.sum = match (merged.sum.take(), range.sum) {
7904            (Some(held), Some(next)) if !first => held.checked_add(next),
7905            (_, next) if first => next,
7906            _ => None,
7907        };
7908        merged.exact = if first { range.exact } else { merged.exact && range.exact };
7909        if first {
7910            merged.low = range.low;
7911            merged.high = range.high;
7912            first = false;
7913            continue;
7914        }
7915        merged.low = match (merged.low.take(), range.low) {
7916            (Some(held), Some(next)) => Some(held.smaller(next)),
7917            _ => None,
7918        };
7919        merged.high = match (merged.high.take(), range.high) {
7920            (Some(held), Some(next)) => Some(held.larger(next)),
7921            _ => None,
7922        };
7923    }
7924    merged
7925}
7926
7927/// One stripe's sieves for one column: the part count, a length for each part, then their bytes.
7928///
7929/// One page for the whole stripe rather than one per part, because a part's sieve is a few hundred
7930/// bytes and sixty four of those are sixty four directory entries and sixty four reads for something
7931/// a scan walks straight through. A part with no sieve writes a length of zero and costs four bytes.
7932/// `bound` cut down to [`PART_BOUND_BYTES`], still a bound of the side it was.
7933///
7934/// A prefix of a string sorts at or before the string, so cutting one down leaves a low end that is
7935/// still a low end. A high end has to go the other way, so the cut prefix is stepped up at the last
7936/// byte that can carry it, and a prefix of nothing but `0xFF` has no such byte and gives up the
7937/// bound rather than claiming one that is too small. Anything that is not a string is already a
7938/// fixed width and is left alone.
7939fn shortened(bound: Option<Bound>, high: bool) -> Option<Bound> {
7940    match bound {
7941        Some(Bound::Bytes(mut value)) if value.len() > PART_BOUND_BYTES => {
7942            value.truncate(PART_BOUND_BYTES);
7943            if !high {
7944                return Some(Bound::Bytes(value));
7945            }
7946            while let Some(last) = value.pop() {
7947                if last < u8::MAX {
7948                    value.push(last + 1);
7949                    return Some(Bound::Bytes(value));
7950                }
7951            }
7952            None
7953        }
7954        other => other,
7955    }
7956}
7957
7958/// The ranges of one column's parts of one stripe, as a page.
7959///
7960/// The two ends and the null count, and not `exact` or the total. Those two answer a `MIN` or a
7961/// `SUM` out of the directory, and the directory already answers those per stripe, where the same
7962/// number costs sixty times less to keep. What a part range is for is skipping the part, and
7963/// skipping needs the ends. So a range read back from here says it is not exact, which is true of a
7964/// string end that was cut down anyway.
7965fn encode_part_ranges(ranges: &[Range]) -> Result<Vec<u8>> {
7966    let mut out = Vec::new();
7967    put_u32(
7968        &mut out,
7969        u32::try_from(ranges.len()).map_err(|_| invalid("too many parts in a stripe"))?,
7970    );
7971    for range in ranges {
7972        put_bound(&mut out, shortened(range.low.clone(), false).as_ref())?;
7973        put_bound(&mut out, shortened(range.high.clone(), true).as_ref())?;
7974        put_u32(&mut out, u32::try_from(range.nulls).map_err(|_| invalid("null count overflow"))?);
7975    }
7976    Ok(out)
7977}
7978
7979/// The ranges one encoded page holds, one entry per part of the stripe.
7980fn decode_part_ranges(bytes: &[u8]) -> Result<Vec<Range>> {
7981    let mut cur = Cursor::new(bytes);
7982    let parts = cur.u32()? as usize;
7983    let mut out = Vec::new();
7984    for _ in 0..parts {
7985        let low = cur.bound()?;
7986        let high = cur.bound()?;
7987        let nulls = cur.u32()? as usize;
7988        out.push(Range { low, high, nulls, exact: false, sum: None });
7989    }
7990    Ok(out)
7991}
7992
7993fn encode_sieves<'a>(sieves: impl Iterator<Item = &'a Option<Sieve>>) -> Result<Vec<u8>> {
7994    let held: Vec<&Option<Sieve>> = sieves.collect();
7995    let mut out = Vec::new();
7996    put_u32(
7997        &mut out,
7998        u32::try_from(held.len()).map_err(|_| invalid("too many parts in a stripe"))?,
7999    );
8000    for sieve in &held {
8001        let length = sieve.as_ref().map_or(0, Sieve::len);
8002        put_u32(&mut out, u32::try_from(length).map_err(|_| invalid("sieve length overflow"))?);
8003    }
8004    // flatten: a part with no sieve wrote a length of zero above and contributes no bytes here.
8005    for sieve in held.into_iter().flatten() {
8006        out.extend_from_slice(&sieve.to_bytes());
8007    }
8008    Ok(out)
8009}
8010
8011/// The sieves one encoded page holds, one entry per part of the stripe.
8012///
8013/// A part whose bytes are not a sieve this version understands comes back as `None`, which is a part
8014/// that gets read. That is how a file written by a later version of the sieve stays readable rather
8015/// than being a corrupt page.
8016fn decode_sieves(bytes: &[u8]) -> Result<Vec<Option<Sieve>>> {
8017    let parts = u32::from_le_bytes(
8018        bytes
8019            .get(..4)
8020            .ok_or_else(|| invalid("sieve page is truncated"))?
8021            .try_into()
8022            .map_err(|_| invalid("sieve page is truncated"))?,
8023    ) as usize;
8024    let mut lengths = Vec::with_capacity(parts);
8025    for part in 0..parts {
8026        let at = 4 + part * 4;
8027        let field = bytes.get(at..at + 4).ok_or_else(|| invalid("sieve page is truncated"))?;
8028        lengths.push(u32::from_le_bytes(
8029            field.try_into().map_err(|_| invalid("sieve page is truncated"))?,
8030        ) as usize);
8031    }
8032    let mut at = 4 + parts * 4;
8033    let mut out = Vec::with_capacity(parts);
8034    for length in lengths {
8035        if length == 0 {
8036            out.push(None);
8037            continue;
8038        }
8039        let end = at.checked_add(length).ok_or_else(|| invalid("sieve page is truncated"))?;
8040        let field = bytes.get(at..end).ok_or_else(|| invalid("sieve page is truncated"))?;
8041        out.push(Sieve::from_bytes(field));
8042        at = end;
8043    }
8044    if at != bytes.len() {
8045        return Err(invalid("sieve page has trailing bytes"));
8046    }
8047    Ok(out)
8048}
8049
8050/// One stripe's membership index: the code count and then the codes as ascending deltas.
8051///
8052/// The codes have to be sorted and distinct already, which is what [`unique_codes`] and
8053/// [`merged_codes`] hand over. Anything else decodes as different codes, so neither of those two is
8054/// a step a caller can skip.
8055fn encode_membership(unique: &[u32]) -> Vec<u8> {
8056    let mut out = Vec::with_capacity(unique.len().saturating_mul(2).saturating_add(5));
8057    put_varint(&mut out, u32::try_from(unique.len()).unwrap_or(u32::MAX));
8058    let mut previous = 0;
8059    for (at, &code) in unique.iter().enumerate() {
8060        put_varint(&mut out, if at == 0 { code } else { code - previous });
8061        previous = code;
8062    }
8063    out
8064}
8065
8066fn take_varint(bytes: &[u8], at: &mut usize) -> Result<u32> {
8067    let mut value = 0_u32;
8068    for shift in (0..35).step_by(7) {
8069        let byte = *bytes.get(*at).ok_or_else(|| invalid("membership varint is truncated"))?;
8070        *at += 1;
8071        let part = u32::from(byte & 0x7f);
8072        if shift == 28 && part > 0x0f {
8073            return Err(invalid("membership varint overflow"));
8074        }
8075        value = value
8076            .checked_add(
8077                part.checked_shl(shift).ok_or_else(|| invalid("membership varint overflow"))?,
8078            )
8079            .ok_or_else(|| invalid("membership varint overflow"))?;
8080        if byte & 0x80 == 0 {
8081            return Ok(value);
8082        }
8083    }
8084    Err(invalid("membership varint is too long"))
8085}
8086
8087fn decode_membership(bytes: &[u8]) -> Result<Vec<u32>> {
8088    let mut at = 0;
8089    let count = take_varint(bytes, &mut at)? as usize;
8090    let mut codes = Vec::with_capacity(count);
8091    let mut previous = 0_u32;
8092    for index in 0..count {
8093        let delta = take_varint(bytes, &mut at)?;
8094        let code = if index == 0 {
8095            delta
8096        } else {
8097            previous.checked_add(delta).ok_or_else(|| invalid("membership code overflow"))?
8098        };
8099        if index > 0 && code <= previous {
8100            return Err(invalid("membership codes are not increasing"));
8101        }
8102        codes.push(code);
8103        previous = code;
8104    }
8105    if at != bytes.len() {
8106        return Err(invalid("membership page has trailing bytes"));
8107    }
8108    Ok(codes)
8109}
8110
8111fn string_dictionary(vector: &Vector) -> Result<Option<Vec<u8>>> {
8112    let mut by_text = HashMap::new();
8113    let mut values = Vec::new();
8114    let mut codes = Vec::with_capacity(vector.len());
8115    let mut plain_bytes = 0_usize;
8116    for row in 0..vector.len() {
8117        let text = vector.text_at(row).unwrap_or("");
8118        plain_bytes = plain_bytes.saturating_add(text.len());
8119        let code = match by_text.get(text) {
8120            Some(&code) => code,
8121            None => {
8122                let code = u32::try_from(values.len())
8123                    .map_err(|_| invalid("too many dictionary values"))?;
8124                by_text.insert(text, code);
8125                values.push(text);
8126                code
8127            }
8128        };
8129        codes.push(code);
8130    }
8131    let dictionary_bytes = values.iter().map(|value| value.len()).sum::<usize>();
8132    let encoded = 8_usize
8133        .saturating_add((values.len() + 1).saturating_mul(4))
8134        .saturating_add(dictionary_bytes)
8135        .saturating_add(codes.len().saturating_mul(4));
8136    let plain = (vector.len() + 1).saturating_mul(4).saturating_add(plain_bytes);
8137    if encoded >= plain {
8138        return Ok(None);
8139    }
8140    let mut out = Vec::with_capacity(encoded);
8141    put_u32(
8142        &mut out,
8143        u32::try_from(values.len()).map_err(|_| invalid("too many dictionary values"))?,
8144    );
8145    put_u32(
8146        &mut out,
8147        u32::try_from(dictionary_bytes).map_err(|_| invalid("dictionary payload exceeds 4GiB"))?,
8148    );
8149    let mut offset = 0_u32;
8150    put_u32(&mut out, offset);
8151    for value in &values {
8152        offset = offset
8153            .checked_add(
8154                u32::try_from(value.len()).map_err(|_| invalid("dictionary value is too long"))?,
8155            )
8156            .ok_or_else(|| invalid("dictionary payload exceeds 4GiB"))?;
8157        put_u32(&mut out, offset);
8158    }
8159    for value in values {
8160        out.extend_from_slice(value.as_bytes());
8161    }
8162    for code in codes {
8163        put_u32(&mut out, code);
8164    }
8165    Ok(Some(out))
8166}
8167
8168struct EncodedDictionary {
8169    index: Vec<u8>,
8170    ranks: Vec<u8>,
8171    grams: Vec<u8>,
8172}
8173
8174/// Sorts codes into the byte order of the values they name, eight bytes of depth at a time.
8175///
8176/// # What the shape of the data does to a comparison sort
8177///
8178/// Distinct values against distinct prefixes, on the eight million row `hits`:
8179///
8180/// ```text
8181///   distinct   first 8   first 16   first 32   column
8182///  2,266,417        50      8,892    232,630   URL
8183///  2,346,025        49      8,534    204,060   Referer
8184///  1,357,764    81,362    348,340    861,579   Title
8185/// ```
8186///
8187/// Two and a quarter million URLs have fifty distinct first eight bytes between them, because they
8188/// all begin `http://` and then a host and there are not many hosts. So a sort that leads with
8189/// those eight bytes settles almost nothing on `URL` and `Referer`, whatever the comment on it used
8190/// to say, and almost every pair falls through to a comparison of whole values that agree for most
8191/// of their length. `Title` is free text and separates at eight bytes, which is why the design
8192/// looked right when it was written.
8193///
8194/// # What is done about it
8195///
8196/// Sort on eight bytes of the value at the current depth, held beside the code, and then take each
8197/// run that those eight bytes leave tied and sort it again on the next eight. A value is fetched
8198/// from the payload once per eight bytes of depth rather than once per comparison, and the sort
8199/// itself runs over an array of integers that is in cache rather than over pointers into a payload
8200/// that is hundreds of megabytes.
8201///
8202/// That is the whole trick, and it matters because the payload touch is the expensive part. The
8203/// bytes themselves are nearly free once the line is in cache, so reading eight at a time and
8204/// throwing away the ones that were not needed beats going back for each one.
8205///
8206/// # Why the length has to be carried
8207///
8208/// The eight bytes are padded with zero when the value has fewer than eight left, and a zero byte
8209/// can appear in a value, so equal keys do not mean equal bytes. What is true is that a value which
8210/// ran out inside the window is a prefix of any other value with the same key, and a prefix sorts
8211/// first, so how many of the eight bytes were real is the tie break and nothing further is needed.
8212/// A run is only worth another pass when all eight were real, because otherwise the run is one
8213/// value: a dictionary holds a value once.
8214fn sort_by_value<'a>(codes: &mut [u32], values: impl Fn(u32) -> &'a [u8]) {
8215    let mut work = vec![(0, codes.len(), 0)];
8216    let mut keyed: Vec<(u64, u8, u32)> = Vec::new();
8217    while let Some((from, to, depth)) = work.pop() {
8218        let part = &mut codes[from..to];
8219        keyed.clear();
8220        keyed.extend(part.iter().map(|&code| {
8221            let value = values(code);
8222            let rest = value.get(depth..).unwrap_or_default();
8223            (head(rest), rest.len().min(8) as u8, code)
8224        }));
8225        keyed.sort_unstable();
8226        for (slot, entry) in part.iter_mut().zip(keyed.iter()) {
8227            *slot = entry.2;
8228        }
8229        let mut start = 0;
8230        while start < keyed.len() {
8231            let (key, taken, _) = keyed[start];
8232            let mut end = start + 1;
8233            while end < keyed.len() && keyed[end].0 == key && keyed[end].1 == taken {
8234                end += 1;
8235            }
8236            if taken == 8 && end - start > 1 {
8237                work.push((from + start, from + end, depth + 8));
8238            }
8239            start = end;
8240        }
8241    }
8242}
8243
8244/// How few codes are worth sorting on more than one thread.
8245const PARALLEL_SORT_MIN: usize = 1 << 16;
8246
8247/// How many buckets a thread gets in [`sort_by_value_across`], so that a thread that drew a slow
8248/// bucket is not what the others wait for.
8249const BUCKETS_PER_WORKER: usize = 4;
8250
8251/// How many sampled codes stand for each bucket when the splitters are picked.
8252const SAMPLES_PER_BUCKET: usize = 32;
8253
8254/// [`sort_by_value`] over `workers` threads, with the same answer.
8255///
8256/// A sample sort. A sample of the codes is sorted and cut into as many equal runs as there are
8257/// buckets, and the values at the cuts are the splitters. Every code goes to the bucket its value
8258/// falls in by a binary search of the splitters, the buckets are laid end to end in splitter order,
8259/// and each bucket is then sorted on its own by whichever thread takes it. Every value in a bucket
8260/// sorts after every value in the bucket before, so the buckets sorted one by one are the codes
8261/// sorted.
8262///
8263/// The answer is the one [`sort_by_value`] gives down to the order of equal values, not only the
8264/// order of different ones. A global dictionary holds each value once, so there are none, but the
8265/// sort does not rely on it: equal values land in the same bucket in code order, which is the order
8266/// [`sort_by_value`] leaves them in, since the code is the last thing it sorts on.
8267///
8268/// On the 10m ClickBench sample the close sorts five columns of one to three and a half million
8269/// distinct values, one column at a time, and until this each sort ran on one thread while the
8270/// other thirty one waited for it.
8271fn sort_by_value_across<'a>(
8272    codes: &mut [u32],
8273    values: impl Fn(u32) -> &'a [u8] + Sync,
8274    workers: usize,
8275) {
8276    if workers <= 1 || codes.len() < PARALLEL_SORT_MIN {
8277        sort_by_value(codes, values);
8278        return;
8279    }
8280    let buckets = workers * BUCKETS_PER_WORKER;
8281    let wanted = buckets * SAMPLES_PER_BUCKET;
8282    let mut sample = (0..wanted).map(|at| codes[at * codes.len() / wanted]).collect::<Vec<_>>();
8283    sort_by_value(&mut sample, &values);
8284    let splitters =
8285        (1..buckets).map(|cut| values(sample[cut * sample.len() / buckets])).collect::<Vec<_>>();
8286    let values = &values;
8287    let splitters = &splitters;
8288    let per = codes.len().div_ceil(workers);
8289    // Which bucket each code goes to, a run of the codes per thread.
8290    let places = std::thread::scope(|scope| {
8291        codes
8292            .chunks(per)
8293            .map(|run| {
8294                scope.spawn(move || {
8295                    run.iter()
8296                        .map(|&code| {
8297                            let value = values(code);
8298                            splitters.partition_point(|splitter| *splitter <= value) as u32
8299                        })
8300                        .collect::<Vec<_>>()
8301                })
8302            })
8303            .collect::<Vec<_>>()
8304            .into_iter()
8305            .flat_map(|handle| {
8306                handle.join().unwrap_or_else(|panic| std::panic::resume_unwind(panic))
8307            })
8308            .collect::<Vec<_>>()
8309    });
8310    let mut starts = vec![0_usize; buckets + 1];
8311    for &place in &places {
8312        starts[place as usize + 1] += 1;
8313    }
8314    for bucket in 0..buckets {
8315        starts[bucket + 1] += starts[bucket];
8316    }
8317    let mut laid = vec![0_u32; codes.len()];
8318    let mut next = starts.clone();
8319    for (&code, &place) in codes.iter().zip(&places) {
8320        laid[next[place as usize]] = code;
8321        next[place as usize] += 1;
8322    }
8323    drop(places);
8324    let mut runs = Vec::with_capacity(buckets);
8325    let mut rest = laid.as_mut_slice();
8326    for bucket in 0..buckets {
8327        let (run, after) = rest.split_at_mut(starts[bucket + 1] - starts[bucket]);
8328        runs.push(run);
8329        rest = after;
8330    }
8331    // The largest buckets first, since they are taken from the back.
8332    runs.sort_by_key(|run| run.len());
8333    let queue = Mutex::new(runs);
8334    std::thread::scope(|scope| {
8335        for _ in 0..workers {
8336            scope.spawn(|| {
8337                loop {
8338                    let taken =
8339                        queue.lock().unwrap_or_else(std::sync::PoisonError::into_inner).pop();
8340                    let Some(run) = taken else { break };
8341                    sort_by_value(run, values);
8342                }
8343            });
8344        }
8345    });
8346    codes.copy_from_slice(&laid);
8347}
8348
8349/// The first eight bytes of a value as an integer that sorts the way the bytes sort.
8350fn head(bytes: &[u8]) -> u64 {
8351    let mut word = [0; 8];
8352    let take = bytes.len().min(8);
8353    word[..take].copy_from_slice(&bytes[..take]);
8354    u64::from_be_bytes(word)
8355}
8356
8357/// One column's dictionary page, which is its index and its sorted order.
8358///
8359/// The payload is not in it. Its blocks are in the file already, written as each was encoded, and
8360/// `places` says where, in block order. With `scattered` set the index records each block's start
8361/// and length, so a reader can find one wherever it went.
8362///
8363/// `scattered` false lays the blocks out the way a file written before format 26 has them, one
8364/// behind the next with only the ends recorded. Nothing in the writer asks for that any more. It is
8365/// kept because [`open_global_dictionary`] still reads those files and a reading path that nothing
8366/// can produce is a reading path nothing tests.
8367fn encode_global_dictionary(
8368    dictionary: &GlobalDictionary,
8369    order: &[(u64, u32)],
8370    places: &[Placed],
8371    scattered: bool,
8372) -> Result<EncodedDictionary> {
8373    let values = dictionary.values();
8374    if order.len() != values {
8375        return Err(invalid("global dictionary order does not cover its values"));
8376    }
8377    let blocks = values.div_ceil(TEXT_PAYLOAD_VALUES);
8378    if places.len() != blocks {
8379        return Err(invalid("global dictionary payload is not the blocks it says it is"));
8380    }
8381    if dictionary.grams.len() != blocks {
8382        return Err(invalid("global dictionary signatures do not cover its blocks"));
8383    }
8384    let (ranks, rank_ends) = encode_ranks(order, code_width(values))?;
8385    let rank_blocks = values.div_ceil(TEXT_RANK_BLOCK);
8386    let offset_bits = offset_width(&dictionary.ends);
8387    let payload_words = if scattered { 3 } else { 2 };
8388    let index_len = DICTIONARY_HEADER
8389        .checked_add(offset_bytes(values, offset_bits))
8390        .and_then(|len| len.checked_add(blocks.checked_mul(payload_words * 8)?))
8391        .and_then(|len| len.checked_add(rank_blocks.checked_mul(16)?))
8392        .and_then(|len| len.checked_add(8))
8393        .ok_or_else(|| invalid("global dictionary index length overflow"))?;
8394    let mut index = Vec::with_capacity(index_len);
8395    put_u32(
8396        &mut index,
8397        u32::try_from(values).map_err(|_| invalid("global dictionary has too many values"))?,
8398    );
8399    put_u32(&mut index, TEXT_PAYLOAD_VALUES as u32);
8400    put_u32(
8401        &mut index,
8402        u32::try_from(blocks).map_err(|_| invalid("global dictionary has too many blocks"))?,
8403    );
8404    let flag = (if scattered { DICTIONARY_SCATTERED } else { 0 }) | DICTIONARY_GRAMS;
8405    put_u32(&mut index, offset_bits as u32 | flag);
8406    encode_offsets(&dictionary.ends, offset_bits, &mut index)?;
8407    // Where each block is and how long it is, so a reader can find one. The stored blocks are
8408    // shorter than the decoded ones and by a different amount each, so their lengths are the one
8409    // thing the offsets above no longer say, and where they start is no longer arithmetic on the
8410    // block before once a block is written the moment it is encoded.
8411    let mut end = 0_u64;
8412    for place in places {
8413        if scattered {
8414            put_u64(&mut index, place.start);
8415            put_u64(&mut index, place.length);
8416        } else {
8417            end = end
8418                .checked_add(place.length)
8419                .ok_or_else(|| invalid("global dictionary payload overflow"))?;
8420            put_u64(&mut index, end);
8421        }
8422    }
8423    for place in places {
8424        put_u64(&mut index, place.hash);
8425    }
8426    // The same two lists for the sorted order. A rank block is packed at whatever width its own
8427    // heads need, so where one ends is no longer arithmetic on the block number.
8428    if rank_ends.len() != rank_blocks {
8429        return Err(invalid("global dictionary order is not the blocks it says it is"));
8430    }
8431    for end in &rank_ends {
8432        put_u64(&mut index, *end);
8433    }
8434    let mut at = 0_usize;
8435    for end in &rank_ends {
8436        let end = usize::try_from(*end).map_err(|_| invalid("global dictionary order overflow"))?;
8437        put_u64(&mut index, checksum(&ranks[at..end]));
8438        at = end;
8439    }
8440    let gram_len = blocks
8441        .checked_mul(TEXT_GRAM_BYTES)
8442        .ok_or_else(|| invalid("global dictionary signature count overflow"))?;
8443    let mut grams = Vec::with_capacity(gram_len);
8444    for block in &dictionary.grams {
8445        grams.extend_from_slice(block);
8446    }
8447    put_u64(&mut index, checksum(&grams));
8448    if index.len() != index_len {
8449        return Err(invalid("global dictionary index is not the length it was laid out for"));
8450    }
8451    Ok(EncodedDictionary { index, ranks, grams })
8452}
8453
8454/// How many blocks of the payload the shape is settled on.
8455///
8456/// Eight blocks is 8,192 values, which is the sample `chooser::Sampled` draws and is that size for
8457/// the same reason. They are spread across the dictionary rather than taken off the front, because
8458/// a dictionary is in the order values were first seen and the front of it is the first morsel of
8459/// the load.
8460const PAYLOAD_SAMPLE_BLOCKS: usize = 8;
8461
8462/// The shapes the payload encoder picks between.
8463///
8464/// Narrow on purpose. The exhaustive search encodes every candidate at every level and runs at two
8465/// to six megabytes a second on this data, which over the twelve gigabytes of dictionary `hits`
8466/// carries is about an hour of processor time, so it cannot be what a load does. Each of these
8467/// settles the outer level and the one below it, which is where almost all of that hour goes, and
8468/// leaves the levels under them to the exhaustive search where the chunks are small enough for it
8469/// to cost nothing.
8470///
8471/// Measured on the five ClickBench columns that have a dictionary worth the name, at 1,024 values a
8472/// block, against the exhaustive search over the same blocks:
8473///
8474/// | column | exhaustive | FRONT then LZ | LZ then FSST | LZ then PLAIN |
8475/// |---|---|---|---|---|
8476/// | 2 | 2.923 at 4.3 MB/s | 2.587 at 21.2 | 2.593 at 36.1 | 2.538 at 53.6 |
8477/// | 13 | 3.093 at 3.1 | 3.029 at 36.4 | 2.921 at 35.7 | 2.770 at 82.9 |
8478/// | 14 | 2.330 at 2.1 | 2.283 at 24.3 | 2.213 at 23.5 | 2.113 at 67.6 |
8479/// | 39 | 2.459 at 5.3 | 2.147 at 10.6 | 2.145 at 29.3 | 2.088 at 43.1 |
8480/// | 56 | 4.694 at 6.3 | 4.381 at 51.0 | 4.172 at 50.6 | 3.983 at 86.8 |
8481///
8482/// The best of the three per column is 98 percent of the exhaustive ratio for a tenth of the time.
8483/// `FSST` and `PLAIN` on their own are in the list as a floor rather than to win. `FSST` is the
8484/// right answer for text that does not share prefixes with its neighbours, and `PLAIN` is there so
8485/// that a column nothing compresses is found out in the sample and written at a gigabyte a second
8486/// rather than searched for an answer that does not exist.
8487fn payload_shapes() -> Vec<chooser::Settled> {
8488    let integers = vec![integer::Kind::Packed];
8489    [
8490        vec![string::Kind::Front, string::Kind::Lz],
8491        vec![string::Kind::Lz, string::Kind::Fsst],
8492        vec![string::Kind::Lz, string::Kind::Plain],
8493        vec![string::Kind::Fsst],
8494        vec![string::Kind::Plain],
8495    ]
8496    .into_iter()
8497    .map(|strings| chooser::Settled::new(strings, integers.clone()))
8498    .collect()
8499}
8500
8501/// Encodes every payload block that filled during the stripe just written, across threads.
8502///
8503/// This is where the load's dictionary work happens, and where it happens matters more than it
8504/// looks. It used to happen in [`Writer::close`], over every block of a column at once, which meant
8505/// the raw bytes of every block had to still exist when the load ended. Doing it a block at a time
8506/// inside the stripe encode instead was measured at 2.2 times the wall clock for the same user time:
8507/// a stripe is a barrier the parquet reader waits on, one column's blocks are one thread, and two of
8508/// `hits`'s five dictionary columns hold most of the distinct values, so the whole load ran at less
8509/// than one core.
8510///
8511/// Here is neither. The stripe's columns have all been encoded and handed back by the time this
8512/// runs, so every waiting block of every column is one flat queue and it fans out over all of them
8513/// the way [`Writer::close`] used to fan out over one column's. The work and the parallelism are
8514/// what they were. It happens sixty times during the load rather than once at the end of it, and the
8515/// raw bytes go as it goes.
8516/// Syncs the file, and counts the sync and how long it took as a publish wait when a load is being
8517/// profiled.
8518///
8519/// A wait rather than time, because the time is already in the publish span around it. What the
8520/// wait columns add is how much of publish was the device, which on the WSL2 disk of the gaming PC
8521/// is most of it: a sync there costs about two milliseconds (see `rudb_device_card`).
8522fn synced(file: &File, profile: Option<&LoadProfile>) -> Result<()> {
8523    let started = profile.map(|_| std::time::Instant::now());
8524    file.sync_all().map_err(io)?;
8525    if let (Some(profile), Some(started)) = (profile, started) {
8526        profile.waited(
8527            Stage::Publish,
8528            u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX),
8529        );
8530    }
8531    Ok(())
8532}
8533
8534fn encode_ready(dictionaries: &mut [Option<GlobalDictionary>]) -> Result<()> {
8535    for dictionary in dictionaries.iter_mut().flatten() {
8536        dictionary.settle()?;
8537    }
8538    // A column with no shape yet is a column with fewer blocks than the sample wants, so its blocks
8539    // wait. There are at most `PAYLOAD_SAMPLE_BLOCKS` of them and they are about to be encoded one
8540    // way or the other, and encoding them now would be encoding them without having looked at the
8541    // column.
8542    encode_waiting(dictionaries, false)
8543}
8544
8545/// Encodes every block still raw at the end of a load: the part block each column ends on and,
8546/// for a column too small to have settled a shape, every block it has.
8547///
8548/// Across threads, the way [`encode_ready`] does it. This ran one column at a time on the thread
8549/// closing the table, and a column that never settled a shape encodes each block by trying every
8550/// candidate, so on a million rows of `hits` it was most of the load's CPU on one core.
8551fn finish_dictionaries(dictionaries: &mut [Option<GlobalDictionary>]) -> Result<()> {
8552    for dictionary in dictionaries.iter_mut().flatten() {
8553        dictionary.seal_rest();
8554    }
8555    encode_waiting(dictionaries, true)
8556}
8557
8558/// Encodes the waiting blocks of every dictionary with a shape, or of every dictionary when
8559/// `closing`, across threads, and appends them to their columns in order.
8560fn encode_waiting(dictionaries: &mut [Option<GlobalDictionary>], closing: bool) -> Result<()> {
8561    let jobs = dictionaries
8562        .iter()
8563        .enumerate()
8564        .filter(|(_, held)| held.as_ref().is_some_and(|held| closing || held.shape.is_some()))
8565        .flat_map(|(column, held)| {
8566            (0..held.as_ref().map_or(0, |held| held.waiting.len())).map(move |at| (column, at))
8567        })
8568        .collect::<Vec<_>>();
8569    if jobs.is_empty() {
8570        return Ok(());
8571    }
8572    let one = |column: usize, at: usize| -> Result<(usize, usize, Vec<u8>)> {
8573        let held = dictionaries[column].as_ref().ok_or_else(|| Error::internal("no dictionary"))?;
8574        Ok((column, at, held.encode_waiting(at)?))
8575    };
8576    let workers = std::thread::available_parallelism()
8577        .map_or(1, usize::from)
8578        .min(MAX_FREQUENCY_WORKERS)
8579        .min(jobs.len());
8580    let made = if workers <= 1 {
8581        jobs.iter().map(|&(column, at)| one(column, at)).collect::<Result<Vec<_>>>()?
8582    } else {
8583        let next = AtomicUsize::new(0);
8584        let jobs = &jobs;
8585        let pieces = std::thread::scope(|scope| {
8586            (0..workers)
8587                .map(|_| {
8588                    scope.spawn(|| {
8589                        let mut mine = Vec::new();
8590                        loop {
8591                            let job = next.fetch_add(1, Atomic::Relaxed);
8592                            let Some(&(column, at)) = jobs.get(job) else { break };
8593                            mine.push(one(column, at)?);
8594                        }
8595                        Ok(mine)
8596                    })
8597                })
8598                .collect::<Vec<_>>()
8599                .into_iter()
8600                .map(|handle| {
8601                    handle
8602                        .join()
8603                        .map_err(|_| Error::internal("a dictionary encode worker panicked"))?
8604                })
8605                .collect::<Result<Vec<_>>>()
8606        })?;
8607        pieces.into_iter().flatten().collect()
8608    };
8609    let mut done: Vec<Vec<(usize, Vec<u8>)>> =
8610        (0..dictionaries.len()).map(|_| Vec::new()).collect();
8611    for (column, at, bytes) in made {
8612        done[column].push((at, bytes));
8613    }
8614    for (column, mut made) in done.into_iter().enumerate() {
8615        if made.is_empty() {
8616            continue;
8617        }
8618        let Some(held) = dictionaries[column].as_mut() else { continue };
8619        made.sort_by_key(|(at, _)| *at);
8620        let waiting = std::mem::take(&mut held.waiting);
8621        for ((block, _), (_, bytes)) in waiting.into_iter().zip(made) {
8622            if held.encoded() != block {
8623                return Err(Error::internal("a dictionary block was encoded out of order"));
8624            }
8625            held.blocks.push(bytes);
8626        }
8627    }
8628    Ok(())
8629}
8630
8631/// Which of [`payload_shapes`] comes out smallest over a sample of the blocks.
8632///
8633/// Every shape is encoded over the same sample and the smallest wins, which is the exhaustive
8634/// search moved up a level: over shapes of a column rather than over candidates of a chunk. The
8635/// sample is spread across the dictionary so that the first and last blocks are both in it, because
8636/// a dictionary written in first seen order has its common values at the front and its long tail at
8637/// the back, and those do not compress alike. Which blocks those are is
8638/// [`GlobalDictionary::seal`]'s to decide, because by the time this is called the rest of them have
8639/// been encoded and the raw bytes are gone.
8640fn settle_shape(sample: &[Vec<&[u8]>]) -> Result<chooser::Settled> {
8641    let mut best: Option<(chooser::Settled, usize)> = None;
8642    for shape in payload_shapes() {
8643        let mut size = 0;
8644        for block in sample {
8645            size += string::encode_with(block, &shape)?.len();
8646        }
8647        if best.as_ref().is_none_or(|(_, smallest)| size < *smallest) {
8648            best = Some((shape, size));
8649        }
8650    }
8651    best.map(|(shape, _)| shape)
8652        .ok_or_else(|| invalid("no shape applies to a global dictionary payload"))
8653}
8654
8655/// The sorted order laid out the way a reader reads it, in blocks of [`TEXT_RANK_BLOCK`] entries.
8656///
8657/// Each block holds its heads first and then its codes, rather than pairing them, because a search
8658/// asks for a head at every probe and for a code about once a search. Keeping the heads together
8659/// means a probe touches eight bytes of a block rather than twelve spread over it, and the last few
8660/// probes of a search, which are the ones that land in the same block, touch the same cache line.
8661fn encode_ranks(order: &[(u64, u32)], code_bits: usize) -> Result<(Vec<u8>, Vec<u64>)> {
8662    let mut out = Vec::with_capacity(order.len() * 4);
8663    let mut ends = Vec::with_capacity(order.len().div_ceil(TEXT_RANK_BLOCK));
8664    let mut heads = Vec::with_capacity(TEXT_RANK_BLOCK);
8665    let mut codes = Vec::with_capacity(TEXT_RANK_BLOCK);
8666    for block in order.chunks(TEXT_RANK_BLOCK) {
8667        // The order is sorted by value and a head is a prefix of a value, so the heads of a block
8668        // rise, the smallest is the first and the largest is the last.
8669        let base = block.first().map_or(0, |&(head, _)| head);
8670        let span = block.last().map_or(0, |&(head, _)| head.wrapping_sub(base));
8671        let width = (u64::BITS - span.leading_zeros()) as usize;
8672        heads.clear();
8673        codes.clear();
8674        for &(head, code) in block {
8675            heads.push(head.wrapping_sub(base));
8676            codes.push(u64::from(code));
8677        }
8678        put_u64(&mut out, base);
8679        out.push(width as u8);
8680        bitpack::pack_tail(&heads, width, &mut out)
8681            .map_err(|_| invalid("global dictionary heads do not pack"))?;
8682        bitpack::pack_tail(&codes, code_bits, &mut out)
8683            .map_err(|_| invalid("global dictionary codes do not pack"))?;
8684        ends.push(out.len() as u64);
8685    }
8686    Ok((out, ends))
8687}
8688
8689/// Opens a column's global dictionary, which reads its index and none of its payload.
8690///
8691/// `keep_budget` is how many decoded payload bytes this dictionary may hold on to, and every
8692/// caller bar the test of the ceiling passes [`TEXT_KEEP_BUDGET`]. It is a parameter rather than
8693/// the constant read where it is used because a test of a ceiling that cannot be moved has to build
8694/// a quarter of a gigabyte of dictionary to reach it.
8695fn open_global_dictionary(
8696    file: Arc<File>,
8697    page: Page,
8698    ty: &LogicalType,
8699    keep_budget: usize,
8700) -> Result<Vector> {
8701    if ty != &LogicalType::Varchar {
8702        return Err(invalid("global dictionary belongs to a non-string column"));
8703    }
8704    let mut header = [0; DICTIONARY_HEADER];
8705    read_at(&file, page.offset, &mut header)?;
8706    let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
8707    let per_block = u32::from_le_bytes(header[4..8].try_into().expect("four bytes")) as usize;
8708    let blocks = u32::from_le_bytes(header[8..12].try_into().expect("four bytes")) as usize;
8709    let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
8710    let scattered = width & DICTIONARY_SCATTERED != 0;
8711    let has_grams = width & DICTIONARY_GRAMS != 0;
8712    let offset_bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
8713    if per_block != TEXT_PAYLOAD_VALUES {
8714        return Err(invalid("global dictionary block width differs"));
8715    }
8716    if blocks != count.div_ceil(TEXT_PAYLOAD_VALUES) {
8717        return Err(invalid("global dictionary block count differs from its value count"));
8718    }
8719    if offset_bits > u32::BITS as usize {
8720        return Err(invalid("global dictionary packs offsets past a payload"));
8721    }
8722    let offset_len = offset_bytes(count, offset_bits);
8723    // The sorted order is kept out of the index on purpose. The index is read and checksummed in
8724    // full the moment the column is first touched, and the order is half again the size of the
8725    // offsets, so putting it there would make every query that reads a string column pay for a
8726    // search that most of them never make.
8727    let ranks = count;
8728    let rank_blocks = ranks.div_ceil(TEXT_RANK_BLOCK);
8729    // Three words a payload block, for where it starts, how long it is and what it hashes to, or
8730    // two of them on a file that has the blocks back to back and needs no start. Two a rank block
8731    // either way, since those are still one run.
8732    let payload_words = if scattered { 3 } else { 2 };
8733    let hash_len = blocks
8734        .checked_mul(payload_words * 8)
8735        .and_then(|len| len.checked_add(rank_blocks.checked_mul(16)?))
8736        .and_then(|len| len.checked_add(usize::from(has_grams) * 8))
8737        .ok_or_else(|| invalid("global dictionary block count overflow"))?;
8738    let gram_len = if has_grams {
8739        blocks
8740            .checked_mul(TEXT_GRAM_BYTES)
8741            .ok_or_else(|| invalid("global dictionary signature count overflow"))?
8742    } else {
8743        0
8744    };
8745    let index_len = DICTIONARY_HEADER
8746        .checked_add(offset_len)
8747        .and_then(|len| len.checked_add(hash_len))
8748        .ok_or_else(|| invalid("global dictionary header overflow"))?;
8749    if index_len > page.length as usize {
8750        return Err(invalid("global dictionary offset index exceeds its page"));
8751    }
8752    let mut index = vec![0; index_len];
8753    index[..DICTIONARY_HEADER].copy_from_slice(&header);
8754    read_at(&file, page.offset + DICTIONARY_HEADER as u64, &mut index[DICTIONARY_HEADER..])?;
8755    if checksum(&index) != page.hash {
8756        return Err(invalid("global dictionary index checksum differs"));
8757    }
8758    let offsets = index[DICTIONARY_HEADER..DICTIONARY_HEADER + offset_len].to_vec();
8759    let word_end = index_len - usize::from(has_grams) * 8;
8760    let gram_hash = has_grams
8761        .then(|| u64::from_le_bytes(index[word_end..index_len].try_into().expect("eight bytes")));
8762    let mut words = index[DICTIONARY_HEADER + offset_len..word_end]
8763        .chunks_exact(8)
8764        .map(|part| u64::from_le_bytes(part.try_into().expect("eight bytes")))
8765        .collect::<Vec<_>>();
8766    let mut rest = words.split_off(blocks * payload_words);
8767    let rank_hashes = rest.split_off(rank_blocks);
8768    let rank_ends = rest;
8769    // A rank block packs its heads at whatever width its own values need, so its length is no longer
8770    // arithmetic on the block number and the reader has to be told where each one ends.
8771    if rank_ends.windows(2).any(|pair| pair[0] >= pair[1]) {
8772        return Err(invalid("global dictionary order blocks do not rise"));
8773    }
8774    let rank_len = usize::try_from(rank_ends.last().copied().unwrap_or_default())
8775        .map_err(|_| invalid("global dictionary rank overflow"))?;
8776    let body_len = index_len
8777        .checked_add(rank_len)
8778        .ok_or_else(|| invalid("global dictionary header overflow"))?;
8779    if body_len > page.length as usize {
8780        return Err(invalid("global dictionary order exceeds its page"));
8781    }
8782    let gram_end = body_len
8783        .checked_add(gram_len)
8784        .ok_or_else(|| invalid("global dictionary signature length overflow"))?;
8785    if gram_end > page.length as usize {
8786        return Err(invalid("global dictionary signatures exceed their page"));
8787    }
8788    let grams = gram_hash.map(|hash| NativeGrams {
8789        start: page.offset + body_len as u64,
8790        length: gram_len,
8791        hash,
8792        loaded: OnceLock::new(),
8793    });
8794    let hashes = words.split_off(blocks * (payload_words - 1));
8795    let (starts, lengths) = if scattered {
8796        let mut starts = Vec::with_capacity(blocks);
8797        let mut lengths = Vec::with_capacity(blocks);
8798        for pair in words.chunks_exact(2) {
8799            starts.push(pair[0]);
8800            lengths.push(pair[1]);
8801        }
8802        (starts, lengths)
8803    } else {
8804        // A file written before the blocks said where they were has them behind one another at the
8805        // end of the page, so the base is where the sorted order stops and each end is the start of
8806        // the one after it. Turning them round here is what lets everything below take one shape.
8807        let base = page.offset + gram_end as u64;
8808        let mut starts = Vec::with_capacity(blocks);
8809        let mut lengths = Vec::with_capacity(blocks);
8810        let mut at = 0_u64;
8811        for &end in &words {
8812            let len = end
8813                .checked_sub(at)
8814                .ok_or_else(|| invalid("global dictionary block ends before it starts"))?;
8815            starts.push(base + at);
8816            lengths.push(len);
8817            at = end;
8818        }
8819        (starts, lengths)
8820    };
8821    // What the offsets bound is the decoded payload, and what the page length counts is the stored
8822    // one, so on a format 26 file the block lengths adding up to the rest of the page is the one
8823    // thing that ties the index to the page. From format 27 the blocks are written during the load
8824    // and the page is only the index and the order, so there the most that can be said is that
8825    // every block is somewhere in the file past its header.
8826    let stored_len = page.length as u64 - gram_end as u64;
8827    if scattered && stored_len == 0 {
8828        let size = file.metadata().map_err(io)?.len();
8829        let inside = starts.iter().zip(&lengths).all(|(&start, &len)| {
8830            start >= HEADER && start.checked_add(len).is_some_and(|end| end <= size)
8831        });
8832        if !inside {
8833            return Err(invalid("global dictionary block lies outside the file"));
8834        }
8835    } else if lengths.iter().try_fold(0_u64, |sum, len| sum.checked_add(*len)) != Some(stored_len) {
8836        return Err(invalid("global dictionary blocks do not bound the payload"));
8837    }
8838    Vector::external_text(
8839        LogicalType::Varchar,
8840        Arc::new(NativeText {
8841            file,
8842            values: count,
8843            offsets,
8844            offset_bits,
8845            value_ends: OnceLock::new(),
8846            value_lens: OnceLock::new(),
8847            ends_asked: AtomicUsize::new(0),
8848            ranks,
8849            rank_at: page.offset + index_len as u64,
8850            rank_ends,
8851            rank_hashes,
8852            rank_blocks: (0..rank_blocks).map(|_| OnceLock::new()).collect(),
8853            code_bits: code_width(count),
8854            code_ranks: OnceLock::new(),
8855            starts,
8856            lengths,
8857            hashes,
8858            grams,
8859            blocks: (0..blocks).map(|_| OnceLock::new()).collect(),
8860            keep_budget,
8861            payload_kept: AtomicUsize::new(0),
8862            swept: (0..blocks).map(|_| AtomicBool::new(false)).collect(),
8863            searched: Mutex::new(HashMap::new()),
8864        }),
8865    )
8866}
8867
8868/// What a stored page is, without decoding a value out of it.
8869///
8870/// Two layers, and both of them belong in the answer. The codec byte at the front of every page is
8871/// the format's own choice, and it is what says whether the column came back as codes into a table
8872/// wide dictionary, as a bit packed page, as an encoding cascade or as the bytes themselves. Under
8873/// the cascade codecs there is a second choice the encoder made per chunk, and that is what
8874/// [`integer::describe`] and [`string::describe`] already write out as `DICT(PACKED, PACKED)`.
8875///
8876/// This mirrors the tags [`decode`] reads and has to be kept beside it. A page whose header this
8877/// cannot walk comes back as text rather than as an error, because a caller asking what a file
8878/// looks like is usually asking because something is wrong with it, and a report that stops at the
8879/// first bad page is a report that says nothing about the other nine hundred.
8880fn page_encoding(ty: &LogicalType, rows: usize, bytes: &[u8]) -> String {
8881    /// The page header is the codec, the validity tag and, for a page that stores a mask, the mask.
8882    fn cascade_at(rows: usize, bytes: &[u8]) -> Result<(u8, usize)> {
8883        let mut cur = Cursor::new(bytes);
8884        let codec = cur.u8()?;
8885        if cur.u8()? == 2 {
8886            cur.take(rows.div_ceil(8))?;
8887        }
8888        Ok((codec, cur.at))
8889    }
8890    let Ok((codec, at)) = cascade_at(rows, bytes) else {
8891        return "UNREADABLE".to_string();
8892    };
8893    let tail = &bytes[at..];
8894    let described = |described: Result<String>| described.unwrap_or_else(|_| "UNREADABLE".into());
8895    match codec {
8896        0 => match ty {
8897            LogicalType::Varchar | LogicalType::Blob => "PLAIN".to_string(),
8898            _ => "FIXED".to_string(),
8899        },
8900        1 => "DICT(PLAIN)".to_string(),
8901        2 => "FOR+BITPACK".to_string(),
8902        3 => "TABLE DICT".to_string(),
8903        4 => format!("TABLE DICT({})", described(integer::describe(tail))),
8904        5 => described(integer::describe(tail)),
8905        6 => described(string::describe(tail)),
8906        other => format!("CODEC {other}"),
8907    }
8908}
8909
8910/// Selected stable dictionary codes from one page.
8911///
8912/// Pair-frequency construction needs at most the bounded heavy-hitter rows. Reading those code
8913/// positions directly avoids materializing every code in each part that contains a candidate.
8914fn decode_selected_stable_codes(
8915    rows: usize,
8916    bytes: &[u8],
8917    positions: &[usize],
8918    out: &mut Vec<Option<u32>>,
8919) -> Result<bool> {
8920    if positions.windows(2).any(|pair| pair[0] >= pair[1])
8921        || positions.last().is_some_and(|&position| position >= rows)
8922    {
8923        return Err(invalid("selected code positions are not sorted and in range"));
8924    }
8925    let mut cur = Cursor::new(bytes);
8926    let codec = cur.u8()?;
8927    if codec != 3 && codec != 4 {
8928        return Ok(false);
8929    }
8930    let flag = cur.u8()?;
8931    let mask = match flag {
8932        0 | 1 => None,
8933        2 => {
8934            let at = cur.at;
8935            let len = rows.div_ceil(8);
8936            cur.take(len)?;
8937            Some((at, len))
8938        }
8939        _ => return Err(invalid("page validity tag differs")),
8940    };
8941    let valid = |row: usize| match flag {
8942        0 => true,
8943        1 => false,
8944        2 => mask.is_some_and(|(at, _)| bytes[at + row / 8] >> (row % 8) & 1 == 1),
8945        _ => unreachable!("the validity tag was checked"),
8946    };
8947    if codec == 4 {
8948        let wide = integer::decode_selected(&bytes[cur.at..], positions)?;
8949        for (&row, code) in positions.iter().zip(wide) {
8950            let code = u32::try_from(code).map_err(|_| invalid("code is not a code"))?;
8951            out.push(valid(row).then_some(code));
8952        }
8953        return Ok(true);
8954    }
8955    let codes_at = cur.at;
8956    let codes_len = rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?;
8957    cur.take(codes_len)?;
8958    if cur.at != bytes.len() {
8959        return Err(invalid("global code page has trailing bytes"));
8960    }
8961    let codes = &bytes[codes_at..codes_at + codes_len];
8962    for &row in positions {
8963        let at = row.checked_mul(4).ok_or_else(|| invalid("dictionary code offset overflow"))?;
8964        let code = u32::from_le_bytes(
8965            codes[at..at + 4].try_into().map_err(|_| invalid("dictionary code is truncated"))?,
8966        );
8967        out.push(valid(row).then_some(code));
8968    }
8969    Ok(true)
8970}
8971
8972fn decode(
8973    ty: &LogicalType,
8974    rows: usize,
8975    bytes: &[u8],
8976    global: Option<Arc<Vector>>,
8977) -> Result<Vector> {
8978    let mut cur = Cursor::new(bytes);
8979    let codec = cur.u8()?;
8980    let flag = cur.u8()?;
8981    let validity = match flag {
8982        0 => Validity::AllValid,
8983        1 => Validity::AllInvalid,
8984        2 => {
8985            let mask = cur.take(rows.div_ceil(8))?;
8986            Validity::from_iter(rows, |row| mask[row / 8] >> (row % 8) & 1 == 1)
8987        }
8988        _ => return Err(invalid("page validity tag differs")),
8989    };
8990    if codec == 1 {
8991        if ty != &LogicalType::Varchar {
8992            return Err(invalid("dictionary codec belongs to a non-string page"));
8993        }
8994        let count = cur.u32()? as usize;
8995        let payload_len = cur.u32()? as usize;
8996        let offset_bytes = cur.take(
8997            (count + 1)
8998                .checked_mul(4)
8999                .ok_or_else(|| invalid("dictionary offset count overflow"))?,
9000        )?;
9001        let offsets = offset_bytes
9002            .chunks_exact(4)
9003            .map(|part| u32::from_le_bytes(part.try_into().expect("four bytes")))
9004            .collect::<Vec<_>>();
9005        let payload = cur.take(payload_len)?.to_vec();
9006        if offsets.first() != Some(&0)
9007            || offsets.last().copied().map(|last| last as usize) != Some(payload.len())
9008            || offsets.windows(2).any(|pair| pair[0] > pair[1])
9009        {
9010            return Err(invalid("dictionary offsets do not bound the payload"));
9011        }
9012        // A page, because every chunk cut out of this dictionary points at the same payload and a
9013        // page is what lets a cut be the views and nothing else.
9014        let mut strings = StringColumn::over(Buffer::from_vec(payload).into_page());
9015        for pair in offsets.windows(2) {
9016            strings.push_in_place(pair[0] as usize, (pair[1] - pair[0]) as usize)?;
9017        }
9018        let mut codes = Vec::with_capacity(rows);
9019        for _ in 0..rows {
9020            codes.push(cur.u32()?);
9021        }
9022        if codes.iter().any(|code| *code as usize >= count) {
9023            return Err(invalid("dictionary code is out of range"));
9024        }
9025        if cur.at != bytes.len() {
9026            return Err(invalid("dictionary page has trailing bytes"));
9027        }
9028        let dictionary = Vector::flat(LogicalType::Varchar, Data::Varlen(strings))?;
9029        return Ok(Vector::dictionary(codes, dictionary)?.with_validity(validity));
9030    }
9031    if codec == 3 || codec == 4 {
9032        let dictionary = global.ok_or_else(|| invalid("global code page has no dictionary"))?;
9033        let codes = if codec == 4 {
9034            // The cascade holds the whole tail of the page and says how long it is itself, so the
9035            // check that nothing is left over is the one the decoder already makes.
9036            let wide = integer::decode(&bytes[cur.at..])?;
9037            if wide.len() != rows {
9038                return Err(invalid("encoded code page holds the wrong number of rows"));
9039            }
9040            // Converted in one pass and checked in the same one, rather than a fallible conversion
9041            // per code. A `Result` an element is a short circuit the loop cannot be vectorized past,
9042            // and it was costing about twelve instructions a row to narrow a number that already
9043            // fits. Every code a file holds is inside a `u32` or the file is corrupt, so the check
9044            // belongs once at the end: or the codes together and the answer has a bit set above the
9045            // low thirty two, or the sign bit, exactly when one of them did.
9046            let mut codes = Vec::with_capacity(wide.len());
9047            let mut seen = 0_i64;
9048            for &code in &wide {
9049                seen |= code;
9050                codes.push(code as u32);
9051            }
9052            if seen < 0 || seen > i64::from(u32::MAX) {
9053                return Err(invalid("code is not a code"));
9054            }
9055            codes
9056        } else {
9057            let mut codes = Vec::with_capacity(rows);
9058            for _ in 0..rows {
9059                codes.push(cur.u32()?);
9060            }
9061            if cur.at != bytes.len() {
9062                return Err(invalid("global code page has trailing bytes"));
9063            }
9064            codes
9065        };
9066        let highest = codes.iter().copied().max();
9067        return Ok(Vector::stable_dictionary_validated(codes, dictionary, highest)?
9068            .with_validity(validity));
9069    }
9070    if codec == 6 {
9071        if ty != &LogicalType::Varchar {
9072            return Err(invalid("compressed text codec belongs to a non-string page"));
9073        }
9074        // As codec 5, the layer holds the whole tail of the page and says how long it is itself.
9075        // It comes back as one buffer with the values laid end to end and where each one ends, which
9076        // is the raw form's layout, so what is left to do here is what codec 0 does.
9077        let (payload, ends) = string::decode_flat(&bytes[cur.at..])?.into_parts();
9078        if ends.len() != rows {
9079            return Err(invalid("compressed text page holds the wrong number of rows"));
9080        }
9081        // A page, because this is read once and handed out a chunk at a time, and a cut of a paged
9082        // payload moves views rather than bytes.
9083        let mut values = StringColumn::over(Buffer::from_vec(payload).into_page());
9084        let mut start = 0;
9085        for end in ends {
9086            let len = end
9087                .checked_sub(start)
9088                .ok_or_else(|| invalid("compressed text value ends before it starts"))?;
9089            values.push_in_place(start, len)?;
9090            start = end;
9091        }
9092        return Ok(Vector::flat(ty.clone(), Data::Varlen(values))?.with_validity(validity));
9093    }
9094    if codec == 5 {
9095        // The cascade holds the whole tail of the page and says how long it is itself.
9096        let values = integer::decode(&bytes[cur.at..])?;
9097        if values.len() != rows {
9098            return Err(invalid("cascade page holds the wrong number of rows"));
9099        }
9100        let data = narrowed(ty, values)?;
9101        return Ok(Vector::flat(ty.clone(), data)?.with_validity(validity));
9102    }
9103    if codec == 2 {
9104        let width = u32::from(cur.u8()?);
9105        let base = i128::from_le_bytes(cur.take(16)?.try_into().expect("sixteen bytes"));
9106        let count = cur.u32()? as usize;
9107        let length = count.checked_mul(8).ok_or_else(|| invalid("packed page is too long"))?;
9108        let words: Vec<u64> = cur
9109            .take(length)?
9110            .chunks_exact(8)
9111            .map(|word| u64::from_le_bytes(word.try_into().expect("eight bytes")))
9112            .collect();
9113        if cur.at != bytes.len() {
9114            return Err(invalid("packed page has trailing bytes"));
9115        }
9116        return Ok(Vector::packed(ty.clone(), words, width, base, rows)?.with_validity(validity));
9117    }
9118    if codec != 0 {
9119        return Err(invalid("page codec is unknown"));
9120    }
9121    let data = match ty {
9122        LogicalType::TinyInt => {
9123            let values = cur.take(rows)?;
9124            Data::Int8(values.iter().map(|item| *item as i8).collect::<Vec<_>>().into())
9125        }
9126        LogicalType::UTinyInt => Data::UInt8(cur.take(rows)?.to_vec().into()),
9127        LogicalType::SmallInt => {
9128            let values =
9129                cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9130            Data::Int16(
9131                values
9132                    .chunks_exact(2)
9133                    .map(|item| i16::from_le_bytes(item.try_into().expect("two bytes")))
9134                    .collect::<Vec<_>>()
9135                    .into(),
9136            )
9137        }
9138        LogicalType::USmallInt => {
9139            let values =
9140                cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9141            Data::UInt16(
9142                values
9143                    .chunks_exact(2)
9144                    .map(|item| u16::from_le_bytes(item.try_into().expect("two bytes")))
9145                    .collect::<Vec<_>>()
9146                    .into(),
9147            )
9148        }
9149        LogicalType::UInteger => {
9150            let values =
9151                cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9152            Data::UInt32(
9153                values
9154                    .chunks_exact(4)
9155                    .map(|item| u32::from_le_bytes(item.try_into().expect("four bytes")))
9156                    .collect::<Vec<_>>()
9157                    .into(),
9158            )
9159        }
9160        LogicalType::UBigInt => {
9161            let values =
9162                cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9163            Data::UInt64(
9164                values
9165                    .chunks_exact(8)
9166                    .map(|item| u64::from_le_bytes(item.try_into().expect("eight bytes")))
9167                    .collect::<Vec<_>>()
9168                    .into(),
9169            )
9170        }
9171        LogicalType::Integer | LogicalType::Date => {
9172            let values =
9173                cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9174            Data::Int32(
9175                values
9176                    .chunks_exact(4)
9177                    .map(|item| i32::from_le_bytes(item.try_into().expect("four bytes")))
9178                    .collect::<Vec<_>>()
9179                    .into(),
9180            )
9181        }
9182        LogicalType::BigInt
9183        | LogicalType::Timestamp
9184        | LogicalType::Time
9185        | LogicalType::TimeTz
9186        | LogicalType::TimestampTz
9187        | LogicalType::TimestampS
9188        | LogicalType::TimestampMs
9189        | LogicalType::TimestampNs => {
9190            let values =
9191                cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9192            Data::Int64(
9193                values
9194                    .chunks_exact(8)
9195                    .map(|item| i64::from_le_bytes(item.try_into().expect("eight bytes")))
9196                    .collect::<Vec<_>>()
9197                    .into(),
9198            )
9199        }
9200        LogicalType::HugeInt | LogicalType::Uuid => {
9201            let values =
9202                cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9203            Data::Int128(
9204                values
9205                    .chunks_exact(16)
9206                    .map(|item| i128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9207                    .collect::<Vec<_>>()
9208                    .into(),
9209            )
9210        }
9211        LogicalType::UHugeInt => {
9212            let values =
9213                cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9214            Data::UInt128(
9215                values
9216                    .chunks_exact(16)
9217                    .map(|item| u128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9218                    .collect::<Vec<_>>()
9219                    .into(),
9220            )
9221        }
9222        LogicalType::Float => {
9223            let values =
9224                cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9225            Data::Float32(
9226                values
9227                    .chunks_exact(4)
9228                    .map(|item| f32::from_le_bytes(item.try_into().expect("four bytes")))
9229                    .collect::<Vec<_>>()
9230                    .into(),
9231            )
9232        }
9233        LogicalType::Double => {
9234            let values =
9235                cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9236            Data::Float64(
9237                values
9238                    .chunks_exact(8)
9239                    .map(|item| f64::from_le_bytes(item.try_into().expect("eight bytes")))
9240                    .collect::<Vec<_>>()
9241                    .into(),
9242            )
9243        }
9244        LogicalType::Interval => {
9245            let values =
9246                cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9247            Data::Interval(
9248                values
9249                    .chunks_exact(16)
9250                    .map(|item| {
9251                        (
9252                            i32::from_le_bytes(item[..4].try_into().expect("four bytes")),
9253                            i32::from_le_bytes(item[4..8].try_into().expect("four bytes")),
9254                            i64::from_le_bytes(item[8..].try_into().expect("eight bytes")),
9255                        )
9256                    })
9257                    .collect::<Vec<_>>()
9258                    .into(),
9259            )
9260        }
9261        LogicalType::Boolean => {
9262            let values = cur.take(rows)?;
9263            if values.iter().any(|value| *value > 1) {
9264                return Err(invalid("boolean page has another value"));
9265            }
9266            Data::Bool(values.iter().map(|value| *value == 1).collect::<Vec<_>>().into())
9267        }
9268        // Whichever integer the declared width says, which is the mapping the rest of the engine
9269        // already uses for a decimal in memory.
9270        LogicalType::Decimal { .. } => match ty.physical() {
9271            PhysicalType::Int16 => {
9272                let values =
9273                    cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9274                Data::Int16(
9275                    values
9276                        .chunks_exact(2)
9277                        .map(|item| i16::from_le_bytes(item.try_into().expect("two bytes")))
9278                        .collect::<Vec<_>>()
9279                        .into(),
9280                )
9281            }
9282            PhysicalType::Int32 => {
9283                let values =
9284                    cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9285                Data::Int32(
9286                    values
9287                        .chunks_exact(4)
9288                        .map(|item| i32::from_le_bytes(item.try_into().expect("four bytes")))
9289                        .collect::<Vec<_>>()
9290                        .into(),
9291                )
9292            }
9293            PhysicalType::Int64 => {
9294                let values =
9295                    cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9296                Data::Int64(
9297                    values
9298                        .chunks_exact(8)
9299                        .map(|item| i64::from_le_bytes(item.try_into().expect("eight bytes")))
9300                        .collect::<Vec<_>>()
9301                        .into(),
9302                )
9303            }
9304            _ => {
9305                let values =
9306                    cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9307                Data::Int128(
9308                    values
9309                        .chunks_exact(16)
9310                        .map(|item| i128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9311                        .collect::<Vec<_>>()
9312                        .into(),
9313                )
9314            }
9315        },
9316        LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit => {
9317            let offset_bytes = cur
9318                .take((rows + 1).checked_mul(4).ok_or_else(|| invalid("offset count overflow"))?)?;
9319            let offsets = offset_bytes
9320                .chunks_exact(4)
9321                .map(|part| u32::from_le_bytes(part.try_into().expect("four bytes")))
9322                .collect::<Vec<_>>();
9323            let payload = cur.take(bytes.len() - cur.at)?.to_vec();
9324            if offsets.first() != Some(&0)
9325                || offsets.last().copied().map(|last| last as usize) != Some(payload.len())
9326                || offsets.windows(2).any(|pair| pair[0] > pair[1])
9327            {
9328                return Err(invalid("string offsets do not bound the payload"));
9329            }
9330            // A page for the reason the dictionary payload above is one: the page is read once and
9331            // handed out a chunk at a time, and a cut of a paged payload moves views rather than
9332            // bytes.
9333            //
9334            // A varchar is checked for text on the way in and a blob and a bit string are not,
9335            // because the second pair never claimed to hold any. Reading them through the checking
9336            // seam would refuse a column for holding exactly what it was told to hold.
9337            let mut values = StringColumn::over(Buffer::from_vec(payload).into_page());
9338            let text = ty == &LogicalType::Varchar;
9339            for pair in offsets.windows(2) {
9340                let (at, len) = (pair[0] as usize, (pair[1] - pair[0]) as usize);
9341                if text {
9342                    values.push_in_place(at, len)?;
9343                } else {
9344                    values.push_bytes_in_place(at, len)?;
9345                }
9346            }
9347            Data::Varlen(values)
9348        }
9349        _ => return Err(Error::not_implemented(format!("native page for {ty}"))),
9350    };
9351    if cur.at != bytes.len() {
9352        return Err(invalid("page has trailing bytes"));
9353    }
9354    Ok(Vector::flat(ty.clone(), data)?.with_validity(validity))
9355}
9356
9357#[cfg(test)]
9358mod tests {
9359    use std::fs;
9360    use std::io::{Seek, SeekFrom, Write};
9361    use std::path::PathBuf;
9362    use std::time::{SystemTime, UNIX_EPOCH};
9363
9364    use rudb_common::Stat;
9365    use rudb_common::Value;
9366    use rudb_common::bounds::{Frequencies, Op, Remainder, Zones};
9367    use rudb_common::stat::Provenance;
9368
9369    use super::*;
9370
9371    /// The chooser as it was before it could rule kinds out up front: the same narrowing, with every
9372    /// kind tested for. What it writes is what the file used to hold.
9373    #[derive(Debug)]
9374    struct TestsEverything<'a>(&'a dyn chooser::Chooser);
9375
9376    impl chooser::Chooser for TestsEverything<'_> {
9377        fn name(&self) -> &'static str {
9378            "tests everything"
9379        }
9380
9381        fn narrow_strings(
9382            &self,
9383            values: &[&[u8]],
9384            offered: &[string::Kind],
9385            depth: u8,
9386        ) -> Vec<string::Kind> {
9387            self.0.narrow_strings(values, offered, depth)
9388        }
9389
9390        fn narrow_integers(
9391            &self,
9392            values: &[i64],
9393            offered: &[integer::Kind],
9394            depth: u8,
9395        ) -> Vec<integer::Kind> {
9396            self.0.narrow_integers(values, offered, depth)
9397        }
9398    }
9399
9400    #[test]
9401    fn ruling_kinds_out_before_testing_for_them_writes_the_same_bytes() {
9402        let columns: Vec<Vec<i64>> = vec![
9403            vec![],
9404            vec![5; 1000],
9405            (0..1000).collect(),
9406            (0..1000).map(|row| 1_600_000_000_000_000 + row * 1_000_000).collect(),
9407            (0..1000).map(|row| row / 50).collect(),
9408            (0..1000).map(|row| if row % 97 == 0 { row } else { 0 }).collect(),
9409            (0..1000).map(|row| (row * 7919) % 13).collect(),
9410            (0..1000).map(|row| (row * 2_654_435_761) % 1_000_003).collect(),
9411            (0..1000).map(|row| [3, 3, 3, 9, 9, 1][row as usize % 6]).collect(),
9412            (0..1000).map(|row| i64::MIN + row % 3).collect(),
9413        ];
9414        let choosers: [&dyn chooser::Chooser; 2] = [&Fixed, &Codes];
9415        for column in &columns {
9416            for chooser in choosers {
9417                let quick = integer::encode_with(column, chooser).unwrap();
9418                let full = integer::encode_with(column, &TestsEverything(chooser)).unwrap();
9419                assert_eq!(
9420                    quick,
9421                    full,
9422                    "{} on {:?}",
9423                    chooser.name(),
9424                    &column[..column.len().min(8)]
9425                );
9426            }
9427        }
9428    }
9429
9430    #[test]
9431    fn checksum_matches_fixed_vectors() {
9432        assert_eq!(checksum(b""), 0xef46_db37_51d8_e999);
9433        assert_eq!(checksum(b"a"), 0xd24e_c4f1_a98c_6e5b);
9434        assert_eq!(checksum(b"abc"), 0x44bc_2cf5_ad77_0999);
9435    }
9436
9437    #[test]
9438    fn sorting_across_threads_matches_sorting_on_one() {
9439        let mut state = 0x9e37_79b9_7f4a_7c15_u64;
9440        let mut next = move || {
9441            state ^= state << 13;
9442            state ^= state >> 7;
9443            state ^= state << 17;
9444            state
9445        };
9446        let mut values = Vec::new();
9447        for at in 0..150_000_u64 {
9448            let value = match next() % 6 {
9449                0 => Vec::new(),
9450                1 => format!("https://example.com/{}", next() % 5_000).into_bytes(),
9451                2 => format!("https://example.com/path/{at}").into_bytes(),
9452                3 => b"same".to_vec(),
9453                4 => vec![0xff; (next() % 12) as usize],
9454                _ => (0..next() % 20).map(|_| (next() % 3) as u8).collect(),
9455            };
9456            values.push(value);
9457        }
9458        let value = |code: u32| values[code as usize].as_slice();
9459        for workers in [1, 2, 3, 8, 32] {
9460            let mut one = (0..values.len() as u32).rev().collect::<Vec<_>>();
9461            let mut across = one.clone();
9462            sort_by_value(&mut one, value);
9463            sort_by_value_across(&mut across, value, workers);
9464            assert_eq!(one, across, "{workers} workers");
9465        }
9466        let mut sorted = (0..values.len() as u32).collect::<Vec<_>>();
9467        sort_by_value_across(&mut sorted, value, 8);
9468        assert!(sorted.windows(2).all(|pair| value(pair[0]) <= value(pair[1])));
9469    }
9470
9471    fn path(label: &str) -> PathBuf {
9472        let stamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("time advances").as_nanos();
9473        std::env::temp_dir().join(format!("rudb-native-{label}-{}-{stamp}.rdb", std::process::id()))
9474    }
9475
9476    /// Every value of a dictionary in code order, which the tests have no other way to ask for now
9477    /// that a dictionary does not keep the bytes of the values it has seen.
9478    ///
9479    /// Only valid once `finish_blocks` has run, because until then the last part block is still raw.
9480    fn dictionary_values(dictionary: &GlobalDictionary) -> Vec<Vec<u8>> {
9481        let (flat, bases) = dictionary.decoded(None).expect("the blocks decode");
9482        (0..dictionary.values())
9483            .map(|code| {
9484                let (from, to) = GlobalDictionary::value_span(&dictionary.ends, &bases, code);
9485                flat[from..to].to_vec()
9486            })
9487            .collect()
9488    }
9489
9490    /// The sections a test put in the table, which is every one the writer did not.
9491    ///
9492    /// A table now carries a summary and a sketch per column out of the write itself, and a test
9493    /// about the section table is not about those. Filtering by kind rather than by count, so a
9494    /// table that turns out to have no room for its summaries does not quietly change what these
9495    /// tests are asserting over.
9496    fn attached(table: &Table) -> Vec<&Section> {
9497        table.sections().iter().filter(|held| !held.among(section::STATISTICS_KINDS)).collect()
9498    }
9499
9500    /// A read names the offset it wants, so a cursor somebody else moved cannot reach it.
9501    #[test]
9502    fn a_read_at_an_offset_ignores_where_another_thread_left_the_cursor() {
9503        const SPANS: usize = 64;
9504        const SPAN: usize = 512;
9505        let path = path("positional");
9506        let content: Vec<u8> =
9507            (0..SPANS).flat_map(|span| std::iter::repeat_n(span as u8, SPAN)).collect();
9508        fs::write(&path, &content).expect("the file is written");
9509        let file = Arc::new(File::open(&path).expect("the file opens"));
9510        std::thread::scope(|scope| {
9511            for _ in 0..8 {
9512                let file = Arc::clone(&file);
9513                scope.spawn(move || {
9514                    for _ in 0..64 {
9515                        for span in 0..SPANS {
9516                            let mut bytes = [0_u8; SPAN];
9517                            read_at(&file, (span * SPAN) as u64, &mut bytes)
9518                                .expect("the span reads");
9519                            assert!(
9520                                bytes.iter().all(|byte| *byte == span as u8),
9521                                "span {span} came back as {}",
9522                                bytes[0],
9523                            );
9524                        }
9525                    }
9526                });
9527            }
9528        });
9529        let mut past = [0_u8; SPAN];
9530        let end = (SPANS * SPAN) as u64;
9531        let error = read_at(&file, end, &mut past).expect_err("a read past the end is refused");
9532        assert!(error.message().contains("ends before its declared length"), "{error}");
9533        drop(file);
9534        let _ = fs::remove_file(&path);
9535    }
9536
9537    /// The writer records where it put a page and puts it there, whatever the cursor is doing.
9538    ///
9539    /// The cursor is moved between the steps that record an offset, which is what reading the pages
9540    /// back to build the frequencies does on a platform with no `pread`. Without the fix the
9541    /// directory lands on top of a page and the file fails to reopen.
9542    #[test]
9543    fn a_writer_puts_a_page_where_it_said_it_did_wherever_the_cursor_has_got_to() {
9544        let path = path("cursor");
9545        let mut writer = Writer::create(
9546            &path,
9547            "items",
9548            vec![
9549                Field::required("id", LogicalType::Integer),
9550                Field::new("text", LogicalType::Varchar),
9551            ],
9552        )
9553        .expect("new file");
9554        writer.append(&sample()).expect("first part");
9555        writer.file.seek(SeekFrom::Start(0)).expect("the cursor goes back to the header");
9556        writer.append(&sample()).expect("second part");
9557        writer.file.seek(SeekFrom::Start(1)).expect("and somewhere useless again");
9558        writer.finish().expect("commit");
9559        let reader = Reader::open(&path).expect("reopen from disk");
9560        assert_eq!(reader.table().rows(), 6);
9561        let ids = reader.read(0, &[0]).expect("the integer page reads back");
9562        assert_eq!(ids.value_at(0, 0), Value::Integer(4));
9563        assert_eq!(ids.value_at(2, 0), Value::Integer(-2));
9564        let text = reader.read(1, &[1]).expect("the text page reads back");
9565        assert_eq!(text.value_at(1, 0), Value::Null);
9566        assert_eq!(text.value_at(2, 0), Value::Varchar("long text after a slash".into()));
9567        // Nothing the directory points at may run past the end of the file, which is the shape the
9568        // failure took: a page recorded at an offset the directory had already been written over.
9569        let end = reader.table().stripes().iter().flat_map(|stripe| {
9570            stripe
9571                .pages
9572                .iter()
9573                .map(|page| page.offset + u64::from(page.length))
9574                .chain(std::iter::once(stripe.index.offset + u64::from(stripe.index.length)))
9575        });
9576        let last = end.fold(HEADER, u64::max);
9577        let directory = fs::metadata(&path).expect("the file is there").len();
9578        assert!(last <= directory, "a page runs to {last} in a file of {directory} bytes");
9579        fs::remove_file(path).expect("remove scratch file");
9580    }
9581
9582    /// How long a global dictionary index is, read out of the page's own header.
9583    ///
9584    /// The tests below damage a byte of the order or of the payload, so they need to know where each
9585    /// one starts, and working it out here rather than writing a number down means adding something
9586    /// to the index does not quietly turn one of them into a test that damages the index instead.
9587    fn dictionary_index_len(header: &[u8; DICTIONARY_HEADER]) -> u64 {
9588        let count = u64::from(u32::from_le_bytes(header[0..4].try_into().expect("four bytes")));
9589        let blocks = u64::from(u32::from_le_bytes(header[8..12].try_into().expect("four bytes")));
9590        let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
9591        let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
9592        let payload_words = if width & DICTIONARY_SCATTERED == 0 { 2 } else { 3 };
9593        let rank_blocks = count.div_ceil(TEXT_RANK_BLOCK as u64);
9594        DICTIONARY_HEADER as u64
9595            + offset_bytes(count as usize, bits) as u64
9596            + blocks * payload_words * 8
9597            + rank_blocks * 16
9598            + if width & DICTIONARY_GRAMS == 0 { 0 } else { 8 }
9599    }
9600
9601    fn sample() -> Chunk {
9602        Chunk::new(vec![
9603            Vector::from_values(
9604                LogicalType::Integer,
9605                &[Value::Integer(4), Value::Integer(9), Value::Integer(-2)],
9606            )
9607            .expect("integers"),
9608            Vector::from_values(
9609                LogicalType::Varchar,
9610                &[
9611                    Value::Varchar("alpha".into()),
9612                    Value::Null,
9613                    Value::Varchar("long text after a slash".into()),
9614                ],
9615            )
9616            .expect("strings"),
9617        ])
9618        .expect("matching rows")
9619    }
9620
9621    fn sample_ids() -> Chunk {
9622        Chunk::new(vec![
9623            Vector::flat(LogicalType::Integer, Data::Int32(vec![7, 8, 9].into()))
9624                .expect("integers"),
9625        ])
9626        .expect("one column")
9627    }
9628
9629    #[test]
9630    fn the_planner_gets_the_null_count_off_the_same_directory_the_bounds_are_in() {
9631        // Six rows, two of them null. `IS NULL` used to get the same fifth any unreadable
9632        // condition gets, and the number was in the stripe entry next to the bounds all along.
9633        let path = path("nulls_for_the_planner");
9634        let mut writer =
9635            Writer::create(&path, "items", vec![Field::new("a", LogicalType::Integer)])
9636                .expect("new file");
9637        let rows = Chunk::new(vec![
9638            Vector::from_values(
9639                LogicalType::Integer,
9640                &[
9641                    Value::Integer(4),
9642                    Value::Null,
9643                    Value::Integer(9),
9644                    Value::Null,
9645                    Value::Integer(1),
9646                    Value::Integer(2),
9647                ],
9648            )
9649            .expect("integers"),
9650        ])
9651        .expect("one column");
9652        writer.append(&rows).expect("the only part");
9653        writer.finish().expect("commit");
9654        let reader = Reader::open(&path).expect("reopen from disk");
9655        let stripes = Stripes::new(reader);
9656        let column = stripes.column("a").expect("the file has that column");
9657        assert_eq!(stripes.nulls(column), Stat::exact(2, Provenance::NullCount));
9658        // A column the file does not have. Zero here would be a fact about a column that is not
9659        // there, which the planner would then divide by.
9660        assert_eq!(stripes.nulls(column + 1), Stat::Unknown);
9661        fs::remove_file(&path).expect("clean up");
9662    }
9663
9664    #[test]
9665    fn the_planner_gets_a_row_count_per_value_off_a_complete_synopsis() {
9666        // The whole of the frequency half of #1106, end to end over a real file. Six rows, three
9667        // of one value and two of another, and a complete synopsis because six rows is well inside
9668        // what the writer can account for. The estimate for `id = 4` is three rows rather than a
9669        // sixth of the table, and for a value the file does not hold it is none.
9670        let path = path("frequencies_for_the_planner");
9671        let mut writer =
9672            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9673                .expect("new file");
9674        let rows = Chunk::new(vec![
9675            Vector::from_values(
9676                LogicalType::Integer,
9677                &[
9678                    Value::Integer(4),
9679                    Value::Integer(4),
9680                    Value::Integer(4),
9681                    Value::Integer(9),
9682                    Value::Integer(9),
9683                    Value::Integer(1),
9684                ],
9685            )
9686            .expect("integers"),
9687        ])
9688        .expect("one column");
9689        writer.append(&rows).expect("the only part");
9690        writer.finish().expect("commit");
9691        let reader = Reader::open(&path).expect("reopen from disk");
9692        let common = Common::new(reader);
9693        assert_eq!(common.rows(), 6);
9694        let column = common.column("id").expect("the file has that column");
9695        assert_eq!(common.column("nothing"), None);
9696        assert_eq!(
9697            common.rows_with(column, &Bound::Int(4)),
9698            Stat::exact(3, Provenance::FrequencySynopsis)
9699        );
9700        // Not in the file, and a synopsis that accounts for all six rows proves it.
9701        assert_eq!(
9702            common.rows_with(column, &Bound::Int(7)),
9703            Stat::exact(0, Provenance::FrequencySynopsis)
9704        );
9705        // A constant of another domain against an integer column. Nothing in the list compares
9706        // with it, so the zero above would be an artefact of the mismatch rather than a fact.
9707        assert_eq!(common.rows_with(column, &Bound::Bytes(b"four".to_vec())), Stat::Unknown);
9708        // A complete list has no remainder. Answering one of no rows over no values would hand the
9709        // caller a division to special case, and the counts above already answer this column.
9710        assert_eq!(common.remainder(column), None);
9711        fs::remove_file(&path).expect("clean up");
9712    }
9713
9714    #[test]
9715    fn string_frequency_estimates_do_not_open_the_global_dictionary() {
9716        let path = path("string_frequencies_for_the_planner");
9717        let mut writer =
9718            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
9719                .expect("new file");
9720        let rows = Chunk::new(vec![
9721            Vector::from_values(
9722                LogicalType::Varchar,
9723                &[
9724                    Value::Varchar(String::new()),
9725                    Value::Varchar("alpha".into()),
9726                    Value::Varchar(String::new()),
9727                    Value::Varchar("beta".into()),
9728                    Value::Varchar(String::new()),
9729                ],
9730            )
9731            .expect("strings"),
9732        ])
9733        .expect("one column");
9734        writer.append(&rows).expect("the only part");
9735        writer.finish().expect("commit");
9736
9737        let reader = Reader::open(&path).expect("reopen from disk");
9738        assert_eq!(reader.reads().dictionaries, 0, "open reads only the directory");
9739        let common = Common::new(reader.clone());
9740        let column = common.column("text").expect("the file has that column");
9741        assert_eq!(
9742            common.rows_with(column, &Bound::Bytes(Vec::new())),
9743            Stat::exact(3, Provenance::FrequencySynopsis)
9744        );
9745        assert_eq!(
9746            common.rows_with(column, &Bound::Bytes(b"missing".to_vec())),
9747            Stat::exact(0, Provenance::FrequencySynopsis)
9748        );
9749        assert_eq!(
9750            reader.reads().dictionaries,
9751            0,
9752            "the bounded spellings answer without opening the dictionary index"
9753        );
9754        fs::remove_file(&path).expect("clean up");
9755    }
9756
9757    #[test]
9758    fn host_groups_certify_omitted_hosts_and_keep_exact_aggregates() {
9759        let path = path("certified_host_groups");
9760        let mut writer =
9761            Writer::create(&path, "hits", vec![Field::required("Referer", LogicalType::Varchar)])
9762                .expect("new file");
9763        let mut values = vec![Value::Varchar("http://www.example.com/a".into()); 150];
9764        values.extend(vec![Value::Varchar("https://example.com/b".into()); 70]);
9765        values.extend((0..550).map(|at| Value::Varchar(format!("https://site{at}.test/x"))));
9766        values.push(Value::Varchar(String::new()));
9767        for part in values.chunks(512) {
9768            writer
9769                .append(
9770                    &Chunk::new(vec![
9771                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
9772                    ])
9773                    .expect("one column"),
9774                )
9775                .expect("part written");
9776        }
9777        writer.finish().expect("commit");
9778        let reader = Reader::open(&path).expect("reopen");
9779        let summary = reader.table.host_groups.as_ref().expect("bounded host metadata");
9780        assert!(summary.omitted_max < 220);
9781        assert!(reader.host_groups(0, summary.omitted_max).expect("valid column").is_none());
9782        let groups = reader.host_groups(0, 220).expect("valid column").expect("certified");
9783        let example = groups.iter().find(|entry| entry.host == "example.com").expect("leader");
9784        assert_eq!(example.count, 220);
9785        assert_eq!(example.bytes_sum, 150 * 24 + 70 * 21);
9786        assert_eq!(example.minimum, "http://www.example.com/a");
9787        assert_eq!(reader.reads().dictionaries, 0, "the directory settles the question");
9788        fs::remove_file(&path).expect("clean up");
9789    }
9790
9791    /// A table directory with nothing in it but a name and one column, for the section tests.
9792    ///
9793    /// The section table is orthogonal to everything else in a directory, so the tests that pin it
9794    /// say so by starting from the emptiest table that encodes.
9795    fn bare_table(sections: Vec<Section>) -> Table {
9796        Table {
9797            name: "linked".to_owned(),
9798            fields: vec![Field::required("id", LogicalType::Integer)],
9799            stripes: Vec::new(),
9800            rows: 0,
9801            dictionaries: vec![None],
9802            dictionary_payloads: Vec::new(),
9803            distincts: vec![None],
9804            frequencies: vec![None],
9805            pair_frequencies: Vec::new(),
9806            frequency_texts: Vec::new(),
9807            host_groups: None,
9808            clustering: None,
9809            generation: 1,
9810            sections,
9811        }
9812    }
9813
9814    fn a_key_map_section() -> Section {
9815        Section {
9816            kind: *section::KEY_MAP,
9817            id: 1,
9818            generation: 3,
9819            extents: 1,
9820            extent_page: HEADER,
9821            extent_bytes: section::EXTENT_BYTES as u32,
9822            hash: 0x1234_5678_9abc_def0,
9823            flags: 0,
9824            header_bytes: 24,
9825        }
9826    }
9827
9828    #[test]
9829    fn a_section_table_round_trips_through_a_directory() {
9830        let mut later = a_key_map_section();
9831        later.kind = *b"RUDBZZ9\0";
9832        later.id = 2;
9833        let table = bare_table(vec![a_key_map_section(), later]);
9834        let directory = encode_directory(&table).expect("directory");
9835        let decoded = decode_directory(&directory, 1 << 20).expect("reopen");
9836        assert_eq!(decoded.sections(), &[a_key_map_section(), later]);
9837        // The second is a kind this build has no name for, and it survived the round trip anyway.
9838        // That is what keeps an old build from silently discarding a newer build's work when it
9839        // rewrites a directory.
9840        assert!(decoded.sections()[0].known());
9841        assert!(!decoded.sections()[1].known());
9842    }
9843
9844    #[test]
9845    fn a_directory_written_before_the_section_table_reads_as_a_table_with_none() {
9846        // The G1 exit criterion, at the directory level. A format 22 directory is exactly this
9847        // build's directory with the trailing section block cut off, so cutting it off is the
9848        // honest way to make one: no fixture to go stale, and no separate encoder to drift.
9849        let directory = encode_directory(&bare_table(Vec::new())).expect("directory");
9850        let block = SECTIONS.len() + size_of::<u64>() + size_of::<u16>();
9851        let older = &directory[..directory.len() - block];
9852        let decoded = decode_directory(older, 1 << 20).expect("a directory from before sections");
9853        assert!(decoded.sections().is_empty());
9854        assert_eq!(decoded.generation(), 0, "a format 22 table recorded no generation");
9855        assert_eq!(decoded.name(), "linked");
9856        assert_eq!(decoded.fields().len(), 1, "everything before the block still decodes");
9857    }
9858
9859    #[test]
9860    fn a_file_stamped_with_the_previous_format_still_opens_and_reads() {
9861        // The same criterion end to end, which is the one the milestone actually asks for: a build
9862        // that knows about sections opens a file written by a build that did not, with no rewrite
9863        // and no repair, and answers from it. The version field is patched rather than a file
9864        // committed by an old binary because the bytes either side of it are identical: format 22
9865        // and format 23 differ only in a trailing directory block, and a reader that stops before
9866        // that block gets a table with no sections.
9867        let path = path("format_twenty_two");
9868        let mut writer =
9869            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9870                .expect("new file");
9871        let rows = Chunk::new(vec![
9872            Vector::from_values(
9873                LogicalType::Integer,
9874                &[Value::Integer(1), Value::Integer(2), Value::Integer(3)],
9875            )
9876            .expect("integers"),
9877        ])
9878        .expect("one column");
9879        writer.append(&rows).expect("the only part");
9880        writer.finish().expect("commit");
9881
9882        let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
9883        write_at(&file, 8, &22_u32.to_le_bytes()).expect("stamp the older format");
9884        drop(file);
9885
9886        let reader = Reader::open(&path).expect("a format 22 file opens unchanged");
9887        assert_eq!(reader.table().rows(), 3);
9888        // The rows and not the section table, because the section block is found by the magic at
9889        // the end of the directory rather than by the number in the header, so stamping the header
9890        // back does not take away the summaries this writer put there. What the test is about is
9891        // that the version check accepts 22, and the rows coming back is what says it did.
9892        assert_eq!(reader.read(0, &[0]).expect("the part still reads").len(), 3);
9893
9894        // And a format this build has never written is still refused, so the accept set is a list
9895        // and not an absence of a check.
9896        let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
9897        write_at(&file, 8, &21_u32.to_le_bytes()).expect("stamp an unreadable format");
9898        drop(file);
9899        let error = Reader::open(&path).expect_err("format 21 is not readable");
9900        assert!(error.to_string().contains("format 21"), "{error}");
9901
9902        fs::remove_file(&path).expect("clean up");
9903    }
9904
9905    #[test]
9906    fn a_section_whose_extent_table_is_outside_the_file_is_refused() {
9907        // The bound the format has to check and `section` cannot, because only the reader knows how
9908        // big the file is. Reading the payload a section like this names would be reading whatever
9909        // else happens to be at that offset, which is the one way a graph section could turn into a
9910        // wrong answer rather than a slow one.
9911        let mut past = a_key_map_section();
9912        past.extent_page = 1 << 30;
9913        let directory = encode_directory(&bare_table(vec![past])).expect("directory");
9914        let error = decode_directory(&directory, 1 << 20).expect_err("refused");
9915        assert!(error.to_string().contains("outside the file"), "{error}");
9916
9917        let mut inside_the_header = a_key_map_section();
9918        inside_the_header.extent_page = 8;
9919        let directory = encode_directory(&bare_table(vec![inside_the_header])).expect("directory");
9920        assert!(
9921            decode_directory(&directory, 1 << 20).is_err(),
9922            "a section may not overlap a header"
9923        );
9924    }
9925
9926    #[test]
9927    fn a_section_recorded_as_not_built_is_legal_and_names_no_bytes() {
9928        // Section 3.7: a relationship that does not fit the budget is recorded with its size so
9929        // that `rudb_links()` can report what a larger budget would buy. That record is a section
9930        // entry with no extents, so it has to survive a round trip while naming nothing.
9931        let not_built = Section {
9932            kind: *section::FORWARD_LINK,
9933            id: 9,
9934            generation: 3,
9935            extents: 0,
9936            extent_page: 0,
9937            extent_bytes: 0,
9938            hash: 0,
9939            flags: 0,
9940            header_bytes: 0,
9941        };
9942        let directory = encode_directory(&bare_table(vec![not_built])).expect("directory");
9943        let decoded = decode_directory(&directory, 1 << 20).expect("reopen");
9944        assert_eq!(decoded.sections(), &[not_built]);
9945
9946        // But a section with no extents that still names an extent table is incoherent, and an
9947        // incoherent entry is a torn directory rather than a relationship that was skipped.
9948        let mut incoherent = not_built;
9949        incoherent.extent_bytes = 28;
9950        incoherent.extent_page = HEADER;
9951        let directory = encode_directory(&bare_table(vec![incoherent])).expect("directory");
9952        assert!(decode_directory(&directory, 1 << 20).is_err());
9953    }
9954
9955    #[test]
9956    fn a_directory_naming_more_sections_than_the_bound_is_refused() {
9957        let directory = encode_directory(&bare_table(Vec::new())).expect("directory");
9958        let mut torn = directory.clone();
9959        let count_at = torn.len() - size_of::<u16>();
9960        torn[count_at..].copy_from_slice(&u16::MAX.to_le_bytes());
9961        // Not an allocation of sixty five thousand entries off a torn count: either the bound
9962        // refuses it or the bytes run out, and both are errors rather than a read past the end.
9963        assert!(decode_directory(&torn, 1 << 20).is_err());
9964    }
9965
9966    /// A committed one column file of `rows` integers, for the attach tests.
9967    fn linked_file(label: &str, rows: i32) -> PathBuf {
9968        let path = path(label);
9969        let mut writer =
9970            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9971                .expect("new file");
9972        let values = (0..rows).map(Value::Integer).collect::<Vec<_>>();
9973        let chunk =
9974            Chunk::new(vec![Vector::from_values(LogicalType::Integer, &values).expect("integers")])
9975                .expect("one column");
9976        writer.append(&chunk).expect("the only part");
9977        writer.finish().expect("commit");
9978        path
9979    }
9980
9981    fn a_key_map_payload() -> Vec<u8> {
9982        // Shaped like one without being one: this crate never reads a payload, so what matters here
9983        // is that every byte comes back and that the header the entry measures is at the front.
9984        (0..512_u32).flat_map(u32::to_le_bytes).collect()
9985    }
9986
9987    #[test]
9988    fn a_section_attached_to_a_committed_file_reads_back_byte_for_byte() {
9989        let path = linked_file("attach", 64);
9990        let payload = a_key_map_payload();
9991        let table = attach(
9992            &path,
9993            "items",
9994            &[section::Attachment {
9995                kind: *section::KEY_MAP,
9996                id: 0,
9997                flags: 2,
9998                header_bytes: 40,
9999                bytes: &payload,
10000            }],
10001        )
10002        .expect("attach a key map");
10003        assert_eq!(attached(&table).len(), 1);
10004
10005        let reader = Reader::open(&path).expect("reopen after the attach");
10006        let held = attached(reader.table());
10007        assert_eq!(held.len(), 1);
10008        assert_eq!(held[0].kind, *section::KEY_MAP);
10009        assert_eq!(held[0].flags, 2, "the form a reader must not have to guess");
10010        assert_eq!(held[0].header_bytes, 40);
10011        // The generation is the one the pages were written at, not the one the attach committed at.
10012        // Attaching a section moved no row, so a section written by it is current, and a second
10013        // table added to this file later would not make it stale.
10014        assert_eq!(held[0].generation, 1);
10015        assert!(held[0].usable(reader.table().generation()));
10016        assert_eq!(reader.payload(held[0]).expect("read the payload"), payload);
10017        assert_eq!(reader.extents(held[0]).expect("extent table").len(), 1);
10018
10019        fs::remove_file(&path).expect("clean up");
10020    }
10021
10022    #[test]
10023    fn attaching_a_section_answers_every_row_exactly_as_before() {
10024        // Section 3.1 end to end, and the reason the whole layer is safe to build incrementally. A
10025        // file with a section in it and the same file without one have to agree row for row, so the
10026        // comparison is made against the answers taken before the attach rather than against a
10027        // constant somebody typed.
10028        let path = linked_file("attach_changes_nothing", 300);
10029        let before = Reader::open(&path).expect("open before");
10030        let rows = before.table().rows();
10031        let first = before.read(0, &[0]).expect("read before");
10032        let values = (0..rows).map(|at| first.value_at(at, 0)).collect::<Vec<_>>();
10033        let layout = before.layout().columns_total();
10034        drop(before);
10035
10036        let payload = a_key_map_payload();
10037        attach(
10038            &path,
10039            "items",
10040            &[section::Attachment {
10041                kind: *section::KEY_MAP,
10042                id: 0,
10043                flags: 0,
10044                header_bytes: 0,
10045                bytes: &payload,
10046            }],
10047        )
10048        .expect("attach");
10049
10050        let after = Reader::open(&path).expect("open after");
10051        assert_eq!(after.table().rows(), rows);
10052        let read = after.read(0, &[0]).expect("read after");
10053        for (at, value) in values.iter().enumerate() {
10054            assert_eq!(&read.value_at(at, 0), value, "row {at} moved");
10055        }
10056        assert_eq!(
10057            after.layout().columns_total(),
10058            layout,
10059            "an attach appends and does not rewrite a column page"
10060        );
10061
10062        fs::remove_file(&path).expect("clean up");
10063    }
10064
10065    #[test]
10066    fn a_rebuilt_section_replaces_the_one_it_supersedes() {
10067        // Rebuilding a key map has to be a write and not a question. If an attach added rather than
10068        // replaced, a table rebuilt a few times would name several maps for one column and a reader
10069        // would have to pick, which is a decision with no right answer in it.
10070        let path = linked_file("attach_twice", 32);
10071        let one = a_key_map_payload();
10072        let two = vec![7_u8; 1024];
10073        let entry = |bytes| section::Attachment {
10074            kind: *section::KEY_MAP,
10075            id: 4,
10076            flags: 1,
10077            header_bytes: 0,
10078            bytes,
10079        };
10080        attach(&path, "items", &[entry(&one)]).expect("first build");
10081        attach(&path, "items", &[entry(&two)]).expect("rebuild");
10082
10083        let reader = Reader::open(&path).expect("reopen");
10084        let held = attached(reader.table());
10085        assert_eq!(held.len(), 1, "one map per column and not one per build");
10086        assert_eq!(reader.payload(held[0]).expect("payload"), two);
10087
10088        fs::remove_file(&path).expect("clean up");
10089    }
10090
10091    #[test]
10092    fn an_attach_carries_through_a_kind_it_does_not_know() {
10093        // The first of section 3.2's three rules, at the point where it is easiest to break: a build
10094        // that rewrites a directory has to carry an entry it has no name for, or opening a file with
10095        // an older binary and attaching one section quietly deletes the work of a newer one.
10096        let path = linked_file("attach_unknown", 16);
10097        let payload = vec![3_u8; 96];
10098        attach(
10099            &path,
10100            "items",
10101            &[section::Attachment {
10102                kind: *b"RUDBZZ9\0",
10103                id: 1,
10104                flags: 0,
10105                header_bytes: 0,
10106                bytes: &payload,
10107            }],
10108        )
10109        .expect("a kind this build does not know still writes");
10110        let key_map = a_key_map_payload();
10111        attach(
10112            &path,
10113            "items",
10114            &[section::Attachment {
10115                kind: *section::KEY_MAP,
10116                id: 0,
10117                flags: 0,
10118                header_bytes: 0,
10119                bytes: &key_map,
10120            }],
10121        )
10122        .expect("attach beside it");
10123
10124        let reader = Reader::open(&path).expect("reopen");
10125        let held = attached(reader.table());
10126        assert_eq!(held.len(), 2, "the unfamiliar entry survived a directory rewrite");
10127        let unknown = held.iter().find(|one| !one.known()).expect("the unfamiliar one");
10128        assert_eq!(reader.payload(unknown).expect("its bytes are still there"), payload);
10129
10130        fs::remove_file(&path).expect("clean up");
10131    }
10132
10133    #[test]
10134    fn a_payload_of_nothing_is_a_relationship_recorded_as_not_built() {
10135        let path = linked_file("attach_not_built", 8);
10136        attach(
10137            &path,
10138            "items",
10139            &[section::Attachment {
10140                kind: *section::FORWARD_LINK,
10141                id: 2,
10142                flags: 0,
10143                header_bytes: 0,
10144                bytes: &[],
10145            }],
10146        )
10147        .expect("record a link that did not fit the budget");
10148
10149        let reader = Reader::open(&path).expect("reopen");
10150        let held = attached(reader.table());
10151        assert_eq!(held.len(), 1);
10152        assert_eq!(held[0].extents, 0);
10153        assert_eq!(held[0].extent_page, 0, "an entry that names no bytes points at none");
10154        assert!(reader.extents(held[0]).expect("no extent table").is_empty());
10155        assert!(reader.payload(held[0]).expect("no payload").is_empty());
10156
10157        fs::remove_file(&path).expect("clean up");
10158    }
10159
10160    #[test]
10161    fn a_payload_past_one_extent_is_split_and_joined_back() {
10162        // Issue #745's rule, exercised rather than argued. One byte past the bound is the smallest
10163        // payload that has to be two extents, and it is the case a split written for the common
10164        // size gets wrong.
10165        let path = linked_file("attach_two_extents", 8);
10166        let payload = vec![0x5a_u8; section::MAX_EXTENT as usize + 1];
10167        attach(
10168            &path,
10169            "items",
10170            &[section::Attachment {
10171                kind: *section::KEY_MAP,
10172                id: 0,
10173                flags: 0,
10174                header_bytes: 0,
10175                bytes: &payload,
10176            }],
10177        )
10178        .expect("attach a payload past the bound");
10179
10180        let reader = Reader::open(&path).expect("reopen");
10181        let held = attached(reader.table());
10182        let extents = reader.extents(held[0]).expect("extent table");
10183        assert_eq!(extents.len(), 2, "one byte past the bound is two extents");
10184        assert_eq!(extents[0].length, section::MAX_EXTENT);
10185        assert_eq!(extents[1].length, 1);
10186        assert_eq!(extents[1].first, u64::from(section::MAX_EXTENT));
10187        // And the extent the caller wants is readable on its own, which is the point of the split.
10188        assert_eq!(reader.extent(&extents[1]).expect("the last extent"), vec![0x5a]);
10189        assert_eq!(reader.payload(held[0]).expect("the whole payload").len(), payload.len());
10190
10191        fs::remove_file(&path).expect("clean up");
10192    }
10193
10194    #[test]
10195    fn a_torn_extent_is_refused_rather_than_decoded() {
10196        let path = linked_file("attach_torn", 8);
10197        let payload = a_key_map_payload();
10198        attach(
10199            &path,
10200            "items",
10201            &[section::Attachment {
10202                kind: *section::KEY_MAP,
10203                id: 0,
10204                flags: 0,
10205                header_bytes: 0,
10206                bytes: &payload,
10207            }],
10208        )
10209        .expect("attach");
10210
10211        let reader = Reader::open(&path).expect("reopen");
10212        let extent = reader.extents(&reader.table().sections()[0]).expect("extent table")[0];
10213        let file = OpenOptions::new().write(true).open(&path).expect("reopen to corrupt");
10214        write_at(&file, extent.offset + 7, &[0xff]).expect("flip a byte of the payload");
10215        drop(file);
10216
10217        let reader = Reader::open(&path).expect("the table still opens");
10218        let error = reader
10219            .payload(&reader.table().sections()[0])
10220            .expect_err("a corrupt payload is not handed out");
10221        assert!(error.to_string().contains("checksum"), "{error}");
10222        // And the table is still readable, which is section 3.1: a section that cannot be trusted
10223        // costs the query its shortcut and nothing else.
10224        assert_eq!(reader.read(0, &[0]).expect("the column is untouched").width(), 1);
10225
10226        fs::remove_file(&path).expect("clean up");
10227    }
10228
10229    #[test]
10230    fn attaching_to_a_file_of_the_previous_format_is_refused_rather_than_done() {
10231        // Readable is not writable. A format 22 directory has no section block, and adding one
10232        // without moving the number in the header would leave a file claiming a format it is not.
10233        let path = linked_file("attach_old_format", 8);
10234        let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
10235        write_at(&file, 8, &22_u32.to_le_bytes()).expect("stamp the older format");
10236        drop(file);
10237
10238        let payload = a_key_map_payload();
10239        let error = attach(
10240            &path,
10241            "items",
10242            &[section::Attachment {
10243                kind: *section::KEY_MAP,
10244                id: 0,
10245                flags: 0,
10246                header_bytes: 0,
10247                bytes: &payload,
10248            }],
10249        )
10250        .expect_err("format 22 cannot gain a section");
10251        assert!(error.to_string().contains("format 22"), "{error}");
10252        assert!(Reader::open(&path).expect("and the file is untouched").table().rows() == 8);
10253
10254        fs::remove_file(&path).expect("clean up");
10255    }
10256
10257    #[test]
10258    fn a_section_header_longer_than_its_payload_is_refused_at_the_write() {
10259        let path = linked_file("attach_bad_header", 8);
10260        let error = attach(
10261            &path,
10262            "items",
10263            &[section::Attachment {
10264                kind: *section::KEY_MAP,
10265                id: 0,
10266                flags: 0,
10267                header_bytes: 40,
10268                bytes: &[1, 2, 3],
10269            }],
10270        )
10271        .expect_err("a writer's bug stops at the write");
10272        assert!(error.to_string().contains("header is longer"), "{error}");
10273
10274        fs::remove_file(&path).expect("clean up");
10275    }
10276
10277    #[test]
10278    fn attaching_to_a_name_the_file_does_not_hold_says_so() {
10279        let path = linked_file("attach_wrong_name", 8);
10280        let error = attach(&path, "orders", &[]).expect_err("no such table");
10281        assert!(error.to_string().contains("orders"), "{error}");
10282        fs::remove_file(&path).expect("clean up");
10283    }
10284
10285    #[test]
10286    fn the_planner_gets_an_exact_count_for_a_leading_value_of_an_incomplete_synopsis() {
10287        // The case a complete synopsis does not cover, and the one worth the most. 16,000 rows over
10288        // 601 distinct values, 10,000 of them holding a single value and the rest spread ten apiece
10289        // over six hundred more. The writer holds 512 values, so the list is a prefix and most of
10290        // the tail is outside it. The counts inside it are still exact, because the pass recounts
10291        // the candidates that survived it, so `id = 1` is ten thousand rows rather than the
10292        // twenty six a distinct count of 601 would divide its way to.
10293        let path = path("frequency_prefix_for_the_planner");
10294        let mut writer =
10295            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10296                .expect("new file");
10297        let mut values = vec![Value::Integer(1); 10_000];
10298        for _ in 0..10 {
10299            values.extend((0..600).map(|tail| Value::Integer(1_000 + tail)));
10300        }
10301        // A vector holds 8,192 rows, so this goes in as several parts. The pass that takes the
10302        // synopsis walks the whole column rather than a part, so the counts are the same either way.
10303        for part in values.chunks(8_000) {
10304            let rows = Chunk::new(vec![
10305                Vector::from_values(LogicalType::Integer, part).expect("integers"),
10306            ])
10307            .expect("one column");
10308            writer.append(&rows).expect("a part");
10309        }
10310        writer.finish().expect("commit");
10311        let reader = Reader::open(&path).expect("reopen from disk");
10312        let prefix =
10313            reader.frequency_prefix(0).expect("a readable synopsis").expect("the column has one");
10314        // A prefix and not the whole column, and the writer said how many rows anything left out of
10315        // it can hold.
10316        assert_eq!(prefix.entries.len(), 512);
10317        assert_eq!(prefix.omitted_max, 10);
10318        let common = Common::new(reader);
10319        assert_eq!(common.rows(), 16_000);
10320        let column = common.column("id").expect("the file has that column");
10321        assert_eq!(
10322            common.rows_with(column, &Bound::Int(1)),
10323            Stat::exact(10_000, Provenance::FrequencySynopsis)
10324        );
10325        // In the prefix, because ties go to the smaller value and the prefix reaches 1,510.
10326        assert_eq!(
10327            common.rows_with(column, &Bound::Int(1_100)),
10328            Stat::exact(10, Provenance::FrequencySynopsis)
10329        );
10330        // Outside it, and a prefix says nothing about a value it does not list. Not zero, which is
10331        // what a complete list would say, and the file holds ten rows of this one.
10332        assert_eq!(common.rows_with(column, &Bound::Int(1_550)), Stat::Unknown);
10333        // Not in the file at all, and still nothing rather than a zero. A prefix cannot tell the
10334        // two apart, which is the whole of what it gives up.
10335        assert_eq!(common.rows_with(column, &Bound::Int(9_999)), Stat::Unknown);
10336        // What the prefix left out, which is what turns the unknown above into a number. The 512
10337        // entries account for 15,110 rows, so 890 are left for the 89 values the writer dropped,
10338        // and 890 over 89 is the ten rows each of them really holds.
10339        let remainder = common.remainder(column).expect("the list is a prefix");
10340        assert_eq!(remainder, Remainder { rows: 890, listed: 512, most: 10 });
10341        assert_eq!(remainder.rows / (601 - remainder.listed), 10);
10342        fs::remove_file(&path).expect("clean up");
10343    }
10344
10345    /// A file with no table in it is a file, and opening it says so rather than failing.
10346    #[test]
10347    fn a_file_holding_no_table_commits_and_opens_and_a_table_can_be_added_to_it() {
10348        let path = path("empty");
10349        Writer::empty(&path, &[]).expect("a file with nothing in it");
10350        let catalog = Catalog::open(&path).expect("the empty file opens");
10351        assert_eq!(catalog.len(), 0);
10352        assert!(catalog.is_empty());
10353        assert_eq!(catalog.names().count(), 0);
10354        // The next generation goes over the top of it the way it goes over any other, which is what
10355        // says this is a committed file and not a special case somebody has to know about.
10356        let mut writer =
10357            Writer::open(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10358                .expect("a table goes into the empty file");
10359        writer.append(&sample_ids()).expect("rows");
10360        writer.finish().expect("commit");
10361        let catalog = Catalog::open(&path).expect("the file opens again");
10362        assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10363        fs::remove_file(&path).expect("clean up");
10364    }
10365
10366    /// A committed table with no rows is a name the next generation takes over, and one with rows
10367    /// is a name it refuses.
10368    ///
10369    /// The refusal is what it always was and it is load bearing: carrying a table that holds rows
10370    /// forward means reading and rewriting its pages, and a writer that quietly wrote a second
10371    /// entry under the same name would leave a file with two tables a reader cannot tell apart. An
10372    /// empty one has no pages and no reader, so there is nothing to carry and nothing to lose, and
10373    /// taking its place is what lets a schema committed by an earlier session be loaded by a stream
10374    /// instead of through memory.
10375    #[test]
10376    fn a_committed_empty_table_gives_up_its_name_and_one_with_rows_does_not() {
10377        let path = path("empty-name");
10378        let field = || vec![Field::required("id", LogicalType::Integer)];
10379        Writer::create(&path, "items", field()).expect("new file").finish().expect("commit");
10380        let catalog = Catalog::open(&path).expect("the file opens");
10381        assert_eq!(catalog.rows().collect::<Vec<_>>(), vec![("items", 0)]);
10382
10383        let mut writer = Writer::open(&path, "items", field()).expect("the empty name is free");
10384        writer.append(&sample_ids()).expect("rows");
10385        writer.finish().expect("commit");
10386        let catalog = Catalog::open(&path).expect("the file opens again");
10387        // One entry and not two. The generation replaced the empty table rather than joining it.
10388        assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10389        let held = catalog.rows().collect::<Vec<_>>();
10390        assert_eq!(held.len(), 1);
10391        assert!(held[0].1 > 0, "the rows that were appended are the ones the catalog counts");
10392
10393        // The same call against the same name now that it holds rows, which is still refused.
10394        let error = Writer::open(&path, "items", field()).expect_err("a name with rows is taken");
10395        assert!(error.to_string().contains("same name"), "{error}");
10396        fs::remove_file(&path).expect("clean up");
10397    }
10398
10399    /// A view, with everything about it that a reopened catalog has to be able to answer from.
10400    fn sample_view(name: &str) -> ViewEntry {
10401        ViewEntry {
10402            name: name.to_string(),
10403            sql: "SELECT id FROM items WHERE id > 0".to_string(),
10404            statement: format!("CREATE VIEW {name} AS SELECT id FROM items WHERE (id > 0);"),
10405            aliases: vec!["n".to_string()],
10406            columns: vec![Field::new("n", LogicalType::Integer)],
10407        }
10408    }
10409
10410    #[test]
10411    fn a_view_written_into_the_catalog_comes_back_whole() {
10412        let path = path("views");
10413        let mut writer =
10414            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10415                .expect("new file");
10416        writer.append(&sample_ids()).expect("rows");
10417        writer.with_views(vec![sample_view("v")]).finish().expect("commit");
10418        let catalog = Catalog::open(&path).expect("reopen");
10419        assert_eq!(catalog.views().cloned().collect::<Vec<_>>(), vec![sample_view("v")]);
10420        // The tables are still there and are still read the same way, so the section on the end did
10421        // not move anything in front of it.
10422        assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10423        fs::remove_file(&path).expect("clean up");
10424    }
10425
10426    /// A writer opened to append a table says nothing about views and must not lose them.
10427    #[test]
10428    fn appending_a_table_carries_the_views_forward() {
10429        let path = path("viewscarry");
10430        let mut writer =
10431            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10432                .expect("new file");
10433        writer.append(&sample_ids()).expect("rows");
10434        writer.with_views(vec![sample_view("v")]).finish().expect("commit");
10435        let mut writer =
10436            Writer::open(&path, "other", vec![Field::required("id", LogicalType::Integer)])
10437                .expect("a second table");
10438        writer.append(&sample_ids()).expect("rows");
10439        writer.finish().expect("commit");
10440        let catalog = Catalog::open(&path).expect("reopen");
10441        assert_eq!(catalog.views().count(), 1);
10442        assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items", "other"]);
10443        fs::remove_file(&path).expect("clean up");
10444    }
10445
10446    /// The whole point of [`Writer::restate`]: the views change and the pages do not move.
10447    #[test]
10448    fn restating_the_views_leaves_every_table_where_it_was() {
10449        let path = path("restate");
10450        let mut writer =
10451            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10452                .expect("new file");
10453        writer.append(&sample_ids()).expect("rows");
10454        writer.finish().expect("commit");
10455        let before = fs::metadata(&path).expect("the file is there").len();
10456        Writer::restate(&path, &[sample_view("v"), sample_view("w")]).expect("two views");
10457        let catalog = Catalog::open(&path).expect("reopen");
10458        assert_eq!(catalog.views().count(), 2);
10459        assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10460        // A catalog on the end and nothing else, so what it grew by is the size of a catalog rather
10461        // than the size of the table.
10462        let after = fs::metadata(&path).expect("the file is there").len();
10463        assert!(after > before, "a generation was written");
10464        assert!(after - before < before, "the table was not written again");
10465        // The rows are still readable through the new generation, which is the part that would go
10466        // wrong if the catalog carried the wrong directory pointers forward.
10467        let reader = Catalog::open(&path).expect("reopen").table("items").expect("the table");
10468        assert_eq!(reader.table().rows, 3);
10469        // And a restate over a restate keeps working, because each one reads the slot that
10470        // checksummed rather than the highest number in the header.
10471        Writer::restate(&path, &[]).expect("no views at all");
10472        assert_eq!(Catalog::open(&path).expect("reopen").views().count(), 0);
10473        fs::remove_file(&path).expect("clean up");
10474    }
10475
10476    /// Two entries under one name is a catalog no lookup can answer, whichever two they are.
10477    #[test]
10478    fn a_view_named_after_a_table_is_refused_when_the_catalog_is_read() {
10479        let bytes = encode_catalog(
10480            &[Entry {
10481                name: "items".to_string(),
10482                fields: vec![Field::required("id", LogicalType::Integer)],
10483                rows: 1,
10484                directory: Page { offset: HEADER, length: 8, hash: 0 },
10485            }],
10486            &[sample_view("items")],
10487        )
10488        .expect("it encodes, because encoding does not look");
10489        let error = decode_catalog(&bytes, HEADER + 8).expect_err("and decoding does");
10490        assert!(error.to_string().contains("same name"), "{error}");
10491    }
10492
10493    #[test]
10494    fn committed_file_reopens_and_reads_only_requested_columns() {
10495        let path = path("reopen");
10496        let mut writer = Writer::create(
10497            &path,
10498            "items",
10499            vec![
10500                Field::required("id", LogicalType::Integer),
10501                Field::new("text", LogicalType::Varchar),
10502            ],
10503        )
10504        .expect("new file");
10505        writer.append(&sample()).expect("first part");
10506        writer.append(&sample()).expect("second part");
10507        writer.finish().expect("commit");
10508        let reader = Reader::open(&path).expect("reopen from disk");
10509        assert_eq!(reader.table().rows(), 6);
10510        // Two appends below the stripe bound are two parts of one stripe, which is the whole point
10511        // of the split: the directory describes the stripe and the scan still reads a part.
10512        assert_eq!(reader.table().stripes().len(), 1);
10513        assert_eq!(reader.parts(), 2);
10514        assert_eq!(reader.part_rows(0), 3);
10515        assert_eq!(reader.part_rows(1), 3);
10516        let text = reader.read(1, &[1]).expect("only text page");
10517        assert_eq!(text.width(), 1);
10518        assert_eq!(text.value_at(1, 0), Value::Null);
10519        assert_eq!(text.value_at(2, 0), Value::Varchar("long text after a slash".into()));
10520        let sparse = reader.read_sparse(1, &[1]).expect("one part without its whole page");
10521        assert_eq!(sparse.width(), 1);
10522        assert_eq!(sparse.value_at(1, 0), Value::Null);
10523        assert_eq!(sparse.value_at(2, 0), Value::Varchar("long text after a slash".into()));
10524        assert!(!reader.skips_codes(0, 1, &[0]).expect("alpha is in the stripe"));
10525        assert!(!reader.skips_codes(0, 1, &[2]).expect("long text is in the stripe"));
10526        assert!(reader.skips_codes(0, 1, &[3]).expect("unknown code is absent"));
10527        let count = reader.read(0, &[]).expect("no page is needed for count");
10528        assert_eq!(count.len(), 3);
10529        assert!(reader.skips(0, &[Probe { column: 0, op: Op::Greater, value: Bound::Int(100) }]));
10530        assert!(!reader.skips(0, &[Probe { column: 0, op: Op::Greater, value: Bound::Int(0) }]));
10531        let integers = reader.top_frequencies(0, 1).expect("valid integer synopsis").expect("kept");
10532        assert_eq!(
10533            integers,
10534            vec![(Value::Integer(-2), 2), (Value::Integer(4), 2), (Value::Integer(9), 2),]
10535        );
10536        let strings = reader.top_frequencies(1, 1).expect("valid string synopsis").expect("kept");
10537        assert_eq!(strings.len(), 3);
10538        assert!(strings.contains(&(Value::Null, 2)));
10539        assert!(strings.contains(&(Value::Varchar("alpha".into()), 2)));
10540        assert!(strings.contains(&(Value::Varchar("long text after a slash".into()), 2)));
10541        fs::remove_file(path).expect("remove scratch file");
10542    }
10543
10544    /// Two pipeline instances handing over whole runs, which is what makes the native sink safe to
10545    /// instance.
10546    ///
10547    /// The runs arrive in the order the instances finished reading them rather than in source
10548    /// order, and the second one to finish is the one that read the earlier rows. Each run is still
10549    /// a stripe of its own and the table still reads back in source order, which is the whole of
10550    /// what the writer promises about ordering.
10551    #[test]
10552    fn runs_handed_over_out_of_order_still_read_back_in_source_order() {
10553        let path = path("interleaved-runs");
10554        let mut writer =
10555            Writer::create(&path, "interleaved", vec![Field::new("v", LogicalType::BigInt)])
10556                .expect("new file");
10557        for morsel in [2_u64, 0, 3, 1] {
10558            let parts = (0..4_u64)
10559                .map(|chunk| {
10560                    let first = i64::try_from(morsel * 32 + chunk * 8).expect("small");
10561                    let values =
10562                        (0..8_i64).map(|row| Value::BigInt(first + row)).collect::<Vec<_>>();
10563                    let column =
10564                        Vector::from_values(LogicalType::BigInt, &values).expect("a column");
10565                    ((morsel, chunk), Chunk::new(vec![column]).expect("one column"))
10566                })
10567                .collect::<Vec<_>>();
10568            writer.append_stripe(parts).expect("a stripe");
10569        }
10570        writer.finish().expect("commit");
10571
10572        let reader = Reader::open(&path).expect("valid directory");
10573        assert_eq!(reader.table().stripes().len(), 4, "a run is a stripe of its own");
10574        assert_eq!(reader.table().rows(), 128);
10575        for part in 0..16_usize {
10576            let read = reader.read(part, &[0]).expect("a part back");
10577            for row in 0..8_usize {
10578                let want = i64::try_from(part * 8 + row).expect("small");
10579                assert_eq!(read.value_at(row, 0), Value::BigInt(want), "part {part} row {row}");
10580            }
10581        }
10582        fs::remove_file(path).expect("remove scratch file");
10583    }
10584
10585    /// Runs from different callers may interleave and may not overlap, and the commit is what
10586    /// catches an overlap.
10587    #[test]
10588    fn runs_that_overlap_each_other_are_refused_at_commit() {
10589        let path = path("overlapping-runs");
10590        let mut writer =
10591            Writer::create(&path, "overlapping", vec![Field::new("v", LogicalType::BigInt)])
10592                .expect("new file");
10593        let one = |order: (u64, u64)| {
10594            let column =
10595                Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a column");
10596            (order, Chunk::new(vec![column]).expect("one column"))
10597        };
10598        // The second run sits inside the first rather than after it, which is a thing no instance
10599        // holding its own contiguous run can produce and a thing the file cannot represent.
10600        writer.append_stripe(vec![one((0, 0)), one((0, 2))]).expect("a stripe");
10601        writer.append_stripe(vec![one((0, 1))]).expect("a stripe");
10602        let error = writer.finish().expect_err("the runs overlap");
10603        assert!(error.message().contains("source order"), "{error}");
10604        fs::remove_file(path).expect("remove scratch file");
10605    }
10606
10607    /// A stripe holds [`STRIPE_PARTS`] parts, so a run longer than that is a caller bug rather than
10608    /// something to split, and the writer says so at the door instead of quietly cutting it in two.
10609    #[test]
10610    fn a_run_longer_than_a_stripe_is_refused() {
10611        let path = path("overlong-run");
10612        let mut writer =
10613            Writer::create(&path, "overlong", vec![Field::new("v", LogicalType::BigInt)])
10614                .expect("new file");
10615        let parts = (0..=STRIPE_PARTS)
10616            .map(|at| {
10617                let column = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)])
10618                    .expect("a column");
10619                let chunk = Chunk::new(vec![column]).expect("one column");
10620                ((0, u64::try_from(at).expect("small")), chunk)
10621            })
10622            .collect::<Vec<_>>();
10623        let error = writer.append_stripe(parts).expect_err("one part too many");
10624        assert!(error.message().contains("more parts than it holds"), "{error}");
10625        fs::remove_file(path).expect("remove scratch file");
10626    }
10627
10628    /// Parts past the stripe bound start a new stripe, and every part stays addressable on its own.
10629    ///
10630    /// This is the shape the format exists for, so both ends of the split are checked here. The
10631    /// directory holds three stripes rather than a hundred and thirty one, and a read of any one
10632    /// part still answers with that part's rows rather than with its whole stripe's.
10633    #[test]
10634    fn parts_past_the_stripe_bound_start_a_new_stripe() {
10635        let path = path("stripe-bound");
10636        let mut writer = Writer::create(
10637            &path,
10638            "items",
10639            vec![
10640                Field::required("id", LogicalType::Integer),
10641                Field::new("text", LogicalType::Varchar),
10642            ],
10643        )
10644        .expect("new file");
10645        let parts = STRIPE_PARTS * 2 + 3;
10646        for part in 0..parts {
10647            let id = part as i32;
10648            let chunk = Chunk::new(vec![
10649                Vector::from_values(
10650                    LogicalType::Integer,
10651                    &[Value::Integer(id), Value::Integer(-id)],
10652                )
10653                .expect("integers"),
10654                Vector::from_values(
10655                    LogicalType::Varchar,
10656                    &[Value::Varchar(format!("value {part}")), Value::Null],
10657                )
10658                .expect("strings"),
10659            ])
10660            .expect("matching rows");
10661            writer.append(&chunk).expect("one part");
10662        }
10663        writer.finish().expect("commit");
10664
10665        let reader = Reader::open(&path).expect("reopen from disk");
10666        assert_eq!(reader.parts(), parts);
10667        assert_eq!(reader.table().rows(), parts * 2);
10668        assert_eq!(reader.table().stripes().len(), parts.div_ceil(STRIPE_PARTS));
10669        assert_eq!(reader.table().stripes()[0].parts(), STRIPE_PARTS);
10670        assert_eq!(reader.table().stripes()[0].rows(), STRIPE_PARTS * 2);
10671        assert_eq!(reader.table().stripes()[2].parts(), 3);
10672        // Backwards on purpose. The reader keeps four stripes a column, so a scan that walks the
10673        // table the other way is what catches a cache that only ever holds what it just read.
10674        for part in (0..parts).rev() {
10675            let dense = reader.read(part, &[0, 1]).expect("a whole page read");
10676            let sparse = reader.read_sparse(part, &[0, 1]).expect("one part read");
10677            for chunk in [&dense, &sparse] {
10678                assert_eq!(chunk.len(), 2, "part {part} has its own row count");
10679                assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
10680                assert_eq!(chunk.value_at(1, 0), Value::Integer(-(part as i32)));
10681                assert_eq!(chunk.value_at(0, 1), Value::Varchar(format!("value {part}")));
10682                assert_eq!(chunk.value_at(1, 1), Value::Null);
10683            }
10684        }
10685        // The bounds are merged over the stripe, so they answer for the range the whole stripe
10686        // covers and not for the part that was asked about.
10687        let above = [Probe { column: 0, op: Op::Greater, value: Bound::Int(100) }];
10688        assert!(reader.skips(0, &above), "the first stripe stops at 63");
10689        assert!(!reader.skips(STRIPE_PARTS * 2, &above), "the third stripe reaches 130");
10690        fs::remove_file(path).expect("remove scratch file");
10691    }
10692
10693    /// A scattered value in the column that decides `WHERE UserID = ?`.
10694    fn scattered(n: i64) -> i64 {
10695        n.wrapping_mul(-7_046_029_254_386_353_131)
10696    }
10697
10698    /// A part whose sieve does not hold the constant is skipped, and a range would skip none of them.
10699    ///
10700    /// This is ClickBench query 19 in miniature. The values are spread over the whole of `BIGINT`, so
10701    /// every stripe's bounds cover nearly all of it and rule out nothing, and the part that really
10702    /// holds the value is the only one a scan has to read.
10703    #[test]
10704    fn a_part_is_skipped_when_its_sieve_does_not_hold_the_constant() {
10705        let path = path("sieve-skip");
10706        let mut writer =
10707            Writer::create(&path, "hits", vec![Field::required("id", LogicalType::BigInt)])
10708                .expect("new file");
10709        let parts = STRIPE_PARTS + 3;
10710        // Big enough that the filter is worth its bytes. A part of eight numbers packs to under a
10711        // hundred bytes and the smallest filter there is is sixty nine, so a filter over a part
10712        // that small costs about as much to read as the rows do and is no longer written.
10713        let per_part = 128;
10714        for part in 0..parts {
10715            let held: Vec<Value> = (0..per_part)
10716                .map(|row| Value::BigInt(scattered((part * per_part + row) as i64)))
10717                .collect();
10718            let chunk =
10719                Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10720                    .expect("one column");
10721            writer.append(&chunk).expect("one part");
10722        }
10723        writer.finish().expect("commit");
10724
10725        let reader = Reader::open(&path).expect("reopen from disk");
10726        let probe = |value: i64| Probe {
10727            column: 0,
10728            op: Op::Equal,
10729            value: Bound::Int(i128::from(scattered(value))),
10730        };
10731        for wanted in [0_i64, (per_part + 1) as i64, (parts * per_part - 1) as i64] {
10732            let tests = [probe(wanted)];
10733            let kept: Vec<usize> = (0..parts).filter(|&part| !reader.skips(part, &tests)).collect();
10734            let home = wanted as usize / per_part;
10735            assert!(kept.contains(&home), "the part holding {wanted} is read");
10736            // A filter answers maybe, so a part it keeps need not hold the value. Sixty seven parts
10737            // of a hundred and twenty eight numbers each, at a dozen bits a value, is about one
10738            // stray part across the whole file and that is what this leaves room for.
10739            assert!(kept.len() <= 2, "{wanted} keeps {kept:?}, which is more than one stray part");
10740        }
10741        let absent = [probe((parts * per_part) as i64 + 1)];
10742        let kept = (0..parts).filter(|&part| !reader.skips(part, &absent)).count();
10743        assert!(kept <= 1, "{kept} parts of {parts} kept a value no part holds");
10744        // The same probes against the bounds alone, which is what this replaces. A column of
10745        // scattered numbers has a range per stripe that covers nearly the whole type.
10746        let tests = [probe(0)];
10747        assert!(
10748            reader.table().stripes().iter().all(|stripe| !stripe.zone.skips(&tests)),
10749            "the bounds rule out no stripe at all"
10750        );
10751        fs::remove_file(path).expect("remove scratch file");
10752    }
10753
10754    /// A part whose own bounds rule out an ordered comparison is skipped where the stripe's keep it.
10755    ///
10756    /// This is the shape of ClickBench 24. Each part covers a narrow stretch of the column and the
10757    /// stripe covers all sixty four of them at once, so a comparison that lands inside the stripe
10758    /// rules out none of it and rules out all but a few parts.
10759    #[test]
10760    fn a_part_is_skipped_when_its_own_bounds_rule_out_a_comparison_the_stripe_keeps() {
10761        let path = path("part-range-skip");
10762        let mut writer =
10763            Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10764                .expect("new file");
10765        let parts = STRIPE_PARTS + 3;
10766        let per_part = 128;
10767        for part in 0..parts {
10768            // Scattered inside the part's own band rather than a run, because a run of
10769            // consecutive numbers encodes to a stride of a few bytes and then the page of ranges
10770            // costs more than reading the column it indexes, which is the case the writer declines.
10771            let held: Vec<Value> = (0..per_part)
10772                .map(|row| {
10773                    Value::BigInt((part * 1_000) as i64 + (scattered(row as i64).rem_euclid(900)))
10774                })
10775                .collect();
10776            let chunk =
10777                Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10778                    .expect("one column");
10779            writer.append(&chunk).expect("one part");
10780        }
10781        writer.finish().expect("commit");
10782
10783        let reader = Reader::open(&path).expect("reopen from disk");
10784        let under = [Probe { column: 0, op: Op::Less, value: Bound::Int(3_000) }];
10785        let kept: Vec<usize> = (0..parts).filter(|&part| !reader.skips(part, &under)).collect();
10786        assert_eq!(kept, vec![0, 1, 2], "only the three parts that start under three thousand");
10787        // The same question asked of the stripe alone, which is what this replaces.
10788        assert!(!reader.stripe_skips(0, &under), "the stripe reaches from zero and keeps itself");
10789        fs::remove_file(path).expect("remove scratch file");
10790    }
10791
10792    /// The other half of the same page. A part whose own bounds put every row of it inside the
10793    /// filter is waved through, so the comparison never runs on it, where the stripe's bounds reach
10794    /// across every part and can prove nothing.
10795    #[test]
10796    fn a_part_is_waved_through_when_its_own_bounds_pass_a_comparison_the_stripe_cannot() {
10797        let path = path("part-range-certain");
10798        let mut writer =
10799            Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10800                .expect("new file");
10801        let parts = STRIPE_PARTS + 3;
10802        let per_part = 128;
10803        for part in 0..parts {
10804            let held: Vec<Value> = (0..per_part)
10805                .map(|row| {
10806                    Value::BigInt((part * 1_000) as i64 + (scattered(row as i64).rem_euclid(900)))
10807                })
10808                .collect();
10809            let chunk =
10810                Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10811                    .expect("one column");
10812            writer.append(&chunk).expect("one part");
10813        }
10814        writer.finish().expect("commit");
10815
10816        let reader = Reader::open(&path).expect("reopen from disk");
10817        let under = [Probe { column: 0, op: Op::Less, value: Bound::Int(3_000) }];
10818        let waved: Vec<usize> = (0..parts).filter(|&part| reader.certain(part, &under)).collect();
10819        assert_eq!(waved, vec![0, 1, 2], "the three parts that end under three thousand");
10820        // The first stripe reaches from zero to past sixty thousand, so it straddles three thousand
10821        // and settles nothing either way. The three yeses above are the parts' own ends talking.
10822        assert!(!reader.stripe_skips(0, &under), "the stripe straddles the comparison");
10823        fs::remove_file(path).expect("remove scratch file");
10824    }
10825
10826    /// The page is worth its bytes on a column with parts to tell apart and is not written on one
10827    /// that has a single part, where the stripe bounds already are the part's.
10828    #[test]
10829    fn a_stripe_of_one_part_writes_no_range_page_and_a_stripe_of_many_does() {
10830        for (parts, wanted) in [(1_usize, false), (STRIPE_PARTS, true)] {
10831            let path = path("part-range-page");
10832            let mut writer =
10833                Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10834                    .expect("new file");
10835            for part in 0..parts {
10836                let held: Vec<Value> = (0..128)
10837                    .map(|row| {
10838                        Value::BigInt((part * 1_000) as i64 + scattered(row as i64).rem_euclid(900))
10839                    })
10840                    .collect();
10841                let chunk = Chunk::new(vec![
10842                    Vector::from_values(LogicalType::BigInt, &held).expect("numbers"),
10843                ])
10844                .expect("one column");
10845                writer.append(&chunk).expect("one part");
10846            }
10847            writer.finish().expect("commit");
10848            let reader = Reader::open(&path).expect("reopen from disk");
10849            let bytes = reader.layout().columns[0].part_ranges;
10850            assert_eq!(bytes > 0, wanted, "{parts} parts wrote {bytes} bytes of ranges");
10851            fs::remove_file(path).expect("remove scratch file");
10852        }
10853    }
10854
10855    /// A cut down string end is still an end on the side it was, which is the only thing that keeps
10856    /// a shortened bound from turning a skip into a wrong answer.
10857    #[test]
10858    fn a_string_end_that_is_cut_down_still_covers_the_value_it_came_from() {
10859        let long = vec![b'a'; PART_BOUND_BYTES * 2];
10860        let low = shortened(Some(Bound::Bytes(long.clone())), false).expect("a low end");
10861        let high = shortened(Some(Bound::Bytes(long.clone())), true).expect("a high end");
10862        let Bound::Bytes(low) = low else { panic!("a string stays a string") };
10863        let Bound::Bytes(high) = high else { panic!("a string stays a string") };
10864        assert!(low.len() <= PART_BOUND_BYTES && high.len() <= PART_BOUND_BYTES);
10865        assert!(low.as_slice() <= long.as_slice(), "the low end is at or under the value");
10866        assert!(high.as_slice() >= long.as_slice(), "the high end is at or over the value");
10867    }
10868
10869    /// A string of nothing but the largest byte has no prefix that can be stepped up, so the high
10870    /// end is given up rather than claimed too small. No end keeps the part, which is always safe.
10871    #[test]
10872    fn a_string_end_with_no_room_to_step_up_gives_up_the_bound() {
10873        let long = vec![u8::MAX; PART_BOUND_BYTES * 2];
10874        assert_eq!(shortened(Some(Bound::Bytes(long.clone())), true), None);
10875        let low = shortened(Some(Bound::Bytes(long)), false).expect("a low end is still a prefix");
10876        assert_eq!(low, Bound::Bytes(vec![u8::MAX; PART_BOUND_BYTES]));
10877    }
10878
10879    /// What a column is stored as, asked of two files holding the same rows in a different order.
10880    ///
10881    /// This is the question the report exists to answer and it is the one the directory cannot. The
10882    /// two files have the same rows, the same schema and the same number of parts, and the column
10883    /// comes out four times smaller in one of them, because ascending keys delta encode to a few
10884    /// bits a row and shuffled ones do not. Nothing about the file's shape says so. The page header
10885    /// says so, and reading it is what this does.
10886    ///
10887    /// It is q18 on TPC-H in miniature: clustering lineitem by ship date leaves `l_orderkey`
10888    /// ascending inside a partition but sparse, its deltas go from six bits to twelve, and the scan
10889    /// pays for the wider ones.
10890    #[test]
10891    fn what_a_column_is_stored_as_follows_the_order_the_rows_were_written_in() {
10892        let parts = 4;
10893        let per_part = 1024;
10894        let rows = parts * per_part;
10895        let written = |name: &str, keys: &[i64]| {
10896            let path = path(name);
10897            let fields = vec![Field::required("key", LogicalType::BigInt)];
10898            let mut writer = Writer::create(&path, "keys", fields).expect("new file");
10899            for part in 0..parts {
10900                let values: Vec<Value> = keys[part * per_part..(part + 1) * per_part]
10901                    .iter()
10902                    .map(|key| Value::BigInt(*key))
10903                    .collect();
10904                let chunk = Chunk::new(vec![
10905                    Vector::from_values(LogicalType::BigInt, &values).expect("numbers"),
10906                ])
10907                .expect("one column");
10908                writer.append(&chunk).expect("one part");
10909            }
10910            writer.finish().expect("commit");
10911            path
10912        };
10913        // Ascending with a small irregular step, which is what a key column in arrival order looks
10914        // like: an order has one to seven line items, so the key repeats and then moves on by one.
10915        let climbing = |step: &dyn Fn(usize) -> i64| {
10916            let mut key = 0;
10917            (0..rows)
10918                .map(|row| {
10919                    key += step(row);
10920                    key
10921                })
10922                .collect::<Vec<i64>>()
10923        };
10924        let ascending = climbing(&|row| (row % 3) as i64);
10925        // The same rows in the same direction over a range a thousand times wider, which is what a
10926        // partition of a clustered table holds: still ascending, and far enough apart that the
10927        // deltas no longer fit in a handful of bits.
10928        let sparse = climbing(&|row| ((row * 2_654_435_761) % 4096) as i64);
10929        let near_path = written("stored-near", &ascending);
10930        let far_path = written("stored-far", &sparse);
10931
10932        let one = Reader::open(&near_path).expect("reopen from disk");
10933        let other = Reader::open(&far_path).expect("reopen from disk");
10934        let near = one.stored(0).expect("the column is stored");
10935        let far = other.stored(0).expect("the column is stored");
10936        assert_eq!(near.len(), parts, "one row per part");
10937        assert_eq!(far.len(), parts);
10938        // The bytes are the same bytes the directory totals, which is the check that this is
10939        // reading the pages the file really holds rather than some other pages.
10940        let total = |stored: &[StoredPart]| stored.iter().map(|part| part.bytes).sum::<u64>();
10941        assert_eq!(total(&near), one.layout().columns[0].pages);
10942        assert_eq!(total(&far), other.layout().columns[0].pages);
10943        assert!(
10944            total(&near) * 2 < total(&far),
10945            "the sparse keys cost more, {} against {}",
10946            total(&far),
10947            total(&near)
10948        );
10949        // Every part accounted for, in order, with the row it starts at following the one before.
10950        for (at, part) in near.iter().enumerate() {
10951            assert_eq!(part.part, at);
10952            assert_eq!(part.row, at * per_part);
10953            assert_eq!(part.rows, per_part);
10954            let held = &ascending[at * per_part..(at + 1) * per_part];
10955            assert_eq!(part.low, Some(Value::BigInt(held[0])));
10956            assert_eq!(part.high, Some(Value::BigInt(held[per_part - 1])));
10957            assert_eq!(part.nulls, Some(0));
10958        }
10959        // And the encoding is a line of text that names what the encoder chose, which is the whole
10960        // point. Both are a cascade over deltas and the widths inside them are what differ.
10961        assert!(near[0].encoding.contains("DELTA"), "{}", near[0].encoding);
10962        assert!(far[0].encoding.contains("DELTA"), "{}", far[0].encoding);
10963        assert_ne!(near[0].encoding, far[0].encoding);
10964        fs::remove_file(near_path).expect("remove scratch file");
10965        fs::remove_file(far_path).expect("remove scratch file");
10966    }
10967
10968    /// A sieve bigger than the part it indexes is not written, and one smaller than it still is.
10969    ///
10970    /// Both columns hold values spread over the whole of `BIGINT`, so neither gets a bitmap and both
10971    /// reach the filter. They differ in what the part costs to read. `spread` is a thousand distinct
10972    /// numbers and packs to eight kilobytes, so a filter of about thirteen hundred bytes is a good
10973    /// trade. `repeated` is the same thousand rows over four numbers in runs and encodes to
10974    /// almost nothing, but the filter is sized for the rows rather than the values it turns out to
10975    /// hold, so it comes out larger than the data. Reading it to decide whether to read the part spends more than
10976    /// the part, every time, and that is the case this drops.
10977    #[test]
10978    fn a_sieve_larger_than_the_part_it_indexes_is_not_written() {
10979        let path = path("sieve-pays");
10980        let fields = vec![
10981            Field::required("spread", LogicalType::BigInt),
10982            Field::required("repeated", LogicalType::BigInt),
10983        ];
10984        let mut writer = Writer::create(&path, "hits", fields).expect("new file");
10985        let parts = 3;
10986        let per_part = 1024;
10987        for part in 0..parts {
10988            let base = (part * per_part) as i64;
10989            let spread: Vec<Value> =
10990                (0..per_part).map(|row| Value::BigInt(scattered(base + row as i64))).collect();
10991            let repeated: Vec<Value> =
10992                (0..per_part).map(|row| Value::BigInt(scattered((row / 256) as i64))).collect();
10993            let chunk = Chunk::new(vec![
10994                Vector::from_values(LogicalType::BigInt, &spread).expect("numbers"),
10995                Vector::from_values(LogicalType::BigInt, &repeated).expect("numbers"),
10996            ])
10997            .expect("two columns");
10998            writer.append(&chunk).expect("one part");
10999        }
11000        writer.finish().expect("commit");
11001
11002        let reader = Reader::open(&path).expect("reopen from disk");
11003        let layout = reader.layout();
11004        let spread = &layout.columns[0];
11005        let repeated = &layout.columns[1];
11006        assert!(spread.sieves > 0, "a column whose parts are worth a filter keeps one");
11007        assert_eq!(
11008            repeated.sieves, 0,
11009            "a column whose filter costs more than its parts keeps none"
11010        );
11011        // Per part this is the rule itself, so it holds over the column as well: a part without a
11012        // sieve adds to one side of this and to nothing on the other.
11013        for column in &layout.columns {
11014            assert!(
11015                column.sieves < column.pages,
11016                "{} spends {} on sieves over {} of data",
11017                column.name,
11018                column.sieves,
11019                column.pages
11020            );
11021        }
11022        // The filter that was kept still does what it is for.
11023        let absent = [Probe {
11024            column: 0,
11025            op: Op::Equal,
11026            value: Bound::Int(i128::from(scattered((parts * per_part) as i64 + 1))),
11027        }];
11028        assert!((0..parts).all(|part| reader.skips(part, &absent)), "no part holds it");
11029        fs::remove_file(path).expect("remove scratch file");
11030    }
11031
11032    /// A damaged sieve page is a part that gets read, not a query that fails.
11033    ///
11034    /// A sieve is an index over rows that are still there and still correct, so losing one costs
11035    /// time and costs no answers. That is the opposite of the membership index beside it, which is
11036    /// the only thing standing between a string page and a wrong answer.
11037    #[test]
11038    fn a_damaged_sieve_page_is_read_through_rather_than_refused() {
11039        let path = path("sieve-damaged");
11040        let mut writer =
11041            Writer::create(&path, "hits", vec![Field::required("id", LogicalType::BigInt)])
11042                .expect("new file");
11043        let rows = 128;
11044        let held: Vec<Value> = (0..rows).map(|row| Value::BigInt(scattered(row))).collect();
11045        let chunk =
11046            Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
11047                .expect("one column");
11048        writer.append(&chunk).expect("one part");
11049        writer.finish().expect("commit");
11050
11051        let page = Reader::open(&path).expect("reopen").table.stripes[0]
11052            .sieves
11053            .get(0)
11054            .expect("a sieve page");
11055        let mut file = OpenOptions::new().write(true).open(&path).expect("open the sieve page");
11056        file.seek(SeekFrom::Start(page.offset + u64::from(page.length) - 1)).expect("seek");
11057        file.write_all(&[0xff]).expect("damage one byte");
11058        drop(file);
11059
11060        let reader = Reader::open(&path).expect("reopen the damaged file");
11061        let absent =
11062            [Probe { column: 0, op: Op::Equal, value: Bound::Int(i128::from(scattered(99))) }];
11063        assert!(!reader.skips(0, &absent), "a sieve that cannot be read skips nothing");
11064        assert_eq!(
11065            reader.read(0, &[0]).expect("the rows are untouched").len(),
11066            usize::try_from(rows).expect("a small count")
11067        );
11068        fs::remove_file(path).expect("remove scratch file");
11069    }
11070
11071    /// Eight workers over one stripe read it once between them.
11072    ///
11073    /// This is the shape a scan actually has. Parts are handed out in order, so every worker on a
11074    /// column crosses into a stripe within a few parts of the others, and before [`Reader::held`]
11075    /// started sharing the read every one of them read the whole page. On the full ClickBench file
11076    /// that was a `MIN(EventDate), MAX(EventDate)` moving 3.2 GB off the disk to look at 400 MB of
11077    /// column, which is most of what a first touch costs.
11078    ///
11079    /// The workers that lose the race still answer, out of the part reads they do instead, which is
11080    /// what the values below are checking.
11081    #[test]
11082    fn workers_that_want_the_same_stripe_read_it_once() {
11083        let path = path("single-flight");
11084        let mut writer =
11085            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11086                .expect("new file");
11087        for part in 0..STRIPE_PARTS {
11088            let id = part as i32;
11089            let chunk = Chunk::new(vec![
11090                Vector::from_values(
11091                    LogicalType::Integer,
11092                    &[Value::Integer(id), Value::Integer(-id)],
11093                )
11094                .expect("integers"),
11095            ])
11096            .expect("matching rows");
11097            writer.append(&chunk).expect("one part");
11098        }
11099        writer.finish().expect("commit");
11100
11101        let reader = Reader::open(&path).expect("reopen from disk");
11102        assert_eq!(reader.table().stripes().len(), 1, "one stripe is the point of the test");
11103        let barrier = std::sync::Barrier::new(8);
11104        std::thread::scope(|scope| {
11105            for worker in 0..8 {
11106                let reader = &reader;
11107                let barrier = &barrier;
11108                scope.spawn(move || {
11109                    barrier.wait();
11110                    for part in (worker..STRIPE_PARTS).step_by(8) {
11111                        let chunk = reader.read(part, &[0]).expect("a whole page read");
11112                        assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11113                        assert_eq!(chunk.value_at(1, 0), Value::Integer(-(part as i32)));
11114                    }
11115                });
11116            }
11117        });
11118        assert_eq!(reader.pages.load(Atomic::Relaxed), 1, "one stripe, one page read, whoever won");
11119        fs::remove_file(path).expect("remove scratch file");
11120    }
11121
11122    /// Opening a file reads the header and the directory, and nothing that depends on the rows.
11123    ///
11124    /// `spec/stats/04-in-memory.md` section 4.2. There are no statistics in the file yet, so this
11125    /// holds today by not having anything to load, and that is exactly why it is worth pinning now.
11126    /// The change that breaks it is the reasonable looking one: summaries are a few hundred bytes,
11127    /// the next query will want them, so read them on the way past. A process that opened the
11128    /// database to run one trivial query pays for all of it and gets nothing.
11129    ///
11130    /// Two files of the same shape and a thousand times the rows in one of them, opened, and the
11131    /// two openings cost the same. The stripe count is held equal so that the directory is the same
11132    /// size in both, which leaves the rows as the only thing that changed. Anything read out of the
11133    /// data would show up here.
11134    #[test]
11135    fn opening_costs_the_same_over_a_thousand_times_the_rows() {
11136        let opened = |label: &str, rows_per_part: i32| {
11137            let path = path(label);
11138            let mut writer =
11139                Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11140                    .expect("new file");
11141            for part in 0..STRIPE_PARTS * 3 {
11142                // Scrambled rather than sequential, so that the fat file is actually fatter. A run
11143                // of consecutive integers encodes to almost nothing and would leave the two files
11144                // the same size, which would make this test pass for the wrong reason.
11145                let values = (0..rows_per_part)
11146                    .map(|row| {
11147                        Value::Integer((part as i32 * rows_per_part + row).wrapping_mul(2_654_435))
11148                    })
11149                    .collect::<Vec<_>>();
11150                let chunk = Chunk::new(vec![
11151                    Vector::from_values(LogicalType::Integer, &values).expect("integers"),
11152                ])
11153                .expect("matching rows");
11154                writer.append(&chunk).expect("one part");
11155            }
11156            writer.finish().expect("commit");
11157            let reader = Reader::open(&path).expect("reopen from disk");
11158            let size = fs::metadata(&path).expect("the file is there").len();
11159            let out = (reader.reads(), reader.table().stripes().len(), size);
11160            fs::remove_file(path).expect("remove scratch file");
11161            out
11162        };
11163
11164        let (thin, thin_stripes, thin_size) = opened("open-thin", 1);
11165        let (fat, fat_stripes, fat_size) = opened("open-fat", 1000);
11166        assert_eq!(
11167            thin_stripes, fat_stripes,
11168            "the same stripe count is what makes this a fair ask"
11169        );
11170        assert!(
11171            fat_size > thin_size * 50,
11172            "the fat file has to actually be larger, and it is {fat_size} against {thin_size}"
11173        );
11174
11175        assert_eq!(thin.opening.reads, fat.opening.reads, "the same reads either way");
11176        assert_eq!(thin.pages, 0, "opening read a page");
11177        assert_eq!(fat.pages, 0, "opening read a page");
11178        assert_eq!(thin.indexes, 0, "opening read an index");
11179        assert_eq!(fat.indexes, 0, "opening read an index");
11180        // Not exactly equal, because a directory holds offsets and a larger file has larger ones,
11181        // and a handful of bytes of varint is not somebody loading statistics. A factor is.
11182        assert!(
11183            fat.opening.bytes < thin.opening.bytes * 2,
11184            "opening the thin file read {} bytes and the fat one read {}",
11185            thin.opening.bytes,
11186            fat.opening.bytes
11187        );
11188    }
11189
11190    /// The reads a file costs to open are fixed by its shape and not by what ran before.
11191    ///
11192    /// `spec/stats/04-in-memory.md` section 4.3, which is the rule that keeps a plan reproducible:
11193    /// the plan is a function of the data, the generation and the settings, and never of what
11194    /// happened to be in cache. Opening the same file twice in the same process has to cost the
11195    /// same, because a second open that read less would be an open that was about to plan
11196    /// differently.
11197    #[test]
11198    fn two_opens_of_one_file_cost_the_same_and_the_second_is_not_cheaper() {
11199        let path = path("open-twice");
11200        let mut writer =
11201            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11202                .expect("new file");
11203        for part in 0..STRIPE_PARTS * 3 {
11204            let chunk = Chunk::new(vec![
11205                Vector::from_values(LogicalType::Integer, &[Value::Integer(part as i32)])
11206                    .expect("integers"),
11207            ])
11208            .expect("matching rows");
11209            writer.append(&chunk).expect("one part");
11210        }
11211        writer.finish().expect("commit");
11212
11213        let first = Reader::open(&path).expect("open");
11214        // A whole scan in between, so the operating system's page cache is as warm as it gets and
11215        // anything that consulted it would show up in the second open.
11216        for part in 0..first.parts() {
11217            first.read(part, &[0]).expect("a part");
11218        }
11219        assert!(first.reads().pages > 0, "the scan has to have read something");
11220        let second = Reader::open(&path).expect("open again");
11221
11222        assert_eq!(first.reads().opening, second.reads().opening);
11223        assert_eq!(
11224            second.reads().pages,
11225            0,
11226            "the second open read a page off the back of the first"
11227        );
11228        assert_eq!(second.reads().indexes, 0, "the second open read an index it inherited");
11229        fs::remove_file(path).expect("remove scratch file");
11230    }
11231
11232    /// A scan reads a stripe's index once for the whole scan, not once per part that misses.
11233    ///
11234    /// The page cache holds four stripes and an index used to ride inside it, so a table with more
11235    /// stripes than that read the index again every time a stripe came back around. The index is a
11236    /// few hundred bytes and the page is a quarter of a megabyte, which is why they are now under
11237    /// different budgets. This is the test that keeps them there, since the saving is small enough
11238    /// that nothing in a benchmark would notice it going away again.
11239    #[test]
11240    fn an_index_is_read_once_per_stripe_however_often_the_page_is_evicted() {
11241        let path = path("index-cache");
11242        let mut writer =
11243            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11244                .expect("new file");
11245        let parts = STRIPE_PARTS * (CACHED_STRIPES_PER_COLUMN + 2);
11246        for part in 0..parts {
11247            let id = part as i32;
11248            let chunk = Chunk::new(vec![
11249                Vector::from_values(LogicalType::Integer, &[Value::Integer(id)]).expect("integers"),
11250            ])
11251            .expect("matching rows");
11252            writer.append(&chunk).expect("one part");
11253        }
11254        writer.finish().expect("commit");
11255
11256        let reader = Reader::open(&path).expect("reopen from disk");
11257        let stripes = reader.table().stripes().len();
11258        assert!(stripes > CACHED_STRIPES_PER_COLUMN, "the page cache has to be too small for this");
11259        // Twice over, so that the second pass finds every page evicted and every index kept.
11260        for _ in 0..2 {
11261            for part in 0..parts {
11262                let chunk = reader.read(part, &[0]).expect("a part");
11263                assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11264            }
11265        }
11266        assert_eq!(reader.indexes.load(Atomic::Relaxed), stripes, "one index read per stripe");
11267        assert!(
11268            reader.pages.load(Atomic::Relaxed) > stripes,
11269            "the pages are the ones that get read again, which is what makes the index count mean \
11270             something"
11271        );
11272        fs::remove_file(path).expect("remove scratch file");
11273    }
11274
11275    /// A worker per stripe reads its stripe once, once the cache has been told how many there are.
11276    ///
11277    /// This is the shape a scan has when it hands out a whole stripe per morsel rather than a part.
11278    /// Nobody races for a page any more, but every worker holds a different one for the length of a
11279    /// stripe, so a cache that keeps four pages while eight workers are in eight stripes evicts
11280    /// every one of them before its owner has finished with it, and the owner reads a quarter of a
11281    /// megabyte again for the next part. The barrier is what makes that certain rather than likely:
11282    /// without it a worker can run a whole stripe before the next one starts and never collide.
11283    #[test]
11284    fn a_worker_per_stripe_reads_its_page_once_when_the_cache_was_told_to_expect_it() {
11285        let workers = CACHED_STRIPES_PER_COLUMN + 4;
11286        let path = path("stripe-per-worker");
11287        let mut writer =
11288            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11289                .expect("new file");
11290        for part in 0..STRIPE_PARTS * workers {
11291            let chunk = Chunk::new(vec![
11292                Vector::from_values(LogicalType::Integer, &[Value::Integer(part as i32)])
11293                    .expect("integers"),
11294            ])
11295            .expect("matching rows");
11296            writer.append(&chunk).expect("one part");
11297        }
11298        writer.finish().expect("commit");
11299
11300        let read = |told: bool| {
11301            let reader = Reader::open(&path).expect("reopen from disk");
11302            assert_eq!(reader.table().stripes().len(), workers, "a stripe per worker");
11303            if told {
11304                reader.keep_stripes(workers);
11305            }
11306            let barrier = std::sync::Barrier::new(workers);
11307            std::thread::scope(|scope| {
11308                for (worker, run) in reader.stripe_parts().into_iter().enumerate() {
11309                    let reader = &reader;
11310                    let barrier = &barrier;
11311                    scope.spawn(move || {
11312                        for part in run {
11313                            barrier.wait();
11314                            let chunk = reader.read(part, &[0]).expect("a part of my own stripe");
11315                            assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11316                        }
11317                        assert!(worker < workers);
11318                    });
11319                }
11320            });
11321            reader.pages.load(Atomic::Relaxed)
11322        };
11323
11324        assert_eq!(read(true), workers, "one page read per stripe and no more");
11325        assert!(read(false) > workers, "a cache that small is read again on every part");
11326        fs::remove_file(path).expect("remove scratch file");
11327    }
11328
11329    /// A damaged index page is caught before anything decodes a part out of it.
11330    ///
11331    /// The index is the one structure a reader trusts to find bytes with, so it carries a checksum
11332    /// per column section rather than one for the page, and this is what says that check runs.
11333    #[test]
11334    fn a_damaged_index_page_is_an_error() {
11335        let path = path("damaged-index");
11336        let mut writer =
11337            Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11338                .expect("new file");
11339        writer.append(&sample_ids()).expect("first part");
11340        writer.append(&sample_ids()).expect("second part");
11341        writer.finish().expect("commit");
11342
11343        let reader = Reader::open(&path).expect("valid directory");
11344        let index = reader.table.stripes[0].index;
11345        let mut byte = [0; 1];
11346        read_at(&reader.file, index.offset, &mut byte).expect("the first part length");
11347        let mut file = OpenOptions::new().write(true).open(&path).expect("open index page");
11348        file.seek(SeekFrom::Start(index.offset)).expect("index start");
11349        file.write_all(&[!byte[0]]).expect("damage the first part length");
11350        let error = reader.read(1, &[0]).expect_err("a damaged index must not be used");
11351        assert!(error.message().contains("index page section checksum differs"), "{error}");
11352        fs::remove_file(path).expect("remove scratch file");
11353    }
11354
11355    /// Every integer width the format knows about, written and read back.
11356    ///
11357    /// The unsigned ones are the reason ClickBench can be stored at all: `hits` types `EventDate`
11358    /// as `USMALLINT`, and one unsupported column meant the whole table was refused. The extremes
11359    /// are in here on purpose, because a width that round trips through the wrong signedness only
11360    /// goes wrong at the end of its range.
11361    #[test]
11362    fn every_integer_width_round_trips_through_a_page() {
11363        let path = path("integer-widths");
11364        let columns = [
11365            (LogicalType::TinyInt, vec![Value::TinyInt(i8::MIN), Value::TinyInt(i8::MAX)]),
11366            (LogicalType::UTinyInt, vec![Value::UTinyInt(0), Value::UTinyInt(u8::MAX)]),
11367            (LogicalType::SmallInt, vec![Value::SmallInt(i16::MIN), Value::SmallInt(i16::MAX)]),
11368            (LogicalType::USmallInt, vec![Value::USmallInt(0), Value::USmallInt(u16::MAX)]),
11369            (LogicalType::Integer, vec![Value::Integer(i32::MIN), Value::Integer(i32::MAX)]),
11370            (LogicalType::UInteger, vec![Value::UInteger(0), Value::UInteger(u32::MAX)]),
11371            (LogicalType::BigInt, vec![Value::BigInt(i64::MIN), Value::BigInt(i64::MAX)]),
11372            (LogicalType::UBigInt, vec![Value::UBigInt(0), Value::UBigInt(u64::MAX)]),
11373        ];
11374        let fields = columns
11375            .iter()
11376            .enumerate()
11377            .map(|(at, (ty, _))| Field::required(format!("c{at}"), ty.clone()))
11378            .collect::<Vec<_>>();
11379        let vectors = columns
11380            .iter()
11381            .map(|(ty, values)| Vector::from_values(ty.clone(), values).expect("a vector"))
11382            .collect::<Vec<_>>();
11383        let mut writer = Writer::create(&path, "widths", fields).expect("new file");
11384        writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11385        writer.finish().expect("commit");
11386
11387        let reader = Reader::open(&path).expect("reopen from disk");
11388        let wanted = (0..columns.len()).collect::<Vec<_>>();
11389        let read = reader.read(0, &wanted).expect("every column");
11390        assert_eq!(read.len(), 2);
11391        // row at a time: each column has its own type and its own pair of extremes.
11392        for (at, (ty, values)) in columns.iter().enumerate() {
11393            assert_eq!(read.value_at(0, at), values[0], "the low end of {ty}");
11394            assert_eq!(read.value_at(1, at), values[1], "the high end of {ty}");
11395        }
11396        fs::remove_file(path).expect("remove scratch file");
11397    }
11398
11399    /// The rest of the fixed width types, and the byte strings, written and read back.
11400    ///
11401    /// The extremes again, and for a float that means more than the ends of the range. Negative
11402    /// zero and a NaN are the two values that go through an encoder unnoticed and come back
11403    /// different, so they are here on purpose, and the NaN is compared by its bits rather than by
11404    /// `==`, which a NaN fails against itself.
11405    ///
11406    /// A blob is here beside them because it is the same round trip asked of bytes that are not
11407    /// text. The value in it is not UTF-8, so a path that reads a payload as a string on the way
11408    /// past turns this test red rather than turning a user's column into nulls.
11409    #[test]
11410    fn every_other_type_the_format_knows_round_trips_through_a_page() {
11411        let path = path("other-types");
11412        let columns = [
11413            (LogicalType::Float, vec![Value::Float(f32::MIN), Value::Float(-0.0)]),
11414            (LogicalType::Double, vec![Value::Double(f64::MIN), Value::Double(f64::MAX)]),
11415            (LogicalType::HugeInt, vec![Value::HugeInt(i128::MIN), Value::HugeInt(i128::MAX)]),
11416            (LogicalType::UHugeInt, vec![Value::UHugeInt(0), Value::UHugeInt(u128::MAX)]),
11417            (LogicalType::Time, vec![Value::Time(0), Value::Time(86_399_999_999)]),
11418            (LogicalType::TimeTz, vec![Value::TimeTz(-50_400_000_000), Value::TimeTz(0)]),
11419            (
11420                LogicalType::TimestampTz,
11421                vec![Value::TimestampTz(i64::MIN + 1), Value::TimestampTz(i64::MAX)],
11422            ),
11423            (
11424                LogicalType::Interval,
11425                vec![
11426                    Value::Interval { months: i32::MIN, days: i32::MAX, micros: i64::MIN },
11427                    Value::Interval { months: 13, days: -1, micros: 1 },
11428                ],
11429            ),
11430            (
11431                LogicalType::Blob,
11432                vec![Value::Blob(vec![0, 0xff, 0x80, 0xfe]), Value::Blob(Vec::new())],
11433            ),
11434        ];
11435        let fields = columns
11436            .iter()
11437            .enumerate()
11438            .map(|(at, (ty, _))| Field::required(format!("c{at}"), ty.clone()))
11439            .collect::<Vec<_>>();
11440        let vectors = columns
11441            .iter()
11442            .map(|(ty, values)| Vector::from_values(ty.clone(), values).expect("a vector"))
11443            .collect::<Vec<_>>();
11444        let mut writer = Writer::create(&path, "others", fields).expect("new file");
11445        writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11446        writer.finish().expect("commit");
11447
11448        let reader = Reader::open(&path).expect("reopen from disk");
11449        let wanted = (0..columns.len()).collect::<Vec<_>>();
11450        let read = reader.read(0, &wanted).expect("every column");
11451        assert_eq!(read.len(), 2);
11452        for (at, (ty, values)) in columns.iter().enumerate() {
11453            assert_eq!(read.value_at(0, at), values[0], "the low end of {ty}");
11454            assert_eq!(read.value_at(1, at), values[1], "the high end of {ty}");
11455        }
11456        // A float keeps its sign through a zero, which `==` says nothing about because negative
11457        // zero and zero compare equal.
11458        let Value::Float(zero) = read.value_at(1, 0) else { panic!("a float stays a float") };
11459        assert!(zero.is_sign_negative(), "a negative zero came back as {zero}");
11460
11461        fs::remove_file(path).expect("remove scratch file");
11462    }
11463
11464    /// A NaN is still a NaN after a trip through a page.
11465    ///
11466    /// Apart from the other floats because it cannot be asserted the same way. A NaN is not equal
11467    /// to itself, so a comparison against the value that was written passes for every NaN and for
11468    /// nothing else, which is the one assertion that would not catch a page that lost it.
11469    #[test]
11470    fn a_nan_survives_being_written_down() {
11471        let path = path("nan");
11472        let nan = Vector::from_values(LogicalType::Double, &[Value::Double(f64::NAN)])
11473            .expect("a NaN vector");
11474        let mut writer =
11475            Writer::create(&path, "nan", vec![Field::required("d", LogicalType::Double)])
11476                .expect("new file");
11477        writer.append(&Chunk::new(vec![nan]).expect("one column")).expect("one stripe");
11478        writer.finish().expect("commit");
11479        let read = Reader::open(&path).expect("reopen").read(0, &[0]).expect("the column");
11480        let Value::Double(back) = read.value_at(0, 0) else { panic!("a double stays a double") };
11481        assert!(back.is_nan(), "a NaN came back as {back}");
11482        fs::remove_file(path).expect("remove scratch file");
11483    }
11484
11485    /// A uuid and a bit string, which have no `Value` arm of their own and are checked as bits.
11486    ///
11487    /// A uuid is the 128 bit lane and a bit string is bytes, and neither of them reads back as
11488    /// anything in `Value` today, so asking for a value here would compare two nulls and pass
11489    /// whatever the file held. The data underneath is what the storage promise is about, so that is
11490    /// what this reads.
11491    #[test]
11492    fn a_uuid_and_a_bit_string_come_back_as_the_bits_that_went_in() {
11493        let path = path("uuid-and-bit");
11494        let uuids = vec![0_i128, i128::MIN, -1];
11495        let mut bits = StringColumn::new();
11496        for value in [&b"\x02\xff"[..], &b""[..], &b"\x00\x01\x02\x03\x04\x05"[..]] {
11497            bits.push_bytes(value);
11498        }
11499        let expected = bits.clone();
11500        let fields =
11501            vec![Field::required("u", LogicalType::Uuid), Field::required("b", LogicalType::Bit)];
11502        let vectors = vec![
11503            Vector::flat(LogicalType::Uuid, Data::Int128(uuids.clone().into())).expect("uuids"),
11504            Vector::flat(LogicalType::Bit, Data::Varlen(bits)).expect("bit strings"),
11505        ];
11506        let mut writer = Writer::create(&path, "ids", fields).expect("new file");
11507        writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11508        writer.finish().expect("commit");
11509
11510        let reader = Reader::open(&path).expect("reopen from disk");
11511        let read = reader.read(0, &[0, 1]).expect("both columns").flatten().expect("flat");
11512        let Some(Data::Int128(back)) = read.column(0).expect("the uuids").data() else {
11513            panic!("a uuid column is the 128 bit lane")
11514        };
11515        assert_eq!(back.as_slice(), uuids.as_slice());
11516        let Some(Data::Varlen(back)) = read.column(1).expect("the bits").data() else {
11517            panic!("a bit column is bytes")
11518        };
11519        for row in 0..expected.len() {
11520            assert_eq!(back.bytes(row), expected.bytes(row), "row {row} of the bit column");
11521        }
11522        fs::remove_file(path).expect("remove scratch file");
11523    }
11524
11525    #[test]
11526    fn numeric_frequency_candidates_keep_bounded_row_ordinals() {
11527        let path = path("frequency-ordinals");
11528        let mut writer =
11529            Writer::create(&path, "items", vec![Field::required("id", LogicalType::BigInt)])
11530                .expect("new file");
11531        let mut values = Vec::new();
11532        for leader in 0..10_i64 {
11533            values.extend(std::iter::repeat_n(leader, 100));
11534        }
11535        values.extend(1_000_i64..41_000);
11536        for part in values.chunks(1_024) {
11537            let vector = Vector::flat(LogicalType::BigInt, Data::Int64(part.to_vec().into()))
11538                .expect("big integers");
11539            writer.append(&Chunk::new(vec![vector]).expect("one column")).expect("one stripe");
11540        }
11541        writer.finish().expect("commit");
11542
11543        let reader = Reader::open(&path).expect("reopen from disk");
11544        let occurrences =
11545            reader.frequency_occurrences(0).expect("valid metadata").expect("bounded ordinals");
11546        assert!(occurrences.omitted_max < 100);
11547        assert!(occurrences.ordinals.len() <= FREQUENCY_ORDINALS);
11548        assert_eq!(occurrences.anchor_indices.len(), occurrences.ordinals.len());
11549        assert!(occurrences.ordinals.windows(2).all(|pair| pair[0] < pair[1]));
11550        assert_eq!(&occurrences.ordinals[..1_000], &(0_u64..1_000).collect::<Vec<_>>());
11551        assert_eq!(
11552            &occurrences.anchor_indices[..1_000]
11553                .iter()
11554                .map(|&entry| occurrences.anchors[entry as usize].clone())
11555                .collect::<Vec<_>>(),
11556            &(0_i64..10)
11557                .flat_map(|leader| std::iter::repeat_n(Value::BigInt(leader), 100))
11558                .collect::<Vec<_>>()
11559        );
11560        fs::remove_file(path).expect("remove scratch file");
11561    }
11562
11563    #[test]
11564    fn numeric_frequencies_count_nulls_and_values_past_the_top_of_bigint() {
11565        // Ten leaders, then more unique values than the candidate table holds, so the first pass
11566        // has to decrement and the counts come from the recount. The unsigned leaders sit above
11567        // `i64::MAX`, where reading the bits as signed would give a different value, and the signed
11568        // ones are negative, where reading them as unsigned would.
11569        let path = path("frequency-bits");
11570        let mut writer = Writer::create(
11571            &path,
11572            "items",
11573            vec![Field::new("u", LogicalType::UBigInt), Field::new("s", LogicalType::BigInt)],
11574        )
11575        .expect("new file");
11576        let mut rows = Vec::new();
11577        let mut leaders = Vec::new();
11578        for leader in 0..10_u64 {
11579            let count = 300 - leader * 10;
11580            let (unsigned, signed) = if leader == 0 {
11581                (Value::Null, Value::Null)
11582            } else {
11583                (Value::UBigInt(u64::MAX - leader), Value::BigInt(-(leader as i64)))
11584            };
11585            rows.extend(std::iter::repeat_n((unsigned.clone(), signed.clone()), count as usize));
11586            leaders.push(((unsigned, count), (signed, count)));
11587        }
11588        rows.extend((1_000..41_000_u64).map(|id| (Value::UBigInt(id), Value::BigInt(id as i64))));
11589        for part in rows.chunks(1_024) {
11590            let unsigned = part.iter().map(|(value, _)| value.clone()).collect::<Vec<_>>();
11591            let signed = part.iter().map(|(_, value)| value.clone()).collect::<Vec<_>>();
11592            let chunk = Chunk::new(vec![
11593                Vector::from_values(LogicalType::UBigInt, &unsigned).expect("unsigned"),
11594                Vector::from_values(LogicalType::BigInt, &signed).expect("signed"),
11595            ])
11596            .expect("matching columns");
11597            writer.append(&chunk).expect("rows");
11598        }
11599        writer.finish().expect("commit");
11600
11601        let reader = Reader::open(&path).expect("reopen from disk");
11602        for column in 0..2 {
11603            let prefix =
11604                reader.frequency_prefix(column).expect("valid metadata").expect("a synopsis");
11605            let wanted = leaders
11606                .iter()
11607                .map(|(unsigned, signed)| if column == 0 { unsigned } else { signed })
11608                .cloned()
11609                .collect::<Vec<_>>();
11610            assert_eq!(&prefix.entries[..10], &wanted[..], "column {column}");
11611            assert!(prefix.omitted_max < 210, "column {column}");
11612            assert_eq!(
11613                reader.distinct_values(column).expect("valid metadata"),
11614                Some(9 + 40_000),
11615                "column {column}"
11616            );
11617        }
11618        fs::remove_file(path).expect("remove scratch file");
11619    }
11620
11621    #[test]
11622    fn numeric_string_pair_leaders_are_certified_in_the_directory() {
11623        let path = path("pair-frequencies");
11624        let mut pairs = Vec::new();
11625        pairs.extend(std::iter::repeat_n((1_i64, "alpha".to_string()), 100));
11626        pairs.extend(std::iter::repeat_n((1_i64, "beta".to_string()), 50));
11627        pairs.extend(std::iter::repeat_n((2_i64, "gamma".to_string()), 40));
11628        pairs.extend((1_000_i64..1_600).map(|id| (id, format!("tail {id}"))));
11629        let mut writer = Writer::create(
11630            &path,
11631            "items",
11632            vec![
11633                Field::required("id", LogicalType::BigInt),
11634                Field::required("phrase", LogicalType::Varchar),
11635            ],
11636        )
11637        .expect("new file");
11638        for part in pairs.chunks(1_024) {
11639            let ids = part.iter().map(|(id, _)| Value::BigInt(*id)).collect::<Vec<_>>();
11640            let phrases =
11641                part.iter().map(|(_, phrase)| Value::Varchar(phrase.clone())).collect::<Vec<_>>();
11642            writer
11643                .append(
11644                    &Chunk::new(vec![
11645                        Vector::from_values(LogicalType::BigInt, &ids).expect("ids"),
11646                        Vector::from_values(LogicalType::Varchar, &phrases).expect("phrases"),
11647                    ])
11648                    .expect("matching columns"),
11649                )
11650                .expect("rows");
11651        }
11652        writer.finish().expect("commit");
11653
11654        let reader = Reader::open(&path).expect("reopen from disk");
11655        let leaders = reader
11656            .top_pair_frequencies(0, 1, 2)
11657            .expect("valid pair metadata")
11658            .expect("the top two beat the omitted tail");
11659        assert!(
11660            leaders.contains(&(vec![Value::BigInt(1), Value::Varchar("alpha".to_string())], 100,))
11661        );
11662        assert!(
11663            leaders.contains(&(vec![Value::BigInt(1), Value::Varchar("beta".to_string())], 50,))
11664        );
11665        fs::remove_file(path).expect("remove scratch file");
11666    }
11667
11668    /// The bug this is here for cost a 43 GB ClickBench table and an hour of reloading it. The
11669    /// format went from 11 to 12, every binary built after that said "magic or major version is
11670    /// unsupported" about the file, and there was no way to tell from the message whether the path
11671    /// was wrong, the file was truncated, or it was ours and simply older. The number this build
11672    /// wants is the whole answer and it was the one thing the message did not carry.
11673    #[test]
11674    fn a_file_from_another_format_says_which_format_it_is() {
11675        let older = path("older-format");
11676        let mut writer =
11677            Writer::create(&older, "items", vec![Field::new("id", LogicalType::Integer)])
11678                .expect("new file");
11679        let chunk = Chunk::new(vec![
11680            Vector::flat(LogicalType::Integer, Data::Int32(vec![1, 2, 3].into()))
11681                .expect("integers"),
11682        ])
11683        .expect("chunk");
11684        writer.append(&chunk).expect("page written");
11685        writer.finish().expect("commit");
11686
11687        // A format below the whole readable set, rather than `FORMAT - 1`, because the set has
11688        // more than one member now: format 22 is deliberately still readable, so the version that
11689        // has to be refused is the one under the oldest one accepted.
11690        let unreadable =
11691            READABLE.iter().copied().min().expect("at least one format is readable") - 1;
11692        let mut file = OpenOptions::new().write(true).open(&older).expect("open for the header");
11693        file.seek(SeekFrom::Start(8)).expect("the version follows the magic");
11694        file.write_all(&unreadable.to_le_bytes()).expect("write an older version");
11695        drop(file);
11696        let complaint = Reader::open(&older).expect_err("an older format is refused").to_string();
11697        assert!(complaint.contains(&format!("format {unreadable}")), "{complaint}");
11698        assert!(complaint.contains(&format!("format {FORMAT}")), "{complaint}");
11699
11700        let mut file = OpenOptions::new().write(true).open(&older).expect("open for the header");
11701        file.seek(SeekFrom::Start(0)).expect("the magic is first");
11702        file.write_all(b"NOTRUDB!").expect("write another engine's magic");
11703        drop(file);
11704        let complaint = Reader::open(&older).expect_err("a foreign file is refused").to_string();
11705        assert!(complaint.contains("magic"), "{complaint}");
11706        assert!(!complaint.contains("format"), "a version has nothing to do with it: {complaint}");
11707        fs::remove_file(older).expect("remove scratch file");
11708    }
11709
11710    #[test]
11711    fn an_unfinished_or_damaged_file_does_not_answer_with_partial_rows() {
11712        let unfinished = path("unfinished");
11713        let mut writer =
11714            Writer::create(&unfinished, "items", vec![Field::new("id", LogicalType::Integer)])
11715                .expect("new file");
11716        let chunk = Chunk::new(vec![
11717            Vector::flat(LogicalType::Integer, Data::Int32(vec![1, 2, 3].into()))
11718                .expect("integers"),
11719        ])
11720        .expect("chunk");
11721        writer.append(&chunk).expect("page written");
11722        drop(writer);
11723        assert!(Reader::open(&unfinished).is_err(), "no directory was committed");
11724        fs::remove_file(unfinished).expect("remove scratch file");
11725
11726        let damaged = path("damaged");
11727        let mut writer =
11728            Writer::create(&damaged, "items", vec![Field::new("id", LogicalType::Integer)])
11729                .expect("new file");
11730        writer.append(&chunk).expect("page written");
11731        writer.finish().expect("commit");
11732        let reader = Reader::open(&damaged).expect("valid directory");
11733        let mut file =
11734            OpenOptions::new().write(true).open(&damaged).expect("open for a damaged page");
11735        file.seek(SeekFrom::Start(HEADER + 1)).expect("inside first page");
11736        file.write_all(&[255]).expect("damage one byte");
11737        assert!(reader.read(0, &[0]).is_err(), "page checksum rejects corruption");
11738        fs::remove_file(damaged).expect("remove scratch file");
11739    }
11740
11741    #[test]
11742    fn damaged_lazy_dictionary_payload_is_an_error() {
11743        let path = path("damaged-dictionary");
11744        let mut writer = Writer::create(
11745            &path,
11746            "items",
11747            vec![
11748                Field::required("id", LogicalType::Integer),
11749                Field::new("text", LogicalType::Varchar),
11750            ],
11751        )
11752        .expect("new file");
11753        writer.append(&sample()).expect("stripe written");
11754        writer.finish().expect("commit");
11755
11756        let reader = Reader::open(&path).expect("valid directory");
11757        let dictionary = reader.table.dictionaries[1].expect("string dictionary page");
11758        // Read the count out of the page rather than writing it here, so that adding something
11759        // else to the index does not silently turn this into a test that damages the index.
11760        let mut header = [0; DICTIONARY_HEADER];
11761        read_at(&reader.file, dictionary.offset, &mut header).expect("dictionary header");
11762        // The first block's start is the first word after the offsets, since the blocks are written
11763        // during the load and are wherever the writer was when each was encoded.
11764        let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
11765        let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
11766        assert_ne!(width & DICTIONARY_SCATTERED, 0, "the blocks say where they are");
11767        let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
11768        let mut start = [0; 8];
11769        let at = dictionary.offset + (DICTIONARY_HEADER + offset_bytes(count, bits)) as u64;
11770        read_at(&reader.file, at, &mut start).expect("the first block's start");
11771        let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
11772        file.seek(SeekFrom::Start(u64::from_le_bytes(start))).expect("inside dictionary payload");
11773        file.write_all(&[255]).expect("damage dictionary payload");
11774
11775        let chunk = reader.read(0, &[1]).expect("code page and dictionary index remain valid");
11776        let error =
11777            chunk.validate_external().expect_err("payload corruption must reach the caller");
11778        assert!(error.message().contains("payload checksum differs"), "{error}");
11779        fs::remove_file(path).expect("remove scratch file");
11780    }
11781
11782    /// A column whose values are all different is written without a dictionary, and one whose
11783    /// values repeat keeps it.
11784    ///
11785    /// The two columns go in the same table and hold the same number of rows, so the only thing
11786    /// separating them is how much of the first stripe was a value it had not seen before. Both have
11787    /// to read back the values that were written, because the decision is about cost and nothing
11788    /// else. The file size is the other half of it: a column written without a dictionary goes
11789    /// through the string cascade instead, so dropping the dictionary must not turn into storing the
11790    /// column raw.
11791    #[test]
11792    fn a_column_of_all_different_values_is_written_without_a_dictionary() {
11793        let path = path("dictionary-decide");
11794        let rows = 20_000;
11795        // Long enough that storing it raw would show, and different in every row.
11796        let unique =
11797            |row: usize| format!("{row:09} a value that appears exactly once in the table");
11798        // The same values in the same shape, each one used forty times over.
11799        let repeated = |row: usize| unique(row / 40);
11800        let mut writer = Writer::create(
11801            &path,
11802            "items",
11803            vec![
11804                Field::required("unique", LogicalType::Varchar),
11805                Field::required("repeated", LogicalType::Varchar),
11806            ],
11807        )
11808        .expect("new file");
11809        for part in (0..rows).step_by(1_000) {
11810            let span = part..(part + 1_000).min(rows);
11811            let left = span.clone().map(|row| Value::Varchar(unique(row))).collect::<Vec<_>>();
11812            let right = span.map(|row| Value::Varchar(repeated(row))).collect::<Vec<_>>();
11813            writer
11814                .append(
11815                    &Chunk::new(vec![
11816                        Vector::from_values(LogicalType::Varchar, &left).expect("strings"),
11817                        Vector::from_values(LogicalType::Varchar, &right).expect("strings"),
11818                    ])
11819                    .expect("two columns"),
11820                )
11821                .expect("a part");
11822        }
11823        writer.finish().expect("commit");
11824
11825        let reader = Reader::open(&path).expect("reopen from disk");
11826        assert!(
11827            reader.table.dictionaries[0].is_none(),
11828            "a column with no repeats has nothing to say twice"
11829        );
11830        assert!(
11831            reader.table.dictionaries[1].is_some(),
11832            "a column whose values come round again keeps its dictionary"
11833        );
11834        let mut first = 0;
11835        for part in 0..reader.parts() {
11836            let chunk = reader.read(part, &[0, 1]).expect("a part");
11837            for row in 0..chunk.len() {
11838                assert_eq!(chunk.value_at(row, 0), Value::Varchar(unique(first + row)));
11839                assert_eq!(chunk.value_at(row, 1), Value::Varchar(repeated(first + row)));
11840            }
11841            first += chunk.len();
11842        }
11843        assert_eq!(first, rows, "every row was read back");
11844        let raw = (0..rows).map(|row| unique(row).len()).sum::<usize>();
11845        let size = fs::metadata(&path).expect("the file is there").len() as usize;
11846        assert!(size < raw, "a column without a dictionary is still encoded: {size} against {raw}");
11847        fs::remove_file(path).expect("remove scratch file");
11848    }
11849
11850    /// A payload of many blocks reads and checks every block of it.
11851    ///
11852    /// The test above has a dictionary of three values, which is one block, so it says nothing
11853    /// about a reader finding the right block among many. This one has thirty two thousand values,
11854    /// which is thirty two blocks, and it reads a value out of the first block and a value out of
11855    /// the last and then damages the last and asks for it again.
11856    ///
11857    /// Forty thousand rows over those thirty two thousand values, because a column the writer finds
11858    /// to be all distinct does not get a dictionary at all and there would be nothing here to test.
11859    /// Four rows in five holding a value the stripe has not seen before is a column that keeps one.
11860    /// The repeats are put at the front so that the values still arrive in order after them, which
11861    /// is what keeps the last part of the table on the last block of the payload.
11862    #[test]
11863    fn a_dictionary_over_many_blocks_checks_every_block_of_it() {
11864        let path = path("dictionary-blocks");
11865        let value = |row: usize| {
11866            let row = row.saturating_sub(8_000);
11867            format!("{row:07} a value long enough to be worth a payload block")
11868        };
11869        let parts = 40;
11870        let per_part = 1000;
11871        let mut writer =
11872            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
11873                .expect("new file");
11874        for part in 0..parts {
11875            let values = (0..per_part)
11876                .map(|row| Value::Varchar(value(part * per_part + row)))
11877                .collect::<Vec<_>>();
11878            let chunk = Chunk::new(vec![
11879                Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
11880            ])
11881            .expect("matching rows");
11882            writer.append(&chunk).expect("a part");
11883        }
11884        writer.finish().expect("commit");
11885
11886        let reader = Reader::open(&path).expect("reopen from disk");
11887        let dictionary = reader.table.dictionaries[0].expect("string dictionary page");
11888        assert!(
11889            parts * per_part > TEXT_PAYLOAD_VALUES * 4,
11890            "the dictionary has to be several blocks for this to be testing anything"
11891        );
11892        for part in [0, parts - 1] {
11893            let chunk = reader.read(part, &[0]).expect("a part");
11894            chunk.validate_external().expect("every payload block checks out");
11895            assert_eq!(chunk.value_at(0, 0), Value::Varchar(value(part * per_part)));
11896        }
11897
11898        // The last block is wherever the writer was when it was encoded, which the index says.
11899        let mut header = [0; DICTIONARY_HEADER];
11900        read_at(&reader.file, dictionary.offset, &mut header).expect("dictionary header");
11901        let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
11902        let blocks = u32::from_le_bytes(header[8..12].try_into().expect("four bytes")) as usize;
11903        let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
11904        let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
11905        let mut place = [0; 16];
11906        let at = DICTIONARY_HEADER + offset_bytes(count, bits) + (blocks - 1) * 16;
11907        read_at(&reader.file, dictionary.offset + at as u64, &mut place).expect("its place");
11908        let start = u64::from_le_bytes(place[..8].try_into().expect("eight bytes"));
11909        let length = u64::from_le_bytes(place[8..].try_into().expect("eight bytes"));
11910        let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
11911        file.seek(SeekFrom::Start(start + length - 4)).expect("the last bytes of the last block");
11912        file.write_all(&[255]).expect("damage the last payload block");
11913        let reader = Reader::open(&path).expect("the directory and the index are untouched");
11914        let chunk = reader.read(parts - 1, &[0]).expect("the code page remains valid");
11915        let error = chunk.validate_external().expect_err("the damage must reach the caller");
11916        assert!(error.message().contains("payload checksum differs"), "{error}");
11917        fs::remove_file(path).expect("remove scratch file");
11918    }
11919
11920    /// Values of different lengths read back where the offsets say they do.
11921    ///
11922    /// The offsets are packed at one width for the column, they are relative to the payload block a
11923    /// value lands in, and they go in runs of half a block, so there are two boundaries where the
11924    /// arithmetic could be off by one and neither shows up on values that are all the same length.
11925    /// This writes 5,000 values whose lengths cycle through a wide range and reads every one back,
11926    /// so the first value of a block, the last value of a run and the last value of a block are all
11927    /// covered several times over. An empty value is in the cycle because a zero length span is the
11928    /// case the reader short circuits.
11929    ///
11930    /// Six thousand rows over those 5,000 values, because a column the writer finds to be all
11931    /// distinct is written without a dictionary and then there are no packed offsets to be off by
11932    /// one in.
11933    #[test]
11934    fn values_of_different_lengths_read_back_out_of_packed_offsets() {
11935        let path = path("dictionary-offsets");
11936        let value = |row: usize| {
11937            let row = row % 5_000;
11938            if row % 511 == 3 { String::new() } else { "x".repeat(row % 97) + &format!("{row:05}") }
11939        };
11940        let rows = 6_000;
11941        let mut writer =
11942            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
11943                .expect("new file");
11944        let values = (0..rows).map(|row| Value::Varchar(value(row))).collect::<Vec<_>>();
11945        for part in values.chunks(1_000) {
11946            let chunk =
11947                Chunk::new(vec![Vector::from_values(LogicalType::Varchar, part).expect("strings")])
11948                    .expect("matching rows");
11949            writer.append(&chunk).expect("a part");
11950        }
11951        writer.finish().expect("commit");
11952
11953        let reader = Reader::open(&path).expect("reopen from disk");
11954        assert!(
11955            rows > TEXT_PAYLOAD_VALUES * 4,
11956            "the dictionary has to be several blocks for this to be testing anything"
11957        );
11958        for part in 0..rows / 1_000 {
11959            let chunk = reader.read(part, &[0]).expect("a part");
11960            for row in 0..1_000 {
11961                let row = part * 1_000 + row;
11962                assert_eq!(
11963                    chunk.value_at(row % 1_000, 0),
11964                    Value::Varchar(value(row)),
11965                    "value {row}"
11966                );
11967            }
11968        }
11969        // The lengths a vector at a time, twice over, because the first pass is what makes the
11970        // table of ends worth building and the second is read out of the lengths worked out of it.
11971        for _ in 0..2 {
11972            for part in 0..rows / 1_000 {
11973                let chunk = reader.read(part, &[0]).expect("a part");
11974                let mut lens = vec![0_i64; 1_000];
11975                let column = chunk.column(0).expect("one column");
11976                assert!(column.try_bytes_lens(&mut lens).expect("lengths"), "a stored column");
11977                for (row, &len) in lens.iter().enumerate() {
11978                    let row = part * 1_000 + row;
11979                    assert_eq!(len as usize, value(row).len(), "the length of value {row}");
11980                }
11981            }
11982        }
11983        fs::remove_file(path).expect("remove scratch file");
11984    }
11985
11986    /// Lengths start again at every block, and ends that go backwards inside one give no table.
11987    #[test]
11988    fn lengths_restart_at_each_block_and_refuse_ends_that_go_backwards() {
11989        let mut ends: Vec<u32> = (1..=TEXT_PAYLOAD_VALUES as u32).map(|at| at * 2).collect();
11990        ends.extend([3, 3, 10]);
11991        let lens = lengths_of(&ends).expect("ordered ends");
11992        assert!(lens[..TEXT_PAYLOAD_VALUES].iter().all(|&len| len == 2));
11993        assert_eq!(&lens[TEXT_PAYLOAD_VALUES..], &[3, 0, 7]);
11994        ends.push(9);
11995        assert_eq!(lengths_of(&ends), None);
11996    }
11997
11998    /// Every worker of a scan wants the dictionary at the same moment and one of them fetches it.
11999    ///
12000    /// Asking a `OnceLock` whether it holds something answers the question a worker that already has
12001    /// the dictionary is asking and not the one a worker without it is asking, which is whether
12002    /// somebody is already on their way with it. Sixteen workers that all miss will all read the
12003    /// page, all verify it and all decode it, and fifteen will drop the result. Nothing about that
12004    /// is incorrect, which is why it went unnoticed, and it showed up as ClickBench 38 getting
12005    /// slower when the scan in front of it got faster and stopped staggering the arrivals.
12006    ///
12007    /// The barrier is what makes the test about that rather than about luck. Without it the first
12008    /// thread is usually finished before the last one starts and the count is one either way.
12009    #[test]
12010    fn a_global_dictionary_is_opened_once_however_many_workers_ask_at_once() {
12011        let path = path("dictionary-once");
12012        let parts = 8;
12013        let per_part = 500;
12014        let value =
12015            |row: usize| format!("{row:07} a value long enough to be worth a payload block");
12016        let mut writer =
12017            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12018                .expect("new file");
12019        for part in 0..parts {
12020            let values = (0..per_part)
12021                .map(|row| Value::Varchar(value(part * per_part + row)))
12022                .collect::<Vec<_>>();
12023            let chunk = Chunk::new(vec![
12024                Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
12025            ])
12026            .expect("matching rows");
12027            writer.append(&chunk).expect("a part");
12028        }
12029        writer.finish().expect("commit");
12030
12031        let reader = Reader::open(&path).expect("reopen from disk");
12032        assert!(reader.table.dictionaries[0].is_some(), "the column has to have one to share");
12033        assert_eq!(reader.reads().dictionaries, 0, "opening the file does not open a dictionary");
12034
12035        let workers = 16;
12036        let gate = std::sync::Barrier::new(workers);
12037        std::thread::scope(|scope| {
12038            for worker in 0..workers {
12039                let reader = reader.clone();
12040                let gate = &gate;
12041                scope.spawn(move || {
12042                    gate.wait();
12043                    let chunk = reader.read(worker % parts, &[0]).expect("a part");
12044                    assert_eq!(
12045                        chunk.value_at(0, 0),
12046                        Value::Varchar(value((worker % parts) * per_part))
12047                    );
12048                });
12049            }
12050        });
12051
12052        assert_eq!(reader.reads().dictionaries, 1, "sixteen workers, one dictionary, one open");
12053        fs::remove_file(path).expect("remove scratch file");
12054    }
12055
12056    /// The sorted order sits outside the index the page checksum covers, because a query that
12057    /// never searches a dictionary should not read it, so it carries its own checksums and this is
12058    /// what says they are checked. A search that trusted a damaged order would give a wrong answer
12059    /// rather than a slow one.
12060    #[test]
12061    fn a_damaged_sorted_order_is_an_error() {
12062        let path = path("damaged-order");
12063        let mut writer = Writer::create(
12064            &path,
12065            "items",
12066            vec![
12067                Field::required("id", LogicalType::Integer),
12068                Field::new("text", LogicalType::Varchar),
12069            ],
12070        )
12071        .expect("new file");
12072        writer.append(&sample()).expect("stripe written");
12073        writer.finish().expect("commit");
12074
12075        let reader = Reader::open(&path).expect("valid directory");
12076        let page = reader.table.dictionaries[1].expect("string dictionary page");
12077        let mut header = [0; DICTIONARY_HEADER];
12078        read_at(&reader.file, page.offset, &mut header).expect("dictionary header");
12079        let index_len = dictionary_index_len(&header);
12080        let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
12081        file.seek(SeekFrom::Start(page.offset + index_len)).expect("the first head");
12082        file.write_all(&[255]).expect("damage the order");
12083
12084        let dictionary = reader.dictionary(1).expect("read").expect("a string column has one");
12085        let error = dictionary.compare_rank(0, b"anything").expect_err("a damaged order is caught");
12086        assert!(error.message().contains("rank checksum differs"), "{error}");
12087        fs::remove_file(path).expect("remove scratch file");
12088    }
12089
12090    /// Codes stay in first appearance order and the sorted order is written beside them, so a
12091    /// reader can put the values back in order without the writer having had to know them all
12092    /// before it handed out the first code.
12093    #[test]
12094    fn a_global_dictionary_carries_the_sorted_order_of_its_values() {
12095        // Chosen so the sort cannot be decided on the first eight bytes alone. Three values share
12096        // a nine byte prefix, one is a prefix of another, and one is empty.
12097        let spellings = ["overlong1z", "b", "", "overlong1a", "overlong", "ab", "a", "overlong1"];
12098        let path = path("dictionary-order");
12099        let mut writer =
12100            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12101                .expect("new file");
12102        writer
12103            .append(
12104                &Chunk::new(vec![
12105                    Vector::from_values(
12106                        LogicalType::Varchar,
12107                        &spellings.map(|text| Value::Varchar(text.into())),
12108                    )
12109                    .expect("strings"),
12110                ])
12111                .expect("one column"),
12112            )
12113            .expect("stripe written");
12114        writer.finish().expect("commit");
12115
12116        let reader = Reader::open(&path).expect("valid directory");
12117        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12118        let count = dictionary.ranks().expect("a v10 file stores one");
12119        assert_eq!(count, spellings.len(), "every distinct value has a rank");
12120        let order = (0..count)
12121            .map(|rank| dictionary.code_at_rank(rank).expect("a code"))
12122            .collect::<Vec<_>>();
12123        let mut seen = order.clone();
12124        seen.sort_unstable();
12125        assert_eq!(seen, (0..spellings.len() as u32).collect::<Vec<_>>(), "a permutation of codes");
12126
12127        let ranked = order
12128            .iter()
12129            .map(|&code| {
12130                dictionary.try_bytes_at(code as usize).expect("read").expect("a value").to_vec()
12131            })
12132            .collect::<Vec<_>>();
12133        let mut expected = spellings.map(|text| text.as_bytes().to_vec()).to_vec();
12134        expected.sort();
12135        assert_eq!(ranked, expected, "rank order is value order");
12136
12137        // What a search asks, on the values themselves rather than through a kernel, so that a
12138        // file whose heads disagree with its bytes is caught here rather than as a wrong answer.
12139        for (rank, value) in expected.iter().enumerate() {
12140            assert_eq!(
12141                dictionary.compare_rank(rank, value).expect("compare"),
12142                Ordering::Equal,
12143                "rank {rank} is its own value"
12144            );
12145            if rank > 0 {
12146                assert_eq!(
12147                    dictionary.compare_rank(rank - 1, value).expect("compare"),
12148                    Ordering::Less,
12149                    "rank {rank} follows the one before it"
12150                );
12151            }
12152        }
12153        fs::remove_file(path).expect("remove scratch file");
12154    }
12155
12156    /// A dictionary large enough to be decoded and sorted on several threads ranks the way one small
12157    /// enough for one thread does.
12158    ///
12159    /// Seventy thousand values over sixty nine blocks, in no order and each four times over so the
12160    /// column is worth a dictionary, written and ranked in the close.
12161    /// Some share a long prefix and some differ only in the last byte, so the buckets of the sort cut
12162    /// through runs of values that agree for a long way.
12163    #[test]
12164    fn a_large_dictionary_ranks_in_value_order() {
12165        let path = path("dictionary-large-rank");
12166        let value = |row: u64| {
12167            let mixed = row.wrapping_mul(0x9e37_79b9_7f4a_7c15) >> 40;
12168            match row % 3 {
12169                0 => format!("https://example.com/a/long/shared/path/{mixed:08}"),
12170                1 => format!("{mixed}"),
12171                _ => format!("x{}", row % 1000).repeat(1 + (row % 4) as usize) + &row.to_string(),
12172            }
12173        };
12174        let distinct = 70_000;
12175        let parts = 4 * distinct / 1000;
12176        let per_part = 1000;
12177        let mut writer =
12178            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12179                .expect("new file");
12180        for part in 0..parts {
12181            let values = (0..per_part)
12182                .map(|row| Value::Varchar(value((part * per_part + row) / 4)))
12183                .collect::<Vec<_>>();
12184            let chunk = Chunk::new(vec![
12185                Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
12186            ])
12187            .expect("matching rows");
12188            writer.append(&chunk).expect("a part");
12189        }
12190        writer.finish().expect("commit");
12191
12192        let reader = Reader::open(&path).expect("reopen from disk");
12193        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12194        let count = dictionary.ranks().expect("a ranked dictionary");
12195        assert_eq!(count, distinct as usize, "every distinct value has a rank");
12196        assert!(count >= PARALLEL_SORT_MIN, "too few values to be sorted on more than one thread");
12197        let ranked = (0..count)
12198            .map(|rank| {
12199                let code = dictionary.code_at_rank(rank).expect("a code");
12200                dictionary.try_bytes_at(code as usize).expect("read").expect("a value").to_vec()
12201            })
12202            .collect::<Vec<_>>();
12203        let mut expected = (0..distinct).map(|row| value(row).into_bytes()).collect::<Vec<_>>();
12204        expected.sort();
12205        assert_eq!(ranked, expected, "rank order is value order");
12206        fs::remove_file(path).expect("remove scratch file");
12207    }
12208
12209    /// A string column's synopsis is turned into values without keeping the blocks it went through.
12210    ///
12211    /// Three thousand values, every fifth of them four times over, so the synopsis is a prefix of
12212    /// five hundred and twelve codes spread over all three payload blocks. Reading it used to leave
12213    /// all three decoded for as long as the reader lived. It leaves none of them now, and the second
12214    /// read answers out of what the first remembered.
12215    /// A directory read out of the file a window at a time is the directory read whole.
12216    ///
12217    /// The windows here are far smaller than any field is long, so every kind of field is split
12218    /// across a refill somewhere, and a bound is offered to its codec short more than once. The
12219    /// synopses are left in the file, and each one read back from where it was left is the one the
12220    /// whole read decoded.
12221    #[test]
12222    fn a_directory_read_a_window_at_a_time_is_the_directory_read_whole() {
12223        let path = path("windowed-directory");
12224        let fields = vec![
12225            Field::required("id", LogicalType::BigInt),
12226            Field::required("word", LogicalType::Varchar),
12227            Field::new("score", LogicalType::Double),
12228        ];
12229        let mut writer = Writer::create(&path, "items", fields).expect("new file");
12230        for part in 0..70_i64 {
12231            let ids = (0..100).map(|row| Value::BigInt(part * 100 + row % 7)).collect::<Vec<_>>();
12232            let words = (0..100)
12233                .map(|row| Value::Varchar(format!("word {}", row % 13)))
12234                .collect::<Vec<_>>();
12235            let scores = (0..100)
12236                .map(|row| if row % 4 == 0 { Value::Null } else { Value::Double(row as f64) })
12237                .collect::<Vec<_>>();
12238            let chunk = Chunk::new(vec![
12239                Vector::from_values(LogicalType::BigInt, &ids).expect("integers"),
12240                Vector::from_values(LogicalType::Varchar, &words).expect("strings"),
12241                Vector::from_values(LogicalType::Double, &scores).expect("doubles"),
12242            ])
12243            .expect("three columns");
12244            writer.append(&chunk).expect("a part");
12245        }
12246        writer.finish().expect("commit");
12247
12248        let catalog = Catalog::open(&path).expect("reopen");
12249        let entry = catalog.entries.first().expect("one table").directory;
12250        let (offset, length) = (entry.offset, entry.length as usize);
12251        let mut bytes = vec![0; length];
12252        read_at(&catalog.file, offset, &mut bytes).expect("the directory");
12253        assert_eq!(file_checksum(&catalog.file, offset, length).expect("checksum"), entry.hash);
12254        let whole = decode_directory(&bytes, catalog.size).expect("whole");
12255        assert!(whole.stripes.len() > 1, "the table should span stripes");
12256        for size in [1, 7, 33, 4_096] {
12257            let mut cursor = Cursor::over(&catalog.file, offset, length);
12258            cursor.window.as_mut().expect("a window").size = size;
12259            let windowed = read_directory(cursor, catalog.size, Some(offset)).expect("windowed");
12260            assert_eq!(format!("{:?}", windowed.stripes), format!("{:?}", whole.stripes));
12261            assert_eq!(format!("{:?}", windowed.fields), format!("{:?}", whole.fields));
12262            let mut stored = 0;
12263            for (column, (left, held)) in
12264                windowed.frequencies.iter().zip(&whole.frequencies).enumerate()
12265            {
12266                match (left, held) {
12267                    (None, None) => {}
12268                    (
12269                        Some(super::Frequencies::Stored { span, values }),
12270                        Some(super::Frequencies::Held(summary)),
12271                    ) => {
12272                        let mut one = vec![0; span.length as usize];
12273                        read_at(&catalog.file, span.offset, &mut one).expect("a synopsis");
12274                        let read = decode_summary(
12275                            &mut Cursor::new(&one),
12276                            &whole.fields[column],
12277                            whole.rows,
12278                            *values,
12279                        )
12280                        .expect("a valid synopsis")
12281                        .expect("one is there");
12282                        assert_eq!(format!("{read:?}"), format!("{summary:?}"));
12283                        stored += 1;
12284                    }
12285                    other => panic!("column {column} came back as {other:?}"),
12286                }
12287            }
12288            assert!(stored >= 2, "only {stored} synopses were left in the file");
12289        }
12290        let reader = catalog.table("items").expect("the table");
12291        assert!(reader.frequency_summaries[1].get().is_none());
12292        assert!(reader.top_frequencies(1, 1).expect("a readable synopsis").is_some());
12293        let first = reader.frequency_summaries[1].get().expect("decoded synopsis");
12294        let clone = reader.clone();
12295        assert!(clone.top_frequencies(1, 1).expect("cached synopsis").is_some());
12296        assert!(Arc::ptr_eq(first, clone.frequency_summaries[1].get().expect("same synopsis")));
12297        fs::remove_file(path).expect("remove scratch file");
12298    }
12299
12300    #[test]
12301    fn a_checksum_carried_across_reads_is_the_checksum_of_the_whole() {
12302        let path = path("file-checksum");
12303        let bytes = (0..200_000_u32)
12304            .map(|at| (at.wrapping_mul(2_654_435_761) >> 13) as u8)
12305            .collect::<Vec<_>>();
12306        fs::write(&path, &bytes).expect("scratch file");
12307        let file = File::open(&path).expect("open");
12308        for (offset, length) in [
12309            (0, 0),
12310            (3, 1),
12311            (5, 31),
12312            (0, 32),
12313            (9, 33),
12314            (1, 65_536),
12315            (7, 65_567),
12316            (0, 200_000),
12317            (11, 131_101),
12318        ] {
12319            let whole = checksum(&bytes[offset..offset + length]);
12320            assert_eq!(
12321                file_checksum(&file, offset as u64, length).expect("read"),
12322                whole,
12323                "{offset} {length}"
12324            );
12325        }
12326        fs::remove_file(path).expect("remove scratch file");
12327    }
12328
12329    #[test]
12330    fn a_string_synopsis_is_read_without_keeping_the_dictionary_blocks() {
12331        let path = path("synopsis-keeps-no-block");
12332        let spelled = |index: usize| Value::Varchar(format!("phrase {index:05}"));
12333        let mut values = (0..3_000).map(spelled).collect::<Vec<_>>();
12334        for _ in 0..3 {
12335            values.extend((0..3_000).step_by(5).map(spelled));
12336        }
12337        let mut writer =
12338            Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12339                .expect("new file");
12340        for part in values.chunks(1_024) {
12341            writer
12342                .append(
12343                    &Chunk::new(vec![
12344                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12345                    ])
12346                    .expect("one column"),
12347                )
12348                .expect("a part");
12349        }
12350        writer.finish().expect("commit");
12351
12352        let reader = Reader::open(&path).expect("reopen from disk");
12353        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12354        let resting = dictionary.footprint();
12355        let prefix = reader.frequency_prefix(0).expect("a readable synopsis").expect("one");
12356        assert_eq!(prefix.entries.len(), 512);
12357        for (value, count) in &prefix.entries {
12358            let Value::Varchar(text) = value else { panic!("a string column gave {value:?}") };
12359            let index = text["phrase ".len()..].parse::<usize>().expect("a spelled number");
12360            assert_eq!((index % 5, *count), (0, 4), "{text} came back with {count}");
12361        }
12362        assert_eq!(dictionary.footprint(), resting, "reading the synopsis kept a decoded block");
12363        let again = reader.frequency_prefix(0).expect("a readable synopsis").expect("one");
12364        assert_eq!(again.entries, prefix.entries);
12365        fs::remove_file(path).expect("remove scratch file");
12366    }
12367
12368    /// A sweep of the dictionary reads every value, and the second sweep keeps what it read, up to
12369    /// the budget.
12370    ///
12371    /// The point of the sweep is the resident size rather than the answer, so both are checked
12372    /// here. The first sweep keeps nothing, because a process that runs one statement never reads
12373    /// a block twice. A dictionary this small is well under [`TEXT_KEEP_BUDGET`], so the second
12374    /// sweep keeps everything and a third decodes nothing, which is what makes a session asking the
12375    /// same question again cost what it should. The ceiling is the other half of it and it has its own
12376    /// test below, because a ceiling that never binds is not a ceiling anybody checked.
12377    #[test]
12378    fn a_dictionary_sweep_reads_every_value_and_keeps_it_under_the_budget() {
12379        let path = path("dictionary-sweep");
12380        // Two thousand five hundred distinct values is two whole payload blocks and a part of a
12381        // third, so the sweep has to be called more than once and the last call has to stop short.
12382        let spellings = (0..2_500)
12383            .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12384            .collect::<Vec<_>>();
12385        let mut writer =
12386            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12387                .expect("new file");
12388        // A chunk is a part and a part is at most 1,024 rows, so the values go in three of them.
12389        // The dictionary is table wide and does not care where a value was written.
12390        for part in spellings.chunks(1_024) {
12391            writer
12392                .append(
12393                    &Chunk::new(vec![
12394                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12395                    ])
12396                    .expect("one column"),
12397                )
12398                .expect("stripe written");
12399        }
12400        writer.finish().expect("commit");
12401
12402        let reader = Reader::open(&path).expect("valid directory");
12403        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12404        assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12405        for first in [0, TEXT_PAYLOAD_VALUES, TEXT_PAYLOAD_VALUES * 2] {
12406            assert!(dictionary.text_block_might_contain(first, b"value").expect("signature"));
12407            assert!(!dictionary.text_block_might_contain(first, b"google").expect("signature"));
12408        }
12409
12410        let resting = dictionary.footprint();
12411        let sweep = || {
12412            let mut swept: Vec<Vec<u8>> = Vec::new();
12413            let mut at = 0;
12414            let mut calls = 0;
12415            while at < dictionary.len() {
12416                let stopped = dictionary
12417                    .sweep_text(at, dictionary.len(), &mut |index: usize, text: &[u8]| {
12418                        assert_eq!(index, swept.len(), "a sweep hands its values over in order");
12419                        swept.push(text.to_vec());
12420                        Ok(())
12421                    })
12422                    .expect("a sweep reads");
12423                assert!(stopped > at, "a sweep moves");
12424                at = stopped;
12425                calls += 1;
12426            }
12427            assert_eq!(calls, 3, "a sweep hands over one block at a time");
12428            swept
12429        };
12430        let swept = sweep();
12431        assert_eq!(dictionary.footprint(), resting, "a first sweep keeps nothing it decoded");
12432        assert_eq!(sweep(), swept, "a second sweep reads what the first did");
12433        let after = dictionary.footprint();
12434        assert!(after > resting, "a second sweep under the budget keeps what it decoded");
12435
12436        let read = (0..dictionary.len())
12437            .map(|code| dictionary.try_bytes_at(code).expect("read").expect("a value").to_vec())
12438            .collect::<Vec<_>>();
12439        assert_eq!(swept, read, "a sweep answers what a point read answers");
12440        // A read per value is about what makes the unpacked ends worth building, so whether they
12441        // are built here depends on how many reads the sweep made on the way. They are the one thing
12442        // allowed to grow, by four bytes a value, and nothing of the payload is.
12443        let grown = dictionary.footprint() - after;
12444        assert!(
12445            grown == 0 || grown == dictionary.len() * size_of::<u32>(),
12446            "a point read of a kept block decodes nothing, and {grown} bytes grew"
12447        );
12448        fs::remove_file(path).expect("remove scratch file");
12449    }
12450
12451    #[test]
12452    fn a_damaged_substring_signature_is_checked_only_when_used() {
12453        let path = path("damaged-substring-signature");
12454        let mut writer =
12455            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12456                .expect("new file");
12457        let rows = [Value::Varchar("google".into()), Value::Varchar("example".into())];
12458        writer
12459            .append(
12460                &Chunk::new(vec![
12461                    Vector::from_values(LogicalType::Varchar, &rows).expect("strings"),
12462                ])
12463                .expect("one column"),
12464            )
12465            .expect("stripe written");
12466        writer.finish().expect("commit");
12467
12468        let reader = Reader::open(&path).expect("valid directory");
12469        let page = reader.table.dictionaries[0].expect("string dictionary page");
12470        let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
12471        file.seek(SeekFrom::Start(page.offset + u64::from(page.length) - 1))
12472            .expect("last signature byte");
12473        file.write_all(&[255]).expect("damage signature");
12474        let reader = Reader::open(&path).expect("the directory is still valid");
12475        let dictionary = reader.dictionary(0).expect("index is still valid").expect("dictionary");
12476        let error = dictionary
12477            .text_block_might_contain(0, b"goog")
12478            .expect_err("a used signature checks its own checksum");
12479        assert!(error.message().contains("substring signatures checksum differs"), "{error}");
12480        fs::remove_file(path).expect("remove scratch file");
12481    }
12482
12483    /// A sweep over a block whose second run of offsets is short reads the same values as a point
12484    /// read does.
12485    ///
12486    /// The sweep decodes the offsets of a whole run at a time rather than a value at a time, and a
12487    /// run holds half a block, so the count it asks for is the run length everywhere but at the end
12488    /// of the dictionary. Two thousand five hundred values, which is what the test above writes,
12489    /// never puts a short run second in its block: the last block there begins on a run boundary and
12490    /// holds one run. Two thousand eight hundred does, so the last block is a whole run of five
12491    /// hundred and twelve followed by two hundred and forty, and an off by one in either the count
12492    /// asked for or the slice taken out of the answer shows up as a wrong value or a refusal.
12493    #[test]
12494    fn a_sweep_over_a_block_with_a_short_second_run_reads_what_a_point_read_reads() {
12495        let path = path("dictionary-sweep-short-run");
12496        let spellings = (0..2_800)
12497            .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12498            .collect::<Vec<_>>();
12499        let mut writer =
12500            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12501                .expect("new file");
12502        for part in spellings.chunks(1_024) {
12503            writer
12504                .append(
12505                    &Chunk::new(vec![
12506                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12507                    ])
12508                    .expect("one column"),
12509                )
12510                .expect("stripe written");
12511        }
12512        writer.finish().expect("commit");
12513
12514        let reader = Reader::open(&path).expect("valid directory");
12515        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12516        assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12517        let last = dictionary.len() % TEXT_PAYLOAD_VALUES;
12518        assert!(last > TEXT_OFFSET_RUN, "the last block has to reach into a second run of offsets");
12519        assert!(last < TEXT_PAYLOAD_VALUES, "and that second run has to be short of a whole one");
12520
12521        let mut swept: Vec<Vec<u8>> = Vec::new();
12522        let mut at = 0;
12523        while at < dictionary.len() {
12524            let stopped = dictionary
12525                .sweep_text(at, dictionary.len(), &mut |index: usize, text: &[u8]| {
12526                    assert_eq!(index, swept.len(), "a sweep hands its values over in order");
12527                    swept.push(text.to_vec());
12528                    Ok(())
12529                })
12530                .expect("a sweep reads");
12531            assert!(stopped > at, "a sweep moves");
12532            at = stopped;
12533        }
12534        let read = (0..dictionary.len())
12535            .map(|code| dictionary.try_bytes_at(code).expect("read").expect("a value").to_vec())
12536            .collect::<Vec<_>>();
12537        assert_eq!(swept, read, "a sweep answers what a point read answers");
12538        fs::remove_file(path).expect("remove scratch file");
12539    }
12540
12541    /// The unpacked ends answer what the packed ends answer, on both sides of the switch.
12542    ///
12543    /// A column asked for one offset at a time reads them out of the packed form until the reads
12544    /// are worth a table and out of the table after that, so every value here is read twice and the
12545    /// two passes are compared against the spellings and against each other. Two thousand eight
12546    /// hundred values is two payload blocks and a bit, which puts the switch in the middle of the
12547    /// first pass and means the pass straddles a block boundary, where the start of a value is zero
12548    /// rather than the end of the value before it.
12549    #[test]
12550    fn the_unpacked_ends_answer_what_the_packed_ends_answer() {
12551        let path = path("dictionary-unpacked-ends");
12552        let spellings = (0..2_800)
12553            .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12554            .collect::<Vec<_>>();
12555        let mut writer =
12556            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12557                .expect("new file");
12558        for part in spellings.chunks(1_024) {
12559            writer
12560                .append(
12561                    &Chunk::new(vec![
12562                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12563                    ])
12564                    .expect("one column"),
12565                )
12566                .expect("stripe written");
12567        }
12568        writer.finish().expect("commit");
12569
12570        let reader = Reader::open(&path).expect("valid directory");
12571        let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12572        assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12573        let wanted = (0..spellings.len())
12574            .map(|index| format!("value {index:08} {}", "x".repeat(index % 40)).into_bytes())
12575            .collect::<Vec<_>>();
12576
12577        let pass = |what: &str| {
12578            for (index, value) in wanted.iter().enumerate() {
12579                let len = dictionary.try_bytes_len_at(index).expect("read").expect("a value");
12580                assert_eq!(len, value.len(), "{what} has the wrong length at {index}");
12581                let bytes = dictionary.try_bytes_at(index).expect("read").expect("a value");
12582                assert_eq!(bytes, value.as_slice(), "{what} has the wrong value at {index}");
12583            }
12584        };
12585        pass("the first pass");
12586        pass("the second pass");
12587
12588        // The whole vector in one call, over the text and through codes into it, which is how a
12589        // scan of a stored column hands it out. The codes run backwards and repeat so that they are
12590        // neither the positions nor in order.
12591        let lens = wanted.iter().map(|value| value.len() as i64).collect::<Vec<_>>();
12592        let mut whole = vec![0i64; wanted.len()];
12593        assert!(dictionary.try_bytes_lens(&mut whole).expect("read"), "the text answers whole");
12594        assert_eq!(whole, lens, "a vector of lengths answers what a length at a time answers");
12595        let codes = (0..4_000_u32).map(|row| (7 * (4_000 - row)) % 2_800).collect::<Vec<_>>();
12596        let coded = Vector::dictionary_over(codes.clone(), dictionary).expect("codes in range");
12597        let mut through = vec![0i64; codes.len()];
12598        assert!(coded.try_bytes_lens(&mut through).expect("read"), "the codes answer whole");
12599        for (row, &code) in codes.iter().enumerate() {
12600            assert_eq!(through[row], lens[code as usize], "row {row} reads code {code}");
12601            let one = coded.try_bytes_len_at(row).expect("read").expect("a value");
12602            assert_eq!(through[row], one as i64, "row {row} a row at a time");
12603        }
12604
12605        // A handful of codes over a column nobody has read yet is short of the table, so the same
12606        // call answers out of the packed ends instead, and has to answer the same.
12607        let fresh = Reader::open(&path).expect("valid directory");
12608        let untouched = fresh.dictionary(0).expect("read").expect("a string column has one");
12609        let few = vec![2_799_u32, 0, 1_024, 1_023, 511, 512];
12610        let coded = Vector::dictionary_over(few.clone(), untouched).expect("in range");
12611        let mut short = vec![0i64; few.len()];
12612        assert!(coded.try_bytes_lens(&mut short).expect("read"), "the codes answer whole");
12613        let expected = few.iter().map(|&code| lens[code as usize]).collect::<Vec<_>>();
12614        assert_eq!(short, expected, "the packed ends answer what the table answers");
12615        fs::remove_file(path).expect("remove scratch file");
12616    }
12617
12618    /// Narrowing a page takes what fits and refuses the page for anything that does not.
12619    ///
12620    /// The edges of the range on both sides and one step past each of them, for every type, because
12621    /// checking a page separately from converting it is only right if the check refuses exactly what
12622    /// `TryFrom` would have refused, and off by one there is a file that reads back a different
12623    /// number than it was given. The check is a bit pattern rather than a comparison, so it is not
12624    /// the shape a reader would guess from the bounds, which is why all six are here. The empty page
12625    /// is here because a check written the obvious way starts with the extremes the wrong way round
12626    /// and refuses it.
12627    #[test]
12628    fn narrowing_a_page_takes_what_fits_and_refuses_what_does_not() {
12629        assert_eq!(fit::<i8>(&[]).expect("an empty page fits anything"), Vec::<i8>::new());
12630        assert_eq!(fit::<i8>(&[-128, 0, 127]).expect("the edges fit"), vec![-128_i8, 0, 127]);
12631        fit::<i8>(&[128]).expect_err("one past the top does not fit");
12632        fit::<i8>(&[-129]).expect_err("one past the bottom does not fit");
12633        assert_eq!(fit::<u8>(&[0, 255]).expect("the edges fit"), vec![0_u8, 255]);
12634        fit::<u8>(&[256]).expect_err("one past the top does not fit");
12635        fit::<u8>(&[-1]).expect_err("a negative does not fit an unsigned page");
12636        assert_eq!(
12637            fit::<i16>(&[-32_768, 0, 32_767]).expect("the edges fit"),
12638            vec![-32_768_i16, 0, 32_767]
12639        );
12640        fit::<i16>(&[32_768]).expect_err("one past the top does not fit");
12641        fit::<i16>(&[-32_769]).expect_err("one past the bottom does not fit");
12642        assert_eq!(fit::<u16>(&[0, 65_535]).expect("the edges fit"), vec![0_u16, 65_535]);
12643        fit::<u16>(&[65_536]).expect_err("one past the top does not fit");
12644        fit::<u16>(&[-1]).expect_err("a negative does not fit an unsigned page");
12645        assert_eq!(
12646            fit::<i32>(&[i64::from(i32::MIN), 0, i64::from(i32::MAX)]).expect("the edges fit"),
12647            vec![i32::MIN, 0, i32::MAX]
12648        );
12649        fit::<i32>(&[i64::from(i32::MAX) + 1]).expect_err("one past the top does not fit");
12650        fit::<i32>(&[i64::from(i32::MIN) - 1]).expect_err("one past the bottom does not fit");
12651        assert_eq!(
12652            fit::<u32>(&[0, 4_294_967_295]).expect("the edges fit"),
12653            vec![0_u32, 4_294_967_295]
12654        );
12655        fit::<u32>(&[4_294_967_296]).expect_err("one past the top does not fit");
12656        fit::<u32>(&[-1]).expect_err("a negative does not fit an unsigned page");
12657
12658        // One value in a page that fits is still a page that does not, which is the thing an or
12659        // into an accumulator could get wrong in a way a page of one value would never show.
12660        fit::<i8>(&[0, 1, 2, 128, 3]).expect_err("one bad value spoils the page");
12661    }
12662
12663    /// The residue says yes to exactly what `TryFrom` says yes to.
12664    ///
12665    /// The edges above are the cases anyone would think to write down. This is the argument that
12666    /// there are no others, made by asking both questions about every value either narrow type could
12667    /// have an opinion about, and then about the values around the wide edges and the ends of an
12668    /// `i64`, which a range that size cannot reach.
12669    #[test]
12670    fn the_residue_agrees_with_a_checked_conversion_everywhere() {
12671        for value in -70_000_i64..70_000 {
12672            assert_eq!(fit::<i8>(&[value]).is_ok(), i8::try_from(value).is_ok(), "{value} as i8");
12673            assert_eq!(fit::<u8>(&[value]).is_ok(), u8::try_from(value).is_ok(), "{value} as u8");
12674            assert_eq!(fit::<i16>(&[value]).is_ok(), i16::try_from(value).is_ok(), "{value} i16");
12675            assert_eq!(fit::<u16>(&[value]).is_ok(), u16::try_from(value).is_ok(), "{value} u16");
12676        }
12677        let wide = [i64::MIN, i64::MIN + 1, i64::from(i32::MIN), 0, i64::from(u32::MAX), i64::MAX];
12678        for edge in wide {
12679            for step in -2_i64..=2 {
12680                let value = edge.saturating_add(step);
12681                assert_eq!(
12682                    fit::<i32>(&[value]).is_ok(),
12683                    i32::try_from(value).is_ok(),
12684                    "{value} as i32"
12685                );
12686                assert_eq!(
12687                    fit::<u32>(&[value]).is_ok(),
12688                    u32::try_from(value).is_ok(),
12689                    "{value} as u32"
12690                );
12691            }
12692        }
12693    }
12694
12695    /// All three block layouts come back as the same values in the same order.
12696    ///
12697    /// Blocks outside the page are what every file this build writes holds. Blocks that say where
12698    /// they are but sit inside the page behind the order are format 26, and blocks behind one
12699    /// another with only their ends recorded are older still. Nothing in the writer produces the
12700    /// last two any more, so the only way to find out whether the reader still understands those
12701    /// files is to write them here. The
12702    /// bytes go straight into a file with no directory around them, because what is under test is
12703    /// [`open_global_dictionary`], which is handed a page and a file and asks the directory for
12704    /// nothing.
12705    ///
12706    /// Three thousand values so that there are three payload blocks and a partial fourth, which is
12707    /// what makes the last block the one place where a length and an end disagree about what they
12708    /// are counting.
12709    #[test]
12710    fn a_dictionary_reads_the_same_whether_its_blocks_say_where_they_are() {
12711        let spellings = (0..3_000)
12712            .map(|index| format!("value {index:08} {}", "y".repeat(index % 40)))
12713            .collect::<Vec<_>>();
12714        let mut read = Vec::new();
12715        for layout in ["outside", "inside", "behind"] {
12716            let mut dictionary = GlobalDictionary::new();
12717            for text in &spellings {
12718                dictionary.code(text).expect("a code for every spelling");
12719            }
12720            dictionary.finish_blocks().expect("the last block encodes");
12721            let order = dictionary.ranked(None).expect("a sorted order");
12722            // Where the blocks go if they start at `from` and follow one another.
12723            let laid = |from: u64| {
12724                let mut at = from;
12725                dictionary
12726                    .blocks
12727                    .iter()
12728                    .map(|block| {
12729                        let place =
12730                            Placed { start: at, length: block.len() as u64, hash: checksum(block) };
12731                        at += block.len() as u64;
12732                        place
12733                    })
12734                    .collect::<Vec<_>>()
12735            };
12736            let payload = dictionary.blocks.concat();
12737            let scattered = layout != "behind";
12738            let (bytes, encoded, offset, length) = if layout == "outside" {
12739                let mut bytes = vec![0; HEADER as usize];
12740                bytes.extend_from_slice(&payload);
12741                let encoded = encode_global_dictionary(&dictionary, &order, &laid(HEADER), true)
12742                    .expect("an encoding");
12743                let offset = bytes.len() as u64;
12744                bytes.extend_from_slice(&encoded.index);
12745                bytes.extend_from_slice(&encoded.ranks);
12746                bytes.extend_from_slice(&encoded.grams);
12747                let length = encoded.index.len() + encoded.ranks.len() + encoded.grams.len();
12748                (bytes, encoded, offset, length)
12749            } else {
12750                // The index is the same length wherever the blocks are, so a first pass says where
12751                // the page ends and the second writes the places that follow it.
12752                let first = encode_global_dictionary(&dictionary, &order, &laid(0), scattered)
12753                    .expect("an encoding");
12754                let body = (first.index.len() + first.ranks.len() + first.grams.len()) as u64;
12755                let encoded = encode_global_dictionary(&dictionary, &order, &laid(body), scattered)
12756                    .expect("an encoding");
12757                let mut bytes = encoded.index.clone();
12758                bytes.extend_from_slice(&encoded.ranks);
12759                bytes.extend_from_slice(&encoded.grams);
12760                bytes.extend_from_slice(&payload);
12761                let length = bytes.len();
12762                (bytes, encoded, 0, length)
12763            };
12764            let path = path(&format!("blocks-{layout}"));
12765            fs::write(&path, &bytes).expect("the dictionary is written on its own");
12766            let file = Arc::new(File::open(&path).expect("it opens again"));
12767            let page = Page {
12768                offset,
12769                length: u32::try_from(length).expect("a test dictionary is small"),
12770                hash: checksum(&encoded.index),
12771            };
12772            let opened =
12773                open_global_dictionary(file, page, &LogicalType::Varchar, TEXT_KEEP_BUDGET)
12774                    .expect("a dictionary laid out either way opens");
12775            let mut swept: Vec<Vec<u8>> = Vec::new();
12776            let mut at = 0;
12777            while at < opened.len() {
12778                at = opened
12779                    .sweep_text(at, opened.len(), &mut |_index: usize, text: &[u8]| {
12780                        swept.push(text.to_vec());
12781                        Ok(())
12782                    })
12783                    .expect("a sweep reads");
12784            }
12785            fs::remove_file(&path).expect("clean up");
12786            read.push(swept);
12787        }
12788        let wanted =
12789            spellings.iter().map(|text| text.as_bytes().to_vec()).collect::<Vec<Vec<u8>>>();
12790        assert_eq!(read[0], wanted, "the blocks outside the page hold the values");
12791        assert_eq!(read[1], read[0], "the blocks inside the page hold the same values");
12792        assert_eq!(read[2], read[0], "the blocks behind one another hold the same values");
12793    }
12794
12795    /// A dictionary at its budget sweeps without keeping, and still answers what it answered.
12796    ///
12797    /// The budget is a quarter of a gigabyte in a running database, which is a fine size for a real
12798    /// column and no size at all for a test, so this opens the same dictionary a second time with a
12799    /// budget of zero. That is the shape of the hundred million row case: `URL` fills the budget
12800    /// somewhere in the middle of itself and everything past that point is read and dropped, which
12801    /// costs the decode again and holds none of it.
12802    #[test]
12803    fn a_dictionary_at_its_budget_sweeps_without_keeping() {
12804        let path = path("dictionary-budget");
12805        let spellings = (0..2_500)
12806            .map(|index| Value::Varchar(format!("value {index:08} {}", "y".repeat(index % 40))))
12807            .collect::<Vec<_>>();
12808        let mut writer =
12809            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12810                .expect("new file");
12811        for part in spellings.chunks(1_024) {
12812            writer
12813                .append(
12814                    &Chunk::new(vec![
12815                        Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12816                    ])
12817                    .expect("one column"),
12818                )
12819                .expect("stripe written");
12820        }
12821        writer.finish().expect("commit");
12822
12823        let reader = Reader::open(&path).expect("valid directory");
12824        let page = reader.table.dictionaries[0].expect("a string column has one");
12825        let file = Arc::clone(&reader.file);
12826        let starved = open_global_dictionary(file, page, &LogicalType::Varchar, 0)
12827            .expect("a dictionary opens whatever it may keep");
12828
12829        let resting = starved.footprint();
12830        let mut swept: Vec<Vec<u8>> = Vec::new();
12831        let mut at = 0;
12832        while at < starved.len() {
12833            at = starved
12834                .sweep_text(at, starved.len(), &mut |_index: usize, text: &[u8]| {
12835                    swept.push(text.to_vec());
12836                    Ok(())
12837                })
12838                .expect("a sweep reads");
12839        }
12840        assert_eq!(swept.len(), spellings.len(), "a starved sweep still reads every value");
12841        assert_eq!(starved.footprint(), resting, "and keeps no block it decoded");
12842
12843        let generous = reader.dictionary(0).expect("read").expect("a string column has one");
12844        let read = (0..generous.len())
12845            .map(|code| generous.try_bytes_at(code).expect("read").expect("a value").to_vec())
12846            .collect::<Vec<_>>();
12847        assert_eq!(swept, read, "a starved sweep answers what a point read answers");
12848        fs::remove_file(path).expect("remove scratch file");
12849    }
12850
12851    #[test]
12852    fn damaged_membership_cannot_skip_a_string_page() {
12853        let path = path("damaged-membership");
12854        let mut writer = Writer::create(
12855            &path,
12856            "items",
12857            vec![
12858                Field::required("id", LogicalType::Integer),
12859                Field::new("text", LogicalType::Varchar),
12860            ],
12861        )
12862        .expect("new file");
12863        writer.append(&sample()).expect("stripe written");
12864        writer.finish().expect("commit");
12865
12866        let reader = Reader::open(&path).expect("valid directory");
12867        let membership = reader.table.stripes[0].memberships.get(1).expect("string membership");
12868        let mut file = OpenOptions::new().write(true).open(&path).expect("open membership page");
12869        file.seek(SeekFrom::Start(membership.offset)).expect("membership start");
12870        file.write_all(&[255]).expect("damage membership");
12871        let error = reader.skips_codes(0, 1, &[3]).expect_err("corruption must not skip rows");
12872        assert!(error.message().contains("membership page checksum differs"), "{error}");
12873        fs::remove_file(path).expect("remove scratch file");
12874    }
12875
12876    #[test]
12877    fn membership_delta_stream_is_sorted_exact_and_bounded() {
12878        let unique = unique_codes(&[900, 4, 4, 72, 9, u32::MAX]);
12879        assert_eq!(unique, [4, 9, 72, 900, u32::MAX]);
12880        let encoded = encode_membership(&unique);
12881        assert_eq!(
12882            decode_membership(&encoded).expect("valid membership"),
12883            [4, 9, 72, 900, u32::MAX]
12884        );
12885        // A stripe's index is the union of its parts', so a code in two of them is in it once and
12886        // the result is still one ascending run of deltas.
12887        let merged = merged_codes(vec![vec![4, 900], vec![9, 900, u32::MAX], vec![72]]);
12888        assert_eq!(merged, [4, 9, 72, 900, u32::MAX]);
12889        assert_eq!(
12890            decode_membership(&encode_membership(&merged)).expect("valid membership"),
12891            unique
12892        );
12893        assert!(decode_membership(&[1, 0x80]).is_err(), "a truncated varint is invalid");
12894        assert!(
12895            decode_membership(&[1, 0xff, 0xff, 0xff, 0xff, 0x10]).is_err(),
12896            "a value past u32 is invalid"
12897        );
12898    }
12899
12900    #[test]
12901    fn a_global_dictionary_may_be_larger_than_one_column_page() {
12902        let dictionary = Page {
12903            offset: HEADER,
12904            length: u32::try_from(MAX_PAGE + 1).expect("the page bound fits on disk"),
12905            hash: 0,
12906        };
12907        let table = Table {
12908            name: "items".to_owned(),
12909            fields: vec![Field::new("text", LogicalType::Varchar)],
12910            stripes: Vec::new(),
12911            rows: 0,
12912            dictionaries: vec![Some(dictionary)],
12913            dictionary_payloads: Vec::new(),
12914            distincts: vec![None],
12915            frequencies: vec![None],
12916            pair_frequencies: Vec::new(),
12917            frequency_texts: Vec::new(),
12918            host_groups: None,
12919            clustering: None,
12920            generation: 1,
12921            sections: Vec::new(),
12922        };
12923        let directory = encode_directory(&table).expect("directory");
12924        let file_size = dictionary.offset + u64::from(dictionary.length) + 1;
12925
12926        let decoded = decode_directory(&directory, file_size).expect("large lazy dictionary");
12927        assert_eq!(decoded.dictionaries[0].expect("dictionary").length, dictionary.length);
12928    }
12929
12930    #[test]
12931    fn a_column_with_one_value_everywhere_costs_almost_nothing_a_row() {
12932        let path = path("constant-codes");
12933        let mut writer =
12934            Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12935                .expect("new file");
12936        let empty = vec![Value::Varchar(String::new()); 1024];
12937        for _ in 0..4 {
12938            let column = Vector::from_values(LogicalType::Varchar, &empty).expect("strings");
12939            writer.append(&Chunk::new(vec![column]).expect("one column")).expect("a part");
12940        }
12941        writer.finish().expect("commit");
12942
12943        let reader = Reader::open(&path).expect("valid directory");
12944        let pages = reader.layout().columns.first().expect("one column").pages;
12945        // This column used to cost four bytes a row, 16,384 of them, the same as a column of four
12946        // thousand distinct URLs would. The cascade calls each part a constant, so what is left is
12947        // a tag, a count and the value, and the row count stops being what drives the number.
12948        assert!(pages < 256, "{pages} bytes of pages for 4,096 rows of one value");
12949        let read = reader.read(3, &[0]).expect("the last part back");
12950        assert_eq!(read.value_at(0, 0), Value::Varchar(String::new()));
12951        assert_eq!(read.value_at(1023, 0), Value::Varchar(String::new()));
12952        fs::remove_file(path).expect("remove scratch file");
12953    }
12954
12955    #[test]
12956    fn a_cascade_value_too_wide_for_its_column_is_refused_rather_than_cut() {
12957        // What a damaged page looks like from here: the cascade decoded, so the bytes are not
12958        // truncated, but the values do not belong to the column the directory says they do.
12959        let over = vec![i64::from(i32::MAX) + 1];
12960        let error = narrowed(&LogicalType::Integer, over).expect_err("a page that disagrees");
12961        assert!(format!("{error}").contains("not of its type"), "{error}");
12962        assert!(narrowed(&LogicalType::BigInt, vec![i64::MIN]).is_ok(), "bigint holds all of i64");
12963        assert!(narrowed(&LogicalType::Varchar, vec![0]).is_err(), "strings are not integers");
12964    }
12965
12966    #[test]
12967    fn a_code_stream_the_cascade_cannot_shrink_is_left_alone() {
12968        // A shift register rather than a run, because an arithmetic run is the one wide shape the
12969        // cascade does shrink. This is what a column with tens of millions of distinct values hands
12970        // over: full width codes with no order to them.
12971        let mut state: u32 = 0x9e37_79b9;
12972        let spread: Vec<u32> = (0..1024)
12973            .map(|_| {
12974                state ^= state << 13;
12975                state ^= state >> 17;
12976                state ^= state << 5;
12977                state
12978            })
12979            .collect();
12980        assert_eq!(encoded_codes(&spread).expect("no failure"), None);
12981        let near: Vec<u32> = (0..1024).collect();
12982        let coded = encoded_codes(&near).expect("no failure").expect("counting up is packable");
12983        assert!(coded.len() < near.len() * 4, "{} bytes for a run of 1,024", coded.len());
12984    }
12985
12986    /// The columns of a stripe are encoded on whichever thread got to them, so the one thing that
12987    /// must not depend on which thread that was is the file. Two writes of the same rows are
12988    /// compared byte for byte rather than value for value, because a dictionary that two columns
12989    /// somehow shared would still read back correctly and would hand out its codes in the order the
12990    /// threads happened to run in, which is exactly what this is here to catch.
12991    #[test]
12992    fn two_writes_of_the_same_rows_give_the_same_bytes() {
12993        fn written(path: &PathBuf) {
12994            let fields = (0..40)
12995                .map(|column| {
12996                    let ty =
12997                        if column % 4 == 0 { LogicalType::Varchar } else { LogicalType::BigInt };
12998                    Field::new(format!("c{column}"), ty)
12999                })
13000                .collect::<Vec<_>>();
13001            let mut writer = Writer::create(path, "wide", fields).expect("new file");
13002            for part in 0..70_u64 {
13003                let columns = (0..40)
13004                    .map(|column| {
13005                        let values = (0..64_u64)
13006                            .map(|row| {
13007                                let seed = part.wrapping_mul(31).wrapping_add(row);
13008                                if column % 4 == 0 {
13009                                    Value::Varchar(format!("v{}", seed % 17))
13010                                } else {
13011                                    Value::BigInt(i64::try_from(seed % 97).expect("small"))
13012                                }
13013                            })
13014                            .collect::<Vec<_>>();
13015                        let ty = if column % 4 == 0 {
13016                            LogicalType::Varchar
13017                        } else {
13018                            LogicalType::BigInt
13019                        };
13020                        Vector::from_values(ty, &values).expect("a column")
13021                    })
13022                    .collect::<Vec<_>>();
13023                writer.append(&Chunk::new(columns).expect("forty columns")).expect("a part");
13024            }
13025            writer.finish().expect("commit");
13026        }
13027
13028        let first = path("repeatable-one");
13029        let second = path("repeatable-two");
13030        written(&first);
13031        written(&second);
13032        let left = fs::read(&first).expect("the first file");
13033        let right = fs::read(&second).expect("the second file");
13034        assert_eq!(left.len(), right.len(), "two writes of the same rows differ in length");
13035        assert!(left == right, "two writes of the same rows differ in their bytes");
13036
13037        // And the rows are still there, since a pair of identically wrong files would pass the
13038        // comparison above on its own.
13039        let reader = Reader::open(&first).expect("valid directory");
13040        assert_eq!(reader.table().rows(), 70 * 64);
13041        let read = reader.read(0, &[0, 1]).expect("the first part back");
13042        assert_eq!(read.value_at(0, 0), Value::Varchar("v0".to_owned()));
13043        assert_eq!(read.value_at(0, 1), Value::BigInt(0));
13044        fs::remove_file(first).expect("remove scratch file");
13045        fs::remove_file(second).expect("remove scratch file");
13046    }
13047
13048    /// Three tables of different shapes in one file, read back by name.
13049    fn three_tables(path: &PathBuf) {
13050        let writer = Writer::create(
13051            path,
13052            "region",
13053            vec![
13054                Field::new("r_key", LogicalType::Integer),
13055                Field::new("r_name", LogicalType::Varchar),
13056            ],
13057        )
13058        .expect("new file");
13059        let mut writer = writer;
13060        writer
13061            .append(
13062                &Chunk::new(vec![
13063                    Vector::from_values(
13064                        LogicalType::Integer,
13065                        &[Value::Integer(0), Value::Integer(1)],
13066                    )
13067                    .expect("keys"),
13068                    Vector::from_values(
13069                        LogicalType::Varchar,
13070                        &[Value::Varchar("AFRICA".to_owned()), Value::Varchar("ASIA".to_owned())],
13071                    )
13072                    .expect("names"),
13073                ])
13074                .expect("two columns"),
13075            )
13076            .expect("a part");
13077        let mut writer = writer
13078            .next("empty", vec![Field::new("nothing", LogicalType::BigInt)])
13079            .expect("a second table");
13080        writer
13081            .append(
13082                &Chunk::new(vec![
13083                    Vector::from_values(LogicalType::BigInt, &[Value::BigInt(7)]).expect("a row"),
13084                ])
13085                .expect("one column"),
13086            )
13087            .expect("a part");
13088        let mut writer =
13089            writer.next("wide", vec![Field::new("n", LogicalType::BigInt)]).expect("a third table");
13090        for part in 0..70_i64 {
13091            let values = (0..64).map(|row| Value::BigInt(part * 64 + row)).collect::<Vec<_>>();
13092            writer
13093                .append(
13094                    &Chunk::new(vec![
13095                        Vector::from_values(LogicalType::BigInt, &values).expect("a column"),
13096                    ])
13097                    .expect("one column"),
13098                )
13099                .expect("a part");
13100        }
13101        writer.finish().expect("commit");
13102    }
13103
13104    #[test]
13105    fn three_tables_in_one_file_read_back_by_name() {
13106        let file = path("three-tables");
13107        three_tables(&file);
13108        let catalog = Catalog::open(&file).expect("a committed catalog");
13109        assert_eq!(catalog.names().collect::<Vec<_>>(), ["region", "empty", "wide"]);
13110
13111        let region = catalog.table("region").expect("the first table");
13112        assert_eq!(region.table().rows(), 2);
13113        assert_eq!(
13114            region.read(0, &[1]).expect("names").value_at(1, 0),
13115            Value::Varchar("ASIA".to_owned())
13116        );
13117
13118        let wide = catalog.table("wide").expect("the third table");
13119        assert_eq!(wide.table().rows(), 70 * 64);
13120        assert_eq!(wide.read(0, &[0]).expect("the first part").value_at(0, 0), Value::BigInt(0));
13121
13122        // The middle table is reached without the one after it having been touched, which is what
13123        // a directory per table buys over one directory of everything.
13124        let empty = catalog.table("empty").expect("the second table");
13125        assert_eq!(empty.table().rows(), 1);
13126        assert_eq!(empty.read(0, &[0]).expect("the row").value_at(0, 0), Value::BigInt(7));
13127
13128        fs::remove_file(file).expect("remove scratch file");
13129    }
13130
13131    #[test]
13132    fn a_name_the_file_does_not_hold_is_an_error_rather_than_the_first_table() {
13133        let file = path("three-tables-missing");
13134        three_tables(&file);
13135        let catalog = Catalog::open(&file).expect("a committed catalog");
13136        let error = catalog.table("nation").expect_err("no such table");
13137        assert!(error.message().contains("nation"), "{}", error.message());
13138        fs::remove_file(file).expect("remove scratch file");
13139    }
13140
13141    #[test]
13142    fn a_file_of_three_tables_will_not_open_as_one() {
13143        let file = path("three-tables-unnamed");
13144        three_tables(&file);
13145        let error = Reader::open(&file).expect_err("more than one table");
13146        assert!(error.message().contains("more than one table"), "{}", error.message());
13147        fs::remove_file(file).expect("remove scratch file");
13148    }
13149
13150    /// One column per storage width, because the width is what decides how many bytes a row costs.
13151    #[test]
13152    fn decimals_of_every_storage_width_round_trip() {
13153        let file = path("decimals");
13154        let widths = [(4_u8, 2_u8), (9, 2), (18, 4), (38, 6)];
13155        let fields = widths
13156            .iter()
13157            .enumerate()
13158            .map(|(index, (width, scale))| {
13159                Field::new(
13160                    format!("d{index}"),
13161                    LogicalType::decimal(*width, *scale).expect("a decimal type"),
13162                )
13163            })
13164            .collect::<Vec<_>>();
13165        let mut writer = Writer::create(&file, "money", fields).expect("new file");
13166        let rows: [i128; 3] = [-1234, 0, 999];
13167        let columns = widths
13168            .iter()
13169            .map(|(width, scale)| {
13170                let values = rows
13171                    .iter()
13172                    .map(|unscaled| Value::Decimal {
13173                        unscaled: *unscaled,
13174                        width: *width,
13175                        scale: *scale,
13176                    })
13177                    .collect::<Vec<_>>();
13178                Vector::from_values(
13179                    LogicalType::decimal(*width, *scale).expect("a decimal type"),
13180                    &values,
13181                )
13182                .expect("a decimal column")
13183            })
13184            .collect::<Vec<_>>();
13185        writer.append(&Chunk::new(columns).expect("four columns")).expect("a part");
13186        writer.finish().expect("commit");
13187
13188        let reader = Reader::open(&file).expect("a committed file");
13189        for (index, (width, scale)) in widths.iter().enumerate() {
13190            assert_eq!(
13191                reader.table().fields()[index].ty,
13192                LogicalType::decimal(*width, *scale).expect("a decimal type"),
13193                "column {index} came back as another type"
13194            );
13195            let column = reader.read(0, &[index]).expect("the column");
13196            for (row, unscaled) in rows.iter().enumerate() {
13197                assert_eq!(
13198                    column.value_at(row, 0),
13199                    Value::Decimal { unscaled: *unscaled, width: *width, scale: *scale },
13200                    "column {index} row {row}"
13201                );
13202            }
13203        }
13204        fs::remove_file(file).expect("remove scratch file");
13205    }
13206
13207    #[test]
13208    fn two_tables_of_one_name_are_refused_before_anything_is_committed() {
13209        let file = path("two-of-a-name");
13210        let writer = Writer::create(&file, "t", vec![Field::new("a", LogicalType::BigInt)])
13211            .expect("new file");
13212        let error = writer
13213            .next("t", vec![Field::new("a", LogicalType::BigInt)])
13214            .expect_err("the same name twice");
13215        assert!(error.message().contains("same name"), "{}", error.message());
13216        fs::remove_file(file).expect("remove scratch file");
13217    }
13218
13219    #[test]
13220    fn opening_the_catalog_reads_no_table_directory() {
13221        let file = path("catalog-only");
13222        three_tables(&file);
13223        let catalog = Catalog::open(&file).expect("a committed catalog");
13224        // The header and one slot, and nothing under it. The third table's directory covers seventy
13225        // stripes and reading it here would be the whole point of the two levels thrown away.
13226        assert_eq!(catalog.opening.reads, 2, "opening the catalog read more than the slot");
13227        assert_eq!(catalog.names().len(), 3);
13228        fs::remove_file(file).expect("remove scratch file");
13229    }
13230
13231    /// The checksum answers what it has always answered, at every length its branches split on.
13232    ///
13233    /// This is a compatibility test rather than a correctness one. Nothing about the hash has to be
13234    /// any particular function, but a file already on disk carries the answers the version that
13235    /// wrote it gave, so a change here is a change that makes every stored file fail to verify. The
13236    /// lengths are the ones the code makes decisions about: nothing, under a block, a block exactly,
13237    /// a block and a word, a word and a half word, and a half word and a byte.
13238    ///
13239    /// The empty answer is the published xxHash64 vector for an empty input at seed zero, which is
13240    /// also a check that this is the function it says it is.
13241    #[test]
13242    fn the_checksum_answers_what_it_has_always_answered() {
13243        let bytes: Vec<u8> =
13244            (0..1000_u32).map(|at| (at.wrapping_mul(31).wrapping_add(7) % 251) as u8).collect();
13245        for (length, expected) in [
13246            (0, 0xef46_db37_51d8_e999),
13247            (1, 0xa96c_7f0c_e858_bbb7),
13248            (3, 0x56e6_9576_32a4_87f9),
13249            (4, 0xc60d_15b1_e3ff_8f04),
13250            (5, 0x8088_1585_8624_dd4e),
13251            (7, 0xafbe_fc3d_6c6f_9a8e),
13252            (8, 0x3da5_c7aa_2696_83e0),
13253            (9, 0x465e_c429_b13c_3892),
13254            (15, 0xdee8_9d8a_065a_6233),
13255            (16, 0x1330_489a_7767_9c80),
13256            (31, 0x3391_303d_485e_846e),
13257            (32, 0x40b7_aff7_5d45_bbc8),
13258            (33, 0x4997_cae4_951c_17a5),
13259            (39, 0x5807_28fd_5c14_5739),
13260            (40, 0xf95c_f6f5_c08a_3d3b),
13261            (63, 0x2944_b4da_fc69_b206),
13262            (64, 0xbb76_f6ef_19bd_5a1b),
13263            (65, 0x814e_0c65_4a9f_d640),
13264            (127, 0x00de_aab1_31cf_f89b),
13265            (1000, 0x9e33_00c1_cde3_c58d),
13266        ] {
13267            assert_eq!(checksum(&bytes[..length]), expected, "the checksum of {length} bytes");
13268        }
13269        assert_eq!(checksum(b"the quick brown fox jumps over the lazy dog"), 0xed71_4233_c5a9_a792);
13270    }
13271    /// A declared order survives the file, and a table that declared none stays as it was.
13272    ///
13273    /// The second half is the one worth a test. The clustering section is written only when there
13274    /// is a declaration, so a file of two tables where one is clustered exercises both the present
13275    /// and the absent branch of the decoder in one directory, which is where a length bug would
13276    /// show up as one table reading the other's bytes.
13277    #[test]
13278    fn a_declared_order_comes_back_out_of_the_file() {
13279        let path = path("clustered");
13280        let shipped = vec![
13281            Field::new("key", LogicalType::BigInt),
13282            Field::new("line", LogicalType::Integer),
13283            Field::new("shipdate", LogicalType::Date),
13284        ];
13285        let plain = vec![Field::new("a", LogicalType::Integer)];
13286        let stage_zero = Clustering::new(vec![2, 0, 1], Width::Month, &shipped).expect("valid");
13287
13288        let mut writer = Writer::create(&path, "lineitem", shipped)
13289            .expect("new file")
13290            .declare(stage_zero.clone())
13291            .expect("the columns are the table's");
13292        let column = |ty: LogicalType, values: &[Value]| {
13293            Vector::from_values(ty, values).expect("the values match the type")
13294        };
13295        writer
13296            .append(
13297                &Chunk::new(vec![
13298                    column(
13299                        LogicalType::BigInt,
13300                        &[Value::BigInt(0), Value::BigInt(1), Value::BigInt(2), Value::BigInt(3)],
13301                    ),
13302                    column(
13303                        LogicalType::Integer,
13304                        &[
13305                            Value::Integer(1),
13306                            Value::Integer(1),
13307                            Value::Integer(1),
13308                            Value::Integer(1),
13309                        ],
13310                    ),
13311                    column(
13312                        LogicalType::Date,
13313                        &[Value::Date(0), Value::Date(1), Value::Date(2), Value::Date(3)],
13314                    ),
13315                ])
13316                .expect("three columns"),
13317            )
13318            .expect("four rows");
13319        let mut writer = writer.next("nation", plain).expect("a second table");
13320        writer
13321            .append(
13322                &Chunk::new(vec![column(LogicalType::Integer, &[Value::Integer(7)])])
13323                    .expect("one column"),
13324            )
13325            .expect("one row");
13326        writer.finish().expect("commit");
13327
13328        let catalog = Catalog::open(&path).expect("reopen");
13329        let lineitem = catalog.table("lineitem").expect("the clustered table");
13330        assert_eq!(lineitem.table().clustering(), Some(&stage_zero));
13331        let nation = catalog.table("nation").expect("the plain table");
13332        assert_eq!(nation.table().clustering(), None, "nobody declared one here");
13333
13334        // And the rows are still the rows, because the section goes on the end of the directory
13335        // and the easy way to break that is to leave the cursor somewhere the next read trusts.
13336        assert_eq!(lineitem.table().rows(), 4);
13337        assert_eq!(nation.table().rows(), 1);
13338        fs::remove_file(&path).ok();
13339    }
13340
13341    /// A declaration naming a column the table does not have is refused where it is made.
13342    #[test]
13343    fn a_declaration_off_the_end_of_the_table_never_reaches_the_file() {
13344        let path = path("clustered-bad");
13345        let writer = Writer::create(&path, "items", vec![Field::new("a", LogicalType::Integer)])
13346            .expect("new file");
13347        let four =
13348            (0..4).map(|at| Field::new(format!("c{at}"), LogicalType::Integer)).collect::<Vec<_>>();
13349        let wrong = Clustering::new(vec![3], Width::Exact, &four).expect("valid against four");
13350        assert!(writer.declare(wrong).is_err(), "the table has one column, not four");
13351        fs::remove_file(&path).ok();
13352    }
13353
13354    /// The sorted order is the byte order, whatever the values do before they differ.
13355    ///
13356    /// The values here are the shape the sort is built for and the shape a comparison sort is worst
13357    /// at: a common scheme, a handful of hosts, and a path that only decides the pair thirty bytes
13358    /// in. They also cover what the bucketing has to get right at the edges, which is a value that
13359    /// has run out where another carries on, the empty value, and enough entries to take the range
13360    /// down through several passes and out the bottom into the comparison that finishes it.
13361    #[test]
13362    fn the_dictionary_order_is_the_byte_order_however_deep_the_values_agree() {
13363        let mut values = vec![String::new(), "http://".to_owned()];
13364        for host in 0..7 {
13365            for path in 0..30 {
13366                values.push(format!("http://example{host}.test/page/{path:04}/index.html"));
13367                values.push(format!("http://example{host}.test/page/{path:04}"));
13368            }
13369        }
13370        values.push("http://example0.test/page/0000/index.htmlx".to_owned());
13371
13372        let mut dictionary = GlobalDictionary::new();
13373        for value in &values {
13374            dictionary.code(value).expect("a code for every value");
13375        }
13376        dictionary.finish_blocks().expect("the last block encodes");
13377        let ranked = dictionary.ranked(None).expect("a sorted order");
13378        assert_eq!(ranked.len(), values.len(), "one entry a distinct value");
13379
13380        let spellings = dictionary_values(&dictionary);
13381        let seen = ranked
13382            .iter()
13383            .map(|&(_, code)| {
13384                String::from_utf8(spellings[code as usize].clone()).expect("text in, text out")
13385            })
13386            .collect::<Vec<_>>();
13387        let mut wanted = values.clone();
13388        wanted.sort_unstable();
13389        assert_eq!(seen, wanted, "the order is the order the bytes give");
13390
13391        for &(carried, code) in &ranked {
13392            let value = &spellings[code as usize];
13393            assert_eq!(carried, head(value), "the head belongs to the value it is filed with");
13394        }
13395    }
13396
13397    /// Picking the commonest entries leaves exactly what sorting all of them and cutting left.
13398    ///
13399    /// The counts here are deliberately full of ties, including a tie that straddles the cut, which
13400    /// is where a partition and a sort can disagree if the comparison they are given is not total.
13401    #[test]
13402    fn the_commonest_entries_are_the_ones_a_full_sort_would_have_kept() {
13403        let entry =
13404            |value: u32, count: u64| FrequencyEntry { value: FrequencyValue::Code(value), count };
13405        let mut all = (0..FREQUENCY_ENTRIES as u32 * 3)
13406            .map(|code| entry(code, u64::from(code % 7) + 1))
13407            .collect::<Vec<_>>();
13408        all.push(FrequencyEntry { value: FrequencyValue::Null, count: 4 });
13409
13410        let mut sorted = all.clone();
13411        sorted.sort_unstable_by(|left, right| {
13412            right.count.cmp(&left.count).then_with(|| frequency_order(left.value, right.value))
13413        });
13414        let wanted_omitted = sorted[FREQUENCY_ENTRIES].count;
13415        sorted.truncate(FREQUENCY_ENTRIES);
13416
13417        let mut picked = all.clone();
13418        let omitted = keep_most_frequent(&mut picked);
13419        assert_eq!(omitted, wanted_omitted, "the largest count that did not make the cut");
13420        assert_eq!(picked.len(), FREQUENCY_ENTRIES, "the cut is where it says it is");
13421        assert!(
13422            picked
13423                .iter()
13424                .zip(&sorted)
13425                .all(|(one, two)| one.value == two.value && one.count == two.count),
13426            "the same entries in the same order"
13427        );
13428
13429        let mut short = all[..FREQUENCY_ENTRIES - 1].to_vec();
13430        let omitted = keep_most_frequent(&mut short);
13431        assert_eq!(omitted, 0, "nothing is omitted when everything fits");
13432        assert!(short.windows(2).all(|pair| pair[0].count >= pair[1].count), "still in order");
13433    }
13434
13435    /// A dictionary too small to bucket, and one with nothing in it, come back in order too.
13436    #[test]
13437    fn a_short_dictionary_sorts_without_a_bucketing_pass() {
13438        let empty = GlobalDictionary::new();
13439        assert!(empty.ranked(None).expect("an empty order").is_empty(), "nothing in, nothing out");
13440
13441        let mut dictionary = GlobalDictionary::new();
13442        for value in ["pear", "apple", "", "apples", "app"] {
13443            dictionary.code(value).expect("a code for every value");
13444        }
13445        dictionary.finish_blocks().expect("the one block encodes");
13446        let spellings = dictionary_values(&dictionary);
13447        let seen = dictionary
13448            .ranked(None)
13449            .expect("a sorted order")
13450            .iter()
13451            .map(|&(_, code)| spellings[code as usize].clone())
13452            .collect::<Vec<_>>();
13453        let wanted: Vec<Vec<u8>> =
13454            [&b""[..], b"app", b"apple", b"apples", b"pear"].iter().map(|v| v.to_vec()).collect();
13455        assert_eq!(seen, wanted, "shorter first where one runs out inside another");
13456    }
13457}