Skip to main content

rudb_graph/
keymap.rs

1//! Turning a parent key value into a [`Rid`].
2//!
3//! A link is built from an equality between a child column and a parent column, and to build it the
4//! parent column's values have to become row ids. That map is the key map. It has the three
5//! physical forms of spec/graph/02-the-data-model.md section 2.2, chosen by measurement at build
6//! time rather than by declaration, and the form that was chosen is recorded in the header so that
7//! a reader does not have to guess.
8//!
9//! The three exist because they are three different answers to the same question and the cheapest
10//! one is usually available:
11//!
12//! - [`Form::Identity`] when the keys are exactly `base .. base + n` in order. Nothing is stored
13//!   but two numbers, and TPC-H hits this on six of its eight tables.
14//! - [`Form::Dense`] when the keys are distinct integers packed densely enough into a range that a
15//!   bitmap plus a rank index beats storing them.
16//! - [`Form::Sorted`] for everything else, including every string key, which arrives here as
17//!   dictionary codes rather than as text.
18//!
19//! What is deliberately absent is a hash. A minimal perfect hash is faster to probe than the sorted
20//! form and much slower to build, and there is no measurement yet saying the probe is where the
21//! time goes. spec/graph/11-open-questions.md keeps it open, and adding it later costs nothing
22//! because the form is a tag in a header that a reader is already required to be able to not
23//! recognize.
24
25use rudb_common::{Error, Result};
26use rudb_encoding::bitpack;
27
28use crate::bits::Rank;
29use crate::rid::Rid;
30
31/// How dense a range has to be before the bitmap form beats the sorted form.
32///
33/// One in eight, per section 2.2. Below it the bitmap is larger than storing the keys: a bitmap
34/// costs `range / 8` bytes plus about an eighth again for the rank index, and the sorted form costs
35/// `count` keys plus `count` permutation entries, so the crossover is a ratio rather than a size.
36/// The default is here as a named constant rather than inline because it is a number somebody will
37/// want to move once there is a measurement that says where, and moving it should be a diff.
38pub const DENSE_THRESHOLD: u64 = 8;
39
40/// Which of the three physical forms a key map took.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Form {
43    /// `rid = key - base`, and nothing is stored but `base` and the count.
44    Identity,
45    /// `rid = rank(key - base)` over a bitmap of the range, with a two level rank index.
46    Dense,
47    /// Binary search over the sorted keys, then a permutation lookup.
48    Sorted,
49}
50
51impl Form {
52    /// The tag this form takes in a section header.
53    #[must_use]
54    pub fn tag(self) -> u8 {
55        match self {
56            Self::Identity => 0,
57            Self::Dense => 1,
58            Self::Sorted => 2,
59        }
60    }
61
62    /// What this form is called where a person reads it, which is `rudb_links()`.
63    #[must_use]
64    pub fn label(self) -> &'static str {
65        match self {
66            Self::Identity => "identity",
67            Self::Dense => "dense",
68            Self::Sorted => "sorted",
69        }
70    }
71
72    /// The form a header tag names.
73    ///
74    /// # Errors
75    ///
76    /// If the tag is not one of the three. A reader that meets an unfamiliar form has met a file
77    /// written by a later build, and the right response is the one section 3.2 requires of an
78    /// unfamiliar section kind: ignore this key map and answer the query without it. So this
79    /// returns an error and the caller drops the section rather than failing the open.
80    pub fn from_tag(tag: u8) -> Result<Self> {
81        match tag {
82            0 => Ok(Self::Identity),
83            1 => Ok(Self::Dense),
84            2 => Ok(Self::Sorted),
85            _ => Err(malformed(format!("key map form {tag} is not one this build knows"))),
86        }
87    }
88}
89
90/// What the build saw while it read the parent key column.
91///
92/// This is the cardinality verification of section 2.3, and it is written into the header rather
93/// than recomputed because the build already had every value in front of it. Recording what was
94/// observed rather than what was declared is what keeps a wrong `FOREIGN KEY` from producing a
95/// wrong answer: a declaration that fails verification is reported, and no link is built.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct Observed {
98    /// Non-null values seen.
99    pub rows: u64,
100    /// Nulls seen, which are not keys and match no child row.
101    pub nulls: u64,
102    /// Whether every non-null value was distinct. False means no link may be built at all.
103    pub distinct: bool,
104    /// Whether the values arrived in non-decreasing order.
105    pub sorted: bool,
106    /// The smallest non-null value, or `None` when there were none.
107    pub min: Option<i128>,
108    /// The largest non-null value, or `None` when there were none.
109    pub max: Option<i128>,
110}
111
112impl Observed {
113    /// Whether this column can be the parent side of a link.
114    ///
115    /// Distinctness is the whole requirement. A parent side that is not unique is not an error and
116    /// is not a link: section 2.3 says it is a relationship that has to be executed as an ordinary
117    /// join, and the planner is told so rather than left to find out.
118    #[must_use]
119    pub fn usable_as_parent(&self) -> bool {
120        self.distinct
121    }
122}
123
124/// The three forms, behind one interface.
125#[derive(Debug, Clone)]
126enum Body {
127    Identity {
128        base: i128,
129        count: u64,
130    },
131    Dense {
132        base: i128,
133        range: u64,
134        bits: Vec<u64>,
135        rank: Rank,
136    },
137    Sorted {
138        /// The smallest key, so that every stored key is a `u64` offset from it whatever the
139        /// column's own type was.
140        base: i128,
141        /// Bits one stored key offset takes.
142        key_width: usize,
143        /// The key offsets in ascending order, bit packed.
144        keys: Vec<u8>,
145        /// Bits one permutation entry takes, which is `ceil(log2(rows))`.
146        rid_width: usize,
147        /// Sorted position to `rid`, bit packed.
148        perm: Vec<u8>,
149        count: u64,
150    },
151}
152
153/// A map from a parent key value to the `rid` of the row that holds it.
154#[derive(Debug, Clone)]
155pub struct KeyMap {
156    body: Body,
157    observed: Observed,
158}
159
160impl KeyMap {
161    /// Builds the cheapest correct form for these keys.
162    ///
163    /// `keys` is the parent key column in `rid` order, with `None` for a null. The `rid` of a value
164    /// is its index, which is what makes this the whole build: the caller has already read the
165    /// column in append order, so the row ids are the positions and there is nothing to look up.
166    ///
167    /// String keys arrive here as dictionary codes rather than as text, per section 2.2. That is
168    /// not a convenience, it is the reason a sorted key map over a `VARCHAR` column never touches a
169    /// byte of text: the codes of a file wide stable dictionary are integers with the column's own
170    /// order, so the search is over `u32`.
171    ///
172    /// # Errors
173    ///
174    /// If the column's values span more than a `u64`, if it holds more rows than a `u64` of
175    /// `rid`s, or if a bit packed payload cannot be written. A non-distinct column is not an
176    /// error: it produces a key map whose [`Observed`] says so, and the caller is expected to ask
177    /// before building a link on it.
178    pub fn build(keys: &[Option<i128>]) -> Result<Self> {
179        let mut observed = observe(keys);
180        // See [`KeyMap::build_from`], which takes the same shortcut for the same reason and is
181        // where the reason is written. The two paths agree on every column or a table's key map
182        // depends on which of them built it.
183        if !observed.distinct {
184            return Ok(Self { body: Body::Identity { base: 0, count: 0 }, observed });
185        }
186        Ok(match plan(&observed)? {
187            Plan::Empty => Self { body: Body::Identity { base: 0, count: 0 }, observed },
188            Plan::Identity { base, count } => {
189                Self { body: Body::Identity { base, count }, observed }
190            }
191            Plan::Dense { base, range } => Self { body: dense(keys, base, range)?, observed },
192            Plan::Sorted { base } => {
193                // The sorted form sorts, so it is the one place distinctness can be settled for a
194                // column that did not arrive in order. `observe` can only see an adjacent
195                // duplicate; this sees every duplicate, and the answer replaces the guess.
196                let (body, distinct) = sorted(keys, base, observed.rows)?;
197                observed.distinct = distinct;
198                Self { body, observed }
199            }
200        })
201    }
202
203    /// Builds the cheapest correct form by reading the column rather than by holding it.
204    ///
205    /// The same build as [`KeyMap::build`] and the same decision, taken from a source that can be
206    /// scanned twice instead of from a slice that is already in memory. That difference is the
207    /// whole reason this exists. A parent key column at TPC-H SF10 is fifteen million rows of
208    /// `orders`, and a `Vec<Option<i128>>` of those is four hundred and eighty megabytes held for
209    /// the length of a build that does not need a single one of them twice. At SF100 it is four and
210    /// a half gigabytes, which is not a slow build, it is a build that does not happen.
211    ///
212    /// So the first scan observes and nothing else, and what the second scan does depends on what
213    /// the first one found. The identity form, which is the form every TPC-H parent key takes,
214    /// needs no second scan at all: the four observed facts are the whole map. The dense form fills
215    /// a bitmap sized from the range, which is bounded by the table rather than by the scan. Only
216    /// the sorted form has to hold the column, because sorting is what it is, and it says so here
217    /// rather than surprising a caller with it.
218    ///
219    /// # Errors
220    ///
221    /// If the scan fails, or for any of the reasons [`KeyMap::build`] fails.
222    pub fn build_from<K: Keys + ?Sized>(keys: &K) -> Result<Self> {
223        let mut observer = Observer::new();
224        keys.scan(&mut |key| {
225            observer.push(key);
226            Ok(())
227        })?;
228        let mut observed = observer.observed;
229        // A column the first scan already saw a repeat in gets no body at all. No form answers a
230        // rid for a key that is in two rows, so every byte spent on one is spent on a map nothing
231        // may use, and the bytes are not small: TPC-H SF10 `lineitem(l_orderkey)` sorts sixty
232        // million keys into three hundred and ninety megabytes before the budget throws all of it
233        // away. This is only reachable where the duplicates are adjacent, which is where the column
234        // arrived in order, and that is the case this is for. A repeat that only the sort can find
235        // is still found by the sort, below.
236        if !observed.distinct {
237            return Ok(Self { body: Body::Identity { base: 0, count: 0 }, observed });
238        }
239        Ok(match plan(&observed)? {
240            Plan::Empty => Self { body: Body::Identity { base: 0, count: 0 }, observed },
241            Plan::Identity { base, count } => {
242                Self { body: Body::Identity { base, count }, observed }
243            }
244            Plan::Dense { base, range } => {
245                let mut bits = DenseBits::new(base, range);
246                keys.scan(&mut |key| match key {
247                    Some(key) => bits.push(key),
248                    None => Ok(()),
249                })?;
250                Self { body: bits.finish(), observed }
251            }
252            Plan::Sorted { base } => {
253                let mut held = Vec::with_capacity(
254                    usize::try_from(observed.rows + observed.nulls).unwrap_or_default(),
255                );
256                keys.scan(&mut |key| {
257                    held.push(key);
258                    Ok(())
259                })?;
260                let (body, distinct) = sorted(&held, base, observed.rows)?;
261                observed.distinct = distinct;
262                Self { body, observed }
263            }
264        })
265    }
266
267    /// Which form this map took.
268    #[must_use]
269    pub fn form(&self) -> Form {
270        match self.body {
271            Body::Identity { .. } => Form::Identity,
272            Body::Dense { .. } => Form::Dense,
273            Body::Sorted { .. } => Form::Sorted,
274        }
275    }
276
277    /// The smallest key and how many key values from it the map spans, when the keys are compact.
278    ///
279    /// The identity and dense forms are the two that exist because the keys fill most of a range,
280    /// at most one hole in [`DENSE_THRESHOLD`] values, so a bitmap over that range is at most that
281    /// many bits a parent row. That is what lets a join test a child's key against a set of parents
282    /// with one subtraction and one bit, and with no link at all. The sorted form is the one for
283    /// keys spread over a range too wide for that, and answers `None`.
284    #[must_use]
285    pub fn span(&self) -> Option<(i128, u64)> {
286        match self.body {
287            Body::Identity { base, count } => Some((base, count)),
288            Body::Dense { base, range, .. } => Some((base, range)),
289            Body::Sorted { .. } => None,
290        }
291    }
292
293    /// What the build saw, which is the cardinality verification.
294    #[must_use]
295    pub fn observed(&self) -> &Observed {
296        &self.observed
297    }
298
299    /// The value every stored key is an offset from, which is the smallest key.
300    pub(crate) fn base(&self) -> i128 {
301        match &self.body {
302            Body::Identity { base, .. } | Body::Dense { base, .. } | Body::Sorted { base, .. } => {
303                *base
304            }
305        }
306    }
307
308    /// Appends the form's own bytes, after the header that `wire` has already written.
309    ///
310    /// Nothing here is stored that the header and the form together derive. The identity form
311    /// writes nothing at all, because its count is the header's row count, which is section 3.3's
312    /// "no extents beyond the header" in code rather than in prose.
313    pub(crate) fn write_body(&self, out: &mut Vec<u8>) -> Result<()> {
314        match &self.body {
315            Body::Identity { .. } => Ok(()),
316            Body::Dense { range, bits, rank, .. } => {
317                out.extend_from_slice(&range.to_le_bytes());
318                for word in bits {
319                    out.extend_from_slice(&word.to_le_bytes());
320                }
321                rank.write(out);
322                Ok(())
323            }
324            Body::Sorted { key_width, keys, rid_width, perm, .. } => {
325                // The widths are a byte each, and a width past sixty four is a width no `u64` key
326                // offset can have taken, so it is a torn header rather than a wide key.
327                let widths = [*key_width, *rid_width];
328                for width in widths {
329                    let width = u8::try_from(width)
330                        .map_err(|_| malformed("a sorted key map's width does not fit a byte"))?;
331                    out.push(width);
332                }
333                out.extend_from_slice(keys);
334                out.extend_from_slice(perm);
335                Ok(())
336            }
337        }
338    }
339
340    /// Reads back what [`KeyMap::write_body`] wrote, and fills in the maximum key.
341    ///
342    /// The maximum is not in the header because each form derives it: identity from its count,
343    /// dense from its range, sorted from its last stored key. That is the whole reason this takes
344    /// [`Observed`] and returns a map rather than taking a finished one.
345    ///
346    /// # Errors
347    ///
348    /// If the body is not exactly the length its header implies. Exactly, not at least: a body
349    /// longer than its form needs means the header and the body disagree about which form this is,
350    /// and the safe reading of a disagreement is neither of them.
351    pub(crate) fn read_body(
352        form: Form,
353        base: i128,
354        mut observed: Observed,
355        body: &[u8],
356    ) -> Result<Self> {
357        match form {
358            Form::Identity => {
359                if !body.is_empty() {
360                    return Err(malformed("an identity key map has no body"));
361                }
362                if observed.rows > 0 {
363                    observed.max = Some(
364                        base.checked_add(i128::from(observed.rows) - 1)
365                            .ok_or_else(|| malformed("an identity key map's range overflows"))?,
366                    );
367                }
368                Ok(Self { body: Body::Identity { base, count: observed.rows }, observed })
369            }
370            Form::Dense => {
371                let Some(head) = body.get(..size_of::<u64>()) else {
372                    return Err(malformed("a dense key map has no range"));
373                };
374                let range = u64::from_le_bytes(head.try_into().expect("eight bytes"));
375                let Ok(range_usize) = usize::try_from(range) else {
376                    return Err(malformed("a dense key map's range does not fit this machine"));
377                };
378                let words = range_usize.div_ceil(64);
379                let bitmap = words * size_of::<u64>();
380                let rest = &body[size_of::<u64>()..];
381                if rest.len() < bitmap {
382                    return Err(malformed("a dense key map's bitmap is shorter than its range"));
383                }
384                let bits: Vec<u64> = rest[..bitmap]
385                    .chunks_exact(size_of::<u64>())
386                    .map(|word| u64::from_le_bytes(word.try_into().expect("eight bytes")))
387                    .collect();
388                let rank = Rank::read(&rest[bitmap..], words)?;
389                observed.max = Some(
390                    base.checked_add(i128::from(range) - 1)
391                        .ok_or_else(|| malformed("a dense key map's range overflows"))?,
392                );
393                Ok(Self { body: Body::Dense { base, range, bits, rank }, observed })
394            }
395            Form::Sorted => {
396                if body.len() < 2 {
397                    return Err(malformed("a sorted key map has no widths"));
398                }
399                let key_width = usize::from(body[0]);
400                let rid_width = usize::from(body[1]);
401                if key_width == 0 || key_width > 64 || rid_width == 0 || rid_width > 64 {
402                    return Err(malformed("a sorted key map's width is not one a u64 can take"));
403                }
404                let count = observed.rows;
405                let Ok(count_usize) = usize::try_from(count) else {
406                    return Err(malformed(
407                        "a sorted key map holds more keys than this machine can",
408                    ));
409                };
410                let key_bytes = (count_usize * key_width).div_ceil(8);
411                let perm_bytes = (count_usize * rid_width).div_ceil(8);
412                let rest = &body[2..];
413                if rest.len() != key_bytes + perm_bytes {
414                    return Err(malformed(
415                        "a sorted key map's arrays are not the size its widths and count imply",
416                    ));
417                }
418                let keys = rest[..key_bytes].to_vec();
419                let perm = rest[key_bytes..].to_vec();
420                if count > 0 {
421                    let largest = bitpack::tail_at(&keys, key_width, count_usize - 1)?;
422                    observed.max =
423                        Some(base.checked_add(i128::from(largest)).ok_or_else(|| {
424                            malformed("a sorted key map's largest key overflows")
425                        })?);
426                }
427                Ok(Self {
428                    body: Body::Sorted { base, key_width, keys, rid_width, perm, count },
429                    observed,
430                })
431            }
432        }
433    }
434
435    /// Keys this map resolves.
436    #[must_use]
437    pub fn len(&self) -> u64 {
438        match &self.body {
439            Body::Identity { count, .. } | Body::Sorted { count, .. } => *count,
440            Body::Dense { .. } => self.observed.rows,
441        }
442    }
443
444    /// Whether this map resolves nothing.
445    #[must_use]
446    pub fn is_empty(&self) -> bool {
447        self.len() == 0
448    }
449
450    /// Bytes this map holds resident, for the budget of section 3.7 and the cache of section 4.4.
451    ///
452    /// Identity is twenty four bytes and says so, which is the number that makes the budget
453    /// livable on TPC-H.
454    #[must_use]
455    pub fn bytes(&self) -> usize {
456        match &self.body {
457            Body::Identity { .. } => size_of::<i128>() + size_of::<u64>(),
458            Body::Dense { bits, rank, .. } => bits.len() * size_of::<u64>() + rank.bytes(),
459            Body::Sorted { keys, perm, .. } => keys.len() + perm.len(),
460        }
461    }
462
463    /// The `rid` of the row holding this key, or `None` when no row holds it.
464    ///
465    /// `None` is the ordinary answer and not an exceptional one: a child key with no matching
466    /// parent is what section 2.4 reserves *no parent* for, and a null child key never reaches
467    /// here at all.
468    ///
469    /// # Errors
470    ///
471    /// If a bit packed payload is torn, which is a corrupt section rather than a missing key.
472    pub fn lookup(&self, key: i128) -> Result<Option<Rid>> {
473        match &self.body {
474            Body::Identity { base, count } => {
475                let Some(offset) = key.checked_sub(*base) else {
476                    return Ok(None);
477                };
478                match u64::try_from(offset) {
479                    Ok(rid) if rid < *count => Ok(Some(rid)),
480                    _ => Ok(None),
481                }
482            }
483            Body::Dense { base, range, bits, rank } => {
484                let Some(offset) = key.checked_sub(*base) else {
485                    return Ok(None);
486                };
487                let Ok(offset) = u64::try_from(offset) else {
488                    return Ok(None);
489                };
490                if offset >= *range {
491                    return Ok(None);
492                }
493                #[expect(
494                    clippy::cast_possible_truncation,
495                    reason = "the build checked the range fits a usize"
496                )]
497                let at = offset as usize;
498                if bits[at / 64] >> (at % 64) & 1 == 0 {
499                    return Ok(None);
500                }
501                Ok(Some(rank.rank(bits, at)))
502            }
503            Body::Sorted { base, key_width, keys, rid_width, perm, count } => {
504                let Some(offset) = key.checked_sub(*base) else {
505                    return Ok(None);
506                };
507                let Ok(wanted) = u64::try_from(offset) else {
508                    return Ok(None);
509                };
510                #[expect(
511                    clippy::cast_possible_truncation,
512                    reason = "the build refused a column wider than a usize of rows"
513                )]
514                let len = *count as usize;
515                // A plain binary search over the packed keys. Branchless in the sense that matters
516                // here, which is that the comparison drives an index rather than a branch to a
517                // different loop, and every probe is one `tail_at` rather than a decode of the
518                // block around it.
519                let mut low = 0_usize;
520                let mut high = len;
521                while low < high {
522                    let mid = low + (high - low) / 2;
523                    let at = bitpack::tail_at(keys, *key_width, mid)?;
524                    if at < wanted {
525                        low = mid + 1;
526                    } else {
527                        high = mid;
528                    }
529                }
530                if low >= len || bitpack::tail_at(keys, *key_width, low)? != wanted {
531                    return Ok(None);
532                }
533                Ok(Some(bitpack::tail_at(perm, *rid_width, low)?))
534            }
535        }
536    }
537}
538
539/// A parent key column that can be read more than once, in `rid` order.
540///
541/// The build wants two passes over a column it does not want to hold, so this is what it reads
542/// instead of a slice: something that can be asked to produce the column again. A file can do that
543/// for the price of a read, and the second read is against pages the first one just warmed.
544///
545/// Values arrive as `Option<i128>`, with `None` for a null. A string key arrives as its dictionary
546/// code rather than as text, per section 2.2, which is why one integer signature covers every key
547/// type rudb has.
548pub trait Keys {
549    /// Calls `each` once per row of the column, in `rid` order.
550    ///
551    /// # Errors
552    ///
553    /// If the column cannot be read, or if `each` fails, which stops the scan rather than
554    /// continuing past a value that could not be used.
555    fn scan(&self, each: &mut dyn FnMut(Option<i128>) -> Result<()>) -> Result<()>;
556}
557
558impl Keys for [Option<i128>] {
559    fn scan(&self, each: &mut dyn FnMut(Option<i128>) -> Result<()>) -> Result<()> {
560        for key in self {
561            each(*key)?;
562        }
563        Ok(())
564    }
565}
566
567/// Which form the build chose, decided once and carried out twice.
568///
569/// Separating the decision from the filling is what lets [`KeyMap::build`] and
570/// [`KeyMap::build_from`] be the same build. A second copy of these three conditions is a second
571/// place for the positional guard below to be got wrong.
572enum Plan {
573    Empty,
574    Identity { base: i128, count: u64 },
575    Dense { base: i128, range: u64 },
576    Sorted { base: i128 },
577}
578
579/// Picks the cheapest form that is correct for what the column turned out to hold.
580fn plan(observed: &Observed) -> Result<Plan> {
581    // A column with no keys in it at all is an identity map over nothing. It is worth having rather
582    // than refusing, because an empty parent table is a legal table and a join against it returns
583    // no rows rather than failing.
584    if observed.rows == 0 {
585        return Ok(Plan::Empty);
586    }
587    let (Some(min), Some(max)) = (observed.min, observed.max) else {
588        // A non-zero row count guarantees both, so this is unreachable. It is an error rather than
589        // an `expect` because a key map that panicked on its own bookkeeping would take down a
590        // query that section 3.1 promises can always be answered without it.
591        return Err(malformed("a column with keys in it reported no minimum"));
592    };
593    let range = range_of(min, max)?;
594
595    // Both of the cheap forms answer with a *count of keys below the value*, and both are correct
596    // only where that count is the `rid`. It is the `rid` when the column is ascending and holds no
597    // nulls, and it is not otherwise: a null earlier in the column, or a value out of order, shifts
598    // every row after it. Getting this wrong would not fail, it would resolve every key to a
599    // neighbour of the right row, which is the one failure mode section 3.1 does not catch for
600    // free. So the guard is shared and stated once.
601    let positional = observed.distinct && observed.sorted && observed.nulls == 0;
602
603    if positional && range == observed.rows {
604        // Identity needs more than positional: it needs the values to be exactly the positions,
605        // which on a distinct ascending column is the range equalling the row count. The check is
606        // subtraction rather than a walk because the walk already happened in the observation.
607        return Ok(Plan::Identity { base: min, count: observed.rows });
608    }
609
610    // The bitmap is over the value range, so a range that does not fit a `usize` cannot be one
611    // however dense it is.
612    if positional && usize::try_from(range).is_ok() && range / observed.rows < DENSE_THRESHOLD {
613        return Ok(Plan::Dense { base: min, range });
614    }
615
616    Ok(Plan::Sorted { base: min })
617}
618
619/// The four facts section 3.3 says the build records, accumulated one value at a time.
620///
621/// One value at a time rather than one column at a time so that the pass can be driven by a scan
622/// of a file as easily as by a slice. See [`KeyMap::build_from`] for why that matters.
623struct Observer {
624    observed: Observed,
625    previous: Option<i128>,
626}
627
628impl Observer {
629    fn new() -> Self {
630        Self {
631            observed: Observed {
632                rows: 0,
633                nulls: 0,
634                distinct: true,
635                sorted: true,
636                min: None,
637                max: None,
638            },
639            previous: None,
640        }
641    }
642
643    // Distinctness on a column that is not sorted cannot be settled in one pass without a set, so
644    // this settles it for the sorted case and leaves the unsorted case to the sort that the sorted
645    // form does anyway. That is why `distinct` is fixed up in `sorted` below rather than being
646    // final here, and it is worth the awkwardness: the common case on real keys is ascending, and a
647    // hash set over fifteen million rows to discover what adjacency already proves is the build
648    // cost this avoids.
649    fn push(&mut self, key: Option<i128>) {
650        let Some(key) = key else {
651            self.observed.nulls += 1;
652            return;
653        };
654        self.observed.rows += 1;
655        self.observed.min = Some(self.observed.min.map_or(key, |held| held.min(key)));
656        self.observed.max = Some(self.observed.max.map_or(key, |held| held.max(key)));
657        if let Some(previous) = self.previous {
658            if key < previous {
659                self.observed.sorted = false;
660            } else if key == previous {
661                self.observed.distinct = false;
662            }
663        }
664        self.previous = Some(key);
665    }
666}
667
668/// One pass over the column, recording the four facts section 3.3 says the build records.
669fn observe(keys: &[Option<i128>]) -> Observed {
670    let mut observer = Observer::new();
671    for key in keys {
672        observer.push(*key);
673    }
674    observer.observed
675}
676
677/// How many distinct values lie between `min` and `max` inclusive.
678///
679/// The arithmetic is in `u128` and not `i128` because a column holding both `i128::MIN` and
680/// `i128::MAX` has a range of `2^128`, and `max - min` on an `i128` for that column is an overflow
681/// rather than a number. A `HUGEINT` key column spanning more than a `u64` of values is pathological
682/// but legal, so it gets an error naming what happened rather than a panic in a build: the caller
683/// records the relationship as not built, exactly as it does for one that does not fit the budget.
684///
685/// `max >= min` always holds here, so the wrapping subtraction is exact in `u128`.
686fn range_of(min: i128, max: i128) -> Result<u64> {
687    let span = max.wrapping_sub(min) as u128;
688    u64::try_from(span)
689        .ok()
690        .and_then(|span| span.checked_add(1))
691        .ok_or_else(|| malformed("the key column spans more than a u64 of values"))
692}
693
694/// The offset a key takes from the base.
695///
696/// `range_of` bounded the span to a `u64` before either form that uses this was chosen, so the
697/// subtraction cannot overflow and the offset cannot exceed a `u64`. Both are checked anyway: this
698/// is the one arithmetic in the crate whose silent failure would resolve keys to the wrong rows.
699fn offset_of(key: i128, base: i128) -> Result<u64> {
700    let offset = key
701        .checked_sub(base)
702        .ok_or_else(|| malformed("a key is further from the base than an i128 holds"))?;
703    u64::try_from(offset)
704        .map_err(|_| malformed("a key is below the base or further from it than a u64 holds"))
705}
706
707/// Builds the bitmap form one key at a time.
708///
709/// The caller guarantees the column is distinct, ascending and null free, which is what makes a
710/// rank equal to a `rid`. The assertion restates it where the correctness depends on it rather than
711/// where the decision was made.
712struct DenseBits {
713    base: i128,
714    range: u64,
715    bits: Vec<u64>,
716    previous: Option<i128>,
717}
718
719impl DenseBits {
720    fn new(base: i128, range: u64) -> Self {
721        #[expect(
722            clippy::cast_possible_truncation,
723            reason = "the caller checked the range fits a usize"
724        )]
725        let range_usize = range as usize;
726        Self { base, range, bits: vec![0_u64; range_usize.div_ceil(64)], previous: None }
727    }
728
729    fn push(&mut self, key: i128) -> Result<()> {
730        debug_assert!(
731            self.previous.is_none_or(|held| key > held),
732            "the bitmap form needs a distinct ascending column, because a rank is a count of keys below a value and that is a rid only there"
733        );
734        self.previous = Some(key);
735        let offset = offset_of(key, self.base)?;
736        #[expect(
737            clippy::cast_possible_truncation,
738            reason = "the caller checked the range fits a usize and the offset is inside it"
739        )]
740        let at = offset as usize;
741        self.bits[at / 64] |= 1 << (at % 64);
742        Ok(())
743    }
744
745    fn finish(self) -> Body {
746        let rank = Rank::build(&self.bits);
747        Body::Dense { base: self.base, range: self.range, bits: self.bits, rank }
748    }
749}
750
751fn dense(keys: &[Option<i128>], base: i128, range: u64) -> Result<Body> {
752    let mut bits = DenseBits::new(base, range);
753    for key in keys.iter().flatten() {
754        bits.push(*key)?;
755    }
756    Ok(bits.finish())
757}
758
759/// Builds the general form, and settles distinctness on the way.
760///
761/// Returns the body and whether every key was distinct. The second is not a courtesy: the sort this
762/// form performs is the only place a duplicate that is not adjacent in the column can be seen, and
763/// section 2.3 needs that answer to decide whether a link may be built at all.
764fn sorted(keys: &[Option<i128>], base: i128, rows: u64) -> Result<(Body, bool)> {
765    let mut pairs: Vec<(u64, u64)> = Vec::with_capacity(keys.len());
766    for (rid, key) in keys.iter().enumerate() {
767        let Some(key) = *key else { continue };
768        let offset = offset_of(key, base)?;
769        let rid = u64::try_from(rid).map_err(|_| malformed("the column is too long for a rid"))?;
770        pairs.push((offset, rid));
771    }
772    // Sorted by key, then by rid so that a duplicated key resolves to its first row rather than to
773    // whichever one the sort happened to leave first. A duplicated key means no link gets built, so
774    // this only decides what a map nobody should be using returns, and deciding it anyway is what
775    // keeps a test of this form reproducible.
776    pairs.sort_unstable();
777    let distinct = pairs.windows(2).all(|pair| pair[0].0 != pair[1].0);
778    debug_assert_eq!(
779        u64::try_from(pairs.len()).ok(),
780        Some(rows),
781        "the pair list is the non-null column"
782    );
783    let key_width = width_for(pairs.last().map_or(0, |pair| pair.0));
784    let rows_width = u64::try_from(keys.len().saturating_sub(1))
785        .map_err(|_| malformed("the column is too long for a rid"))?;
786    let rid_width = width_for(rows_width);
787    let mut key_bytes = Vec::new();
788    let mut rid_bytes = Vec::new();
789    let key_values: Vec<u64> = pairs.iter().map(|pair| pair.0).collect();
790    let rid_values: Vec<u64> = pairs.iter().map(|pair| pair.1).collect();
791    // The linear packer and not the tail one, because a tail is bounded at a thousand values and a
792    // parent key column is not. The layout is the same and `tail_at` reads either.
793    bitpack::pack_linear(&key_values, key_width, &mut key_bytes)?;
794    bitpack::pack_linear(&rid_values, rid_width, &mut rid_bytes)?;
795    Ok((
796        Body::Sorted { base, key_width, keys: key_bytes, rid_width, perm: rid_bytes, count: rows },
797        distinct,
798    ))
799}
800
801/// Bits needed to hold every value up to and including `largest`.
802///
803/// One rather than zero for a largest of zero, because a width of zero is a packed payload with no
804/// bytes in it and `tail_at` on one of those has nothing to return. A column of a single key is a
805/// real column.
806fn width_for(largest: u64) -> usize {
807    let bits = u64::BITS - largest.leading_zeros();
808    bits.max(1) as usize
809}
810
811fn malformed(message: impl Into<String>) -> Error {
812    Error::invalid_input(format!("invalid rudb key map: {}", message.into()))
813}
814
815#[cfg(test)]
816mod tests {
817    use super::*;
818
819    fn keys(values: &[i128]) -> Vec<Option<i128>> {
820        values.iter().copied().map(Some).collect()
821    }
822
823    /// Every key in the column resolves to the row that holds it, whatever form was chosen.
824    fn resolves(column: &[Option<i128>], map: &KeyMap) {
825        for (rid, key) in column.iter().enumerate() {
826            let Some(key) = *key else { continue };
827            let found = map.lookup(key).expect("lookup").expect("a key in the column resolves");
828            assert_eq!(found, rid as u64, "key {key} resolved to {found} rather than {rid}");
829        }
830    }
831
832    #[test]
833    fn a_sequence_from_one_is_the_identity_form_and_stores_two_numbers() {
834        // TPC-H's `region`, `nation`, `supplier`, `customer`, `part` and `orders` all land here,
835        // which is the case the whole budget in section 3.7 depends on.
836        let column = keys(&(1..=1000).collect::<Vec<i128>>());
837        let map = KeyMap::build(&column).expect("build");
838        assert_eq!(map.form(), Form::Identity);
839        assert_eq!(map.bytes(), 24, "section 4.2 says identity is twenty four bytes");
840        assert_eq!(map.len(), 1000);
841        resolves(&column, &map);
842        assert_eq!(map.lookup(0).expect("lookup"), None, "below the base");
843        assert_eq!(map.lookup(1001).expect("lookup"), None, "past the end");
844        assert_eq!(map.span(), Some((1, 1000)));
845    }
846
847    #[test]
848    fn a_sequence_from_zero_is_also_the_identity_form() {
849        let column = keys(&(0..64).collect::<Vec<i128>>());
850        let map = KeyMap::build(&column).expect("build");
851        assert_eq!(map.form(), Form::Identity);
852        resolves(&column, &map);
853    }
854
855    #[test]
856    fn a_sequence_with_a_gap_in_it_is_the_dense_form() {
857        // Every other value over a range of two thousand, which is a density of one in two and
858        // comfortably inside the threshold.
859        let column = keys(&(0..1000).map(|value| value * 2).collect::<Vec<i128>>());
860        let map = KeyMap::build(&column).expect("build");
861        assert_eq!(map.form(), Form::Dense);
862        resolves(&column, &map);
863        assert_eq!(
864            map.lookup(1).expect("lookup"),
865            None,
866            "a value in the range and not in the column"
867        );
868        assert_eq!(map.lookup(2001).expect("lookup"), None, "past the range");
869        assert_eq!(map.span(), Some((0, 1999)), "from the smallest key to the largest");
870    }
871
872    #[test]
873    fn a_range_too_sparse_for_a_bitmap_is_the_sorted_form() {
874        // A thousand keys spread over a million, which is a density of one in a thousand: the
875        // bitmap would be 125 KB to hold a thousand values and the sorted form is a few kilobytes.
876        let column = keys(&(0..1000).map(|value| value * 1000).collect::<Vec<i128>>());
877        let map = KeyMap::build(&column).expect("build");
878        assert_eq!(map.form(), Form::Sorted);
879        resolves(&column, &map);
880        assert_eq!(map.lookup(500).expect("lookup"), None);
881        assert_eq!(map.span(), None, "too sparse for a bitmap over the range");
882    }
883
884    #[test]
885    fn the_sorted_form_is_not_bounded_by_a_packed_unit() {
886        // The sorted form packs its keys and its permutation sequentially, and the sequential
887        // packer a column uses is for the remainder past the last transposed unit, so it refuses a
888        // thousand and twenty four values. A parent key column is sixty times that at SF1 and
889        // fifteen thousand times it at SF10, so the form would exist only for toy tables. This is
890        // the smallest column that would have hit it.
891        let column = keys(&(0..5000).map(|value| (value * 7919) % 100_003).collect::<Vec<i128>>());
892        let map = KeyMap::build(&column).expect("build");
893        assert_eq!(map.form(), Form::Sorted);
894        resolves(&column, &map);
895    }
896
897    #[test]
898    fn keys_in_no_order_at_all_resolve_to_the_rows_that_hold_them() {
899        // The case the permutation exists for. The column is not sorted, so the sorted form's
900        // position is not the rid, and a map that confused the two would resolve every key to the
901        // wrong row while looking exactly like a working map.
902        let column = keys(&[500, 3, 9000, 12, 7, 88, 41, 6]);
903        let map = KeyMap::build(&column).expect("build");
904        assert_eq!(map.form(), Form::Sorted);
905        resolves(&column, &map);
906    }
907
908    #[test]
909    fn a_descending_column_dense_enough_for_a_bitmap_still_resolves_correctly() {
910        // The trap in the dense form: a bitmap is in value order, so a rank is a position in value
911        // order, and on a descending column that is not the rid. `dense` detects it and falls back.
912        let column = keys(&(0..500).rev().collect::<Vec<i128>>());
913        let map = KeyMap::build(&column).expect("build");
914        assert_eq!(map.form(), Form::Sorted, "a descending column cannot take the bitmap");
915        resolves(&column, &map);
916    }
917
918    #[test]
919    fn nulls_are_not_keys_and_do_not_shift_the_rows_around_them() {
920        // This column is distinct, ascending, and dense enough for a bitmap on the numbers alone:
921        // three keys over a range of twenty one. It cannot have one, because a rank counts keys
922        // below a value and the nulls in between mean that count is not the row's position. A map
923        // that took the bitmap here would resolve key 20 to row 1 and look entirely healthy doing
924        // it.
925        let column = vec![Some(10), None, Some(20), None, Some(30)];
926        let map = KeyMap::build(&column).expect("build");
927        assert_eq!(
928            map.form(),
929            Form::Sorted,
930            "a null before a key shifts it out of the cheap forms"
931        );
932        resolves(&column, &map);
933        assert_eq!(map.observed().nulls, 2);
934        assert_eq!(map.observed().rows, 3);
935        assert_eq!(
936            map.lookup(20).expect("lookup"),
937            Some(2),
938            "the rid is the position in the column"
939        );
940    }
941
942    #[test]
943    fn a_leading_null_keeps_an_otherwise_perfect_sequence_out_of_the_identity_form() {
944        // The same trap on the form that would otherwise be free. Worth its own test because a
945        // sequence from one is the case every TPC-H table hits, and the version of it with a null
946        // in front is one `INSERT` away.
947        let mut column = vec![None];
948        column.extend((1..=1000).map(Some));
949        let map = KeyMap::build(&column).expect("build");
950        assert_ne!(map.form(), Form::Identity);
951        resolves(&column, &map);
952        assert_eq!(map.lookup(1).expect("lookup"), Some(1), "row zero is the null, not key one");
953    }
954
955    #[test]
956    fn a_null_only_column_builds_and_resolves_nothing() {
957        let column = vec![None, None, None];
958        let map = KeyMap::build(&column).expect("build");
959        assert!(map.is_empty());
960        assert_eq!(map.observed().nulls, 3);
961        assert_eq!(map.lookup(0).expect("lookup"), None);
962    }
963
964    #[test]
965    fn an_empty_column_builds_and_resolves_nothing() {
966        let map = KeyMap::build(&[]).expect("build");
967        assert!(map.is_empty());
968        assert_eq!(map.lookup(0).expect("lookup"), None);
969        assert!(map.observed().usable_as_parent(), "an empty parent is unique, vacuously");
970    }
971
972    #[test]
973    fn a_duplicated_key_is_reported_rather_than_resolved_to_one_of_its_rows() {
974        // Section 2.3's verification. The map still builds, because the caller is the one that
975        // decides what to do about it, and what it decides is to build no link.
976        let column = keys(&[5, 7, 5, 9]);
977        let map = KeyMap::build(&column).expect("build");
978        assert!(!map.observed().distinct);
979        assert!(!map.observed().usable_as_parent(), "a non-unique parent side takes no link");
980    }
981
982    #[test]
983    fn a_column_that_arrives_with_its_repeats_together_is_not_sorted_into_a_map() {
984        // The scan sees the repeat, so nothing is packed. What matters is the bytes: this is
985        // `lineitem(l_orderkey)`, where the form that would have been chosen holds one packed key
986        // and one packed permutation entry per row.
987        let column = keys(&[1, 1, 2, 2, 2, 90_000, 90_000]);
988        let map = KeyMap::build_from(&column[..]).expect("build");
989        assert!(!map.observed().distinct);
990        assert_eq!(map.observed().rows, 7, "the column was still counted");
991        assert_eq!(map.observed().max, Some(90_000));
992        assert_eq!(map.bytes(), KeyMap::build(&keys(&[])).expect("build").bytes());
993        assert_eq!(map.lookup(2).expect("lookup"), None, "and it answers nothing, as it must");
994    }
995
996    #[test]
997    fn a_single_key_column_resolves_it() {
998        // The width of zero case: one key at the base is an offset of zero, and a packed payload of
999        // width zero has no bytes for `tail_at` to read.
1000        let column = keys(&[42]);
1001        let map = KeyMap::build(&column).expect("build");
1002        resolves(&column, &map);
1003        assert_eq!(map.lookup(41).expect("lookup"), None);
1004        assert_eq!(map.lookup(43).expect("lookup"), None);
1005    }
1006
1007    #[test]
1008    fn negative_keys_resolve_because_the_base_is_the_minimum_and_not_zero() {
1009        let column = keys(&[-9000, -3, -1, 0, 7]);
1010        let map = KeyMap::build(&column).expect("build");
1011        resolves(&column, &map);
1012        assert_eq!(map.lookup(-9001).expect("lookup"), None);
1013    }
1014
1015    #[test]
1016    fn a_column_spanning_more_than_a_u64_of_values_is_refused_and_not_panicked_over() {
1017        // `max - min` on a HUGEINT column holding both ends of the type overflows an i128, so this
1018        // is where a build panics if the range arithmetic is done in the column's own type. It is
1019        // refused instead, and the caller records the relationship as not built.
1020        let column = keys(&[i128::MIN, 0, i128::MAX]);
1021        let error = KeyMap::build(&column).expect_err("refused");
1022        assert!(error.to_string().contains("spans more than a u64"), "{error}");
1023    }
1024
1025    #[test]
1026    fn keys_at_the_far_end_of_the_integer_type_resolve_when_their_range_is_narrow() {
1027        // The other half of the same arithmetic: the values are extreme and the range is not, which
1028        // is a column a key map has to handle rather than refuse.
1029        let column = keys(&[i128::MIN, i128::MIN + 5, i128::MIN + 2]);
1030        let map = KeyMap::build(&column).expect("build");
1031        resolves(&column, &map);
1032        assert_eq!(map.lookup(i128::MAX).expect("lookup"), None);
1033        assert_eq!(map.lookup(0).expect("lookup"), None);
1034    }
1035
1036    #[test]
1037    fn the_rank_index_agrees_with_counting_the_bits_by_hand() {
1038        // The rank structure is two levels and an eight word popcount, and an off by one in any of
1039        // the three resolves every key past the fault to the row before or after the right one. So
1040        // it is checked against the naive count over a bitmap wide enough to use every level: 4096
1041        // bits is one superblock exactly, so 20,000 forces five of them and the last one partial.
1042        let column = keys(&(0..10_000).map(|value| value * 2).collect::<Vec<i128>>());
1043        let map = KeyMap::build(&column).expect("build");
1044        assert_eq!(map.form(), Form::Dense);
1045        resolves(&column, &map);
1046    }
1047
1048    #[test]
1049    fn a_string_key_arrives_as_dictionary_codes_and_never_as_text() {
1050        // Section 2.2's composition with the global dictionary. There is nothing string shaped in
1051        // this crate and that is the point: the codes of a file wide stable dictionary carry the
1052        // column's own order, so a sorted key map over a VARCHAR is this and the search is over
1053        // integers.
1054        let codes = keys(&[7, 1, 4, 9, 2]);
1055        let map = KeyMap::build(&codes).expect("build");
1056        resolves(&codes, &map);
1057    }
1058
1059    #[test]
1060    fn the_form_tag_round_trips_and_an_unknown_one_is_refused() {
1061        for form in [Form::Identity, Form::Dense, Form::Sorted] {
1062            assert_eq!(Form::from_tag(form.tag()).expect("a known tag"), form);
1063        }
1064        assert!(Form::from_tag(3).is_err(), "an unfamiliar form is refused rather than guessed");
1065    }
1066
1067    #[test]
1068    fn the_dense_form_costs_a_bitmap_and_about_an_eighth_again() {
1069        // The space claim in section 3.3, checked. A range of 80,000 bits is 10,000 bytes and the
1070        // index is a u16 per 512 bits plus a u32 per 4096, which is about 12.5 percent.
1071        let column = keys(&(0..10_000).map(|value| value * 8).collect::<Vec<i128>>());
1072        let map = KeyMap::build(&column).expect("build");
1073        assert_eq!(map.form(), Form::Dense);
1074        let bitmap = 80_000 / 8;
1075        let bytes = map.bytes();
1076        assert!(bytes > bitmap, "the map took {bytes} bytes and the bitmap alone is {bitmap}");
1077        assert!(
1078            bytes < bitmap * 5 / 4,
1079            "the map took {bytes} bytes, more than a quarter over the bitmap's {bitmap}"
1080        );
1081    }
1082
1083    /// A column that counts how many times it was read, so a test can say what a build cost.
1084    struct Counted {
1085        column: Vec<Option<i128>>,
1086        scans: std::cell::Cell<usize>,
1087    }
1088
1089    impl Keys for Counted {
1090        fn scan(&self, each: &mut dyn FnMut(Option<i128>) -> Result<()>) -> Result<()> {
1091            self.scans.set(self.scans.get() + 1);
1092            self.column.scan(each)
1093        }
1094    }
1095
1096    #[test]
1097    fn a_build_from_a_scan_is_the_same_map_as_a_build_from_a_slice() {
1098        // The two builds have to agree on every column, because the streaming one is not a second
1099        // implementation, it is the same decision carried out against a source that is read twice.
1100        // If these ever disagree, a table's key map depends on which path built it.
1101        let columns: Vec<Vec<Option<i128>>> = vec![
1102            Vec::new(),
1103            keys(&[]),
1104            keys(&(1..=1000).collect::<Vec<i128>>()),
1105            keys(&(0..500).map(|value| value * 4).collect::<Vec<i128>>()),
1106            keys(&[100, 3, 40, 7, 9000]),
1107            keys(&[5, 5, 9]),
1108            vec![Some(10), None, Some(20), None, Some(30)],
1109            vec![None, None],
1110        ];
1111        for column in &columns {
1112            let held = KeyMap::build(column).expect("build from a slice");
1113            let read = KeyMap::build_from(&column[..]).expect("build from a scan");
1114            assert_eq!(read.form(), held.form(), "{column:?}");
1115            assert_eq!(read.observed(), held.observed(), "{column:?}");
1116            assert_eq!(read.len(), held.len(), "{column:?}");
1117            assert_eq!(read.bytes(), held.bytes(), "{column:?}");
1118            // A column with a repeat in it has no one right row for its key, which is exactly why
1119            // section 2.3 refuses to build a link on one. So the round trip is checked where the
1120            // question has an answer.
1121            if read.observed().usable_as_parent() {
1122                resolves(column, &read);
1123            }
1124        }
1125    }
1126
1127    #[test]
1128    fn the_identity_form_is_built_without_reading_the_column_twice() {
1129        // The reason `build_from` exists. Every TPC-H parent key takes the identity form, and the
1130        // identity form is two numbers, so a build of one has no business holding fifteen million
1131        // values or reading them a second time.
1132        let identity =
1133            Counted { column: keys(&(1..=1000).collect::<Vec<i128>>()), scans: 0.into() };
1134        assert_eq!(KeyMap::build_from(&identity).expect("build").form(), Form::Identity);
1135        assert_eq!(
1136            identity.scans.get(),
1137            1,
1138            "the identity form is the observation and nothing more"
1139        );
1140
1141        // The other two forms have something to fill, so they read it again, and once is the number
1142        // that matters: a form that scanned per value would be a build nobody could afford.
1143        let dense = Counted {
1144            column: keys(&(0..500).map(|v| v * 4).collect::<Vec<i128>>()),
1145            scans: 0.into(),
1146        };
1147        assert_eq!(KeyMap::build_from(&dense).expect("build").form(), Form::Dense);
1148        assert_eq!(dense.scans.get(), 2);
1149
1150        let sorted = Counted { column: keys(&[100, 3, 40, 7, 9000]), scans: 0.into() };
1151        assert_eq!(KeyMap::build_from(&sorted).expect("build").form(), Form::Sorted);
1152        assert_eq!(sorted.scans.get(), 2);
1153    }
1154
1155    #[test]
1156    fn a_scan_that_fails_stops_the_build_rather_than_half_finishing_it() {
1157        struct Broken;
1158        impl Keys for Broken {
1159            fn scan(&self, _: &mut dyn FnMut(Option<i128>) -> Result<()>) -> Result<()> {
1160                Err(malformed("the column could not be read"))
1161            }
1162        }
1163        let error = KeyMap::build_from(&Broken).expect_err("a build over an unreadable column");
1164        assert!(error.to_string().contains("could not be read"), "{error}");
1165    }
1166}