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