1#![forbid(unsafe_code)]
34
35use std::borrow::Cow;
36use std::cmp::Ordering;
37use std::collections::{HashMap, VecDeque};
38use std::fs::{File, OpenOptions};
39use std::io::{Read, Seek, SeekFrom};
40use std::mem::{size_of, size_of_val};
41use std::path::Path;
42use std::slice;
43use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as Atomic};
44use std::sync::{Arc, Mutex, OnceLock};
45
46use rudb_common::bounds::{self, Bound, Op, scaled_as};
47use rudb_common::{Clustering, Error, Field, LogicalType, PhysicalType, Result, Value, Width};
48use rudb_encoding::{bitpack, chooser, integer, string};
49use rudb_metrics::{LoadProfile, Stage};
50use rudb_storage::sieve::Sieve;
51use rudb_storage::{Probe, Range, Zone};
52use rudb_vector::string::StringColumn;
53use rudb_vector::validity::Validity;
54use rudb_vector::{Buffer, Chunk, Data, Packed, TextSource, Vector, search_below};
55
56mod distinct;
57pub mod graph;
58pub mod host;
59mod prepare;
60pub mod section;
61pub mod stats;
62mod zones;
63
64pub use prepare::{Merged, Paged, Prepared, Preparer};
65pub use section::Section;
66pub use zones::{Common, Stripes, ascending, distincts};
67
68const MAGIC: &[u8; 8] = b"RUDBNV10";
69const DIRECTORY: &[u8; 8] = b"RUDBDI10";
70const CATALOG: &[u8; 8] = b"RUDBCA10";
71const FORMAT: u32 = 28;
72
73const READABLE: &[u32] = &[22, 23, 24, 25, 26, 27, FORMAT];
102
103const HEADER: u64 = 80;
104const SLOT_BYTES: usize = 28;
105const MAX_PAGE: usize = 256 * 1024 * 1024;
106const MAX_DIRECTORY: usize = 128 * 1024 * 1024;
107const FREQUENCIES_V2: &[u8; 8] = b"RUDBFQ2\0";
108const FREQUENCIES: &[u8; 8] = b"RUDBFQ3\0";
109const FREQUENCY_TEXTS: &[u8; 8] = b"RUDBFT1\0";
117const HOST_GROUPS: &[u8; 8] = b"RUDBHG1\0";
119const PAIR_FREQUENCIES: &[u8; 8] = b"RUDBPF1\0";
125const CLUSTERING: &[u8; 8] = b"RUDBCL1\0";
140const SECTIONS: &[u8; 8] = b"RUDBSE1\0";
148const DICTIONARY_PAYLOADS: &[u8; 8] = b"RUDBDP1\0";
156
157const MAX_SECTIONS: usize = 4096;
164const FREQUENCY_CANDIDATES: usize = 32_768;
165const FREQUENCY_ENTRIES: usize = 512;
166const FREQUENCY_BUILD_RANK: usize = 10;
167const FREQUENCY_ORDINALS: usize = 131_072;
168const MAX_PAIR_FREQUENCIES: usize = 1024;
169const FREQUENCY_TEXT_BUDGET: usize = 1024 * 1024;
174const MAX_FREQUENCY_WORKERS: usize = 32;
181
182fn close_workers() -> usize {
184 std::thread::available_parallelism().map_or(1, usize::from).min(MAX_FREQUENCY_WORKERS)
185}
186
187const MAX_ENCODE_WORKERS: usize = 32;
194
195const SIEVE_BUDGET: usize = 8 * 1024;
203
204const PART_BOUND_BYTES: usize = 24;
213
214fn io(error: std::io::Error) -> Error {
215 Error::io(error.to_string())
216}
217
218fn invalid(message: &str) -> Error {
219 Error::invalid_input(format!("invalid rudb native file: {message}"))
220}
221
222fn sum(counts: impl Iterator<Item = u64>) -> u64 {
224 counts.fold(0, u64::saturating_add)
225}
226
227fn span_bytes(spans: &[Span], at: usize) -> u64 {
229 spans.get(at).map_or(0, |span| u64::from(span.length))
230}
231
232fn page_bytes(pages: &[Option<Page>], at: usize) -> u64 {
234 pages.get(at).and_then(Option::as_ref).map_or(0, Page::bytes)
235}
236
237fn dictionary_bytes(table: &Table, at: usize) -> u64 {
239 page_bytes(&table.dictionaries, at)
240 .saturating_add(table.dictionary_payloads.get(at).copied().unwrap_or(0))
241}
242
243fn checksum(bytes: &[u8]) -> u64 {
253 seeded_checksum(bytes, 0)
254}
255
256#[must_use]
263pub fn content_name(bytes: &[u8]) -> u128 {
264 let seed = u64::from(FORMAT);
265 u128::from(seeded_checksum(bytes, seed)) << 64 | u128::from(seeded_checksum(bytes, !seed))
266}
267
268fn seeded_checksum(bytes: &[u8], seed: u64) -> u64 {
277 let mut blocks = bytes.chunks_exact(32);
280 let rest = blocks.remainder();
281 if bytes.len() < 32 {
282 return checksum_tail(seed.wrapping_add(XXH_P5).wrapping_add(bytes.len() as u64), rest);
283 }
284 let mut lanes = [
285 seed.wrapping_add(XXH_P1).wrapping_add(XXH_P2),
286 seed.wrapping_add(XXH_P2),
287 seed,
288 seed.wrapping_sub(XXH_P1),
289 ];
290 for block in blocks.by_ref() {
291 checksum_block(&mut lanes, block);
292 }
293 finish_checksum(lanes, rest, bytes.len() as u64)
294}
295
296const XXH_P1: u64 = 11_400_714_785_074_694_791;
297const XXH_P2: u64 = 14_029_467_366_897_019_727;
298const XXH_P3: u64 = 1_609_587_929_392_839_161;
299const XXH_P4: u64 = 9_650_029_242_287_828_579;
300const XXH_P5: u64 = 2_870_177_450_012_600_261;
301
302fn checksum_round(state: u64, word: u64) -> u64 {
303 state.wrapping_add(word.wrapping_mul(XXH_P2)).rotate_left(31).wrapping_mul(XXH_P1)
304}
305
306fn checksum_word(chunk: &[u8]) -> u64 {
307 u64::from_le_bytes(chunk.try_into().expect("eight checksum bytes"))
308}
309
310fn checksum_block(lanes: &mut [u64; 4], block: &[u8]) {
312 for (lane, chunk) in lanes.iter_mut().zip(block.chunks_exact(8)) {
313 *lane = checksum_round(*lane, checksum_word(chunk));
314 }
315}
316
317fn finish_checksum(lanes: [u64; 4], rest: &[u8], length: u64) -> u64 {
319 let merge = |state: u64, lane: u64| {
320 (state ^ checksum_round(0, lane)).wrapping_mul(XXH_P1).wrapping_add(XXH_P4)
321 };
322 let [one, two, three, four] = lanes;
323 let combined = one
324 .rotate_left(1)
325 .wrapping_add(two.rotate_left(7))
326 .wrapping_add(three.rotate_left(12))
327 .wrapping_add(four.rotate_left(18));
328 let hash = merge(merge(merge(merge(combined, one), two), three), four);
329 checksum_tail(hash.wrapping_add(length), rest)
330}
331
332fn checksum_tail(mut hash: u64, mut rest: &[u8]) -> u64 {
334 let mut words = rest.chunks_exact(8);
335 for chunk in words.by_ref() {
336 hash ^= checksum_round(0, checksum_word(chunk));
337 hash = hash.rotate_left(27).wrapping_mul(XXH_P1).wrapping_add(XXH_P4);
338 }
339 rest = words.remainder();
340 if rest.len() >= 4 {
341 let (head, tail) = rest.split_at(4);
342 let quarter = u32::from_le_bytes(head.try_into().expect("four checksum bytes"));
343 hash ^= u64::from(quarter).wrapping_mul(XXH_P1);
344 hash = hash.rotate_left(23).wrapping_mul(XXH_P2).wrapping_add(XXH_P3);
345 rest = tail;
346 }
347 for &byte in rest {
348 hash ^= u64::from(byte).wrapping_mul(XXH_P5);
349 hash = hash.rotate_left(11).wrapping_mul(XXH_P1);
350 }
351 hash ^= hash >> 33;
352 hash = hash.wrapping_mul(XXH_P2);
353 hash ^= hash >> 29;
354 hash = hash.wrapping_mul(XXH_P3);
355 hash ^ (hash >> 32)
356}
357
358fn file_checksum(file: &File, offset: u64, length: usize) -> Result<u64> {
364 if length < 32 {
365 let mut bytes = vec![0; length];
366 read_at(file, offset, &mut bytes)?;
367 return Ok(checksum(&bytes));
368 }
369 let mut lanes = [XXH_P1.wrapping_add(XXH_P2), XXH_P2, 0, 0_u64.wrapping_sub(XXH_P1)];
370 let mut buffer = vec![0; DIRECTORY_WINDOW.min(length)];
371 let mut kept = 0;
372 let mut read = 0;
373 while read < length {
374 let want = (buffer.len() - kept).min(length - read);
375 read_at(file, offset + read as u64, &mut buffer[kept..kept + want])?;
376 read += want;
377 let filled = kept + want;
378 let whole = filled / 32 * 32;
379 for block in buffer[..whole].chunks_exact(32) {
380 checksum_block(&mut lanes, block);
381 }
382 buffer.copy_within(whole..filled, 0);
383 kept = filled - whole;
384 }
385 Ok(finish_checksum(lanes, &buffer[..kept], length as u64))
386}
387
388#[derive(Debug, Clone, Copy)]
389struct Slot {
390 offset: u64,
391 length: u32,
392 generation: u64,
393 hash: u64,
394}
395
396impl Slot {
397 fn bytes(self) -> [u8; SLOT_BYTES] {
398 let mut result = [0; SLOT_BYTES];
399 result[..8].copy_from_slice(&self.offset.to_le_bytes());
400 result[8..12].copy_from_slice(&self.length.to_le_bytes());
401 result[12..20].copy_from_slice(&self.generation.to_le_bytes());
402 result[20..28].copy_from_slice(&self.hash.to_le_bytes());
403 result
404 }
405
406 fn read(bytes: &[u8]) -> Self {
407 Self {
408 offset: u64::from_le_bytes(bytes[..8].try_into().expect("eight bytes")),
409 length: u32::from_le_bytes(bytes[8..12].try_into().expect("four bytes")),
410 generation: u64::from_le_bytes(bytes[12..20].try_into().expect("eight bytes")),
411 hash: u64::from_le_bytes(bytes[20..28].try_into().expect("eight bytes")),
412 }
413 }
414}
415
416#[derive(Debug, Clone, Copy)]
417struct Page {
418 offset: u64,
419 length: u32,
420 hash: u64,
421}
422
423impl Page {
424 fn bytes(&self) -> u64 {
426 u64::from(self.length)
427 }
428}
429
430#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
431enum FrequencyValue {
432 Null,
433 Integer(i128),
434 Code(u32),
435}
436
437type FrequencyMap<V> = HashMap<u64, V, Spread>;
443
444#[derive(Debug, Default, Clone, Copy)]
446struct Spread;
447
448impl std::hash::BuildHasher for Spread {
449 type Hasher = SpreadHasher;
450
451 fn build_hasher(&self) -> SpreadHasher {
452 SpreadHasher(0)
453 }
454}
455
456#[derive(Debug)]
463struct SpreadHasher(u64);
464
465impl SpreadHasher {
466 fn mix(&mut self, word: u64) {
467 let product = u128::from(self.0 ^ word) * 0x9E37_79B9_7F4A_7C15_u128;
468 self.0 = (product as u64) ^ ((product >> 64) as u64);
469 }
470}
471
472impl std::hash::Hasher for SpreadHasher {
473 fn write(&mut self, bytes: &[u8]) {
474 for part in bytes.chunks(8) {
475 let mut word = [0; 8];
476 word[..part.len()].copy_from_slice(part);
477 self.mix(u64::from_le_bytes(word));
478 }
479 }
480
481 fn write_u32(&mut self, value: u32) {
482 self.mix(u64::from(value));
483 }
484
485 fn write_u64(&mut self, value: u64) {
486 self.mix(value);
487 }
488
489 fn write_i128(&mut self, value: i128) {
490 self.mix(value as u64);
491 self.mix((value >> 64) as u64);
492 }
493
494 fn write_isize(&mut self, value: isize) {
495 self.mix(value as u64);
496 }
497
498 fn finish(&self) -> u64 {
499 self.0
500 }
501}
502
503#[derive(Debug, Clone)]
504struct FrequencyEntry {
505 value: FrequencyValue,
506 count: u64,
507}
508
509#[derive(Debug, Clone)]
514struct FrequencySummary {
515 entries: Vec<FrequencyEntry>,
516 omitted_max: u64,
517 ordinals: Vec<u64>,
518 ordinal_entries: Vec<u16>,
519}
520
521#[derive(Debug, Clone)]
522struct PairFrequencyEntry {
523 first_entry: u16,
524 second: Option<u32>,
525 count: u64,
526}
527
528#[derive(Debug, Clone)]
534struct PairFrequencySummary {
535 first: u16,
536 second: u16,
537 entries: Vec<PairFrequencyEntry>,
538 omitted_max: u64,
539}
540
541#[derive(Debug, Clone)]
549enum Frequencies {
550 Held(FrequencySummary),
551 Stored {
554 span: Span,
555 values: bool,
556 },
557}
558
559#[derive(Debug, Clone)]
564pub struct FrequencyPrefix {
565 pub entries: Vec<(Value, u64)>,
567 pub omitted_max: u64,
569}
570
571#[derive(Debug, Clone, PartialEq)]
573pub struct FrequencyOccurrences {
574 pub omitted_max: u64,
576 pub ordinals: Vec<u64>,
578 pub anchors: Vec<Value>,
580 pub anchor_indices: Vec<u16>,
582}
583
584pub type PairFrequencyCounts = Vec<(Vec<Value>, u64)>;
586
587#[derive(Debug, Clone, Copy, Default)]
594struct Span {
595 offset: u64,
596 length: u32,
597}
598
599#[derive(Debug, Clone, Default)]
607struct Pages {
608 columns: usize,
609 held: Box<[StripePage]>,
610}
611
612#[derive(Debug, Clone, Copy)]
614struct StripePage {
615 offset: u64,
616 hash: u64,
617 length: u32,
618 column: u32,
619}
620
621impl Pages {
622 fn from_slots(slots: Vec<Option<Page>>) -> Result<Self> {
624 let mut held = Vec::with_capacity(slots.iter().flatten().count());
625 for (column, page) in slots.iter().enumerate() {
626 if let Some(page) = page {
627 let column =
628 u32::try_from(column).map_err(|_| invalid("too many columns for a page"))?;
629 held.push(StripePage {
630 offset: page.offset,
631 hash: page.hash,
632 length: page.length,
633 column,
634 });
635 }
636 }
637 Ok(Self { columns: slots.len(), held: held.into_boxed_slice() })
638 }
639
640 fn get(&self, column: usize) -> Option<Page> {
642 let at = self.held.binary_search_by_key(&column, |placed| placed.column as usize).ok()?;
643 let placed = self.held[at];
644 Some(Page { offset: placed.offset, length: placed.length, hash: placed.hash })
645 }
646
647 fn slots(&self) -> impl Iterator<Item = Option<Page>> + '_ {
649 (0..self.columns).map(|column| self.get(column))
650 }
651
652 fn bytes(&self, column: usize) -> u64 {
654 self.get(column).map_or(0, |page| page.bytes())
655 }
656}
657
658#[derive(Debug, Clone)]
660pub struct Stripe {
661 rows: usize,
662 parts: Vec<u32>,
665 index: Span,
669 pages: Vec<Span>,
670 memberships: Pages,
671 sieves: Pages,
674 part_ranges: Pages,
685 zone: Zone,
686}
687
688impl Stripe {
689 #[must_use]
691 pub fn rows(&self) -> usize {
692 self.rows
693 }
694
695 #[must_use]
697 pub fn parts(&self) -> usize {
698 self.parts.len()
699 }
700
701 #[must_use]
707 pub fn zone(&self) -> &Zone {
708 &self.zone
709 }
710}
711
712#[derive(Debug, Clone)]
714pub struct Table {
715 name: String,
716 fields: Vec<Field>,
717 stripes: Vec<Stripe>,
718 rows: usize,
719 dictionaries: Vec<Option<Page>>,
720 dictionary_payloads: Vec<u64>,
726 frequencies: Vec<Option<Frequencies>>,
727 pair_frequencies: Vec<PairFrequencySummary>,
728 frequency_texts: Vec<Vec<Option<Vec<u8>>>>,
733 host_groups: Option<host::HostSummary>,
735 distincts: Vec<Option<u64>>,
745 clustering: Option<Clustering>,
753 generation: u64,
767 sections: Vec<Section>,
774}
775
776impl Table {
777 #[must_use]
779 pub fn name(&self) -> &str {
780 &self.name
781 }
782
783 #[must_use]
785 pub fn fields(&self) -> &[Field] {
786 &self.fields
787 }
788
789 #[must_use]
791 pub fn rows(&self) -> usize {
792 self.rows
793 }
794
795 #[must_use]
797 pub fn stripes(&self) -> &[Stripe] {
798 &self.stripes
799 }
800
801 #[must_use]
803 pub fn clustering(&self) -> Option<&Clustering> {
804 self.clustering.as_ref()
805 }
806
807 #[must_use]
812 pub fn generation(&self) -> u64 {
813 self.generation
814 }
815
816 #[must_use]
823 pub fn sections(&self) -> &[Section] {
824 &self.sections
825 }
826}
827
828#[derive(Debug, Clone)]
840struct Entry {
841 name: String,
842 fields: Vec<Field>,
843 rows: usize,
844 directory: Page,
846}
847
848#[derive(Debug, Clone, PartialEq, Eq)]
861pub struct ViewEntry {
862 pub name: String,
864 pub sql: String,
866 pub statement: String,
868 pub aliases: Vec<String>,
870 pub columns: Vec<Field>,
872}
873
874#[derive(Debug, Clone)]
876pub struct ColumnLayout {
877 pub name: String,
879 pub kind: String,
881 pub pages: u64,
883 pub memberships: u64,
885 pub sieves: u64,
887 pub part_ranges: u64,
889 pub dictionary: u64,
891}
892
893impl ColumnLayout {
894 #[must_use]
896 pub fn total(&self) -> u64 {
897 self.pages
898 .saturating_add(self.memberships)
899 .saturating_add(self.sieves)
900 .saturating_add(self.part_ranges)
901 .saturating_add(self.dictionary)
902 }
903}
904
905#[derive(Debug, Clone)]
916pub struct Layout {
917 pub file: u64,
919 pub rows: usize,
921 pub stripes: usize,
923 pub parts: usize,
925 pub columns: Vec<ColumnLayout>,
927 pub indexes: u64,
930 pub directory: u64,
932 pub header: u64,
934}
935
936impl Layout {
937 #[must_use]
939 pub fn columns_total(&self) -> u64 {
940 self.columns.iter().map(ColumnLayout::total).fold(0, u64::saturating_add)
941 }
942
943 #[must_use]
949 pub fn unaccounted(&self) -> u64 {
950 self.file
951 .saturating_sub(self.columns_total())
952 .saturating_sub(self.indexes)
953 .saturating_sub(self.directory)
954 .saturating_sub(self.header)
955 }
956}
957
958#[derive(Debug, Clone)]
969pub struct StoredPart {
970 pub stripe: usize,
972 pub part: usize,
974 pub row: usize,
976 pub rows: usize,
978 pub encoding: String,
980 pub bytes: u64,
982 pub page: u64,
984 pub offset: u64,
986 pub low: Option<Value>,
988 pub high: Option<Value>,
990 pub nulls: Option<usize>,
992}
993
994const DICTIONARY_CHECK_SEED: u64 = 11_400_714_819_323_198_485;
1001
1002#[derive(Debug)]
1026struct GlobalDictionary {
1027 primary: HashMap<u64, u32>,
1028 collisions: HashMap<u64, Vec<u32>>,
1029 checks: Vec<u64>,
1031 ends: Vec<u32>,
1033 counts: Vec<u64>,
1034 nulls: u64,
1035 filling: Vec<u8>,
1037 grams: Vec<[u8; TEXT_GRAM_BYTES]>,
1039 waiting: Vec<(usize, Vec<u8>)>,
1044 sample: Vec<(usize, Vec<u8>)>,
1050 stride: usize,
1052 shape: Option<chooser::Settled>,
1054 settled: usize,
1056 blocks: Vec<Vec<u8>>,
1062 placed: Vec<Placed>,
1064}
1065
1066#[derive(Debug, Clone, Copy)]
1068struct Placed {
1069 start: u64,
1070 length: u64,
1071 hash: u64,
1072}
1073
1074type RankedDictionary = (Vec<(u64, u32)>, Vec<u8>, Vec<u64>);
1076
1077impl GlobalDictionary {
1078 fn new() -> Self {
1079 Self {
1080 primary: HashMap::new(),
1081 collisions: HashMap::new(),
1082 checks: Vec::new(),
1083 ends: Vec::new(),
1084 counts: Vec::new(),
1085 nulls: 0,
1086 filling: Vec::new(),
1087 grams: Vec::new(),
1088 waiting: Vec::new(),
1089 sample: Vec::new(),
1090 stride: 1,
1091 shape: None,
1092 settled: 0,
1093 blocks: Vec::new(),
1094 placed: Vec::new(),
1095 }
1096 }
1097
1098 fn values(&self) -> usize {
1100 self.ends.len()
1101 }
1102
1103 fn encoded(&self) -> usize {
1105 self.placed.len() + self.blocks.len()
1106 }
1107
1108 #[cfg(test)]
1109 fn code(&mut self, text: &str) -> Result<u32> {
1110 let bytes = text.as_bytes();
1111 self.code_hashed(bytes, checksum(bytes), seeded_checksum(bytes, DICTIONARY_CHECK_SEED))
1112 }
1113
1114 fn code_hashed(&mut self, text: &[u8], hash: u64, check: u64) -> Result<u32> {
1120 if let Some(&code) = self.primary.get(&hash) {
1121 if self.checks.get(code as usize) == Some(&check) {
1122 return Ok(code);
1123 }
1124 if let Some(codes) = self.collisions.get(&hash) {
1125 if let Some(code) =
1126 codes.iter().copied().find(|&code| self.checks[code as usize] == check)
1127 {
1128 return Ok(code);
1129 }
1130 }
1131 let code = self.insert(text, check)?;
1132 self.collisions.entry(hash).or_default().push(code);
1133 return Ok(code);
1134 }
1135 let code = self.insert(text, check)?;
1136 self.primary.insert(hash, code);
1137 Ok(code)
1138 }
1139
1140 fn insert(&mut self, text: &[u8], check: u64) -> Result<u32> {
1141 let code = u32::try_from(self.ends.len())
1142 .map_err(|_| invalid("global dictionary has too many values"))?;
1143 self.filling.extend_from_slice(text);
1144 self.ends.push(
1145 u32::try_from(self.filling.len())
1146 .map_err(|_| invalid("a global dictionary value exceeds 4 GiB"))?,
1147 );
1148 self.checks.push(check);
1149 self.counts.push(0);
1150 if self.ends.len() % TEXT_PAYLOAD_VALUES == 0 {
1151 self.seal();
1152 }
1153 Ok(code)
1154 }
1155
1156 fn seal(&mut self) {
1162 let at = self.ends.len().div_ceil(TEXT_PAYLOAD_VALUES) - 1;
1163 let bytes = std::mem::take(&mut self.filling);
1164 let mut grams = [0_u8; TEXT_GRAM_BYTES];
1165 for value in self.slices(at, &bytes) {
1166 for gram in value.windows(4) {
1167 for bit in gram_bits(gram) {
1168 grams[bit / 8] |= 1 << (bit % 8);
1169 }
1170 }
1171 }
1172 self.grams.push(grams);
1173 if at % self.stride == 0 {
1174 self.sample.push((at, bytes.clone()));
1175 if self.sample.len() > PAYLOAD_SAMPLE_BLOCKS {
1176 self.stride *= 2;
1177 let stride = self.stride;
1178 self.sample.retain(|(at, _)| at % stride == 0);
1179 }
1180 }
1181 self.waiting.push((at, bytes));
1182 }
1183
1184 fn slices<'a>(&self, at: usize, bytes: &'a [u8]) -> Vec<&'a [u8]> {
1186 let first = at * TEXT_PAYLOAD_VALUES;
1187 let last = (first + TEXT_PAYLOAD_VALUES).min(self.ends.len());
1188 let mut out = Vec::with_capacity(last.saturating_sub(first));
1189 let mut from = 0;
1190 for value in first..last {
1191 let to = self.ends[value] as usize;
1192 out.push(&bytes[from..to]);
1193 from = to;
1194 }
1195 out
1196 }
1197
1198 fn settle(&mut self) -> Result<()> {
1206 if self.sample.len() < PAYLOAD_SAMPLE_BLOCKS {
1207 return Ok(());
1208 }
1209 let complete = self.ends.len() / TEXT_PAYLOAD_VALUES;
1210 if self.shape.is_some() && complete < self.settled.saturating_mul(4) {
1211 return Ok(());
1212 }
1213 let sample =
1214 self.sample.iter().map(|(at, bytes)| self.slices(*at, bytes)).collect::<Vec<_>>();
1215 self.shape = Some(settle_shape(&sample)?);
1216 self.settled = complete;
1217 Ok(())
1218 }
1219
1220 fn seal_rest(&mut self) {
1222 if self.ends.len() % TEXT_PAYLOAD_VALUES != 0 {
1225 self.seal();
1226 }
1227 }
1228
1229 fn encode_waiting(&self, at: usize) -> Result<Vec<u8>> {
1232 let (block, bytes) = &self.waiting[at];
1233 let values = self.slices(*block, bytes);
1234 match &self.shape {
1235 Some(shape) => string::encode_with(&values, shape),
1236 None => string::encode(&values),
1237 }
1238 }
1239
1240 #[cfg(test)]
1242 fn finish_blocks(&mut self) -> Result<()> {
1243 self.seal_rest();
1244 let made = (0..self.waiting.len())
1245 .map(|at| self.encode_waiting(at))
1246 .collect::<Result<Vec<_>>>()?;
1247 for ((at, _), bytes) in std::mem::take(&mut self.waiting).into_iter().zip(made) {
1248 if self.encoded() != at {
1249 return Err(Error::internal("a dictionary block was encoded out of order"));
1250 }
1251 self.blocks.push(bytes);
1252 }
1253 Ok(())
1254 }
1255
1256 fn decoded(&self, file: Option<&File>) -> Result<(Vec<u8>, Vec<u64>)> {
1274 let count = self.placed.len() + self.blocks.len();
1275 if count != self.values().div_ceil(TEXT_PAYLOAD_VALUES) {
1276 return Err(invalid("global dictionary blocks do not cover its values"));
1277 }
1278 let mut bases = Vec::with_capacity(count);
1279 let mut total = 0_usize;
1280 for block in 0..count {
1281 bases.push(total as u64);
1282 let last = ((block + 1) * TEXT_PAYLOAD_VALUES).min(self.values()) - 1;
1283 total = total
1284 .checked_add(self.ends[last] as usize)
1285 .ok_or_else(|| invalid("global dictionary does not fit in memory"))?;
1286 }
1287 let mut flat = vec![0_u8; total];
1288 let mut outs = Vec::with_capacity(count);
1289 let mut rest = flat.as_mut_slice();
1290 for block in 0..count {
1291 let end = bases.get(block + 1).map_or(total, |&base| base as usize);
1292 let (out, after) = rest.split_at_mut(end - bases[block] as usize);
1293 outs.push((block, out));
1294 rest = after;
1295 }
1296 let one = |run: &mut [(usize, &mut [u8])]| -> Result<()> {
1297 let mut stored = Vec::new();
1298 for (block, out) in run {
1299 let encoded = match self.placed.get(*block) {
1300 Some(place) => {
1301 let file = file.ok_or_else(|| {
1302 Error::internal("a written dictionary block has no file")
1303 })?;
1304 let length = usize::try_from(place.length).map_err(|_| {
1305 invalid("global dictionary block does not fit in memory")
1306 })?;
1307 stored.resize(length, 0);
1308 read_at(file, place.start, &mut stored)?;
1309 if checksum(&stored) != place.hash {
1310 return Err(invalid(
1311 "a global dictionary block did not read back as written",
1312 ));
1313 }
1314 stored.as_slice()
1315 }
1316 None => &self.blocks[*block - self.placed.len()],
1317 };
1318 let decoded = string::decode_flat(encoded)?;
1319 if decoded.bytes().len() != out.len() {
1320 return Err(invalid(
1321 "a global dictionary block is not the length its ends say",
1322 ));
1323 }
1324 out.copy_from_slice(decoded.bytes());
1325 }
1326 Ok(())
1327 };
1328 let workers = close_workers().min(count / 16).max(1);
1331 if workers <= 1 {
1332 one(&mut outs)?;
1333 } else {
1334 let per = count.div_ceil(workers);
1335 std::thread::scope(|scope| {
1336 outs.chunks_mut(per)
1337 .map(|run| scope.spawn(|| one(run)))
1338 .collect::<Vec<_>>()
1339 .into_iter()
1340 .try_for_each(|handle| {
1341 handle.join().map_err(|_| {
1342 Error::internal("a global dictionary decode worker panicked")
1343 })?
1344 })
1345 })?;
1346 }
1347 drop(outs);
1348 Ok((flat, bases))
1349 }
1350
1351 fn value_span(ends: &[u32], bases: &[u64], code: usize) -> (usize, usize) {
1356 let Some(&base) = bases.get(code / TEXT_PAYLOAD_VALUES) else { return (0, 0) };
1357 let Some(&end) = ends.get(code) else { return (0, 0) };
1358 let base = base as usize;
1359 let from = if code % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[code - 1] as usize };
1360 (base + from, base + end as usize)
1361 }
1362
1363 fn ranked_with_values(&self, file: Option<&File>) -> Result<RankedDictionary> {
1383 let (flat, bases) = self.decoded(file)?;
1384 let value = |code: u32| {
1385 let (from, to) = Self::value_span(&self.ends, &bases, code as usize);
1386 flat.get(from..to).unwrap_or_default()
1387 };
1388 let mut codes = (0..self.values() as u32).collect::<Vec<_>>();
1389 sort_by_value_across(&mut codes, value, close_workers());
1390 let order = codes.into_iter().map(|code| (head(value(code)), code)).collect();
1391 Ok((order, flat, bases))
1392 }
1393
1394 #[cfg(test)]
1395 fn ranked(&self, file: Option<&File>) -> Result<Vec<(u64, u32)>> {
1396 self.ranked_with_values(file).map(|(order, _, _)| order)
1397 }
1398}
1399
1400#[derive(Debug)]
1408pub struct Writer {
1409 file: File,
1410 at: u64,
1418 table: Table,
1419 generation: u64,
1420 order: Vec<((u64, u64), (u64, u64))>,
1423 next_order: u64,
1424 dictionaries: Vec<Option<GlobalDictionary>>,
1425 coded: Arc<[AtomicBool]>,
1428 gathers: Vec<Option<stats::Gather>>,
1434 pending: Vec<PendingChunk>,
1435 closed: Vec<Entry>,
1437 views: Vec<ViewEntry>,
1442 profile: Option<Arc<LoadProfile>>,
1448}
1449
1450#[derive(Debug)]
1458struct PendingChunk {
1459 order: (u64, u64),
1460 chunk: Chunk,
1461}
1462
1463#[derive(Debug, Clone, Copy)]
1469struct Part {
1470 order: (u64, u64),
1471 rows: usize,
1472 footprint: usize,
1473}
1474
1475impl Part {
1476 fn of(pending: &PendingChunk) -> Self {
1477 Self {
1478 order: pending.order,
1479 rows: pending.chunk.len(),
1480 footprint: pending.chunk.footprint(),
1481 }
1482 }
1483}
1484
1485#[derive(Debug)]
1491struct ColumnStripe {
1492 pages: Vec<Vec<u8>>,
1493 codes: Vec<Option<Vec<u32>>>,
1494 sieves: Vec<Option<Sieve>>,
1495 ranges: Vec<Range>,
1496}
1497
1498fn weight(ty: &LogicalType) -> usize {
1506 match ty {
1507 LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit => 64,
1508 LogicalType::HugeInt
1509 | LogicalType::UHugeInt
1510 | LogicalType::Uuid
1511 | LogicalType::Interval => 16,
1512 LogicalType::BigInt
1513 | LogicalType::UBigInt
1514 | LogicalType::Timestamp
1515 | LogicalType::Time
1516 | LogicalType::TimeTz
1517 | LogicalType::TimestampTz
1518 | LogicalType::TimestampS
1519 | LogicalType::TimestampMs
1520 | LogicalType::TimestampNs
1521 | LogicalType::Double
1522 | LogicalType::Decimal { .. } => 8,
1523 LogicalType::Integer | LogicalType::UInteger | LogicalType::Date | LogicalType::Float => 4,
1524 LogicalType::SmallInt | LogicalType::USmallInt => 2,
1525 _ => 1,
1526 }
1527}
1528
1529pub const STRIPE_PARTS: usize = 64;
1536
1537const DICTIONARY_DECIDE_ROWS: usize = 4_096;
1545
1546const DICTIONARY_DISTINCT_IN_TEN: usize = 9;
1562
1563const INDEX_ENTRY: usize = size_of::<u32>() + size_of::<u64>();
1565
1566fn index_section(parts: usize) -> Result<usize> {
1568 parts
1569 .checked_mul(INDEX_ENTRY)
1570 .and_then(|bytes| bytes.checked_add(size_of::<u64>()))
1571 .ok_or_else(|| invalid("index page length overflow"))
1572}
1573
1574impl Writer {
1575 pub fn open(
1594 path: impl AsRef<Path>,
1595 name: impl Into<String>,
1596 fields: Vec<Field>,
1597 ) -> Result<Self> {
1598 for field in &fields {
1599 type_tag(&field.ty)?;
1600 }
1601 let name = name.into();
1602 let path = path.as_ref();
1603 let (_, size, slot, bytes, _) = slot_bytes(path)?;
1604 let (mut closed, views) = decode_catalog(&bytes, size)?;
1605 if let Some(at) = closed.iter().position(|held| held.name == name) {
1616 if closed[at].rows > 0 {
1617 return Err(invalid("two tables in one native file have the same name"));
1618 }
1619 closed.remove(at);
1620 }
1621 let generation = slot
1626 .generation
1627 .checked_add(1)
1628 .ok_or_else(|| invalid("native file generation overflow"))?;
1629 let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
1630 Ok(Self {
1631 file,
1632 at: size,
1635 dictionaries: fields
1636 .iter()
1637 .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1638 .collect(),
1639 coded: fields
1640 .iter()
1641 .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1642 .collect(),
1643 gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, generation)).collect(),
1644 table: Table {
1645 name,
1646 dictionaries: vec![None; fields.len()],
1647 dictionary_payloads: Vec::new(),
1648 distincts: vec![None; fields.len()],
1649 fields,
1650 stripes: Vec::new(),
1651 rows: 0,
1652 frequencies: Vec::new(),
1653 pair_frequencies: Vec::new(),
1654 frequency_texts: Vec::new(),
1655 host_groups: None,
1656 clustering: None,
1657 generation,
1658 sections: Vec::new(),
1659 },
1660 generation,
1661 order: Vec::new(),
1662 next_order: 0,
1663 pending: Vec::with_capacity(STRIPE_PARTS),
1664 closed,
1665 views,
1666 profile: None,
1667 })
1668 }
1669
1670 pub fn create(
1676 path: impl AsRef<Path>,
1677 name: impl Into<String>,
1678 fields: Vec<Field>,
1679 ) -> Result<Self> {
1680 for field in &fields {
1681 type_tag(&field.ty)?;
1682 }
1683 let file =
1684 OpenOptions::new().write(true).read(true).create_new(true).open(path).map_err(io)?;
1685 let mut header = [0; HEADER as usize];
1686 header[..8].copy_from_slice(MAGIC);
1687 header[8..12].copy_from_slice(&FORMAT.to_le_bytes());
1688 write_at(&file, 0, &header)?;
1689 Ok(Self {
1690 file,
1691 at: HEADER,
1692 dictionaries: fields
1693 .iter()
1694 .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1695 .collect(),
1696 coded: fields
1697 .iter()
1698 .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1699 .collect(),
1700 gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, 1)).collect(),
1701 table: Table {
1702 name: name.into(),
1703 dictionaries: vec![None; fields.len()],
1704 dictionary_payloads: Vec::new(),
1705 distincts: vec![None; fields.len()],
1706 fields,
1707 stripes: Vec::new(),
1708 rows: 0,
1709 frequencies: Vec::new(),
1710 pair_frequencies: Vec::new(),
1711 frequency_texts: Vec::new(),
1712 host_groups: None,
1713 clustering: None,
1714 generation: 1,
1715 sections: Vec::new(),
1716 },
1717 generation: 1,
1718 order: Vec::new(),
1719 next_order: 0,
1720 pending: Vec::with_capacity(STRIPE_PARTS),
1721 closed: Vec::new(),
1722 views: Vec::new(),
1723 profile: None,
1724 })
1725 }
1726
1727 pub fn empty(path: impl AsRef<Path>, views: &[ViewEntry]) -> Result<()> {
1749 let file =
1750 OpenOptions::new().write(true).read(true).create_new(true).open(path).map_err(io)?;
1751 let mut header = [0; HEADER as usize];
1752 header[..8].copy_from_slice(MAGIC);
1753 header[8..12].copy_from_slice(&FORMAT.to_le_bytes());
1754 write_at(&file, 0, &header)?;
1755 let catalog = encode_catalog(&[], views)?;
1756 write_at(&file, HEADER, &catalog)?;
1757 file.sync_all().map_err(io)?;
1761 let slot = Slot {
1762 offset: HEADER,
1763 length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
1764 generation: 1,
1765 hash: checksum(&catalog),
1766 };
1767 write_at(&file, slot_offset(1), &slot.bytes())?;
1768 file.sync_all().map_err(io)?;
1769 Ok(())
1770 }
1771
1772 pub fn next(mut self, name: impl Into<String>, fields: Vec<Field>) -> Result<Self> {
1783 for field in &fields {
1784 type_tag(&field.ty)?;
1785 }
1786 let name = name.into();
1787 let entry = self.close()?;
1788 if self.closed.iter().chain(std::iter::once(&entry)).any(|held| held.name == name) {
1789 return Err(invalid("two tables in one native file have the same name"));
1790 }
1791 let Self { file, at, generation, mut closed, views, .. } = self;
1792 closed.push(entry);
1793 Ok(Self {
1794 file,
1795 at,
1796 generation,
1797 closed,
1798 views,
1799 profile: None,
1800 dictionaries: fields
1801 .iter()
1802 .map(|field| (field.ty == LogicalType::Varchar).then(GlobalDictionary::new))
1803 .collect(),
1804 coded: fields
1805 .iter()
1806 .map(|field| AtomicBool::new(field.ty == LogicalType::Varchar))
1807 .collect(),
1808 gathers: fields.iter().map(|field| stats::Gather::new(&field.ty, generation)).collect(),
1809 table: Table {
1810 name,
1811 dictionaries: vec![None; fields.len()],
1812 dictionary_payloads: Vec::new(),
1813 distincts: vec![None; fields.len()],
1814 fields,
1815 stripes: Vec::new(),
1816 rows: 0,
1817 frequencies: Vec::new(),
1818 pair_frequencies: Vec::new(),
1819 frequency_texts: Vec::new(),
1820 host_groups: None,
1821 clustering: None,
1822 generation,
1823 sections: Vec::new(),
1824 },
1825 order: Vec::new(),
1826 next_order: 0,
1827 pending: Vec::with_capacity(STRIPE_PARTS),
1828 })
1829 }
1830
1831 #[must_use]
1841 pub fn with_views(mut self, views: Vec<ViewEntry>) -> Self {
1842 self.views = views;
1843 self
1844 }
1845
1846 #[must_use]
1852 pub fn with_profile(mut self, profile: Arc<LoadProfile>) -> Self {
1853 self.profile = Some(profile);
1854 self
1855 }
1856
1857 pub fn declare(mut self, clustering: Clustering) -> Result<Self> {
1872 self.table.clustering = Some(Clustering::new(
1875 clustering.columns().to_vec(),
1876 clustering.width(),
1877 &self.table.fields,
1878 )?);
1879 Ok(self)
1880 }
1881
1882 fn put(&mut self, bytes: &[u8]) -> Result<()> {
1887 write_at(&self.file, self.at, bytes)?;
1888 self.at = self
1889 .at
1890 .checked_add(bytes.len() as u64)
1891 .ok_or_else(|| invalid("native file length overflow"))?;
1892 Ok(())
1893 }
1894
1895 pub fn append(&mut self, chunk: &Chunk) -> Result<()> {
1901 let order = (self.next_order, 0);
1902 self.next_order = self.next_order.saturating_add(1);
1903 self.append_at(order, chunk)
1904 }
1905
1906 pub fn append_at(&mut self, order: (u64, u64), chunk: &Chunk) -> Result<()> {
1917 if chunk.is_empty() {
1918 return Ok(());
1919 }
1920 self.admit(chunk)?;
1921 if self.pending.last().is_some_and(|last| last.order > order) {
1922 self.flush_pending()?;
1923 }
1924 self.pending.push(PendingChunk { order, chunk: chunk.clone() });
1929 if self.pending.len() == STRIPE_PARTS {
1930 self.flush_pending()?;
1931 }
1932 Ok(())
1933 }
1934
1935 pub fn append_stripe(&mut self, parts: Vec<((u64, u64), Chunk)>) -> Result<()> {
1951 if parts.len() > STRIPE_PARTS {
1952 return Err(invalid("a stripe was handed more parts than it holds"));
1953 }
1954 self.flush_pending()?;
1957 for (order, chunk) in parts {
1958 if chunk.is_empty() {
1959 continue;
1960 }
1961 self.admit(&chunk)?;
1962 self.pending.push(PendingChunk { order, chunk });
1963 }
1964 self.flush_pending()
1965 }
1966
1967 fn admit(&mut self, chunk: &Chunk) -> Result<()> {
1969 if chunk.width() != self.table.fields.len() {
1970 return Err(invalid("chunk width differs from table schema"));
1971 }
1972 for (index, field) in self.table.fields.iter().enumerate() {
1973 if chunk.column(index)?.logical_type() != &field.ty {
1974 return Err(invalid("chunk type differs from table schema"));
1975 }
1976 }
1977 self.table.rows = self
1978 .table
1979 .rows
1980 .checked_add(chunk.len())
1981 .ok_or_else(|| invalid("row count overflow"))?;
1982 Ok(())
1983 }
1984
1985 fn encode_pages(columns: &[&Vector]) -> Result<ColumnStripe> {
1987 let mut stripe = ColumnStripe {
1988 pages: Vec::with_capacity(columns.len()),
1989 codes: Vec::with_capacity(columns.len()),
1990 sieves: Vec::with_capacity(columns.len()),
1991 ranges: Vec::with_capacity(columns.len()),
1992 };
1993 for &column in columns {
1994 let bytes = encode(column)?;
1995 if bytes.len() > MAX_PAGE {
1996 return Err(invalid("column page exceeds the configured bound"));
1997 }
1998 let range = Range::of(column);
2001 let sieve =
2012 Sieve::of(column, &range, SIEVE_BUDGET).filter(|sieve| sieve.len() < bytes.len());
2013 stripe.pages.push(bytes);
2014 stripe.codes.push(None);
2015 stripe.sieves.push(sieve);
2016 stripe.ranges.push(range);
2017 }
2018 Ok(stripe)
2019 }
2020
2021 fn place_blocks(&mut self) -> Result<()> {
2026 let mut dictionaries = std::mem::take(&mut self.dictionaries);
2027 let placed = dictionaries.iter_mut().flatten().try_for_each(|dictionary| {
2028 for block in std::mem::take(&mut dictionary.blocks) {
2029 let start = self.at;
2030 self.put(&block)?;
2031 dictionary.placed.push(Placed {
2032 start,
2033 length: block.len() as u64,
2034 hash: checksum(&block),
2035 });
2036 }
2037 Ok(())
2038 });
2039 self.dictionaries = dictionaries;
2040 placed
2041 }
2042
2043 fn flush_pending(&mut self) -> Result<()> {
2048 if self.pending.is_empty() {
2049 return Ok(());
2050 }
2051 let held = std::mem::take(&mut self.pending);
2052 let prepared = self.preparer().prepare_held(held)?;
2053 let merged = self.merge_held(prepared)?;
2054 let paged = merged.pages()?;
2055 self.write_paged(paged)
2056 }
2057
2058 fn write_stripe(&mut self, held: &[Part], encoded: Vec<ColumnStripe>) -> Result<()> {
2060 let width = self.table.fields.len();
2061 let parts = held.len();
2062 if encoded.len() != width {
2063 return Err(Error::internal("a stripe came to the writer with the wrong columns"));
2064 }
2065 let profile = self.profile.clone();
2066 if let Some(profile) = &profile {
2067 let rows = held.iter().map(|part| part.rows as u64).sum();
2068 let raw = held.iter().map(|part| part.footprint as u64).sum();
2069 let pages =
2070 encoded.iter().flat_map(|stripe| &stripe.pages).map(|page| page.len() as u64).sum();
2071 profile.moved(Stage::Pages, raw, pages, rows);
2072 }
2073 let timing = profile.as_deref().map(|profile| profile.span(Stage::Dictionary));
2076 let before = self.at;
2077 encode_ready(&mut self.dictionaries)?;
2078 self.place_blocks()?;
2079 drop(timing);
2080 if let Some(profile) = &profile {
2081 profile.moved(Stage::Dictionary, 0, self.at - before, 0);
2082 }
2083 let timing = profile.as_deref().map(|profile| profile.span(Stage::Write));
2084 let before = self.at;
2085 let mut pages = Vec::with_capacity(width);
2086 let mut memberships = vec![None; width];
2087 let mut ranges = Vec::with_capacity(width);
2088 let mut index = Vec::with_capacity(width.saturating_mul(index_section(parts)?));
2089 for stripe in &encoded {
2090 let offset = self.at;
2091 let section = index.len();
2092 let mut length = 0_usize;
2093 for bytes in &stripe.pages {
2094 write_at(&self.file, self.at + length as u64, bytes)?;
2095 put_u32(
2096 &mut index,
2097 u32::try_from(bytes.len()).map_err(|_| invalid("part length overflow"))?,
2098 );
2099 put_u64(&mut index, checksum(bytes));
2100 length = length
2101 .checked_add(bytes.len())
2102 .ok_or_else(|| invalid("column page length overflow"))?;
2103 }
2104 let hash = checksum(&index[section..]);
2105 put_u64(&mut index, hash);
2106 if length > MAX_PAGE {
2107 return Err(invalid("column page exceeds the configured bound"));
2108 }
2109 self.at = self
2110 .at
2111 .checked_add(length as u64)
2112 .ok_or_else(|| invalid("native file length overflow"))?;
2113 pages.push(Span {
2114 offset,
2115 length: u32::try_from(length).map_err(|_| invalid("page length overflow"))?,
2116 });
2117 ranges.push(merged_range(stripe.ranges.iter().cloned()));
2118 }
2119 for (membership, stripe) in memberships.iter_mut().zip(&encoded) {
2120 if stripe.codes.iter().all(Option::is_none) {
2121 continue;
2122 }
2123 let lists = stripe
2124 .codes
2125 .iter()
2126 .map(|codes| codes.clone().unwrap_or_default())
2127 .collect::<Vec<_>>();
2128 let bytes = encode_membership(&merged_codes(lists));
2129 let offset = self.at;
2130 self.put(&bytes)?;
2131 *membership = Some(Page {
2132 offset,
2133 length: u32::try_from(bytes.len())
2134 .map_err(|_| invalid("membership page length overflow"))?,
2135 hash: checksum(&bytes),
2136 });
2137 }
2138 let mut sieves = vec![None; width];
2139 for (page, stripe) in sieves.iter_mut().zip(&encoded) {
2140 if stripe.sieves.iter().all(Option::is_none) {
2141 continue;
2142 }
2143 let bytes = encode_sieves(stripe.sieves.iter())?;
2144 let offset = self.at;
2145 self.put(&bytes)?;
2146 *page = Some(Page {
2147 offset,
2148 length: u32::try_from(bytes.len())
2149 .map_err(|_| invalid("sieve page length overflow"))?,
2150 hash: checksum(&bytes),
2151 });
2152 }
2153 let mut part_ranges = vec![None; width];
2159 if parts > 1 {
2160 for ((page, stripe), span) in part_ranges.iter_mut().zip(&encoded).zip(&pages) {
2161 let bytes = encode_part_ranges(&stripe.ranges)?;
2162 if bytes.len() >= span.length as usize {
2163 continue;
2164 }
2165 let offset = self.at;
2166 self.put(&bytes)?;
2167 *page = Some(Page {
2168 offset,
2169 length: u32::try_from(bytes.len())
2170 .map_err(|_| invalid("part range page length overflow"))?,
2171 hash: checksum(&bytes),
2172 });
2173 }
2174 }
2175 let offset = self.at;
2176 self.put(&index)?;
2177 let index = Span {
2178 offset,
2179 length: u32::try_from(index.len())
2180 .map_err(|_| invalid("index page length overflow"))?,
2181 };
2182 let mut rows = 0_usize;
2183 let mut lengths = Vec::with_capacity(parts);
2184 let mut span = None;
2185 for part in held {
2186 rows = rows.checked_add(part.rows).ok_or_else(|| invalid("row count overflow"))?;
2187 lengths.push(u32::try_from(part.rows).map_err(|_| invalid("part row count overflow"))?);
2188 span = Some(span.map_or((part.order, part.order), |(first, _)| (first, part.order)));
2189 }
2190 self.order.push(span.ok_or_else(|| invalid("a stripe was flushed with no parts"))?);
2191 self.table.stripes.push(Stripe {
2192 rows,
2193 parts: lengths,
2194 index,
2195 pages,
2196 memberships: Pages::from_slots(memberships)?,
2197 sieves: Pages::from_slots(sieves)?,
2198 part_ranges: Pages::from_slots(part_ranges)?,
2199 zone: Zone::from_ranges(ranges),
2200 });
2201 drop(timing);
2202 if let Some(profile) = &profile {
2203 profile.moved(Stage::Write, 0, self.at - before, rows as u64);
2204 }
2205 Ok(())
2206 }
2207
2208 fn numeric_frequency(&self, column: usize) -> Result<(Option<FrequencySummary>, Option<u64>)> {
2224 let signed = match self.table.fields[column].ty {
2225 LogicalType::TinyInt
2226 | LogicalType::SmallInt
2227 | LogicalType::Integer
2228 | LogicalType::BigInt
2229 | LogicalType::Date
2230 | LogicalType::Timestamp => true,
2231 LogicalType::UTinyInt
2232 | LogicalType::USmallInt
2233 | LogicalType::UInteger
2234 | LogicalType::UBigInt => false,
2235 _ => return Ok((None, None)),
2236 };
2237 let value_of = |bits: Option<u64>| match bits {
2238 None => FrequencyValue::Null,
2239 Some(bits) if signed => FrequencyValue::Integer(i128::from(bits as i64)),
2240 Some(bits) => FrequencyValue::Integer(i128::from(bits)),
2241 };
2242 let mut candidates: FrequencyMap<u32> = FrequencyMap::default();
2243 let mut nulls = 0_u32;
2244 let mut decrements = 0_u64;
2245 let mut distinct = distinct::ExactDistinct::new();
2246 self.visit_numeric(column, signed, |_, bits| {
2247 let held = match bits {
2248 Some(bits) => {
2249 distinct.insert(bits);
2250 candidates.get_mut(&bits)
2251 }
2252 None if nulls != 0 => Some(&mut nulls),
2253 None => None,
2254 };
2255 if let Some(count) = held {
2256 *count = count.saturating_add(1);
2257 } else if candidates.len() + usize::from(nulls != 0) < FREQUENCY_CANDIDATES {
2258 match bits {
2259 Some(bits) => {
2260 candidates.insert(bits, 1);
2261 }
2262 None => nulls = 1,
2263 }
2264 } else {
2265 candidates.retain(|_, count| {
2266 *count -= 1;
2267 *count != 0
2268 });
2269 nulls = nulls.saturating_sub(1);
2270 decrements = decrements.saturating_add(1);
2271 }
2272 })?;
2273 let (exact, null_count) = if decrements == 0 {
2274 let exact = candidates
2275 .into_iter()
2276 .map(|(bits, count)| (bits, u64::from(count)))
2277 .collect::<FrequencyMap<_>>();
2278 (exact, (nulls != 0).then_some(u64::from(nulls)))
2279 } else {
2280 let mut lower = candidates.values().copied().collect::<Vec<_>>();
2281 if nulls != 0 {
2282 lower.push(nulls);
2283 }
2284 lower.sort_unstable_by(|left, right| right.cmp(left));
2285 if lower.len() < FREQUENCY_BUILD_RANK
2286 || u64::from(lower[FREQUENCY_BUILD_RANK - 1]) <= decrements
2287 {
2288 return Ok((None, distinct.count()));
2289 }
2290 let mut exact =
2291 candidates.into_keys().map(|bits| (bits, 0_u64)).collect::<FrequencyMap<_>>();
2292 let mut null_count = (nulls != 0).then_some(0_u64);
2293 self.visit_numeric(column, signed, |_, bits| {
2294 let held = match bits {
2295 Some(bits) => exact.get_mut(&bits),
2296 None => null_count.as_mut(),
2297 };
2298 if let Some(count) = held {
2299 *count = count.saturating_add(1);
2300 }
2301 })?;
2302 (exact, null_count)
2303 };
2304 let mut entries = exact
2305 .into_iter()
2306 .map(|(bits, count)| FrequencyEntry { value: value_of(Some(bits)), count })
2307 .chain(null_count.map(|count| FrequencyEntry { value: FrequencyValue::Null, count }))
2308 .collect::<Vec<_>>();
2309 let omitted_max = keep_most_frequent(&mut entries).max(decrements);
2310 let kept_rows = entries.iter().try_fold(0_u64, |total, entry| {
2311 total.checked_add(entry.count).filter(|&total| total <= FREQUENCY_ORDINALS as u64)
2312 });
2313 let mut ordinals = Vec::new();
2314 let mut ordinal_entries = Vec::new();
2315 if let Some(kept_rows) = kept_rows {
2316 let mut kept = FrequencyMap::default();
2317 let mut null_kept = None;
2318 for (at, entry) in entries.iter().enumerate() {
2319 let at = u16::try_from(at)
2320 .map_err(|_| invalid("too many retained frequency entries"))?;
2321 match entry.value {
2322 FrequencyValue::Integer(value) => {
2323 kept.insert(value as u64, at);
2324 }
2325 FrequencyValue::Null => null_kept = Some(at),
2326 FrequencyValue::Code(_) => {}
2327 }
2328 }
2329 ordinals.reserve(usize::try_from(kept_rows).unwrap_or(FREQUENCY_ORDINALS));
2330 ordinal_entries.reserve(usize::try_from(kept_rows).unwrap_or(FREQUENCY_ORDINALS));
2331 self.visit_numeric(column, signed, |ordinal, bits| {
2332 let held = match bits {
2333 Some(bits) => kept.get(&bits).copied(),
2334 None => null_kept,
2335 };
2336 if let Some(entry) = held {
2337 ordinals.push(ordinal);
2338 ordinal_entries.push(entry);
2339 }
2340 })?;
2341 }
2342 Ok((
2343 Some(FrequencySummary { entries, omitted_max, ordinals, ordinal_entries }),
2344 distinct.count(),
2345 ))
2346 }
2347
2348 fn visit_numeric(
2355 &self,
2356 column: usize,
2357 signed: bool,
2358 mut visit: impl FnMut(u64, Option<u64>),
2359 ) -> Result<()> {
2360 let ty = &self.table.fields[column].ty;
2361 let mut start = 0_u64;
2362 let mut block = Vec::new();
2363 for stripe in &self.table.stripes {
2364 let spans = read_index(&self.file, stripe, column)?;
2365 let page = stripe.pages[column];
2366 let mut bytes = vec![0; page.length as usize];
2367 read_at(&self.file, page.offset, &mut bytes)?;
2368 for (span, &rows) in spans.iter().zip(&stripe.parts) {
2369 let part = part_bytes(&bytes, *span)?;
2370 if checksum(part) != span.hash {
2371 return Err(invalid("column page checksum differs while building frequencies"));
2372 }
2373 let rows = rows as usize;
2374 let vector = decode(ty, rows, part, None)?;
2375 if signed && vector.signed_block(&mut block) && block.len() == rows {
2379 if vector.none_null() {
2380 for (row, &value) in block.iter().enumerate() {
2381 visit(start.saturating_add(row as u64), Some(value as u64));
2382 }
2383 } else {
2384 for (row, &value) in block.iter().enumerate() {
2385 let bits = (!vector.is_null_at(row)).then_some(value as u64);
2386 visit(start.saturating_add(row as u64), bits);
2387 }
2388 }
2389 start = start.saturating_add(rows as u64);
2390 continue;
2391 }
2392 for row in 0..rows {
2394 let bits = if vector.is_null_at(row) {
2395 None
2396 } else {
2397 let widened = match vector.signed_at(row) {
2401 Some(value) => Some(value as u64),
2402 None => match vector.value_at(row) {
2403 Value::UTinyInt(value) => Some(u64::from(value)),
2404 Value::USmallInt(value) => Some(u64::from(value)),
2405 Value::UInteger(value) => Some(u64::from(value)),
2406 Value::UBigInt(value) => Some(value),
2407 _ => None,
2408 },
2409 };
2410 Some(widened.ok_or_else(|| {
2411 invalid("numeric frequency page did not contain an integer value")
2412 })?)
2413 };
2414 visit(start.saturating_add(row as u64), bits);
2415 }
2416 start = start.saturating_add(rows as u64);
2417 }
2418 }
2419 Ok(())
2420 }
2421
2422 fn numeric_frequencies(&self) -> Result<Vec<(Option<FrequencySummary>, Option<u64>)>> {
2430 let mut columns = self
2431 .table
2432 .fields
2433 .iter()
2434 .enumerate()
2435 .filter_map(|(column, field)| {
2436 matches!(
2437 field.ty,
2438 LogicalType::TinyInt
2439 | LogicalType::SmallInt
2440 | LogicalType::Integer
2441 | LogicalType::BigInt
2442 | LogicalType::UTinyInt
2443 | LogicalType::USmallInt
2444 | LogicalType::UInteger
2445 | LogicalType::UBigInt
2446 | LogicalType::Date
2447 | LogicalType::Timestamp
2448 )
2449 .then_some(column)
2450 })
2451 .collect::<Vec<_>>();
2452 let workers = std::thread::available_parallelism()
2453 .map_or(1, usize::from)
2454 .min(MAX_FREQUENCY_WORKERS)
2455 .min(columns.len());
2456 let profile = self.profile.as_deref();
2457 if workers <= 1 {
2458 let _timing = profile.map(|profile| profile.span(Stage::Publish));
2459 let mut frequencies = vec![(None, None); self.table.fields.len()];
2460 for column in columns {
2461 frequencies[column] = self.numeric_frequency(column)?;
2462 }
2463 return Ok(frequencies);
2464 }
2465 columns.sort_by_key(|&column| weight(&self.table.fields[column].ty));
2468 let queue = Mutex::new(columns);
2469 let pieces = std::thread::scope(|scope| {
2470 (0..workers)
2471 .map(|_| {
2472 scope.spawn(|| {
2473 let _timing = profile.map(|profile| profile.span(Stage::Publish));
2474 let mut mine = Vec::new();
2475 loop {
2476 let taken = queue
2477 .lock()
2478 .map_err(|_| Error::internal("a native frequency worker panicked"))?
2479 .pop();
2480 let Some(column) = taken else { break };
2481 mine.push((column, self.numeric_frequency(column)?));
2482 }
2483 Ok(mine)
2484 })
2485 })
2486 .collect::<Vec<_>>()
2487 .into_iter()
2488 .map(|handle| {
2489 handle
2490 .join()
2491 .map_err(|_| Error::internal("a native frequency worker panicked"))?
2492 })
2493 .collect::<Result<Vec<_>>>()
2494 })?;
2495 let mut frequencies = vec![(None, None); self.table.fields.len()];
2496 for piece in pieces {
2497 for (column, summary) in piece {
2498 frequencies[column] = summary;
2499 }
2500 }
2501 Ok(frequencies)
2502 }
2503
2504 fn stable_codes_at(&self, column: usize, ordinals: &[u64]) -> Result<Option<Vec<Option<u32>>>> {
2506 if self.dictionaries.get(column).and_then(Option::as_ref).is_none() {
2507 return Ok(None);
2508 }
2509 if ordinals.windows(2).any(|pair| pair[0] >= pair[1]) {
2510 return Err(invalid("frequency ordinals are not sorted and unique"));
2511 }
2512 let mut out = Vec::with_capacity(ordinals.len());
2513 let mut wanted = 0;
2514 let mut stripe_start = 0_u64;
2515 for stripe in &self.table.stripes {
2516 let stripe_end = stripe_start.saturating_add(stripe.rows as u64);
2517 if wanted == ordinals.len() || ordinals[wanted] >= stripe_end {
2518 stripe_start = stripe_end;
2519 continue;
2520 }
2521 let spans = read_index(&self.file, stripe, column)?;
2522 let page = stripe.pages[column];
2523 let mut bytes = vec![0; page.length as usize];
2524 read_at(&self.file, page.offset, &mut bytes)?;
2525 let mut part_start = stripe_start;
2526 for (span, &rows) in spans.iter().zip(&stripe.parts) {
2527 let part_end = part_start.saturating_add(u64::from(rows));
2528 if wanted < ordinals.len() && ordinals[wanted] < part_end {
2529 let part = part_bytes(&bytes, *span)?;
2530 if checksum(part) != span.hash {
2531 return Err(invalid(
2532 "column page checksum differs while building pair frequencies",
2533 ));
2534 }
2535 let upto = ordinals.partition_point(|&ordinal| ordinal < part_end);
2536 let positions = ordinals[wanted..upto]
2537 .iter()
2538 .map(|&ordinal| {
2539 usize::try_from(ordinal.saturating_sub(part_start))
2540 .map_err(|_| invalid("frequency row offset does not fit in memory"))
2541 })
2542 .collect::<Result<Vec<_>>>()?;
2543 if !decode_selected_stable_codes(rows as usize, part, &positions, &mut out)? {
2544 return Ok(None);
2545 }
2546 wanted = upto;
2547 }
2548 part_start = part_end;
2549 }
2550 stripe_start = stripe_end;
2551 }
2552 if wanted != ordinals.len() {
2553 return Err(invalid("frequency ordinal is outside the table"));
2554 }
2555 Ok(Some(out))
2556 }
2557
2558 fn pair_frequencies(&self) -> Result<Vec<PairFrequencySummary>> {
2560 let anchors = self
2561 .table
2562 .frequencies
2563 .iter()
2564 .enumerate()
2565 .filter_map(|(column, summary)| {
2566 match summary {
2568 Some(Frequencies::Held(summary)) => Some(summary),
2569 _ => None,
2570 }
2571 .filter(|summary| {
2572 !summary.ordinals.is_empty()
2573 && summary.ordinal_entries.len() == summary.ordinals.len()
2574 })
2575 .cloned()
2576 .map(|summary| (column, summary))
2577 })
2578 .collect::<Vec<_>>();
2579 let strings = self
2580 .dictionaries
2581 .iter()
2582 .enumerate()
2583 .filter_map(|(column, dictionary)| dictionary.as_ref().map(|_| column))
2584 .collect::<Vec<_>>();
2585 let mut summaries = Vec::new();
2586 for (first, anchors) in anchors {
2587 for &second in &strings {
2588 if summaries.len() == MAX_PAIR_FREQUENCIES {
2589 return Ok(summaries);
2590 }
2591 let Some(codes) = self.stable_codes_at(second, &anchors.ordinals)? else {
2592 continue;
2593 };
2594 if codes.len() != anchors.ordinal_entries.len() {
2595 return Err(invalid("pair frequency columns have different lengths"));
2596 }
2597 let mut counts = HashMap::<(u16, Option<u32>), u64>::new();
2598 for (&anchor, code) in anchors.ordinal_entries.iter().zip(codes) {
2599 *counts.entry((anchor, code)).or_default() += 1;
2600 }
2601 let mut entries = counts
2602 .into_iter()
2603 .map(|((first_entry, second), count)| PairFrequencyEntry {
2604 first_entry,
2605 second,
2606 count,
2607 })
2608 .collect::<Vec<_>>();
2609 entries.sort_unstable_by(|left, right| {
2610 right
2611 .count
2612 .cmp(&left.count)
2613 .then_with(|| left.first_entry.cmp(&right.first_entry))
2614 .then_with(|| left.second.cmp(&right.second))
2615 });
2616 let pair_omitted = entries.get(FREQUENCY_ENTRIES).map_or(0, |entry| entry.count);
2617 entries.truncate(FREQUENCY_ENTRIES);
2618 summaries.push(PairFrequencySummary {
2619 first: u16::try_from(first)
2620 .map_err(|_| invalid("pair frequency column index overflows"))?,
2621 second: u16::try_from(second)
2622 .map_err(|_| invalid("pair frequency column index overflows"))?,
2623 entries,
2624 omitted_max: anchors.omitted_max.max(pair_omitted),
2625 });
2626 }
2627 }
2628 Ok(summaries)
2629 }
2630
2631 fn close(&mut self) -> Result<Entry> {
2642 self.flush_pending()?;
2643 let profile = self.profile.clone();
2647 let timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2648 let before = self.at;
2649 let mut stripes = std::mem::take(&mut self.order)
2650 .into_iter()
2651 .zip(std::mem::take(&mut self.table.stripes))
2652 .collect::<Vec<_>>();
2653 stripes.sort_by_key(|(order, _)| order.0);
2654 let mut previous: Option<(u64, u64)> = None;
2655 for ((first, last), _) in &stripes {
2656 if previous.is_some_and(|previous| previous >= *first) {
2657 return Err(invalid("chunks did not arrive in source order"));
2658 }
2659 previous = Some(*last);
2660 }
2661 self.table.stripes = stripes.into_iter().map(|(_, stripe)| stripe).collect();
2662 drop(timing);
2666 let (frequencies, distincts): (Vec<Option<FrequencySummary>>, _) =
2667 self.numeric_frequencies()?.into_iter().unzip();
2668 self.table.frequencies =
2669 frequencies.into_iter().map(|held| held.map(Frequencies::Held)).collect();
2670 self.table.distincts = distincts;
2671 let timing = profile.as_deref().map(|profile| profile.span(Stage::Dictionary));
2672 let placing = self.at;
2673 finish_dictionaries(&mut self.dictionaries)?;
2674 self.place_blocks()?;
2675 self.table.pair_frequencies = self.pair_frequencies()?;
2676 let dictionaries = std::mem::take(&mut self.dictionaries);
2677 self.table.dictionary_payloads = vec![0; self.table.fields.len()];
2678 self.table.frequency_texts = vec![Vec::new(); self.table.fields.len()];
2679 self.table.host_groups = None;
2680 for (index, dictionary) in dictionaries.into_iter().enumerate() {
2685 let Some(dictionary) = dictionary else { continue };
2686 let (order, flat, bases) = dictionary.ranked_with_values(Some(&self.file))?;
2687 self.table.distincts[index] =
2691 Some(dictionary.counts.iter().filter(|count| **count != 0).count() as u64);
2692 let (frequencies, texts) = code_frequency(&dictionary, &flat, &bases)?;
2693 self.table.frequencies[index] = Some(Frequencies::Held(frequencies));
2694 self.table.frequency_texts[index] = texts;
2695 if self.table.fields[index].name.eq_ignore_ascii_case("Referer") {
2696 self.table.host_groups = host::build(index, &dictionary, &flat, &bases)?;
2697 }
2698 drop(flat);
2699 drop(bases);
2700 let encoded = encode_global_dictionary(&dictionary, &order, &dictionary.placed, true)?;
2701 drop(order);
2702 let offset = self.at;
2703 self.put(&encoded.index)?;
2704 self.put(&encoded.ranks)?;
2705 self.put(&encoded.grams)?;
2706 self.table.dictionary_payloads[index] = dictionary
2707 .placed
2708 .iter()
2709 .try_fold(0_u64, |sum, place| sum.checked_add(place.length))
2710 .ok_or_else(|| invalid("global dictionary payload overflow"))?;
2711 let length = encoded
2712 .index
2713 .len()
2714 .checked_add(encoded.ranks.len())
2715 .and_then(|len| len.checked_add(encoded.grams.len()))
2716 .ok_or_else(|| invalid("dictionary page length overflow"))?;
2717 self.table.dictionaries[index] = Some(Page {
2718 offset,
2719 length: u32::try_from(length)
2720 .map_err(|_| invalid("dictionary page length overflow"))?,
2721 hash: checksum(&encoded.index),
2722 });
2723 }
2724 drop(timing);
2725 let timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2726 let placed = self.at - placing;
2727 self.write_stats()?;
2728 let directory = encode_directory(&self.table)?;
2729 if directory.len() > MAX_DIRECTORY {
2730 return Err(invalid("directory exceeds the configured bound"));
2731 }
2732 let offset = self.at;
2733 self.put(&directory)?;
2734 drop(timing);
2735 if let Some(profile) = &profile {
2736 profile.moved(Stage::Dictionary, 0, placed, 0);
2737 profile.moved(Stage::Publish, 0, self.at - before - placed, 0);
2738 }
2739 Ok(Entry {
2740 name: self.table.name.clone(),
2741 fields: self.table.fields.clone(),
2742 rows: self.table.rows,
2743 directory: Page {
2744 offset,
2745 length: u32::try_from(directory.len())
2746 .map_err(|_| invalid("directory length overflow"))?,
2747 hash: checksum(&directory),
2748 },
2749 })
2750 }
2751
2752 fn write_stats(&mut self) -> Result<()> {
2764 let gathers = std::mem::take(&mut self.gathers);
2765 let rows = self.table.rows as u64;
2766 let mut payloads = Vec::new();
2767 for (column, gather) in gathers.into_iter().enumerate() {
2768 let Some(gather) = gather else { continue };
2769 if gather.rows() != rows {
2775 continue;
2776 }
2777 let Some(stats) = gather.finish() else { continue };
2778 let mut summary = Vec::new();
2779 stats.summary.encode(&mut summary)?;
2780 let mut sketches = Vec::new();
2781 stats.sketches.encode(&mut sketches)?;
2782 payloads.push((column, summary, sketches));
2783 }
2784 if payloads.is_empty() {
2785 return Ok(());
2786 }
2787 let costs = payloads
2788 .iter()
2789 .map(|(_, summary, sketches)| summary.len() + sketches.len())
2790 .collect::<Vec<_>>();
2791 let allowance = stats::allowance(stats::column_bytes(&self.table), stats::BUDGET_SHARE);
2792 let keep = stats::within(&costs, allowance, 0);
2795 for ((column, summary, sketches), _) in
2796 payloads.iter().zip(&keep).filter(|&(_, &keep)| keep)
2797 {
2798 let id = u64::try_from(*column).map_err(|_| invalid("column index overflow"))?;
2799 for (kind, bytes, header_bytes) in [
2800 (*section::SUMMARY, summary, summary.len() as u32),
2803 (*section::SKETCHES, sketches, rudb_stats::sketches::HEADER_BYTES),
2804 ] {
2805 let written = write_section(
2806 &self.file,
2807 &mut self.at,
2808 §ion::Attachment { kind, id, flags: 0, header_bytes, bytes },
2809 self.generation,
2810 )?;
2811 self.table.sections.push(written);
2812 }
2813 }
2814 if self.table.sections.len() > MAX_SECTIONS {
2815 return Err(invalid("the table would name more sections than the bound allows"));
2816 }
2817 Ok(())
2818 }
2819
2820 pub fn finish(mut self) -> Result<Table> {
2830 let entry = self.close()?;
2831 let profile = self.profile.take();
2832 let _timing = profile.as_deref().map(|profile| profile.span(Stage::Publish));
2833 let mut tables = std::mem::take(&mut self.closed);
2834 tables.push(entry);
2835 let catalog = encode_catalog(&tables, &self.views)?;
2836 if catalog.len() > MAX_DIRECTORY {
2837 return Err(invalid("catalog exceeds the configured bound"));
2838 }
2839 let offset = self.at;
2840 self.put(&catalog)?;
2841 if let Some(profile) = &profile {
2842 profile.moved(Stage::Publish, 0, catalog.len() as u64, 0);
2843 }
2844 synced(&self.file, profile.as_deref())?;
2848 let slot = Slot {
2849 offset,
2850 length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
2851 generation: self.generation,
2852 hash: checksum(&catalog),
2853 };
2854 write_at(&self.file, slot_offset(self.generation), &slot.bytes())?;
2859 synced(&self.file, profile.as_deref())?;
2860 Ok(self.table)
2861 }
2862
2863 pub fn restate(path: impl AsRef<Path>, views: &[ViewEntry]) -> Result<()> {
2880 let path = path.as_ref();
2881 let (_, size, slot, bytes, _) = slot_bytes(path)?;
2882 let (closed, _) = decode_catalog(&bytes, size)?;
2883 let generation = slot
2884 .generation
2885 .checked_add(1)
2886 .ok_or_else(|| invalid("native file generation overflow"))?;
2887 let catalog = encode_catalog(&closed, views)?;
2888 if catalog.len() > MAX_DIRECTORY {
2889 return Err(invalid("catalog exceeds the configured bound"));
2890 }
2891 let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
2892 write_at(&file, size, &catalog)?;
2893 file.sync_all().map_err(io)?;
2894 let slot = Slot {
2895 offset: size,
2896 length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
2897 generation,
2898 hash: checksum(&catalog),
2899 };
2900 write_at(&file, slot_offset(generation), &slot.bytes())?;
2901 file.sync_all().map_err(io)?;
2902 Ok(())
2903 }
2904}
2905
2906fn append(file: &File, at: &mut u64, bytes: &[u8]) -> Result<u64> {
2912 let offset = *at;
2913 write_at(file, offset, bytes)?;
2914 *at =
2915 at.checked_add(bytes.len() as u64).ok_or_else(|| invalid("native file length overflow"))?;
2916 Ok(offset)
2917}
2918
2919fn write_section(
2925 file: &File,
2926 at: &mut u64,
2927 one: §ion::Attachment<'_>,
2928 generation: u64,
2929) -> Result<Section> {
2930 if !one.bytes.is_empty() && one.header_bytes as usize > one.bytes.len() {
2934 return Err(invalid("a section's header is longer than its payload"));
2935 }
2936 let mut extents = Vec::new();
2937 let mut first = 0_u64;
2938 for chunk in one.bytes.chunks(section::MAX_EXTENT as usize) {
2939 let offset = append(file, at, chunk)?;
2940 extents.push(section::Extent {
2941 offset,
2942 length: u32::try_from(chunk.len()).map_err(|_| invalid("extent length overflow"))?,
2943 hash: checksum(chunk),
2944 first,
2945 });
2946 first += chunk.len() as u64;
2947 }
2948 let mut table = Vec::with_capacity(extents.len() * section::EXTENT_BYTES);
2949 section::encode_extents(&extents, &mut table)?;
2950 let extent_page = if table.is_empty() { 0 } else { append(file, at, &table)? };
2954 Ok(Section {
2955 kind: one.kind,
2956 id: one.id,
2957 generation,
2958 extents: u32::try_from(extents.len()).map_err(|_| invalid("too many extents"))?,
2959 extent_page,
2960 extent_bytes: u32::try_from(table.len()).map_err(|_| invalid("extent table overflow"))?,
2961 hash: checksum(&table),
2962 flags: one.flags,
2963 header_bytes: one.header_bytes,
2964 })
2965}
2966
2967pub fn attach(
2991 path: impl AsRef<Path>,
2992 table: &str,
2993 attachments: &[section::Attachment<'_>],
2994) -> Result<Table> {
2995 let path = path.as_ref();
2996 let (_, size, slot, bytes, _) = slot_bytes(path)?;
2997 let (mut entries, views) = decode_catalog(&bytes, size)?;
2998 let at = entries
2999 .iter()
3000 .position(|entry| entry.name == table)
3001 .ok_or_else(|| invalid(&format!("the file holds no table called {table}")))?;
3002 let file = OpenOptions::new().write(true).read(true).open(path).map_err(io)?;
3003 let mut version = [0; 4];
3004 read_at(&file, 8, &mut version)?;
3005 let version = u32::from_le_bytes(version);
3006 if version != FORMAT {
3012 return Err(invalid(&format!(
3013 "the file is format {version} and a graph section needs format {FORMAT}, so it has \
3014 to be written again"
3015 )));
3016 }
3017 let mut directory = vec![0; entries[at].directory.length as usize];
3018 read_at(&file, entries[at].directory.offset, &mut directory)?;
3019 if checksum(&directory) != entries[at].directory.hash {
3020 return Err(invalid(&format!("the directory of table {table} does not checksum")));
3021 }
3022 let mut held = decode_directory(&directory, size)?;
3023 let mut cursor = size;
3024 for one in attachments {
3025 let written = write_section(&file, &mut cursor, one, held.generation)?;
3026 held.sections.retain(|old| !(old.kind == one.kind && old.id == one.id));
3027 held.sections.push(written);
3028 }
3029 if held.sections.len() > MAX_SECTIONS {
3030 return Err(invalid("the table would name more sections than the bound allows"));
3031 }
3032 let encoded = encode_directory(&held)?;
3033 if encoded.len() > MAX_DIRECTORY {
3034 return Err(invalid("directory exceeds the configured bound"));
3035 }
3036 let offset = append(&file, &mut cursor, &encoded)?;
3037 entries[at].directory = Page {
3038 offset,
3039 length: u32::try_from(encoded.len()).map_err(|_| invalid("directory length overflow"))?,
3040 hash: checksum(&encoded),
3041 };
3042 let catalog = encode_catalog(&entries, &views)?;
3045 if catalog.len() > MAX_DIRECTORY {
3046 return Err(invalid("catalog exceeds the configured bound"));
3047 }
3048 let offset = append(&file, &mut cursor, &catalog)?;
3049 file.sync_all().map_err(io)?;
3050 let generation =
3051 slot.generation.checked_add(1).ok_or_else(|| invalid("native file generation overflow"))?;
3052 let committed = Slot {
3053 offset,
3054 length: u32::try_from(catalog.len()).map_err(|_| invalid("catalog length overflow"))?,
3055 generation,
3056 hash: checksum(&catalog),
3057 };
3058 write_at(&file, slot_offset(generation), &committed.bytes())?;
3059 file.sync_all().map_err(io)?;
3060 Ok(held)
3061}
3062
3063type Synopsis = Arc<Vec<(Value, u64)>>;
3066
3067#[derive(Debug, Clone)]
3069pub struct Reader {
3070 file: Arc<File>,
3071 table: Arc<Table>,
3072 dictionaries: Arc<Vec<OnceLock<Arc<Vector>>>>,
3073 loading: Arc<Vec<Mutex<()>>>,
3082 frequency_values: Arc<Vec<OnceLock<Synopsis>>>,
3085 frequency_summaries: Arc<Vec<OnceLock<Arc<FrequencySummary>>>>,
3089 opened: Arc<AtomicUsize>,
3093 sieves: Arc<Vec<Vec<SieveSlot>>>,
3097 part_ranges: Arc<Vec<Vec<RangeSlot>>>,
3100 places: Arc<Vec<Place>>,
3102 cache: Arc<Vec<Mutex<Cached>>>,
3103 pages: Arc<AtomicUsize>,
3106 indexes: Arc<AtomicUsize>,
3109 kept: Arc<AtomicUsize>,
3112 size: u64,
3114 directory: u64,
3116 opening: Opening,
3118}
3119
3120#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3132pub struct Opening {
3133 pub reads: u32,
3136 pub bytes: u64,
3138}
3139
3140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3142pub struct Reads {
3143 pub opening: Opening,
3145 pub pages: usize,
3147 pub indexes: usize,
3149 pub dictionaries: usize,
3152}
3153
3154#[derive(Debug, Clone, Copy)]
3156struct Place {
3157 stripe: u32,
3158 part: u32,
3159 rows: u32,
3160}
3161
3162#[derive(Debug, Clone, Copy)]
3164struct PartSpan {
3165 start: usize,
3166 length: usize,
3167 hash: u64,
3168}
3169
3170#[derive(Debug, Clone)]
3176struct CachedColumn {
3177 stripe: usize,
3178 index: Arc<Vec<PartSpan>>,
3179 page: Option<Arc<Vec<u8>>>,
3180}
3181
3182#[derive(Debug, Default)]
3202struct Cached {
3203 pages: Vec<Option<Arc<Vec<u8>>>>,
3204 order: VecDeque<usize>,
3205 loading: Vec<usize>,
3206 index: Vec<Option<Arc<Vec<PartSpan>>>>,
3207}
3208
3209const CACHED_STRIPES_PER_COLUMN: usize = 4;
3221
3222type SieveSlot = OnceLock<Arc<Vec<Option<Sieve>>>>;
3224
3225type RangeSlot = OnceLock<Arc<Vec<Range>>>;
3226
3227#[derive(Debug)]
3228struct NativeText {
3229 file: Arc<File>,
3230 values: usize,
3232 offsets: Vec<u8>,
3241 offset_bits: usize,
3244 value_ends: OnceLock<Option<Vec<u32>>>,
3257 value_lens: OnceLock<Option<Vec<u32>>>,
3267 ends_asked: AtomicUsize,
3273 ranks: usize,
3275 rank_at: u64,
3279 rank_ends: Vec<u64>,
3283 rank_hashes: Vec<u64>,
3284 rank_blocks: Vec<OnceLock<Result<Vec<u8>>>>,
3285 code_bits: usize,
3288 code_ranks: OnceLock<Option<Vec<u32>>>,
3295 starts: Vec<u64>,
3302 lengths: Vec<u64>,
3303 hashes: Vec<u64>,
3304 grams: Option<NativeGrams>,
3306 blocks: Vec<OnceLock<Result<Vec<u8>>>>,
3308 keep_budget: usize,
3311 payload_kept: AtomicUsize,
3319 swept: Vec<AtomicBool>,
3327 searched: Mutex<HashMap<Vec<u8>, (usize, bool)>>,
3344}
3345
3346#[derive(Debug)]
3347struct NativeGrams {
3348 start: u64,
3349 length: usize,
3350 hash: u64,
3351 loaded: OnceLock<Result<Vec<u8>>>,
3352}
3353
3354const TEXT_SEARCH_MEMO: usize = 64;
3359
3360const TEXT_PAYLOAD_VALUES: usize = 1024;
3376
3377const TEXT_GRAM_BYTES: usize = 2048;
3380
3381fn gram_bits(bytes: &[u8]) -> [usize; 2] {
3383 let original = u32::from_le_bytes(bytes.try_into().expect("a four-byte gram"));
3384 let mut first = original ^ (original >> 16);
3385 first = first.wrapping_mul(0x7feb_352d);
3386 first ^= first >> 15;
3387 let mut second = original ^ (original >> 17);
3388 second = second.wrapping_mul(0x846c_a68b);
3389 second ^= second >> 16;
3390 let mask = TEXT_GRAM_BYTES * 8 - 1;
3391 [(first as usize) & mask, (second as usize) & mask]
3392}
3393
3394const TEXT_KEEP_BUDGET: usize = 256 * 1024 * 1024;
3415
3416fn lengths_of(ends: &[u32]) -> Option<Vec<u32>> {
3422 let mut lens = Vec::with_capacity(ends.len());
3423 for block in ends.chunks(TEXT_PAYLOAD_VALUES) {
3424 let mut start = 0;
3425 for &end in block {
3426 lens.push(end.checked_sub(start)?);
3427 start = end;
3428 }
3429 }
3430 Some(lens)
3431}
3432
3433const TEXT_OFFSET_RUN: usize = 512;
3440
3441const DICTIONARY_HEADER: usize = 16;
3444
3445const DICTIONARY_SCATTERED: u32 = 1 << 31;
3459const DICTIONARY_GRAMS: u32 = 1 << 30;
3461
3462const TEXT_RANK_BLOCK: usize = 512;
3473
3474const RANK_BLOCK_HEADER: usize = size_of::<u64>() + 1;
3488
3489impl NativeText {
3490 fn payload_block(&self, block: usize) -> Result<Option<&[u8]>> {
3497 let Some(slot) = self.blocks.get(block) else { return Ok(None) };
3498 let bytes = slot.get_or_init(|| self.decode_block(block)).as_ref().map_err(Clone::clone)?;
3499 Ok(Some(bytes.as_slice()))
3500 }
3501
3502 fn decode_block(&self, block: usize) -> Result<Vec<u8>> {
3507 let len = self.lengths[block];
3508 let mut stored = vec![
3509 0;
3510 usize::try_from(len).map_err(|_| invalid(
3511 "global dictionary block does not fit in memory"
3512 ))?
3513 ];
3514 read_at(&self.file, self.starts[block], &mut stored)?;
3515 if checksum(&stored) != self.hashes[block] {
3516 return Err(invalid("global dictionary payload checksum differs"));
3517 }
3518 let first = block * TEXT_PAYLOAD_VALUES;
3519 let last = (first + TEXT_PAYLOAD_VALUES).min(self.values);
3520 let want = self.end_within(last - 1)? as usize;
3521 let values = string::decode_flat(&stored)?;
3522 if values.len() != last - first {
3523 return Err(invalid("global dictionary block holds the wrong value count"));
3524 }
3525 let bytes = values.into_bytes();
3526 if bytes.len() != want {
3527 return Err(invalid("global dictionary block decodes to the wrong length"));
3528 }
3529 Ok(bytes)
3530 }
3531
3532 fn ends_worth_unpacking(&self) -> usize {
3549 self.values.max(TEXT_PAYLOAD_VALUES)
3550 }
3551
3552 fn value_ends(&self) -> Option<&[u32]> {
3554 if let Some(built) = self.value_ends.get() {
3555 return built.as_deref();
3556 }
3557 if self.ends_asked.fetch_add(1, Atomic::Relaxed) < self.ends_worth_unpacking() {
3558 return None;
3559 }
3560 self.value_ends.get_or_init(|| self.unpack_ends()).as_deref()
3561 }
3562
3563 fn unpack_ends(&self) -> Option<Vec<u32>> {
3569 let mut ends = vec![0u32; self.values];
3570 for (run, into) in ends.chunks_mut(TEXT_OFFSET_RUN).enumerate() {
3571 let bytes = self.offsets.get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)?;
3572 bitpack::unpack_tail_into(bytes, self.offset_bits, into, |bits| {
3573 u32::try_from(bits).unwrap_or(u32::MAX)
3574 })
3575 .ok()?;
3576 }
3577 if ends.contains(&u32::MAX) { None } else { Some(ends) }
3580 }
3581
3582 fn end_within(&self, index: usize) -> Result<u32> {
3584 if let Some(ends) = self.value_ends() {
3585 return ends
3586 .get(index)
3587 .copied()
3588 .ok_or_else(|| invalid("global dictionary offsets are short"));
3589 }
3590 let run = index / TEXT_OFFSET_RUN;
3591 let bytes = self
3592 .offsets
3593 .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3594 .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3595 let end = bitpack::tail_at(bytes, self.offset_bits, index % TEXT_OFFSET_RUN)
3596 .map_err(|_| invalid("global dictionary offsets are short"))?;
3597 u32::try_from(end).map_err(|_| invalid("global dictionary offset is past the payload"))
3598 }
3599
3600 fn ends_within(&self, first: usize, last: usize) -> Result<Vec<u64>> {
3618 let mut ends = vec![0u64; last.saturating_sub(first)];
3619 let mut scratch = Vec::new();
3620 let mut at = first;
3621 while at < last {
3622 let run = at / TEXT_OFFSET_RUN;
3623 let stop = ((run + 1) * TEXT_OFFSET_RUN).min(last);
3624 let held = self.values.saturating_sub(run * TEXT_OFFSET_RUN).min(TEXT_OFFSET_RUN);
3625 let bytes = self
3626 .offsets
3627 .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3628 .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3629 let from = at % TEXT_OFFSET_RUN;
3630 let upto = stop - run * TEXT_OFFSET_RUN;
3631 if upto > held || bytes.len() < bitpack::tail_len(held, self.offset_bits) {
3632 return Err(invalid("global dictionary offsets are short"));
3633 }
3634 let into = &mut ends[at - first..stop - first];
3635 if from == 0 {
3636 bitpack::unpack_tail_into(bytes, self.offset_bits, into, |bits| bits)
3637 .map_err(|_| invalid("global dictionary offsets are short"))?;
3638 } else {
3639 scratch.resize(held, 0);
3640 bitpack::unpack_tail_into(bytes, self.offset_bits, &mut scratch, |bits| bits)
3641 .map_err(|_| invalid("global dictionary offsets are short"))?;
3642 into.copy_from_slice(&scratch[from..upto]);
3643 }
3644 at = stop;
3645 }
3646 Ok(ends)
3647 }
3648
3649 fn start_within(&self, index: usize) -> Result<u32> {
3652 if index % TEXT_PAYLOAD_VALUES == 0 { Ok(0) } else { self.end_within(index - 1) }
3653 }
3654
3655 fn span_within(&self, index: usize) -> Result<(u32, u32)> {
3663 if let Some(ends) = self.value_ends() {
3664 let end =
3665 *ends.get(index).ok_or_else(|| invalid("global dictionary offsets are short"))?;
3666 let start = if index % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[index - 1] };
3669 if start > end {
3670 return Err(invalid("global dictionary value ends before it starts"));
3671 }
3672 return Ok((start, end));
3673 }
3674 let within = index % TEXT_OFFSET_RUN;
3675 let (start, end) = if within == 0 {
3676 (self.start_within(index)?, self.end_within(index)?)
3677 } else {
3678 let run = index / TEXT_OFFSET_RUN;
3679 let bytes = self
3680 .offsets
3681 .get(run * TEXT_OFFSET_RUN / 8 * self.offset_bits..)
3682 .ok_or_else(|| invalid("global dictionary offsets are short"))?;
3683 let (start, end) = bitpack::tail_pair(bytes, self.offset_bits, within)
3684 .map_err(|_| invalid("global dictionary offsets are short"))?;
3685 let ends = u32::try_from(end)
3686 .map_err(|_| invalid("global dictionary offset is past the payload"))?;
3687 let starts = u32::try_from(start)
3688 .map_err(|_| invalid("global dictionary offset is past the payload"))?;
3689 (starts, ends)
3690 };
3691 if start > end {
3692 return Err(invalid("global dictionary value ends before it starts"));
3693 }
3694 Ok((start, end))
3695 }
3696
3697 fn rank_parts(&self, rank: usize) -> Result<(&[u8], usize)> {
3704 let slot = self
3705 .rank_blocks
3706 .get(rank / TEXT_RANK_BLOCK)
3707 .ok_or_else(|| invalid("global dictionary rank is past the order"))?;
3708 let block = slot
3709 .get_or_init(|| {
3710 let which = rank / TEXT_RANK_BLOCK;
3711 let start = if which == 0 { 0 } else { self.rank_ends[which - 1] };
3712 let end = self.rank_ends[which];
3713 let mut bytes = vec![0; (end - start) as usize];
3714 read_at(&self.file, self.rank_at + start, &mut bytes)?;
3715 if checksum(&bytes)
3716 != *self
3717 .rank_hashes
3718 .get(rank / TEXT_RANK_BLOCK)
3719 .ok_or_else(|| invalid("global dictionary rank block has no checksum"))?
3720 {
3721 return Err(invalid("global dictionary rank checksum differs"));
3722 }
3723 Ok(bytes)
3724 })
3725 .as_ref()
3726 .map_err(Clone::clone)?;
3727 Ok((block.as_slice(), rank % TEXT_RANK_BLOCK))
3728 }
3729
3730 fn head_at(&self, rank: usize) -> Result<u64> {
3732 let (block, within) = self.rank_parts(rank)?;
3733 let (base, width, packed) = rank_heads(block)?;
3734 let above = bitpack::tail_at(packed, width, within)
3735 .map_err(|_| invalid("global dictionary rank block is short of heads"))?;
3736 Ok(base.wrapping_add(above))
3737 }
3738
3739 fn rank_codes<'block>(&self, block: &'block [u8], count: usize) -> Result<&'block [u8]> {
3741 let (_, width, packed) = rank_heads(block)?;
3742 packed
3743 .get(bitpack::tail_len(count, width)..)
3744 .ok_or_else(|| invalid("global dictionary rank block is short of codes"))
3745 }
3746
3747 fn rank_block_len(&self, rank: usize) -> usize {
3749 let first = rank / TEXT_RANK_BLOCK * TEXT_RANK_BLOCK;
3750 TEXT_RANK_BLOCK.min(self.ranks - first)
3751 }
3752}
3753
3754fn rank_heads(block: &[u8]) -> Result<(u64, usize, &[u8])> {
3756 let header = block
3757 .get(..RANK_BLOCK_HEADER)
3758 .ok_or_else(|| invalid("global dictionary rank block is short"))?;
3759 let base = u64::from_le_bytes(header[..8].try_into().expect("eight bytes"));
3760 let width = header[8] as usize;
3761 if width > 64 {
3762 return Err(invalid("global dictionary rank block packs heads past a word"));
3763 }
3764 Ok((base, width, &block[RANK_BLOCK_HEADER..]))
3765}
3766
3767fn offset_width(ends: &[u32]) -> usize {
3774 let span = ends.iter().copied().max().unwrap_or(0);
3778 (u32::BITS - span.leading_zeros()) as usize
3779}
3780
3781fn offset_bytes(values: usize, bits: usize) -> usize {
3784 let full = values / TEXT_OFFSET_RUN;
3785 let rest = values % TEXT_OFFSET_RUN;
3786 full * TEXT_OFFSET_RUN / 8 * bits + bitpack::tail_len(rest, bits)
3787}
3788
3789fn encode_offsets(ends: &[u32], bits: usize, out: &mut Vec<u8>) -> Result<()> {
3793 let mut run = Vec::with_capacity(TEXT_OFFSET_RUN);
3794 for chunk in ends.chunks(TEXT_OFFSET_RUN) {
3795 run.clear();
3796 run.extend(chunk.iter().map(|&end| u64::from(end)));
3797 bitpack::pack_tail(&run, bits, out)
3798 .map_err(|_| invalid("global dictionary offsets do not pack"))?;
3799 }
3800 Ok(())
3801}
3802
3803fn code_width(values: usize) -> usize {
3805 match u64::try_from(values).unwrap_or(u64::MAX) {
3806 0 | 1 => 0,
3807 last => (u64::BITS - (last - 1).leading_zeros()) as usize,
3808 }
3809}
3810
3811impl TextSource for NativeText {
3812 fn len(&self) -> usize {
3813 self.values
3814 }
3815
3816 fn might_contain(&self, first: usize, literal: &[u8]) -> Result<bool> {
3817 let Some(grams) = &self.grams else { return Ok(true) };
3818 if literal.len() < 4 || first >= self.values {
3819 return Ok(true);
3820 }
3821 let bytes = grams
3822 .loaded
3823 .get_or_init(|| {
3824 let mut bytes = vec![0; grams.length];
3825 read_at(&self.file, grams.start, &mut bytes)?;
3826 if checksum(&bytes) != grams.hash {
3827 return Err(invalid("global dictionary substring signatures checksum differs"));
3828 }
3829 Ok(bytes)
3830 })
3831 .as_ref()
3832 .map_err(Clone::clone)?;
3833 let block = first / TEXT_PAYLOAD_VALUES;
3834 let Some(bits) = bytes.get(block * TEXT_GRAM_BYTES..(block + 1) * TEXT_GRAM_BYTES) else {
3835 return Ok(true);
3836 };
3837 Ok(literal.windows(4).all(|gram| {
3838 gram_bits(gram).into_iter().all(|bit| bits[bit / 8] & (1 << (bit % 8)) != 0)
3839 }))
3840 }
3841
3842 fn bytes_at(&self, index: usize) -> Result<Option<&[u8]>> {
3843 if index >= self.values {
3844 return Ok(None);
3845 }
3846 let (start, end) = self.span_within(index)?;
3847 if start == end {
3848 return Ok(Some(&[]));
3849 }
3850 let block = index / TEXT_PAYLOAD_VALUES;
3853 let Some(bytes) = self.payload_block(block)? else { return Ok(None) };
3854 Ok(bytes.get(start as usize..end as usize))
3855 }
3856
3857 fn bytes_len_at(&self, index: usize) -> Result<Option<usize>> {
3858 if index >= self.values {
3859 return Ok(None);
3860 }
3861 let (start, end) = self.span_within(index)?;
3862 Ok(Some((end - start) as usize))
3863 }
3864
3865 fn bytes_lens_at(&self, indices: &[u32], into: &mut [i64]) -> Result<()> {
3872 self.ends_asked.fetch_add(indices.len(), Atomic::Relaxed);
3873 let Some(ends) = self.value_ends() else {
3874 for (slot, &index) in into.iter_mut().zip(indices) {
3875 *slot = self
3876 .bytes_len_at(index as usize)?
3877 .map_or(0, |len| i64::try_from(len).unwrap_or(i64::MAX));
3878 }
3879 return Ok(());
3880 };
3881 if let Some(lens) = self.value_lens.get_or_init(|| lengths_of(ends)) {
3882 for (slot, &index) in into.iter_mut().zip(indices) {
3883 *slot = lens.get(index as usize).map_or(0, |&len| i64::from(len));
3886 }
3887 return Ok(());
3888 }
3889 for (slot, &index) in into.iter_mut().zip(indices) {
3890 let index = index as usize;
3891 let Some(&end) = ends.get(index) else {
3893 *slot = 0;
3894 continue;
3895 };
3896 let start = if index % TEXT_PAYLOAD_VALUES == 0 { 0 } else { ends[index - 1] };
3897 if start > end {
3898 return Err(invalid("global dictionary value ends before it starts"));
3899 }
3900 *slot = i64::from(end - start);
3901 }
3902 Ok(())
3903 }
3904
3905 fn sweep(
3918 &self,
3919 first: usize,
3920 limit: usize,
3921 body: &mut dyn FnMut(usize, &[u8]) -> Result<()>,
3922 ) -> Result<usize> {
3923 let limit = limit.min(self.values);
3924 if first >= limit {
3925 return Ok(first);
3926 }
3927 let block = first / TEXT_PAYLOAD_VALUES;
3928 let last = ((block + 1) * TEXT_PAYLOAD_VALUES).min(limit);
3929 let decoded;
3930 let kept = self.blocks.get(block).and_then(OnceLock::get);
3931 let again = kept.is_none()
3932 && self.swept.get(block).is_some_and(|swept| swept.swap(true, Atomic::Relaxed));
3933 let bytes: &[u8] = match kept {
3934 Some(Ok(kept)) => kept,
3935 _ if again && self.payload_kept.load(Atomic::Relaxed) < self.keep_budget => {
3936 let kept = self
3937 .payload_block(block)?
3938 .ok_or_else(|| invalid("global dictionary block is past the payload"))?;
3939 self.payload_kept.fetch_add(kept.len(), Atomic::Relaxed);
3940 kept
3941 }
3942 _ => {
3943 decoded = self.decode_block(block)?;
3944 &decoded
3945 }
3946 };
3947 let ends = self.ends_within(first, last)?;
3948 if ends.len() != last - first {
3949 return Err(invalid("global dictionary offsets are short"));
3950 }
3951 let mut start = u64::from(self.start_within(first)?);
3952 for (index, &end) in (first..last).zip(&ends) {
3955 let value = usize::try_from(start)
3956 .ok()
3957 .zip(usize::try_from(end).ok())
3958 .and_then(|(from, to)| bytes.get(from..to))
3959 .ok_or_else(|| invalid("global dictionary value is past its block"))?;
3960 body(index, value)?;
3961 start = end;
3962 }
3963 Ok(last)
3964 }
3965
3966 fn visit(
3972 &self,
3973 indices: &[usize],
3974 body: &mut dyn FnMut(usize, &[u8]) -> Result<()>,
3975 ) -> Result<()> {
3976 let mut at = 0;
3977 while at < indices.len() {
3978 let block = indices[at] / TEXT_PAYLOAD_VALUES;
3979 let upto =
3980 at + indices[at..].partition_point(|&index| index / TEXT_PAYLOAD_VALUES == block);
3981 let wanted = &indices[at..upto];
3982 if wanted.iter().any(|&index| index >= self.values) {
3983 return Err(invalid("a visited value is past the global dictionary"));
3984 }
3985 let decoded;
3986 let bytes: &[u8] = match self.blocks.get(block).and_then(OnceLock::get) {
3987 Some(Ok(kept)) => kept,
3988 _ => {
3989 decoded = self.decode_block(block)?;
3990 &decoded
3991 }
3992 };
3993 for (offset, &index) in wanted.iter().enumerate() {
3994 let (start, end) = self.span_within(index)?;
3995 let value = bytes
3996 .get(start as usize..end as usize)
3997 .ok_or_else(|| invalid("global dictionary value is past its block"))?;
3998 body(at + offset, value)?;
3999 }
4000 at = upto;
4001 }
4002 Ok(())
4003 }
4004
4005 fn ranks(&self) -> Option<usize> {
4006 (self.ranks > 0).then_some(self.ranks)
4007 }
4008
4009 fn below(&self, ranks: usize, wanted: &[u8]) -> Result<(usize, bool)> {
4017 let mut memo = self.searched.lock().map_err(|_| invalid("a poisoned dictionary search"))?;
4018 if let Some(&answer) = memo.get(wanted) {
4019 return Ok(answer);
4020 }
4021 let answer = search_below(self, ranks, wanted)?;
4022 if memo.len() >= TEXT_SEARCH_MEMO {
4023 memo.clear();
4024 }
4025 memo.insert(wanted.to_vec(), answer);
4026 Ok(answer)
4027 }
4028
4029 fn compare_rank(&self, rank: usize, wanted: &[u8]) -> Result<Ordering> {
4030 let settled = self.head_at(rank)?.cmp(&head(wanted));
4034 if settled != Ordering::Equal {
4035 return Ok(settled);
4036 }
4037 let code = self.code_at_rank(rank)?;
4038 let bytes = self
4039 .bytes_at(code as usize)?
4040 .ok_or_else(|| invalid("global dictionary order names a code it does not have"))?;
4041 Ok(bytes.cmp(wanted))
4042 }
4043
4044 fn code_at_rank(&self, rank: usize) -> Result<u32> {
4045 let (block, within) = self.rank_parts(rank)?;
4046 let codes = self.rank_codes(block, self.rank_block_len(rank))?;
4047 let code = bitpack::tail_at(codes, self.code_bits, within)
4048 .map_err(|_| invalid("global dictionary rank block is short of codes"))?;
4049 let code = u32::try_from(code)
4050 .map_err(|_| invalid("global dictionary order names a code it does not have"))?;
4051 if code as usize >= self.len() {
4052 return Err(invalid("global dictionary order names a code it does not have"));
4053 }
4054 Ok(code)
4055 }
4056
4057 fn code_ranks(&self) -> Option<&[u32]> {
4058 if self.ranks == 0 || self.ranks != self.len() {
4062 return None;
4063 }
4064 self.code_ranks
4065 .get_or_init(|| {
4066 let mut ranks = vec![u32::MAX; self.ranks];
4067 for first in (0..self.ranks).step_by(TEXT_RANK_BLOCK) {
4070 let (block, _) = self.rank_parts(first).ok()?;
4071 let count = self.rank_block_len(first);
4072 let codes = self.rank_codes(block, count).ok()?;
4073 for (within, code) in bitpack::unpack_tail(codes, self.code_bits, count)
4074 .ok()?
4075 .into_iter()
4076 .enumerate()
4077 {
4078 let code = usize::try_from(code).ok()?;
4079 *ranks.get_mut(code)? = u32::try_from(first + within).ok()?;
4080 }
4081 }
4082 if ranks.contains(&u32::MAX) {
4083 return None;
4084 }
4085 Some(ranks)
4086 })
4087 .as_deref()
4088 }
4089
4090 fn footprint(&self) -> usize {
4091 self.offsets.capacity()
4092 + self
4093 .value_ends
4094 .get()
4095 .and_then(Option::as_ref)
4096 .map_or(0, |ends| ends.capacity() * size_of::<u32>())
4097 + self
4098 .value_lens
4099 .get()
4100 .and_then(Option::as_ref)
4101 .map_or(0, |lens| lens.capacity() * size_of::<u32>())
4102 + self
4103 .code_ranks
4104 .get()
4105 .and_then(Option::as_ref)
4106 .map_or(0, |ranks| ranks.capacity() * size_of::<u32>())
4107 + self.rank_hashes.capacity() * size_of::<u64>()
4108 + self.rank_ends.capacity() * size_of::<u64>()
4109 + self.rank_blocks.capacity() * size_of::<OnceLock<Result<Vec<u8>>>>()
4110 + self
4111 .rank_blocks
4112 .iter()
4113 .filter_map(OnceLock::get)
4114 .filter_map(|result| result.as_ref().ok())
4115 .map(Vec::capacity)
4116 .sum::<usize>()
4117 + self.blocks.capacity() * size_of::<OnceLock<Result<Vec<u8>>>>()
4118 + self.hashes.capacity() * size_of::<u64>()
4119 + self.starts.capacity() * size_of::<u64>()
4120 + self.lengths.capacity() * size_of::<u64>()
4121 + self
4122 .grams
4123 .as_ref()
4124 .and_then(|grams| grams.loaded.get())
4125 .and_then(|result| result.as_ref().ok())
4126 .map_or(0, Vec::capacity)
4127 + self
4128 .blocks
4129 .iter()
4130 .filter_map(OnceLock::get)
4131 .filter_map(|result| result.as_ref().ok())
4132 .map(Vec::capacity)
4133 .sum::<usize>()
4134 }
4135}
4136
4137fn places(table: &Table) -> Result<Vec<Place>> {
4139 let mut places = Vec::with_capacity(table.stripes.len().saturating_mul(STRIPE_PARTS));
4140 for (at, stripe) in table.stripes.iter().enumerate() {
4141 let index = u32::try_from(at).map_err(|_| invalid("too many stripes"))?;
4142 for (part, &rows) in stripe.parts.iter().enumerate() {
4143 places.push(Place {
4144 stripe: index,
4145 part: u32::try_from(part).map_err(|_| invalid("too many parts in a stripe"))?,
4146 rows,
4147 });
4148 }
4149 }
4150 Ok(places)
4151}
4152
4153fn read_index(file: &File, stripe: &Stripe, column: usize) -> Result<Vec<PartSpan>> {
4158 let parts = stripe.parts.len();
4159 let section = index_section(parts)?;
4160 let at = column.checked_mul(section).ok_or_else(|| invalid("index page offset overflow"))?;
4161 let end = at.checked_add(section).ok_or_else(|| invalid("index page offset overflow"))?;
4162 if end > stripe.index.length as usize {
4163 return Err(invalid("index page is shorter than its columns"));
4164 }
4165 let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
4166 let mut bytes = vec![0; section];
4167 let offset = stripe
4168 .index
4169 .offset
4170 .checked_add(at as u64)
4171 .ok_or_else(|| invalid("index page offset overflow"))?;
4172 read_at(file, offset, &mut bytes)?;
4173 let entries = section - size_of::<u64>();
4174 let stored = u64::from_le_bytes(bytes[entries..].try_into().expect("eight bytes"));
4175 if checksum(&bytes[..entries]) != stored {
4176 return Err(invalid(&format!(
4179 "index page section checksum differs, column {column} of {parts} parts at {offset}, \
4180 wanted {stored:016x} and got {:016x}",
4181 checksum(&bytes[..entries]),
4182 )));
4183 }
4184 let mut spans = Vec::with_capacity(parts);
4185 let mut start = 0_usize;
4186 for part in 0..parts {
4187 let at = part * INDEX_ENTRY;
4188 let length = u32::from_le_bytes(bytes[at..at + 4].try_into().expect("four bytes")) as usize;
4189 let hash = u64::from_le_bytes(bytes[at + 4..at + 12].try_into().expect("eight bytes"));
4190 spans.push(PartSpan { start, length, hash });
4191 start = start.checked_add(length).ok_or_else(|| invalid("column page length overflow"))?;
4192 }
4193 if start != page.length as usize {
4194 return Err(invalid("column page length differs from its index"));
4195 }
4196 Ok(spans)
4197}
4198
4199fn part_bytes(page: &[u8], span: PartSpan) -> Result<&[u8]> {
4201 let end = span.start.checked_add(span.length).ok_or_else(|| invalid("part range overflow"))?;
4202 page.get(span.start..end).ok_or_else(|| invalid("part exceeds its column page"))
4203}
4204
4205fn remember(cached: &mut Cached, held: &CachedColumn, kept: usize) {
4210 if let Some(slot) = cached.index.get_mut(held.stripe) {
4211 if slot.is_none() {
4212 *slot = Some(Arc::clone(&held.index));
4213 }
4214 }
4215 let Some(page) = held.page.clone() else { return };
4216 let Some(slot) = cached.pages.get_mut(held.stripe) else { return };
4217 if slot.is_none() {
4218 cached.order.push_back(held.stripe);
4219 }
4220 *slot = Some(page);
4221 while cached.order.len() > kept.max(1) {
4222 let Some(oldest) = cached.order.pop_front() else { break };
4223 if let Some(slot) = cached.pages.get_mut(oldest) {
4224 *slot = None;
4225 }
4226 }
4227}
4228
4229#[derive(Debug, Clone)]
4238pub struct Catalog {
4239 file: Arc<File>,
4240 size: u64,
4241 entries: Arc<Vec<Entry>>,
4242 views: Arc<Vec<ViewEntry>>,
4244 opening: Opening,
4245}
4246
4247impl Catalog {
4248 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
4254 let (file, size, _, bytes, opening) = slot_bytes(path)?;
4255 let (entries, views) = decode_catalog(&bytes, size)?;
4256 Ok(Self {
4257 file: Arc::new(file),
4258 size,
4259 entries: Arc::new(entries),
4260 views: Arc::new(views),
4261 opening,
4262 })
4263 }
4264
4265 pub fn names(&self) -> impl ExactSizeIterator<Item = &str> {
4267 self.entries.iter().map(|entry| entry.name.as_str())
4268 }
4269
4270 pub fn rows(&self) -> impl ExactSizeIterator<Item = (&str, usize)> {
4277 self.entries.iter().map(|entry| (entry.name.as_str(), entry.rows))
4278 }
4279
4280 pub fn views(&self) -> impl ExactSizeIterator<Item = &ViewEntry> {
4286 self.views.iter()
4287 }
4288
4289 #[must_use]
4291 pub fn len(&self) -> usize {
4292 self.entries.len()
4293 }
4294
4295 #[must_use]
4298 pub fn is_empty(&self) -> bool {
4299 self.entries.is_empty()
4300 }
4301
4302 pub fn table(&self, name: &str) -> Result<Reader> {
4308 let entry = self
4309 .entries
4310 .iter()
4311 .find(|entry| entry.name == name)
4312 .ok_or_else(|| invalid(&format!("the file holds no table called {name}")))?;
4313 let (offset, length) = (entry.directory.offset, entry.directory.length as usize);
4317 if file_checksum(&self.file, offset, length)? != entry.directory.hash {
4318 return Err(invalid(&format!("the directory of table {name} does not checksum")));
4319 }
4320 let mut opening = self.opening;
4321 opening.reads += 1;
4322 opening.bytes += u64::from(entry.directory.length);
4323 Reader::build(
4324 Arc::clone(&self.file),
4325 self.size,
4326 read_directory(Cursor::over(&self.file, offset, length), self.size, Some(offset))?,
4327 u64::from(entry.directory.length),
4328 opening,
4329 )
4330 }
4331}
4332
4333fn slot_offset(generation: u64) -> u64 {
4338 16 + (generation - 1) % 2 * SLOT_BYTES as u64
4339}
4340
4341fn slot_bytes(path: impl AsRef<Path>) -> Result<(File, u64, Slot, Vec<u8>, Opening)> {
4346 let mut file = File::open(path).map_err(io)?;
4347 let size = file.metadata().map_err(io)?.len();
4348 if size < HEADER {
4349 return Err(invalid("file is shorter than its header"));
4350 }
4351 let mut header = [0; HEADER as usize];
4352 file.read_exact(&mut header).map_err(io)?;
4353 let mut opening = Opening { reads: 1, bytes: HEADER };
4354 let version = u32::from_le_bytes([header[8], header[9], header[10], header[11]]);
4355 if &header[..8] != MAGIC {
4360 return Err(invalid("the header does not begin with a rudb native magic"));
4361 }
4362 if !READABLE.contains(&version) {
4363 return Err(invalid(&format!(
4364 "the file is format {version} and this build reads format {FORMAT}, so it has to \
4365 be written again"
4366 )));
4367 }
4368 let mut selected = None;
4369 for start in [16, 16 + SLOT_BYTES] {
4370 let slot = Slot::read(&header[start..start + SLOT_BYTES]);
4371 if slot.generation == 0 || slot.length == 0 || slot.length as usize > MAX_DIRECTORY {
4372 continue;
4373 }
4374 let Some(end) = slot.offset.checked_add(u64::from(slot.length)) else { continue };
4375 if slot.offset < HEADER || end > size {
4376 continue;
4377 }
4378 let mut bytes = vec![0; slot.length as usize];
4379 file.seek(SeekFrom::Start(slot.offset)).map_err(io)?;
4380 file.read_exact(&mut bytes).map_err(io)?;
4381 opening.reads += 1;
4382 opening.bytes += u64::from(slot.length);
4383 if checksum(&bytes) == slot.hash
4384 && selected
4385 .as_ref()
4386 .is_none_or(|(old, _): &(Slot, Vec<u8>)| old.generation < slot.generation)
4387 {
4388 selected = Some((slot, bytes));
4389 }
4390 }
4391 let (slot, bytes) = selected.ok_or_else(|| invalid("no committed directory slot is valid"))?;
4392 Ok((file, size, slot, bytes, opening))
4393}
4394
4395impl Reader {
4396 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
4403 let catalog = Catalog::open(path)?;
4404 let mut names = catalog.names();
4405 let name = names.next().ok_or_else(|| invalid("the file holds no table"))?.to_string();
4406 if names.next().is_some() {
4407 return Err(invalid(
4408 "the file holds more than one table, so it has to be opened by name",
4409 ));
4410 }
4411 catalog.table(&name)
4412 }
4413
4414 fn build(
4416 file: Arc<File>,
4417 size: u64,
4418 table: Table,
4419 directory: u64,
4420 opening: Opening,
4421 ) -> Result<Self> {
4422 let places = places(&table)?;
4423 let dictionaries = (0..table.fields.len()).map(|_| OnceLock::new()).collect();
4424 let table_fields = table.fields.len();
4425 let stripes = table.stripes.len();
4426 let cache = (0..table.fields.len())
4427 .map(|_| {
4428 Mutex::new(Cached {
4429 pages: (0..stripes).map(|_| None).collect(),
4430 index: (0..stripes).map(|_| None).collect(),
4431 ..Cached::default()
4432 })
4433 })
4434 .collect::<Vec<_>>();
4435 let sieves: Vec<Vec<SieveSlot>> = (0..table.fields.len())
4436 .map(|_| table.stripes.iter().map(|_| OnceLock::new()).collect())
4437 .collect();
4438 let part_ranges: Vec<Vec<RangeSlot>> = (0..table.fields.len())
4439 .map(|_| table.stripes.iter().map(|_| OnceLock::new()).collect())
4440 .collect();
4441 Ok(Self {
4442 file,
4443 table: Arc::new(table),
4444 dictionaries: Arc::new(dictionaries),
4445 loading: Arc::new((0..table_fields).map(|_| Mutex::new(())).collect()),
4446 frequency_values: Arc::new((0..table_fields).map(|_| OnceLock::new()).collect()),
4447 frequency_summaries: Arc::new((0..table_fields).map(|_| OnceLock::new()).collect()),
4448 opened: Arc::new(AtomicUsize::new(0)),
4449 sieves: Arc::new(sieves),
4450 part_ranges: Arc::new(part_ranges),
4451 places: Arc::new(places),
4452 cache: Arc::new(cache),
4453 pages: Arc::new(AtomicUsize::new(0)),
4454 indexes: Arc::new(AtomicUsize::new(0)),
4455 kept: Arc::new(AtomicUsize::new(CACHED_STRIPES_PER_COLUMN)),
4456 size,
4457 directory,
4458 opening,
4459 })
4460 }
4461
4462 #[must_use]
4469 pub fn reads(&self) -> Reads {
4470 Reads {
4471 opening: self.opening,
4472 pages: self.pages.load(Atomic::Relaxed),
4473 indexes: self.indexes.load(Atomic::Relaxed),
4474 dictionaries: self.opened.load(Atomic::Relaxed),
4475 }
4476 }
4477
4478 #[must_use]
4483 pub fn layout(&self) -> Layout {
4484 let table = &self.table;
4485 let stripes = table.stripes.as_slice();
4486 let columns = table
4487 .fields
4488 .iter()
4489 .enumerate()
4490 .map(|(at, field)| ColumnLayout {
4491 name: field.name.clone(),
4492 kind: field.ty.to_string(),
4493 pages: sum(stripes.iter().map(|stripe| span_bytes(&stripe.pages, at))),
4494 memberships: sum(stripes.iter().map(|stripe| stripe.memberships.bytes(at))),
4495 sieves: sum(stripes.iter().map(|stripe| stripe.sieves.bytes(at))),
4496 part_ranges: sum(stripes.iter().map(|stripe| stripe.part_ranges.bytes(at))),
4497 dictionary: dictionary_bytes(table, at),
4498 })
4499 .collect();
4500 Layout {
4501 file: self.size,
4502 rows: table.rows,
4503 stripes: stripes.len(),
4504 parts: self.places.len(),
4505 columns,
4506 indexes: sum(stripes.iter().map(|stripe| u64::from(stripe.index.length))),
4507 directory: self.directory,
4508 header: HEADER,
4509 }
4510 }
4511
4512 pub fn stored(&self, column: usize) -> Result<Vec<StoredPart>> {
4529 let field = self
4530 .table
4531 .fields
4532 .get(column)
4533 .ok_or_else(|| invalid("stored column index out of range"))?;
4534 let mut stored = Vec::with_capacity(self.places.len());
4535 let mut row = 0;
4536 for (at, stripe) in self.table.stripes.iter().enumerate() {
4537 let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
4538 let index = read_index(&self.file, stripe, column)?;
4539 let mut bytes = vec![0; page.length as usize];
4540 read_at(&self.file, page.offset, &mut bytes)?;
4541 let ranges = self.stripe_part_ranges(at, column);
4542 for (part, &rows) in stripe.parts.iter().enumerate() {
4543 let span = *index.get(part).ok_or_else(|| invalid("part index out of range"))?;
4544 let held = part_bytes(&bytes, span)?;
4545 let range = ranges.and_then(|held| held.get(part));
4546 stored.push(StoredPart {
4547 stripe: at,
4548 part,
4549 row,
4550 rows: rows as usize,
4551 encoding: page_encoding(&field.ty, rows as usize, held),
4552 bytes: span.length as u64,
4553 page: page.offset,
4554 offset: span.start as u64,
4555 low: range
4556 .and_then(|range| range.low.clone())
4557 .and_then(|bound| bound.into_value(&field.ty)),
4558 high: range
4559 .and_then(|range| range.high.clone())
4560 .and_then(|bound| bound.into_value(&field.ty)),
4561 nulls: range.map(|range| range.nulls),
4562 });
4563 row += rows as usize;
4564 }
4565 }
4566 Ok(stored)
4567 }
4568
4569 #[must_use]
4571 pub fn parts(&self) -> usize {
4572 self.places.len()
4573 }
4574
4575 #[must_use]
4582 pub fn stripe_parts(&self) -> Vec<std::ops::Range<usize>> {
4583 let mut runs = Vec::with_capacity(self.table.stripes.len());
4584 let mut start = 0;
4585 for stripe in &self.table.stripes {
4586 let end = start + stripe.parts.len();
4587 runs.push(start..end);
4588 start = end;
4589 }
4590 runs
4591 }
4592
4593 #[must_use]
4598 pub fn stripe_rows(&self, stripe: usize) -> usize {
4599 self.table.stripes.get(stripe).map_or(0, |held| held.rows)
4600 }
4601
4602 pub fn keep_stripes(&self, stripes: usize) {
4609 self.kept.fetch_max(stripes, Atomic::Relaxed);
4610 }
4611
4612 #[must_use]
4614 pub fn part_rows(&self, at: usize) -> usize {
4615 self.places.get(at).map_or(0, |place| place.rows as usize)
4616 }
4617
4618 #[must_use]
4620 pub fn table(&self) -> &Table {
4621 &self.table
4622 }
4623
4624 pub fn top_frequencies(&self, column: usize, top: usize) -> Result<Option<Vec<(Value, u64)>>> {
4633 let field = self
4634 .table
4635 .fields
4636 .get(column)
4637 .ok_or_else(|| invalid("frequency column index out of range"))?;
4638 let Some(summary) = self.frequency_summary(column)? else {
4639 return Ok(None);
4640 };
4641 if top == 0 || summary.entries.len() < top {
4642 return Ok(None);
4643 }
4644 let boundary = summary.entries[top - 1].count;
4645 if boundary <= summary.omitted_max {
4646 return Ok(None);
4647 }
4648 self.decode_frequencies(column, &field.ty, &summary.entries).map(Some)
4649 }
4650
4651 pub fn top_pair_frequencies(
4662 &self,
4663 first: usize,
4664 second: usize,
4665 top: usize,
4666 ) -> Result<Option<PairFrequencyCounts>> {
4667 if first >= self.table.fields.len() || second >= self.table.fields.len() {
4668 return Err(invalid("pair frequency column index out of range"));
4669 }
4670 let Some(summary) =
4671 self.table.pair_frequencies.iter().find(|summary| {
4672 summary.first as usize == first && summary.second as usize == second
4673 })
4674 else {
4675 return Ok(None);
4676 };
4677 if top == 0 || summary.entries.len() < top {
4678 return Ok(None);
4679 }
4680 let boundary = summary.entries[top - 1].count;
4681 if boundary <= summary.omitted_max {
4682 return Ok(None);
4683 }
4684 let first_summary = self
4685 .frequency_summary(first)?
4686 .ok_or_else(|| invalid("pair frequency first column has no synopsis"))?;
4687 let anchors = self
4688 .decode_frequencies(first, &self.table.fields[first].ty, &first_summary.entries)?
4689 .into_iter()
4690 .map(|(value, _)| value)
4691 .collect::<Vec<_>>();
4692 let dictionary = self
4693 .dictionary(second)?
4694 .ok_or_else(|| invalid("pair frequency second column has no dictionary"))?;
4695 let mut codes = summary.entries.iter().filter_map(|entry| entry.second).collect::<Vec<_>>();
4696 codes.sort_unstable();
4697 codes.dedup();
4698 let texts = dictionary
4699 .try_values_visited(&codes.iter().map(|&code| code as usize).collect::<Vec<_>>())?;
4700 let mut out = Vec::with_capacity(summary.entries.len());
4701 for entry in &summary.entries {
4702 if entry.count < boundary {
4703 break;
4704 }
4705 let first = anchors
4706 .get(entry.first_entry as usize)
4707 .cloned()
4708 .ok_or_else(|| invalid("pair frequency anchor is outside its values"))?;
4709 let second = match entry.second {
4710 None => Value::Null,
4711 Some(code) => {
4712 let at = codes
4713 .binary_search(&code)
4714 .map_err(|_| invalid("pair frequency code was not among the codes read"))?;
4715 texts[at].clone()
4716 }
4717 };
4718 out.push((vec![first, second], entry.count));
4719 }
4720 Ok(Some(out))
4721 }
4722
4723 pub fn exact_frequencies(&self, column: usize) -> Result<Option<Vec<(Value, u64)>>> {
4743 let Some(prefix) = self.frequency_prefix(column)? else {
4744 return Ok(None);
4745 };
4746 Ok((prefix.omitted_max == 0).then_some(prefix.entries))
4747 }
4748
4749 pub fn frequency_prefix(&self, column: usize) -> Result<Option<FrequencyPrefix>> {
4772 let field = self
4773 .table
4774 .fields
4775 .get(column)
4776 .ok_or_else(|| invalid("frequency column index out of range"))?;
4777 let Some(summary) = self.frequency_summary(column)? else {
4778 return Ok(None);
4779 };
4780 let entries = self.decode_frequencies(column, &field.ty, &summary.entries)?;
4781 Ok(Some(FrequencyPrefix { entries, omitted_max: summary.omitted_max }))
4782 }
4783
4784 fn frequency_summary(&self, column: usize) -> Result<Option<Cow<'_, FrequencySummary>>> {
4786 Ok(match self.table.frequencies.get(column) {
4787 None | Some(None) => None,
4788 Some(Some(Frequencies::Held(summary))) => Some(Cow::Borrowed(summary)),
4789 Some(Some(Frequencies::Stored { span, values })) => {
4790 let slot = self
4791 .frequency_summaries
4792 .get(column)
4793 .ok_or_else(|| invalid("frequency column index out of range"))?;
4794 if let Some(summary) = slot.get() {
4795 return Ok(Some(Cow::Borrowed(summary.as_ref())));
4796 }
4797 let field = self
4798 .table
4799 .fields
4800 .get(column)
4801 .ok_or_else(|| invalid("frequency column index out of range"))?;
4802 let mut bytes = vec![0; span.length as usize];
4803 read_at(&self.file, span.offset, &mut bytes)?;
4804 let summary =
4805 decode_summary(&mut Cursor::new(&bytes), field, self.table.rows, *values)?;
4806 let summary = summary.ok_or_else(|| invalid("a stored synopsis is missing"))?;
4807 let _ = slot.set(Arc::new(summary));
4808 Some(Cow::Borrowed(slot.get().expect("the decoded summary was stored").as_ref()))
4809 }
4810 })
4811 }
4812
4813 fn decode_frequencies(
4821 &self,
4822 column: usize,
4823 ty: &LogicalType,
4824 entries: &[FrequencyEntry],
4825 ) -> Result<Vec<(Value, u64)>> {
4826 if let Some(values) = self.frequency_values.get(column).and_then(OnceLock::get) {
4827 return Ok(values.as_ref().clone());
4828 }
4829 let values = self.decode_frequencies_once(column, ty, entries)?;
4830 if let Some(slot) = self.frequency_values.get(column) {
4831 let _ = slot.set(Arc::new(values.clone()));
4832 }
4833 Ok(values)
4834 }
4835
4836 fn decode_frequencies_once(
4837 &self,
4838 column: usize,
4839 ty: &LogicalType,
4840 entries: &[FrequencyEntry],
4841 ) -> Result<Vec<(Value, u64)>> {
4842 let stored_texts = self.table.frequency_texts.get(column).filter(|texts| !texts.is_empty());
4843 if stored_texts.is_some_and(|texts| texts.len() != entries.len()) {
4844 return Err(invalid("frequency text count differs from its synopsis"));
4845 }
4846 let dictionary = if *ty == LogicalType::Varchar && stored_texts.is_none() {
4847 self.dictionary(column)?
4848 } else {
4849 None
4850 };
4851 let mut codes = entries
4852 .iter()
4853 .filter_map(|entry| match entry.value {
4854 FrequencyValue::Code(code) => Some(code as usize),
4855 _ => None,
4856 })
4857 .collect::<Vec<_>>();
4858 codes.sort_unstable();
4859 codes.dedup();
4860 let texts = match &dictionary {
4861 Some(dictionary) if !codes.is_empty() => dictionary.try_values_visited(&codes)?,
4862 _ => Vec::new(),
4863 };
4864 let mut out = Vec::with_capacity(entries.len());
4865 for (entry_at, entry) in entries.iter().enumerate() {
4866 let value = match entry.value {
4867 FrequencyValue::Null => {
4868 if stored_texts.and_then(|texts| texts[entry_at].as_ref()).is_some() {
4869 return Err(invalid("a null frequency entry has text"));
4870 }
4871 Value::Null
4872 }
4873 FrequencyValue::Integer(value) => match *ty {
4874 LogicalType::TinyInt => Value::TinyInt(
4875 i8::try_from(value)
4876 .map_err(|_| invalid("frequency TINYINT is out of range"))?,
4877 ),
4878 LogicalType::UTinyInt => Value::UTinyInt(
4879 u8::try_from(value)
4880 .map_err(|_| invalid("frequency UTINYINT is out of range"))?,
4881 ),
4882 LogicalType::USmallInt => Value::USmallInt(
4883 u16::try_from(value)
4884 .map_err(|_| invalid("frequency USMALLINT is out of range"))?,
4885 ),
4886 LogicalType::UInteger => Value::UInteger(
4887 u32::try_from(value)
4888 .map_err(|_| invalid("frequency UINTEGER is out of range"))?,
4889 ),
4890 LogicalType::UBigInt => Value::UBigInt(
4891 u64::try_from(value)
4892 .map_err(|_| invalid("frequency UBIGINT is out of range"))?,
4893 ),
4894 LogicalType::SmallInt => Value::SmallInt(
4895 i16::try_from(value)
4896 .map_err(|_| invalid("frequency SMALLINT is out of range"))?,
4897 ),
4898 LogicalType::Integer => Value::Integer(
4899 i32::try_from(value)
4900 .map_err(|_| invalid("frequency INTEGER is out of range"))?,
4901 ),
4902 LogicalType::BigInt => Value::BigInt(
4903 i64::try_from(value)
4904 .map_err(|_| invalid("frequency BIGINT is out of range"))?,
4905 ),
4906 LogicalType::Date => Value::Date(
4907 i32::try_from(value)
4908 .map_err(|_| invalid("frequency DATE is out of range"))?,
4909 ),
4910 LogicalType::Timestamp => Value::Timestamp(
4911 i64::try_from(value)
4912 .map_err(|_| invalid("frequency TIMESTAMP is out of range"))?,
4913 ),
4914 _ => return Err(invalid("integer frequency belongs to another type")),
4915 },
4916 FrequencyValue::Code(code) => {
4917 if let Some(text) = stored_texts.and_then(|texts| texts[entry_at].as_ref()) {
4918 Value::Varchar(
4919 String::from_utf8(text.clone())
4920 .map_err(|_| invalid("frequency text is not UTF-8"))?,
4921 )
4922 } else {
4923 if dictionary.is_none() {
4924 return Err(invalid("frequency code has no dictionary or stored text"));
4925 }
4926 let at = codes
4927 .binary_search(&(code as usize))
4928 .map_err(|_| invalid("frequency code was not among the codes read"))?;
4929 texts[at].clone()
4930 }
4931 }
4932 };
4933 out.push((value, entry.count));
4934 }
4935 Ok(out)
4936 }
4937
4938 pub fn frequency_occurrences(&self, column: usize) -> Result<Option<FrequencyOccurrences>> {
4948 let field = self
4949 .table
4950 .fields
4951 .get(column)
4952 .ok_or_else(|| invalid("frequency column index out of range"))?;
4953 let Some(summary) = self.frequency_summary(column)? else {
4954 return Ok(None);
4955 };
4956 if summary.ordinals.is_empty() {
4957 return Ok(None);
4958 }
4959 let (anchors, anchor_indices) = if summary.ordinal_entries.len() == summary.ordinals.len() {
4960 let entries = self.decode_frequencies(column, &field.ty, &summary.entries)?;
4961 (entries.into_iter().map(|(value, _)| value).collect(), summary.ordinal_entries.clone())
4962 } else {
4963 (Vec::new(), Vec::new())
4964 };
4965 Ok(Some(FrequencyOccurrences {
4966 omitted_max: summary.omitted_max,
4967 ordinals: summary.ordinals.clone(),
4968 anchors,
4969 anchor_indices,
4970 }))
4971 }
4972
4973 pub fn distinct_values(&self, column: usize) -> Result<Option<u64>> {
4999 self.table
5000 .distincts
5001 .get(column)
5002 .copied()
5003 .ok_or_else(|| invalid("distinct column index out of range"))
5004 }
5005
5006 pub fn null_count(&self, column: usize) -> Result<u64> {
5017 if column >= self.table.fields.len() {
5018 return Err(invalid("null count column index out of range"));
5019 }
5020 let mut nulls = 0_u64;
5021 for stripe in &self.table.stripes {
5022 let range = stripe
5023 .zone
5024 .column(column)
5025 .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5026 nulls = nulls
5027 .checked_add(range.nulls as u64)
5028 .ok_or_else(|| invalid("null count overflow"))?;
5029 }
5030 Ok(nulls)
5031 }
5032
5033 pub fn text_extremes(&self, column: usize) -> Result<Option<(Value, Value)>> {
5048 if self.null_count(column)? > 0 {
5049 return Ok(None);
5050 }
5051 let Some(dictionary) = self.dictionary(column)? else { return Ok(None) };
5052 let Some(ranks) = dictionary.ranks() else { return Ok(None) };
5053 if ranks == 0 {
5054 return Ok(None);
5055 }
5056 let low = text_at_rank(&dictionary, 0)?;
5057 let high = text_at_rank(&dictionary, ranks - 1)?;
5058 Ok(Some((low, high)))
5059 }
5060
5061 pub fn exact_extremes(&self, column: usize) -> Result<Option<(Bound, Bound)>> {
5084 if column >= self.table.fields.len() {
5085 return Err(invalid("extremes column index out of range"));
5086 }
5087 let mut low: Option<Bound> = None;
5088 let mut high: Option<Bound> = None;
5089 for stripe in &self.table.stripes {
5090 let range = stripe
5091 .zone
5092 .column(column)
5093 .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5094 if !range.exact {
5095 return Ok(None);
5096 }
5097 let (Some(small), Some(large)) = (range.low.as_ref(), range.high.as_ref()) else {
5102 if stripe.rows > range.nulls {
5103 return Ok(None);
5104 }
5105 continue;
5106 };
5107 low = Some(low.map_or_else(|| small.clone(), |held| held.smaller(small.clone())));
5108 high = Some(high.map_or_else(|| large.clone(), |held| held.larger(large.clone())));
5109 }
5110 Ok(low.zip(high))
5111 }
5112
5113 pub fn exact_sum(&self, column: usize) -> Result<Option<(i128, u64)>> {
5126 if column >= self.table.fields.len() {
5127 return Err(invalid("sum column index out of range"));
5128 }
5129 let mut total = 0_i128;
5130 let mut rows = 0_u64;
5131 for stripe in &self.table.stripes {
5132 let range = stripe
5133 .zone
5134 .column(column)
5135 .ok_or_else(|| invalid("stripe zone is narrower than the schema"))?;
5136 let Some(part) = range.sum else { return Ok(None) };
5137 let Some(sum) = total.checked_add(part) else { return Ok(None) };
5138 total = sum;
5139 rows = rows.saturating_add(stripe.rows as u64 - range.nulls as u64);
5140 }
5141 Ok(Some((total, rows)))
5142 }
5143
5144 pub fn host_groups(
5147 &self,
5148 column: usize,
5149 minimum_count: u64,
5150 ) -> Result<Option<Vec<host::HostEntry>>> {
5151 if column >= self.table.fields.len() {
5152 return Err(invalid("host group column index out of range"));
5153 }
5154 let Some(summary) = &self.table.host_groups else { return Ok(None) };
5155 if summary.column != column || minimum_count <= summary.omitted_max {
5156 return Ok(None);
5157 }
5158 Ok(Some(summary.entries.clone()))
5159 }
5160
5161 fn dictionary(&self, column: usize) -> Result<Option<Arc<Vector>>> {
5170 let Some(page) = self.table.dictionaries[column] else { return Ok(None) };
5171 if let Some(dictionary) = self.dictionaries[column].get() {
5172 return Ok(Some(Arc::clone(dictionary)));
5173 }
5174 let _queued = self.loading[column].lock().map_err(|_| invalid("a poisoned dictionary"))?;
5175 if let Some(dictionary) = self.dictionaries[column].get() {
5176 return Ok(Some(Arc::clone(dictionary)));
5177 }
5178 self.opened.fetch_add(1, Atomic::Relaxed);
5179 let dictionary = Arc::new(open_global_dictionary(
5180 Arc::clone(&self.file),
5181 page,
5182 &self.table.fields[column].ty,
5183 TEXT_KEEP_BUDGET,
5184 )?);
5185 let _ = self.dictionaries[column].set(Arc::clone(&dictionary));
5186 Ok(Some(dictionary))
5187 }
5188
5189 pub fn extents(&self, of: &Section) -> Result<Vec<section::Extent>> {
5196 if of.extent_bytes == 0 {
5197 return Ok(Vec::new());
5198 }
5199 let mut bytes = vec![0; of.extent_bytes as usize];
5200 read_at(&self.file, of.extent_page, &mut bytes)?;
5201 if checksum(&bytes) != of.hash {
5202 return Err(invalid("a section's extent table does not checksum"));
5203 }
5204 let extents = section::decode_extents(&bytes)?;
5205 if extents.len() != of.extents as usize {
5206 return Err(invalid("a section's extent table is not the length the entry says"));
5207 }
5208 Ok(extents)
5209 }
5210
5211 pub fn extent(&self, of: §ion::Extent) -> Result<Vec<u8>> {
5221 let end = of
5222 .offset
5223 .checked_add(u64::from(of.length))
5224 .ok_or_else(|| invalid("an extent overflows the file"))?;
5225 if of.offset < HEADER || end > self.size {
5226 return Err(invalid("an extent is outside the file"));
5227 }
5228 let mut bytes = vec![0; of.length as usize];
5229 read_at(&self.file, of.offset, &mut bytes)?;
5230 if checksum(&bytes) != of.hash {
5231 return Err(invalid("an extent does not checksum"));
5232 }
5233 Ok(bytes)
5234 }
5235
5236 pub fn payload(&self, of: &Section) -> Result<Vec<u8>> {
5245 let extents = self.extents(of)?;
5246 let mut bytes =
5247 Vec::with_capacity(sum(extents.iter().map(|one| u64::from(one.length))) as usize);
5248 for one in &extents {
5249 if one.first != bytes.len() as u64 {
5250 return Err(invalid("a section's extents do not join up"));
5251 }
5252 bytes.extend_from_slice(&self.extent(one)?);
5253 }
5254 if !bytes.is_empty() && of.header_bytes as usize > bytes.len() {
5257 return Err(invalid("a section's header is longer than its payload"));
5258 }
5259 Ok(bytes)
5260 }
5261
5262 pub fn read(&self, part: usize, columns: &[usize]) -> Result<Chunk> {
5271 self.read_impl(part, columns, true)
5272 }
5273
5274 pub fn read_sparse(&self, part: usize, columns: &[usize]) -> Result<Chunk> {
5284 self.read_impl(part, columns, false)
5285 }
5286
5287 pub fn skips_codes(&self, part: usize, column: usize, candidates: &[u32]) -> Result<bool> {
5294 if candidates.is_empty() {
5295 return Ok(true);
5296 }
5297 if candidates.windows(2).any(|pair| pair[0] >= pair[1]) {
5298 return Err(Error::internal("native code candidates are not sorted and unique"));
5299 }
5300 let stripe = self.stripe_of(part)?;
5301 let Some(page) = stripe.memberships.get(column) else {
5302 return Ok(false);
5303 };
5304 let mut bytes = vec![0; page.length as usize];
5305 read_at(&self.file, page.offset, &mut bytes)?;
5306 if checksum(&bytes) != page.hash {
5307 return Err(invalid("membership page checksum differs"));
5308 }
5309 let codes = decode_membership(&bytes)?;
5310 let mut left = 0;
5311 let mut right = 0;
5312 while left < codes.len() && right < candidates.len() {
5313 match codes[left].cmp(&candidates[right]) {
5314 Ordering::Less => left += 1,
5315 Ordering::Greater => right += 1,
5316 Ordering::Equal => return Ok(false),
5317 }
5318 }
5319 Ok(true)
5320 }
5321
5322 fn stripe_of(&self, part: usize) -> Result<&Stripe> {
5323 let place = self.places.get(part).ok_or_else(|| invalid("part index out of range"))?;
5324 self.table
5325 .stripes
5326 .get(place.stripe as usize)
5327 .ok_or_else(|| invalid("stripe index out of range"))
5328 }
5329
5330 fn held(&self, at: usize, stripe: &Stripe, column: usize, whole: bool) -> Result<CachedColumn> {
5347 let cache = self.cache.get(column).ok_or_else(|| invalid("column index out of range"))?;
5348 let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5349 let known = cached.index.get(at).and_then(Clone::clone);
5350 let page = cached.pages.get(at).and_then(Clone::clone);
5351 if let Some(index) = known.clone() {
5352 if !whole || page.is_some() {
5353 return Ok(CachedColumn { stripe: at, index, page });
5354 }
5355 }
5356 if cached.loading.contains(&at) {
5357 drop(cached);
5358 if let Some(index) = known {
5362 return Ok(CachedColumn { stripe: at, index, page: None });
5363 }
5364 let held = self.page_of(stripe, column, at, false, None)?;
5365 let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5366 remember(&mut cached, &held, self.kept.load(Atomic::Relaxed));
5367 return Ok(held);
5368 }
5369 cached.loading.push(at);
5370 drop(cached);
5371
5372 let read = self.page_of(stripe, column, at, whole, known);
5373
5374 let mut cached = cache.lock().map_err(|_| invalid("column page cache is poisoned"))?;
5378 if let Some(position) = cached.loading.iter().position(|loading| *loading == at) {
5379 cached.loading.remove(position);
5380 }
5381 let held = read?;
5382 remember(&mut cached, &held, self.kept.load(Atomic::Relaxed));
5383 Ok(held)
5384 }
5385
5386 fn page_of(
5392 &self,
5393 stripe: &Stripe,
5394 column: usize,
5395 at: usize,
5396 whole: bool,
5397 known: Option<Arc<Vec<PartSpan>>>,
5398 ) -> Result<CachedColumn> {
5399 let index = match known {
5400 Some(index) => index,
5401 None => {
5402 self.indexes.fetch_add(1, Atomic::Relaxed);
5403 Arc::new(read_index(&self.file, stripe, column)?)
5404 }
5405 };
5406 let page = if whole {
5407 self.pages.fetch_add(1, Atomic::Relaxed);
5408 let span = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
5409 let mut bytes = vec![0; span.length as usize];
5410 read_at(&self.file, span.offset, &mut bytes)?;
5411 Some(Arc::new(bytes))
5412 } else {
5413 None
5414 };
5415 Ok(CachedColumn { stripe: at, index, page })
5416 }
5417
5418 fn read_impl(&self, at: usize, columns: &[usize], whole: bool) -> Result<Chunk> {
5419 let place = *self.places.get(at).ok_or_else(|| invalid("part index out of range"))?;
5420 let index = place.stripe as usize;
5421 let stripe =
5422 self.table.stripes.get(index).ok_or_else(|| invalid("stripe index out of range"))?;
5423 let rows = place.rows as usize;
5424 let mut picked = Vec::with_capacity(columns.len());
5425 for &column in columns {
5426 let field = self
5427 .table
5428 .fields
5429 .get(column)
5430 .ok_or_else(|| invalid("column index out of range"))?;
5431 let page = stripe.pages.get(column).ok_or_else(|| invalid("stripe page is missing"))?;
5432 let held = self.held(index, stripe, column, whole)?;
5433 let span = *held
5434 .index
5435 .get(place.part as usize)
5436 .ok_or_else(|| invalid("part index out of range"))?;
5437 let owned;
5438 let bytes = match &held.page {
5439 Some(held) => part_bytes(held, span)?,
5440 None => {
5441 let offset = page
5442 .offset
5443 .checked_add(span.start as u64)
5444 .ok_or_else(|| invalid("part range overflow"))?;
5445 let mut bytes = vec![0; span.length];
5446 read_at(&self.file, offset, &mut bytes)?;
5447 owned = bytes;
5448 &owned
5449 }
5450 };
5451 if checksum(bytes) != span.hash {
5452 return Err(invalid(&format!(
5453 "column page checksum differs, column {column} part {} at {}+{} of {} bytes, \
5454 wanted {:016x} and got {:016x}",
5455 place.part,
5456 page.offset,
5457 span.start,
5458 span.length,
5459 span.hash,
5460 checksum(bytes),
5461 )));
5462 }
5463 let dictionary = self.dictionary(column)?;
5464 picked.push(decode(&field.ty, rows, bytes, dictionary)?.into_pages());
5470 }
5471 Chunk::with_rows(picked, rows)
5472 }
5473
5474 #[must_use]
5490 pub fn skips(&self, part: usize, probes: &[Probe]) -> bool {
5491 let Some(place) = self.places.get(part).copied() else { return false };
5492 let Some(stripe) = self.table.stripes.get(place.stripe as usize) else { return false };
5493 if stripe.zone.skips(probes) {
5494 return true;
5495 }
5496 probes.iter().any(|probe| self.outside(place, probe) || self.sifted(place, probe))
5497 }
5498
5499 fn outside(&self, place: Place, probe: &Probe) -> bool {
5505 match self.stripe_part_ranges(place.stripe as usize, probe.column) {
5506 Some(ranges) => ranges
5507 .get(place.part as usize)
5508 .is_some_and(|range| range.excludes(probe.op, &probe.value)),
5509 None => false,
5510 }
5511 }
5512
5513 fn stripe_part_ranges(&self, stripe: usize, column: usize) -> Option<&[Range]> {
5519 let slot = self.part_ranges.get(column)?.get(stripe)?;
5520 if let Some(held) = slot.get() {
5521 return Some(held);
5522 }
5523 let page = self.table.stripes.get(stripe)?.part_ranges.get(column)?;
5524 let mut bytes = vec![0; page.length as usize];
5525 read_at(&self.file, page.offset, &mut bytes).ok()?;
5526 if checksum(&bytes) != page.hash {
5527 return None;
5528 }
5529 let ranges = Arc::new(decode_part_ranges(&bytes).ok()?);
5530 let _ = slot.set(ranges);
5531 slot.get().map(|held| held.as_slice())
5532 }
5533
5534 #[must_use]
5551 pub fn certain(&self, part: usize, probes: &[Probe]) -> bool {
5552 let Some(place) = self.places.get(part).copied() else { return false };
5553 let Some(stripe) = self.table.stripes.get(place.stripe as usize) else { return false };
5554 if stripe.zone.certain(probes) {
5555 return true;
5556 }
5557 probes
5558 .iter()
5559 .all(|probe| stripe.zone.certain(slice::from_ref(probe)) || self.inside(place, probe))
5560 }
5561
5562 fn inside(&self, place: Place, probe: &Probe) -> bool {
5568 match self.stripe_part_ranges(place.stripe as usize, probe.column) {
5569 Some(ranges) => ranges
5570 .get(place.part as usize)
5571 .is_some_and(|range| range.certain(probe.op, &probe.value)),
5572 None => false,
5573 }
5574 }
5575
5576 #[must_use]
5587 pub fn stripe_skips(&self, stripe: usize, probes: &[Probe]) -> bool {
5588 self.table.stripes.get(stripe).is_some_and(|held| held.zone.skips(probes))
5589 }
5590
5591 fn sifted(&self, place: Place, probe: &Probe) -> bool {
5597 if probe.op != Op::Equal {
5598 return false;
5599 }
5600 match self.stripe_sieves(place.stripe as usize, probe.column) {
5601 Some(sieves) => sieves
5602 .get(place.part as usize)
5603 .and_then(Option::as_ref)
5604 .is_some_and(|sieve| sieve.excludes(&probe.value)),
5605 None => false,
5606 }
5607 }
5608
5609 fn stripe_sieves(&self, stripe: usize, column: usize) -> Option<&[Option<Sieve>]> {
5616 let slot = self.sieves.get(column)?.get(stripe)?;
5617 if let Some(held) = slot.get() {
5618 return Some(held);
5619 }
5620 let page = self.table.stripes.get(stripe)?.sieves.get(column)?;
5621 let mut bytes = vec![0; page.length as usize];
5622 read_at(&self.file, page.offset, &mut bytes).ok()?;
5623 if checksum(&bytes) != page.hash {
5624 return None;
5625 }
5626 let sieves = Arc::new(decode_sieves(&bytes).ok()?);
5627 let _ = slot.set(sieves);
5628 slot.get().map(|held| held.as_slice())
5629 }
5630}
5631
5632fn text_at_rank(dictionary: &Vector, rank: usize) -> Result<Value> {
5634 let code = dictionary.code_at_rank(rank)? as usize;
5635 let text = dictionary
5636 .try_text_at(code)?
5637 .ok_or_else(|| invalid("global dictionary order names a code it does not have"))?;
5638 Ok(Value::Varchar(text.into()))
5639}
5640
5641#[cfg(unix)]
5646fn write_at(file: &File, mut offset: u64, mut bytes: &[u8]) -> Result<()> {
5647 use std::os::unix::fs::FileExt;
5648 while !bytes.is_empty() {
5649 let written = file.write_at(bytes, offset).map_err(io)?;
5650 if written == 0 {
5651 return Err(invalid("a write to the native file wrote nothing"));
5652 }
5653 offset += written as u64;
5654 bytes = &bytes[written..];
5655 }
5656 Ok(())
5657}
5658
5659#[cfg(windows)]
5661fn write_at(file: &File, mut offset: u64, mut bytes: &[u8]) -> Result<()> {
5662 use std::os::windows::fs::FileExt;
5663 while !bytes.is_empty() {
5664 let written = file.seek_write(bytes, offset).map_err(io)?;
5665 if written == 0 {
5666 return Err(invalid("a write to the native file wrote nothing"));
5667 }
5668 offset += written as u64;
5669 bytes = &bytes[written..];
5670 }
5671 Ok(())
5672}
5673
5674#[cfg(not(any(unix, windows)))]
5676fn write_at(file: &File, offset: u64, bytes: &[u8]) -> Result<()> {
5677 use std::io::Write;
5678 let mut file = file.try_clone().map_err(io)?;
5679 file.seek(SeekFrom::Start(offset)).map_err(io)?;
5680 file.write_all(bytes).map_err(io)
5681}
5682
5683#[cfg(unix)]
5693fn read_at(file: &File, mut offset: u64, mut bytes: &mut [u8]) -> Result<()> {
5694 use std::os::unix::fs::FileExt;
5695 while !bytes.is_empty() {
5696 let read = file.read_at(bytes, offset).map_err(io)?;
5697 if read == 0 {
5698 return Err(invalid("column page ends before its declared length"));
5699 }
5700 offset += read as u64;
5701 bytes = &mut bytes[read..];
5702 }
5703 Ok(())
5704}
5705
5706#[cfg(windows)]
5712fn read_at(file: &File, mut offset: u64, mut bytes: &mut [u8]) -> Result<()> {
5713 use std::os::windows::fs::FileExt;
5714 while !bytes.is_empty() {
5715 let read = file.seek_read(bytes, offset).map_err(io)?;
5716 if read == 0 {
5717 return Err(invalid("column page ends before its declared length"));
5718 }
5719 offset += read as u64;
5720 bytes = &mut bytes[read..];
5721 }
5722 Ok(())
5723}
5724
5725#[cfg(not(any(unix, windows)))]
5730fn read_at(file: &File, offset: u64, bytes: &mut [u8]) -> Result<()> {
5731 let mut file = file.try_clone().map_err(io)?;
5732 file.seek(SeekFrom::Start(offset)).map_err(io)?;
5733 file.read_exact(bytes).map_err(io)
5734}
5735
5736fn type_tag(ty: &LogicalType) -> Result<u8> {
5743 match ty {
5744 LogicalType::SmallInt => Ok(1),
5745 LogicalType::Integer => Ok(2),
5746 LogicalType::BigInt => Ok(3),
5747 LogicalType::Varchar => Ok(4),
5748 LogicalType::Date => Ok(5),
5749 LogicalType::Timestamp => Ok(6),
5750 LogicalType::Boolean => Ok(7),
5751 LogicalType::TinyInt => Ok(8),
5752 LogicalType::UTinyInt => Ok(9),
5753 LogicalType::USmallInt => Ok(10),
5754 LogicalType::UInteger => Ok(11),
5755 LogicalType::UBigInt => Ok(12),
5756 LogicalType::Decimal { .. } => Ok(13),
5757 LogicalType::Float => Ok(14),
5758 LogicalType::Double => Ok(15),
5759 LogicalType::HugeInt => Ok(16),
5760 LogicalType::UHugeInt => Ok(17),
5761 LogicalType::Time => Ok(18),
5762 LogicalType::TimeTz => Ok(19),
5763 LogicalType::TimestampTz => Ok(20),
5764 LogicalType::Interval => Ok(21),
5765 LogicalType::Uuid => Ok(22),
5766 LogicalType::Blob => Ok(23),
5767 LogicalType::Bit => Ok(24),
5768 LogicalType::TimestampS => Ok(25),
5769 LogicalType::TimestampMs => Ok(26),
5770 LogicalType::TimestampNs => Ok(27),
5771 _ => Err(Error::not_implemented(format!("native storage for {ty}"))),
5772 }
5773}
5774
5775fn put_type(out: &mut Vec<u8>, ty: &LogicalType) -> Result<()> {
5781 out.push(type_tag(ty)?);
5782 if let LogicalType::Decimal { width, scale } = ty {
5783 out.push(*width);
5784 out.push(*scale);
5785 }
5786 Ok(())
5787}
5788
5789fn read_type(cur: &mut Cursor<'_>) -> Result<LogicalType> {
5791 let tag = cur.u8()?;
5792 if tag == 13 {
5793 let width = cur.u8()?;
5794 let scale = cur.u8()?;
5795 return LogicalType::decimal(width, scale)
5796 .map_err(|_| invalid("decimal column width and scale are not a decimal"));
5797 }
5798 tag_type(tag)
5799}
5800
5801fn tag_type(tag: u8) -> Result<LogicalType> {
5802 match tag {
5803 1 => Ok(LogicalType::SmallInt),
5804 2 => Ok(LogicalType::Integer),
5805 3 => Ok(LogicalType::BigInt),
5806 4 => Ok(LogicalType::Varchar),
5807 5 => Ok(LogicalType::Date),
5808 6 => Ok(LogicalType::Timestamp),
5809 7 => Ok(LogicalType::Boolean),
5810 8 => Ok(LogicalType::TinyInt),
5811 9 => Ok(LogicalType::UTinyInt),
5812 10 => Ok(LogicalType::USmallInt),
5813 11 => Ok(LogicalType::UInteger),
5814 12 => Ok(LogicalType::UBigInt),
5815 14 => Ok(LogicalType::Float),
5816 15 => Ok(LogicalType::Double),
5817 16 => Ok(LogicalType::HugeInt),
5818 17 => Ok(LogicalType::UHugeInt),
5819 18 => Ok(LogicalType::Time),
5820 19 => Ok(LogicalType::TimeTz),
5821 20 => Ok(LogicalType::TimestampTz),
5822 21 => Ok(LogicalType::Interval),
5823 22 => Ok(LogicalType::Uuid),
5824 23 => Ok(LogicalType::Blob),
5825 24 => Ok(LogicalType::Bit),
5826 25 => Ok(LogicalType::TimestampS),
5827 26 => Ok(LogicalType::TimestampMs),
5828 27 => Ok(LogicalType::TimestampNs),
5829 _ => Err(invalid("column type tag is unknown")),
5830 }
5831}
5832
5833fn put_u16(out: &mut Vec<u8>, value: u16) {
5834 out.extend_from_slice(&value.to_le_bytes());
5835}
5836fn put_u32(out: &mut Vec<u8>, value: u32) {
5837 out.extend_from_slice(&value.to_le_bytes());
5838}
5839fn put_u64(out: &mut Vec<u8>, value: u64) {
5840 out.extend_from_slice(&value.to_le_bytes());
5841}
5842fn put_var_u64(out: &mut Vec<u8>, mut value: u64) {
5843 while value >= 0x80 {
5844 out.push((value as u8 & 0x7f) | 0x80);
5845 value >>= 7;
5846 }
5847 out.push(value as u8);
5848}
5849
5850fn frequency_order(left: FrequencyValue, right: FrequencyValue) -> Ordering {
5851 match (left, right) {
5852 (FrequencyValue::Null, FrequencyValue::Null) => Ordering::Equal,
5853 (FrequencyValue::Null, _) => Ordering::Less,
5854 (_, FrequencyValue::Null) => Ordering::Greater,
5855 (FrequencyValue::Integer(left), FrequencyValue::Integer(right)) => left.cmp(&right),
5856 (FrequencyValue::Code(left), FrequencyValue::Code(right)) => left.cmp(&right),
5857 (FrequencyValue::Integer(_), FrequencyValue::Code(_)) => Ordering::Less,
5858 (FrequencyValue::Code(_), FrequencyValue::Integer(_)) => Ordering::Greater,
5859 }
5860}
5861
5862fn keep_most_frequent(entries: &mut Vec<FrequencyEntry>) -> u64 {
5875 let order = |left: &FrequencyEntry, right: &FrequencyEntry| {
5876 right.count.cmp(&left.count).then_with(|| frequency_order(left.value, right.value))
5877 };
5878 let omitted_max = if entries.len() > FREQUENCY_ENTRIES {
5879 let (_, next, _) = entries.select_nth_unstable_by(FREQUENCY_ENTRIES, order);
5880 let omitted_max = next.count;
5881 entries.truncate(FREQUENCY_ENTRIES);
5882 omitted_max
5883 } else {
5884 0
5885 };
5886 entries.sort_unstable_by(order);
5887 omitted_max
5888}
5889
5890fn code_frequency(
5891 dictionary: &GlobalDictionary,
5892 flat: &[u8],
5893 bases: &[u64],
5894) -> Result<(FrequencySummary, Vec<Option<Vec<u8>>>)> {
5895 let mut entries = dictionary
5896 .counts
5897 .iter()
5898 .enumerate()
5899 .filter(|(_, count)| **count != 0)
5900 .map(|(code, &count)| FrequencyEntry { value: FrequencyValue::Code(code as u32), count })
5901 .collect::<Vec<_>>();
5902 if dictionary.nulls != 0 {
5903 entries.push(FrequencyEntry { value: FrequencyValue::Null, count: dictionary.nulls });
5904 }
5905 let omitted_max = keep_most_frequent(&mut entries);
5906 let mut spans = Vec::with_capacity(entries.len());
5907 let mut text_bytes = 0_usize;
5908 for entry in &entries {
5909 let span = match entry.value {
5910 FrequencyValue::Code(code) => {
5911 let span = GlobalDictionary::value_span(&dictionary.ends, bases, code as usize);
5912 let bytes = flat
5913 .get(span.0..span.1)
5914 .ok_or_else(|| invalid("a frequency code is outside its dictionary"))?;
5915 text_bytes = text_bytes.saturating_add(bytes.len());
5916 Some(span)
5917 }
5918 FrequencyValue::Null | FrequencyValue::Integer(_) => None,
5919 };
5920 spans.push(span);
5921 }
5922 let texts = if text_bytes > FREQUENCY_TEXT_BUDGET {
5923 Vec::new()
5924 } else {
5925 spans.into_iter().map(|span| span.map(|(from, to)| flat[from..to].to_vec())).collect()
5926 };
5927 Ok((
5928 FrequencySummary {
5929 entries,
5930 omitted_max,
5931 ordinals: Vec::new(),
5932 ordinal_entries: Vec::new(),
5933 },
5934 texts,
5935 ))
5936}
5937
5938fn encode_directory(table: &Table) -> Result<Vec<u8>> {
5939 let mut out = DIRECTORY.to_vec();
5940 let name = table.name.as_bytes();
5941 put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("table name too long"))?);
5942 out.extend_from_slice(name);
5943 put_u16(&mut out, u16::try_from(table.fields.len()).map_err(|_| invalid("too many columns"))?);
5944 for field in &table.fields {
5945 let name = field.name.as_bytes();
5946 put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?);
5947 out.extend_from_slice(name);
5948 put_type(&mut out, &field.ty)?;
5949 out.push(u8::from(field.not_null));
5950 }
5951 for dictionary in &table.dictionaries {
5952 match dictionary {
5953 None => out.push(0),
5954 Some(page) => {
5955 out.push(1);
5956 put_u64(&mut out, page.offset);
5957 put_u32(&mut out, page.length);
5958 put_u64(&mut out, page.hash);
5959 }
5960 }
5961 }
5962 for distinct in &table.distincts {
5963 match distinct {
5964 None => out.push(0),
5965 Some(count) => {
5966 out.push(1);
5967 put_u64(&mut out, *count);
5968 }
5969 }
5970 }
5971 put_u64(&mut out, u64::try_from(table.rows).map_err(|_| invalid("row count overflow"))?);
5972 put_u32(&mut out, u32::try_from(table.stripes.len()).map_err(|_| invalid("too many stripes"))?);
5973 for stripe in &table.stripes {
5974 put_u32(
5975 &mut out,
5976 u32::try_from(stripe.parts.len()).map_err(|_| invalid("too many parts in a stripe"))?,
5977 );
5978 for &rows in &stripe.parts {
5979 put_u32(&mut out, rows);
5980 }
5981 put_u64(&mut out, stripe.index.offset);
5982 put_u32(&mut out, stripe.index.length);
5983 for page in &stripe.pages {
5984 put_u64(&mut out, page.offset);
5985 put_u32(&mut out, page.length);
5986 }
5987 for ((field, dictionary), membership) in
5992 table.fields.iter().zip(&table.dictionaries).zip(stripe.memberships.slots())
5993 {
5994 if field.ty != LogicalType::Varchar || dictionary.is_none() {
5995 continue;
5996 }
5997 let page =
5998 membership.ok_or_else(|| invalid("string page has no code membership index"))?;
5999 put_u64(&mut out, page.offset);
6000 put_u32(&mut out, page.length);
6001 put_u64(&mut out, page.hash);
6002 }
6003 for sieve in stripe.sieves.slots() {
6004 match sieve {
6005 None => out.push(0),
6006 Some(page) => {
6007 out.push(1);
6008 put_u64(&mut out, page.offset);
6009 put_u32(&mut out, page.length);
6010 put_u64(&mut out, page.hash);
6011 }
6012 }
6013 }
6014 for held in stripe.part_ranges.slots() {
6015 match held {
6016 None => out.push(0),
6017 Some(page) => {
6018 out.push(1);
6019 put_u64(&mut out, page.offset);
6020 put_u32(&mut out, page.length);
6021 put_u64(&mut out, page.hash);
6022 }
6023 }
6024 }
6025 for range in stripe.zone.columns() {
6026 put_bound(&mut out, range.low.as_ref())?;
6027 put_bound(&mut out, range.high.as_ref())?;
6028 put_u32(
6029 &mut out,
6030 u32::try_from(range.nulls).map_err(|_| invalid("null count overflow"))?,
6031 );
6032 out.push(u8::from(range.exact));
6033 match range.sum {
6034 None => out.push(0),
6035 Some(total) => {
6036 out.push(1);
6037 out.extend_from_slice(&total.to_le_bytes());
6038 }
6039 }
6040 }
6041 }
6042 out.extend_from_slice(FREQUENCIES);
6043 put_u16(
6044 &mut out,
6045 u16::try_from(table.frequencies.len())
6046 .map_err(|_| invalid("too many frequency columns"))?,
6047 );
6048 for summary in &table.frequencies {
6049 let summary = match summary {
6050 None => {
6051 out.push(0);
6052 continue;
6053 }
6054 Some(Frequencies::Held(summary)) => summary,
6055 Some(Frequencies::Stored { .. }) => {
6057 return Err(invalid("a synopsis left in the file cannot be written back"));
6058 }
6059 };
6060 out.push(1);
6061 put_u64(&mut out, summary.omitted_max);
6062 put_u32(
6063 &mut out,
6064 u32::try_from(summary.entries.len())
6065 .map_err(|_| invalid("too many frequency entries"))?,
6066 );
6067 for entry in &summary.entries {
6068 match entry.value {
6069 FrequencyValue::Null => out.push(0),
6070 FrequencyValue::Integer(value) => {
6071 out.push(1);
6072 out.extend_from_slice(&value.to_le_bytes());
6073 }
6074 FrequencyValue::Code(value) => {
6075 out.push(2);
6076 put_u32(&mut out, value);
6077 }
6078 }
6079 put_u64(&mut out, entry.count);
6080 }
6081 put_u32(
6082 &mut out,
6083 u32::try_from(summary.ordinals.len())
6084 .map_err(|_| invalid("too many frequency ordinals"))?,
6085 );
6086 let mut previous = 0_u64;
6087 for (at, &ordinal) in summary.ordinals.iter().enumerate() {
6088 let delta = if at == 0 {
6089 ordinal
6090 } else {
6091 ordinal
6092 .checked_sub(previous)
6093 .ok_or_else(|| invalid("frequency ordinals are not ordered"))?
6094 };
6095 if at != 0 && delta == 0 {
6096 return Err(invalid("frequency ordinals are not unique"));
6097 }
6098 put_var_u64(&mut out, delta);
6099 previous = ordinal;
6100 }
6101 if summary.ordinal_entries.len() != summary.ordinals.len() {
6102 return Err(invalid("frequency ordinal values have a different length"));
6103 }
6104 for &entry in &summary.ordinal_entries {
6105 if entry as usize >= summary.entries.len() {
6106 return Err(invalid("frequency ordinal value is outside its entries"));
6107 }
6108 put_u16(&mut out, entry);
6109 }
6110 }
6111 if !table.pair_frequencies.is_empty() {
6112 out.extend_from_slice(PAIR_FREQUENCIES);
6113 put_u16(
6114 &mut out,
6115 u16::try_from(table.pair_frequencies.len())
6116 .map_err(|_| invalid("too many pair frequency summaries"))?,
6117 );
6118 for summary in &table.pair_frequencies {
6119 put_u16(&mut out, summary.first);
6120 put_u16(&mut out, summary.second);
6121 put_u64(&mut out, summary.omitted_max);
6122 put_u16(
6123 &mut out,
6124 u16::try_from(summary.entries.len())
6125 .map_err(|_| invalid("too many pair frequency entries"))?,
6126 );
6127 for entry in &summary.entries {
6128 put_u16(&mut out, entry.first_entry);
6129 match entry.second {
6130 None => out.push(0),
6131 Some(code) => {
6132 out.push(1);
6133 put_u32(&mut out, code);
6134 }
6135 }
6136 put_u64(&mut out, entry.count);
6137 }
6138 }
6139 }
6140 let text_columns = table.frequency_texts.iter().filter(|texts| !texts.is_empty()).count();
6141 if text_columns != 0 {
6142 out.extend_from_slice(FREQUENCY_TEXTS);
6143 put_u16(
6144 &mut out,
6145 u16::try_from(text_columns)
6146 .map_err(|_| invalid("too many string frequency columns"))?,
6147 );
6148 for (column, texts) in table.frequency_texts.iter().enumerate() {
6149 if texts.is_empty() {
6150 continue;
6151 }
6152 put_u16(
6153 &mut out,
6154 u16::try_from(column).map_err(|_| invalid("frequency text column overflows"))?,
6155 );
6156 put_u16(
6157 &mut out,
6158 u16::try_from(texts.len())
6159 .map_err(|_| invalid("too many frequency text entries"))?,
6160 );
6161 for text in texts {
6162 match text {
6163 None => out.push(0),
6164 Some(text) => {
6165 out.push(1);
6166 put_u32(
6167 &mut out,
6168 u32::try_from(text.len())
6169 .map_err(|_| invalid("frequency text is too long"))?,
6170 );
6171 out.extend_from_slice(text);
6172 }
6173 }
6174 }
6175 }
6176 }
6177 if let Some(summary) = &table.host_groups {
6178 out.extend_from_slice(HOST_GROUPS);
6179 put_u16(
6180 &mut out,
6181 u16::try_from(summary.column).map_err(|_| invalid("host column overflows"))?,
6182 );
6183 put_u64(&mut out, summary.omitted_max);
6184 put_u16(
6185 &mut out,
6186 u16::try_from(summary.entries.len()).map_err(|_| invalid("too many host groups"))?,
6187 );
6188 for entry in &summary.entries {
6189 put_u32(
6190 &mut out,
6191 u32::try_from(entry.host.len()).map_err(|_| invalid("host name is too long"))?,
6192 );
6193 out.extend_from_slice(entry.host.as_bytes());
6194 put_u64(&mut out, entry.count);
6195 out.extend_from_slice(&entry.bytes_sum.to_le_bytes());
6196 put_u32(
6197 &mut out,
6198 u32::try_from(entry.minimum.len())
6199 .map_err(|_| invalid("host minimum is too long"))?,
6200 );
6201 out.extend_from_slice(entry.minimum.as_bytes());
6202 }
6203 }
6204 if let Some(clustering) = &table.clustering {
6207 out.extend_from_slice(CLUSTERING);
6208 out.push(clustering.width().tag());
6209 put_u16(
6210 &mut out,
6211 u16::try_from(clustering.columns().len())
6212 .map_err(|_| invalid("too many clustering columns"))?,
6213 );
6214 for &column in clustering.columns() {
6215 put_u16(
6216 &mut out,
6217 u16::try_from(column).map_err(|_| invalid("clustering column index overflow"))?,
6218 );
6219 }
6220 }
6221 out.extend_from_slice(SECTIONS);
6227 put_u64(&mut out, table.generation);
6228 put_u16(
6229 &mut out,
6230 u16::try_from(table.sections.len()).map_err(|_| invalid("too many sections"))?,
6231 );
6232 for held in &table.sections {
6233 held.encode(&mut out)?;
6234 }
6235 if table.dictionary_payloads.iter().any(|&bytes| bytes != 0) {
6236 out.extend_from_slice(DICTIONARY_PAYLOADS);
6237 put_u16(
6238 &mut out,
6239 u16::try_from(table.fields.len()).map_err(|_| invalid("too many columns"))?,
6240 );
6241 for at in 0..table.fields.len() {
6242 put_u64(&mut out, table.dictionary_payloads.get(at).copied().unwrap_or(0));
6243 }
6244 }
6245 Ok(out)
6246}
6247
6248fn encode_catalog(entries: &[Entry], views: &[ViewEntry]) -> Result<Vec<u8>> {
6257 let mut out = CATALOG.to_vec();
6258 put_u32(&mut out, u32::try_from(entries.len()).map_err(|_| invalid("too many tables"))?);
6259 for entry in entries {
6260 let name = entry.name.as_bytes();
6261 put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("table name too long"))?);
6262 out.extend_from_slice(name);
6263 put_u64(&mut out, u64::try_from(entry.rows).map_err(|_| invalid("row count overflow"))?);
6264 put_u16(
6265 &mut out,
6266 u16::try_from(entry.fields.len()).map_err(|_| invalid("too many columns"))?,
6267 );
6268 for field in &entry.fields {
6269 let name = field.name.as_bytes();
6270 put_u16(
6271 &mut out,
6272 u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?,
6273 );
6274 out.extend_from_slice(name);
6275 put_type(&mut out, &field.ty)?;
6276 out.push(u8::from(field.not_null));
6277 }
6278 put_u64(&mut out, entry.directory.offset);
6279 put_u32(&mut out, entry.directory.length);
6280 put_u64(&mut out, entry.directory.hash);
6281 }
6282 put_u32(&mut out, u32::try_from(views.len()).map_err(|_| invalid("too many views"))?);
6283 for view in views {
6284 let name = view.name.as_bytes();
6285 put_u16(&mut out, u16::try_from(name.len()).map_err(|_| invalid("view name too long"))?);
6286 out.extend_from_slice(name);
6287 put_long_text(&mut out, &view.sql, "view body")?;
6288 put_long_text(&mut out, &view.statement, "view statement")?;
6289 put_u16(
6290 &mut out,
6291 u16::try_from(view.aliases.len()).map_err(|_| invalid("too many aliases"))?,
6292 );
6293 for alias in &view.aliases {
6294 let alias = alias.as_bytes();
6295 put_u16(
6296 &mut out,
6297 u16::try_from(alias.len()).map_err(|_| invalid("alias name too long"))?,
6298 );
6299 out.extend_from_slice(alias);
6300 }
6301 put_u16(
6302 &mut out,
6303 u16::try_from(view.columns.len()).map_err(|_| invalid("too many columns"))?,
6304 );
6305 for field in &view.columns {
6306 let name = field.name.as_bytes();
6307 put_u16(
6308 &mut out,
6309 u16::try_from(name.len()).map_err(|_| invalid("column name too long"))?,
6310 );
6311 out.extend_from_slice(name);
6312 put_type(&mut out, &field.ty)?;
6313 out.push(u8::from(field.not_null));
6314 }
6315 }
6316 Ok(out)
6317}
6318
6319fn put_long_text(out: &mut Vec<u8>, text: &str, what: &str) -> Result<()> {
6321 let bytes = text.as_bytes();
6322 put_u32(out, u32::try_from(bytes.len()).map_err(|_| invalid(&format!("{what} too long")))?);
6323 out.extend_from_slice(bytes);
6324 Ok(())
6325}
6326
6327fn decode_catalog(bytes: &[u8], size: u64) -> Result<(Vec<Entry>, Vec<ViewEntry>)> {
6330 let mut cur = Cursor::new(bytes);
6331 if cur.take(8)? != CATALOG {
6332 return Err(invalid("catalog magic differs"));
6333 }
6334 let count = cur.u32()? as usize;
6335 let mut entries: Vec<Entry> = Vec::with_capacity(count.min(1024));
6336 for _ in 0..count {
6337 let name = cur.text()?;
6338 let rows = usize::try_from(cur.u64()?).map_err(|_| invalid("row count does not fit"))?;
6339 let width = cur.u16()? as usize;
6340 let mut fields = Vec::with_capacity(width);
6341 for _ in 0..width {
6342 let name = cur.text()?;
6343 let ty = read_type(&mut cur)?;
6344 let not_null = match cur.u8()? {
6345 0 => false,
6346 1 => true,
6347 _ => return Err(invalid("nullability flag differs")),
6348 };
6349 fields.push(Field { name, ty, not_null });
6350 }
6351 let directory = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6352 let end = directory
6353 .offset
6354 .checked_add(u64::from(directory.length))
6355 .ok_or_else(|| invalid("table directory offset overflow"))?;
6356 if directory.offset < HEADER
6357 || end > size
6358 || directory.length as usize > MAX_DIRECTORY
6359 || directory.length == 0
6360 {
6361 return Err(invalid("table directory range is outside the file"));
6362 }
6363 if entries.iter().any(|held| held.name == name) {
6364 return Err(invalid("two tables in the catalog have the same name"));
6365 }
6366 entries.push(Entry { name, fields, rows, directory });
6367 }
6368 let count = if cur.done() { 0 } else { cur.u32()? as usize };
6373 let mut views: Vec<ViewEntry> = Vec::with_capacity(count.min(1024));
6374 for _ in 0..count {
6375 let name = cur.text()?;
6376 let sql = cur.long_text()?;
6377 let statement = cur.long_text()?;
6378 let width = cur.u16()? as usize;
6379 let mut aliases = Vec::with_capacity(width);
6380 for _ in 0..width {
6381 aliases.push(cur.text()?);
6382 }
6383 let width = cur.u16()? as usize;
6384 let mut columns = Vec::with_capacity(width);
6385 for _ in 0..width {
6386 let name = cur.text()?;
6387 let ty = read_type(&mut cur)?;
6388 let not_null = match cur.u8()? {
6389 0 => false,
6390 1 => true,
6391 _ => return Err(invalid("nullability flag differs")),
6392 };
6393 columns.push(Field { name, ty, not_null });
6394 }
6395 if views.iter().any(|held| held.name == name) {
6399 return Err(invalid("two views in the catalog have the same name"));
6400 }
6401 if entries.iter().any(|held| held.name == name) {
6402 return Err(invalid("a table and a view in the catalog have the same name"));
6403 }
6404 views.push(ViewEntry { name, sql, statement, aliases, columns });
6405 }
6406 Ok((entries, views))
6407}
6408
6409struct Cursor<'a> {
6417 bytes: &'a [u8],
6418 at: usize,
6419 window: Option<Window<'a>>,
6420}
6421
6422struct Window<'a> {
6424 file: &'a File,
6425 offset: u64,
6426 length: usize,
6427 start: usize,
6429 held: Vec<u8>,
6430 size: usize,
6432}
6433
6434const DIRECTORY_WINDOW: usize = 64 << 10;
6436
6437impl<'a> Cursor<'a> {
6438 fn new(bytes: &'a [u8]) -> Self {
6439 Self { bytes, at: 0, window: None }
6440 }
6441
6442 fn over(file: &'a File, offset: u64, length: usize) -> Self {
6444 let window =
6445 Window { file, offset, length, start: 0, held: Vec::new(), size: DIRECTORY_WINDOW };
6446 Self { bytes: &[], at: 0, window: Some(window) }
6447 }
6448
6449 fn len(&self) -> usize {
6451 self.window.as_ref().map_or(self.bytes.len(), |window| window.length)
6452 }
6453
6454 fn ensure(&mut self, len: usize) -> Result<()> {
6456 let end = self.at.checked_add(len).ok_or_else(|| invalid("directory offset overflow"))?;
6457 if end > self.len() {
6458 return Err(invalid("directory is truncated"));
6459 }
6460 let Some(window) = &mut self.window else { return Ok(()) };
6461 if self.at < window.start || end > window.start + window.held.len() {
6462 let want = len.max(window.size).min(window.length - self.at);
6463 window.start = self.at;
6464 window.held.resize(want, 0);
6465 read_at(window.file, window.offset + self.at as u64, &mut window.held)?;
6466 }
6467 Ok(())
6468 }
6469
6470 fn held(&self, at: usize, len: usize) -> &[u8] {
6472 match &self.window {
6473 Some(window) => &window.held[at - window.start..at - window.start + len],
6474 None => &self.bytes[at..at + len],
6475 }
6476 }
6477
6478 #[inline]
6480 fn peek(&mut self, len: usize) -> Result<&[u8]> {
6481 if self.window.is_none() {
6482 let bytes = self.bytes;
6483 return Ok(&bytes[self.at..self.end(len)?]);
6484 }
6485 self.ensure(len)?;
6486 Ok(self.held(self.at, len))
6487 }
6488
6489 #[inline]
6495 fn take(&mut self, len: usize) -> Result<&[u8]> {
6496 if self.window.is_none() {
6497 let bytes = self.bytes;
6498 let (at, end) = (self.at, self.end(len)?);
6499 self.at = end;
6500 return Ok(&bytes[at..end]);
6501 }
6502 self.take_windowed(len)
6503 }
6504
6505 #[inline]
6507 fn end(&self, len: usize) -> Result<usize> {
6508 let end = self.at.checked_add(len).ok_or_else(|| invalid("directory offset overflow"))?;
6509 if end > self.bytes.len() {
6510 return Err(invalid("directory is truncated"));
6511 }
6512 Ok(end)
6513 }
6514
6515 #[inline(never)]
6517 fn take_windowed(&mut self, len: usize) -> Result<&[u8]> {
6518 self.ensure(len)?;
6519 self.at += len;
6520 Ok(self.held(self.at - len, len))
6521 }
6522 #[inline]
6523 fn u8(&mut self) -> Result<u8> {
6524 Ok(self.take(1)?[0])
6525 }
6526 #[inline]
6527 fn u16(&mut self) -> Result<u16> {
6528 Ok(u16::from_le_bytes(self.take(2)?.try_into().expect("two bytes")))
6529 }
6530 #[inline]
6531 fn u32(&mut self) -> Result<u32> {
6532 Ok(u32::from_le_bytes(self.take(4)?.try_into().expect("four bytes")))
6533 }
6534 #[inline]
6535 fn u64(&mut self) -> Result<u64> {
6536 Ok(u64::from_le_bytes(self.take(8)?.try_into().expect("eight bytes")))
6537 }
6538 fn var_u64(&mut self) -> Result<u64> {
6539 let mut value = 0_u64;
6540 for shift in (0..=63).step_by(7) {
6541 let byte = self.u8()?;
6542 let part = u64::from(byte & 0x7f);
6543 if shift == 63 && part > 1 {
6544 return Err(invalid("frequency ordinal varint overflows"));
6545 }
6546 value |= part << shift;
6547 if byte & 0x80 == 0 {
6548 return Ok(value);
6549 }
6550 }
6551 Err(invalid("frequency ordinal varint is too long"))
6552 }
6553 fn bound(&mut self) -> Result<Option<Bound>> {
6562 let rest = self.len().saturating_sub(self.at);
6563 let mut want = 32;
6564 loop {
6565 let offered = self.peek(want.min(rest))?;
6566 let mut used = 0;
6567 match bounds::get(offered, &mut used) {
6568 Ok(bound) => {
6569 self.at += used;
6570 return Ok(bound);
6571 }
6572 Err(_) if want < rest => want *= 2,
6573 Err(error) => return Err(error),
6574 }
6575 }
6576 }
6577 fn text(&mut self) -> Result<String> {
6578 let len = self.u16()? as usize;
6579 String::from_utf8(self.take(len)?.to_vec()).map_err(|_| invalid("name is not UTF-8"))
6580 }
6581 fn done(&self) -> bool {
6584 self.at >= self.len()
6585 }
6586 fn long_text(&mut self) -> Result<String> {
6593 let len = self.u32()? as usize;
6594 String::from_utf8(self.take(len)?.to_vec()).map_err(|_| invalid("text is not UTF-8"))
6595 }
6596}
6597
6598fn decode_summary(
6600 cur: &mut Cursor<'_>,
6601 field: &Field,
6602 rows: usize,
6603 values: bool,
6604) -> Result<Option<FrequencySummary>> {
6605 Ok(match cur.u8()? {
6606 0 => None,
6607 1 => {
6608 let omitted_max = cur.u64()?;
6609 let count = cur.u32()? as usize;
6610 if count > FREQUENCY_ENTRIES {
6611 return Err(invalid("frequency entry count exceeds its bound"));
6612 }
6613 let mut entries = Vec::with_capacity(count);
6614 for _ in 0..count {
6616 let value = match cur.u8()? {
6617 0 => FrequencyValue::Null,
6618 1 => FrequencyValue::Integer(i128::from_le_bytes(
6619 cur.take(16)?.try_into().expect("sixteen bytes"),
6620 )),
6621 2 => FrequencyValue::Code(cur.u32()?),
6622 _ => return Err(invalid("frequency value tag differs")),
6623 };
6624 let valid = matches!(
6625 (&field.ty, value),
6626 (_, FrequencyValue::Null)
6627 | (LogicalType::Varchar, FrequencyValue::Code(_))
6628 | (
6629 LogicalType::TinyInt
6630 | LogicalType::SmallInt
6631 | LogicalType::Integer
6632 | LogicalType::BigInt
6633 | LogicalType::UTinyInt
6634 | LogicalType::USmallInt
6635 | LogicalType::UInteger
6636 | LogicalType::UBigInt
6637 | LogicalType::Date
6638 | LogicalType::Timestamp,
6639 FrequencyValue::Integer(_),
6640 )
6641 );
6642 if !valid {
6643 return Err(invalid("frequency value does not match its column"));
6644 }
6645 let count = cur.u64()?;
6646 if count == 0 || count > rows as u64 {
6647 return Err(invalid("frequency count is outside the table"));
6648 }
6649 entries.push(FrequencyEntry { value, count });
6650 }
6651 if entries.windows(2).any(|pair| pair[0].count < pair[1].count) {
6652 return Err(invalid("frequency entries are not descending"));
6653 }
6654 let ordinals = {
6655 let ordinal_count = cur.u32()? as usize;
6656 if ordinal_count > FREQUENCY_ORDINALS || ordinal_count > rows {
6657 return Err(invalid("frequency ordinal count exceeds its bound"));
6658 }
6659 let mut ordinals = Vec::with_capacity(ordinal_count);
6660 let mut previous = 0_u64;
6661 for at in 0..ordinal_count {
6662 let delta = cur.var_u64()?;
6663 if at != 0 && delta == 0 {
6664 return Err(invalid("frequency ordinals are not increasing"));
6665 }
6666 let ordinal = if at == 0 {
6667 delta
6668 } else {
6669 previous
6670 .checked_add(delta)
6671 .ok_or_else(|| invalid("frequency ordinal overflows"))?
6672 };
6673 if ordinal >= rows as u64 {
6674 return Err(invalid("frequency ordinal is outside the table"));
6675 }
6676 ordinals.push(ordinal);
6677 previous = ordinal;
6678 }
6679 ordinals
6680 };
6681 let ordinal_entries = if values {
6682 let mut ordinal_entries = Vec::with_capacity(ordinals.len());
6683 for _ in 0..ordinals.len() {
6684 let entry = cur.u16()?;
6685 if entry as usize >= entries.len() {
6686 return Err(invalid("frequency ordinal value is outside its entries"));
6687 }
6688 ordinal_entries.push(entry);
6689 }
6690 ordinal_entries
6691 } else {
6692 Vec::new()
6693 };
6694 Some(FrequencySummary { entries, omitted_max, ordinals, ordinal_entries })
6695 }
6696 _ => return Err(invalid("frequency summary tag differs")),
6697 })
6698}
6699
6700fn decode_directory(bytes: &[u8], size: u64) -> Result<Table> {
6701 read_directory(Cursor::new(bytes), size, None)
6702}
6703
6704fn read_directory(mut cur: Cursor<'_>, size: u64, stored_at: Option<u64>) -> Result<Table> {
6709 if cur.take(8)? != DIRECTORY {
6710 return Err(invalid("directory magic differs"));
6711 }
6712 let name = cur.text()?;
6713 let width = cur.u16()? as usize;
6714 let mut fields = Vec::with_capacity(width);
6715 for _ in 0..width {
6716 let name = cur.text()?;
6717 let ty = read_type(&mut cur)?;
6718 let not_null = match cur.u8()? {
6719 0 => false,
6720 1 => true,
6721 _ => return Err(invalid("nullability flag differs")),
6722 };
6723 fields.push(Field { name, ty, not_null });
6724 }
6725 let mut dictionaries = Vec::with_capacity(width);
6726 for _ in 0..width {
6727 dictionaries.push(match cur.u8()? {
6728 0 => None,
6729 1 => {
6730 let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6731 let end = page
6732 .offset
6733 .checked_add(u64::from(page.length))
6734 .ok_or_else(|| invalid("dictionary page offset overflow"))?;
6735 if page.offset < HEADER || end > size {
6740 return Err(invalid("dictionary page range is outside the file"));
6741 }
6742 Some(page)
6743 }
6744 _ => return Err(invalid("dictionary page tag differs")),
6745 });
6746 }
6747 let mut distincts = Vec::with_capacity(width);
6748 for _ in 0..width {
6749 distincts.push(match cur.u8()? {
6750 0 => None,
6751 1 => Some(cur.u64()?),
6752 _ => return Err(invalid("distinct count tag differs")),
6753 });
6754 }
6755 let rows = usize::try_from(cur.u64()?).map_err(|_| invalid("row count does not fit"))?;
6756 let count = cur.u32()? as usize;
6757 let mut stripes = Vec::with_capacity(count);
6758 let mut total = 0_usize;
6759 for _ in 0..count {
6760 let count = cur.u32()? as usize;
6761 if count == 0 || count > STRIPE_PARTS {
6762 return Err(invalid("stripe part count is outside its bound"));
6763 }
6764 let mut parts = Vec::with_capacity(count);
6765 let mut stripe_rows = 0_usize;
6766 for _ in 0..count {
6767 let rows = cur.u32()?;
6768 if rows == 0 {
6769 return Err(invalid("empty part"));
6770 }
6771 parts.push(rows);
6772 stripe_rows = stripe_rows
6773 .checked_add(rows as usize)
6774 .ok_or_else(|| invalid("stripe row count overflow"))?;
6775 }
6776 total =
6777 total.checked_add(stripe_rows).ok_or_else(|| invalid("stripe row count overflow"))?;
6778 let index = Span { offset: cur.u64()?, length: cur.u32()? };
6779 let section = index_section(count)?;
6780 let wanted = section
6781 .checked_mul(width)
6782 .and_then(|bytes| u32::try_from(bytes).ok())
6783 .ok_or_else(|| invalid("index page length overflow"))?;
6784 let end = index
6785 .offset
6786 .checked_add(u64::from(index.length))
6787 .ok_or_else(|| invalid("index page offset overflow"))?;
6788 if index.offset < HEADER || end > size || index.length != wanted {
6789 return Err(invalid("index page range is outside the file"));
6790 }
6791 let mut pages = Vec::with_capacity(width);
6792 for _ in 0..width {
6793 let offset = cur.u64()?;
6794 let length = cur.u32()?;
6795 let end = offset
6796 .checked_add(u64::from(length))
6797 .ok_or_else(|| invalid("page offset overflow"))?;
6798 if offset < HEADER || end > size || length as usize > MAX_PAGE {
6799 return Err(invalid("page range is outside the file"));
6800 }
6801 pages.push(Span { offset, length });
6802 }
6803 let mut memberships = vec![None; width];
6804 for (column, field) in fields.iter().enumerate() {
6805 if field.ty != LogicalType::Varchar || dictionaries[column].is_none() {
6806 continue;
6807 }
6808 let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6809 let end = page
6810 .offset
6811 .checked_add(u64::from(page.length))
6812 .ok_or_else(|| invalid("membership page offset overflow"))?;
6813 if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6814 return Err(invalid("membership page range is outside the file"));
6815 }
6816 memberships[column] = Some(page);
6817 }
6818 let mut sieves = vec![None; width];
6819 for sieve in sieves.iter_mut().take(width) {
6820 match cur.u8()? {
6821 0 => continue,
6822 1 => {}
6823 _ => return Err(invalid("a sieve page has an unknown tag")),
6824 }
6825 let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6826 let end = page
6827 .offset
6828 .checked_add(u64::from(page.length))
6829 .ok_or_else(|| invalid("sieve page offset overflow"))?;
6830 if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6831 return Err(invalid("sieve page range is outside the file"));
6832 }
6833 *sieve = Some(page);
6834 }
6835 let mut part_ranges = vec![None; width];
6836 for held in part_ranges.iter_mut().take(width) {
6837 match cur.u8()? {
6838 0 => continue,
6839 1 => {}
6840 _ => return Err(invalid("a part range page has an unknown tag")),
6841 }
6842 let page = Page { offset: cur.u64()?, length: cur.u32()?, hash: cur.u64()? };
6843 let end = page
6844 .offset
6845 .checked_add(u64::from(page.length))
6846 .ok_or_else(|| invalid("part range page offset overflow"))?;
6847 if page.offset < HEADER || end > size || page.length as usize > MAX_PAGE {
6848 return Err(invalid("part range page range is outside the file"));
6849 }
6850 *held = Some(page);
6851 }
6852 let mut ranges = Vec::with_capacity(width);
6853 for column in 0..width {
6854 let low = cur.bound()?;
6855 let high = cur.bound()?;
6856 let nulls = cur.u32()? as usize;
6857 if nulls > stripe_rows {
6858 return Err(invalid("null count exceeds stripe rows"));
6859 }
6860 let exact = cur.u8()? != 0;
6861 let sum = match cur.u8()? {
6862 0 => None,
6863 1 => Some(i128::from_le_bytes(
6864 cur.take(16)?.try_into().map_err(|_| invalid("a stripe sum is truncated"))?,
6865 )),
6866 _ => return Err(invalid("a stripe sum has an unknown tag")),
6867 };
6868 let ty = &fields.get(column).ok_or_else(|| invalid("a stripe range has no column"))?.ty;
6874 let low = low.map(|bound| scaled_as(bound, ty));
6875 let high = high.map(|bound| scaled_as(bound, ty));
6876 ranges.push(Range { low, high, nulls, exact, sum });
6877 }
6878 stripes.push(Stripe {
6879 rows: stripe_rows,
6880 parts,
6881 index,
6882 pages,
6883 memberships: Pages::from_slots(memberships)?,
6884 sieves: Pages::from_slots(sieves)?,
6885 part_ranges: Pages::from_slots(part_ranges)?,
6886 zone: Zone::from_ranges(ranges),
6887 });
6888 }
6889 if total != rows {
6890 return Err(invalid("table row count differs from stripes"));
6891 }
6892 let mut entry_counts = vec![0; width];
6895 let frequencies = if cur.done() {
6896 vec![None; width]
6897 } else {
6898 let frequency_magic = cur.take(8)?;
6899 let frequency_values = frequency_magic == FREQUENCIES;
6900 if !frequency_values && frequency_magic != FREQUENCIES_V2 {
6901 return Err(invalid("directory extension magic differs"));
6902 }
6903 if cur.u16()? as usize != width {
6904 return Err(invalid("frequency column count differs"));
6905 }
6906 let mut frequencies = Vec::with_capacity(width);
6907 for (field, entry_count) in fields.iter().zip(&mut entry_counts) {
6908 let start = cur.at;
6909 let summary = decode_summary(&mut cur, field, rows, frequency_values)?;
6910 *entry_count = summary.as_ref().map_or(0, |summary| summary.entries.len());
6911 frequencies.push(match (summary, stored_at) {
6912 (None, _) => None,
6913 (Some(summary), None) => Some(Frequencies::Held(summary)),
6914 (Some(_), Some(offset)) => Some(Frequencies::Stored {
6915 span: Span {
6916 offset: offset + start as u64,
6917 length: u32::try_from(cur.at - start)
6918 .map_err(|_| invalid("a frequency synopsis is too long"))?,
6919 },
6920 values: frequency_values,
6921 }),
6922 });
6923 }
6924 frequencies
6925 };
6926 let mut clustering = None;
6936 let mut sections = Vec::new();
6937 let mut pair_frequencies = Vec::new();
6938 let mut seen_pair_frequencies = false;
6939 let mut frequency_texts = vec![Vec::new(); width];
6940 let mut seen_frequency_texts = false;
6941 let mut host_groups = None;
6942 let mut seen_sections = false;
6943 let mut dictionary_payloads = Vec::new();
6944 let mut seen_payloads = false;
6945 let mut generation = 0;
6948 while !cur.done() {
6949 let mut tag = [0u8; 8];
6950 tag.copy_from_slice(cur.take(8)?);
6951 if &tag == PAIR_FREQUENCIES {
6952 if seen_pair_frequencies {
6953 return Err(invalid("directory names two pair frequency blocks"));
6954 }
6955 seen_pair_frequencies = true;
6956 let count = cur.u16()? as usize;
6957 if count > MAX_PAIR_FREQUENCIES {
6958 return Err(invalid("pair frequency count exceeds its bound"));
6959 }
6960 pair_frequencies = Vec::with_capacity(count);
6961 for _ in 0..count {
6962 let first = cur.u16()?;
6963 let second = cur.u16()?;
6964 let first_at = first as usize;
6965 let second_at = second as usize;
6966 if frequencies.get(first_at).and_then(Option::as_ref).is_none() {
6967 return Err(invalid("pair frequency first column has no synopsis"));
6968 }
6969 let first_entries = entry_counts[first_at];
6970 if !matches!(fields.get(second_at), Some(field) if field.ty == LogicalType::Varchar)
6971 || dictionaries.get(second_at).copied().flatten().is_none()
6972 {
6973 return Err(invalid("pair frequency second column has no stable dictionary"));
6974 }
6975 if pair_frequencies
6976 .iter()
6977 .any(|held: &PairFrequencySummary| held.first == first && held.second == second)
6978 {
6979 return Err(invalid("directory repeats a pair frequency summary"));
6980 }
6981 let omitted_max = cur.u64()?;
6982 if omitted_max > rows as u64 {
6983 return Err(invalid("pair frequency omitted count exceeds the table"));
6984 }
6985 let entries_count = cur.u16()? as usize;
6986 if entries_count > FREQUENCY_ENTRIES {
6987 return Err(invalid("pair frequency entry count exceeds its bound"));
6988 }
6989 let mut entries = Vec::with_capacity(entries_count);
6990 for _ in 0..entries_count {
6991 let first_entry = cur.u16()?;
6992 if first_entry as usize >= first_entries {
6993 return Err(invalid("pair frequency anchor is outside its synopsis"));
6994 }
6995 let second = match cur.u8()? {
6996 0 => None,
6997 1 => Some(cur.u32()?),
6998 _ => return Err(invalid("pair frequency string tag differs")),
6999 };
7000 let count = cur.u64()?;
7001 if count == 0 || count > rows as u64 {
7002 return Err(invalid("pair frequency count is outside the table"));
7003 }
7004 entries.push(PairFrequencyEntry { first_entry, second, count });
7005 }
7006 if entries.windows(2).any(|pair| pair[0].count < pair[1].count) {
7007 return Err(invalid("pair frequency entries are not descending"));
7008 }
7009 pair_frequencies.push(PairFrequencySummary { first, second, entries, omitted_max });
7010 }
7011 } else if &tag == FREQUENCY_TEXTS {
7012 if seen_frequency_texts {
7013 return Err(invalid("directory names two frequency text blocks"));
7014 }
7015 seen_frequency_texts = true;
7016 let columns = cur.u16()? as usize;
7017 if columns > width {
7018 return Err(invalid("frequency text column count exceeds the schema"));
7019 }
7020 for _ in 0..columns {
7021 let column = cur.u16()? as usize;
7022 if !frequency_texts.get(column).is_some_and(Vec::is_empty) {
7023 return Err(invalid("frequency text column is repeated or out of range"));
7024 }
7025 if !matches!(fields.get(column), Some(field) if field.ty == LogicalType::Varchar)
7026 || dictionaries.get(column).copied().flatten().is_none()
7027 || frequencies.get(column).and_then(Option::as_ref).is_none()
7028 {
7029 return Err(invalid("frequency texts belong to a non-string synopsis"));
7030 }
7031 let count = cur.u16()? as usize;
7032 if count == 0 || count != entry_counts[column] {
7033 return Err(invalid("frequency text count differs from its synopsis"));
7034 }
7035 let mut texts = Vec::with_capacity(count);
7036 for _ in 0..count {
7037 texts.push(match cur.u8()? {
7038 0 => None,
7039 1 => {
7040 let length = cur.u32()? as usize;
7041 let bytes = cur.take(length)?.to_vec();
7042 std::str::from_utf8(&bytes)
7043 .map_err(|_| invalid("frequency text is not UTF-8"))?;
7044 Some(bytes)
7045 }
7046 _ => return Err(invalid("frequency text tag differs")),
7047 });
7048 }
7049 frequency_texts[column] = texts;
7050 }
7051 } else if &tag == HOST_GROUPS {
7052 if host_groups.is_some() {
7053 return Err(invalid("directory names two host group blocks"));
7054 }
7055 let column = cur.u16()? as usize;
7056 if !matches!(fields.get(column), Some(field) if field.ty == LogicalType::Varchar)
7057 || dictionaries.get(column).copied().flatten().is_none()
7058 {
7059 return Err(invalid("host groups belong to a non-string dictionary"));
7060 }
7061 let omitted_max = cur.u64()?;
7062 if omitted_max > rows as u64 {
7063 return Err(invalid("host group bound exceeds the table"));
7064 }
7065 let count = cur.u16()? as usize;
7066 if count > host::CAPACITY {
7067 return Err(invalid("host group count exceeds its bound"));
7068 }
7069 let mut entries = Vec::with_capacity(count);
7070 let mut bytes = 0_usize;
7071 for _ in 0..count {
7072 let host_len = cur.u32()? as usize;
7073 bytes =
7074 bytes.checked_add(host_len).ok_or_else(|| invalid("host bytes overflow"))?;
7075 if bytes > host::BYTE_BUDGET {
7076 return Err(invalid("host groups exceed their byte budget"));
7077 }
7078 let host = std::str::from_utf8(cur.take(host_len)?)
7079 .map_err(|_| invalid("host is not UTF-8"))?
7080 .to_owned();
7081 let count = cur.u64()?;
7082 if count == 0 || count > rows as u64 {
7083 return Err(invalid("host group count exceeds the table"));
7084 }
7085 let bytes_sum = i128::from_le_bytes(
7086 cur.take(16)?
7087 .try_into()
7088 .map_err(|_| invalid("host length sum is truncated"))?,
7089 );
7090 if bytes_sum < 0 {
7091 return Err(invalid("host length sum is negative"));
7092 }
7093 let minimum_len = cur.u32()? as usize;
7094 bytes =
7095 bytes.checked_add(minimum_len).ok_or_else(|| invalid("host bytes overflow"))?;
7096 if bytes > host::BYTE_BUDGET {
7097 return Err(invalid("host groups exceed their byte budget"));
7098 }
7099 let minimum = std::str::from_utf8(cur.take(minimum_len)?)
7100 .map_err(|_| invalid("host minimum is not UTF-8"))?
7101 .to_owned();
7102 entries.push(host::HostEntry { host, count, bytes_sum, minimum });
7103 }
7104 if entries.windows(2).any(|pair| pair[0].count < pair[1].count)
7105 || entries.iter().any(|entry| entry.host.is_empty() || entry.minimum.is_empty())
7106 {
7107 return Err(invalid("host groups are not in certified order"));
7108 }
7109 host_groups = Some(host::HostSummary { column, omitted_max, entries });
7110 } else if &tag == CLUSTERING {
7111 if clustering.is_some() {
7112 return Err(invalid("directory names two clustering declarations"));
7113 }
7114 let bucket = Width::from_tag(cur.u8()?)
7115 .ok_or_else(|| invalid("clustering width tag differs"))?;
7116 let count = cur.u16()? as usize;
7117 let mut columns = Vec::with_capacity(count.min(fields.len()));
7118 for _ in 0..count {
7119 columns.push(u32::from(cur.u16()?));
7120 }
7121 clustering = Some(Clustering::new(columns, bucket, &fields).map_err(|_| {
7124 invalid("stored clustering declaration does not match the table it is on")
7125 })?);
7126 } else if &tag == SECTIONS {
7127 if seen_sections {
7128 return Err(invalid("directory names two section tables"));
7129 }
7130 seen_sections = true;
7131 generation = cur.u64()?;
7132 let count = cur.u16()? as usize;
7133 if count > MAX_SECTIONS {
7134 return Err(invalid("section count exceeds its bound"));
7135 }
7136 sections = Vec::with_capacity(count);
7137 for _ in 0..count {
7140 sections.push(Section::decode(cur.take(section::ENTRY_BYTES)?)?);
7141 }
7142 for held in §ions {
7143 let Some(end) = held.extent_page.checked_add(u64::from(held.extent_bytes)) else {
7144 return Err(invalid("a section's extent table overflows the file"));
7145 };
7146 if held.extent_bytes != 0 && (held.extent_page < HEADER || end > size) {
7150 return Err(invalid("a section's extent table is outside the file"));
7151 }
7152 if held.extents == 0 && held.extent_bytes != 0 {
7153 return Err(invalid("a section with no extents names an extent table"));
7154 }
7155 }
7156 } else if &tag == DICTIONARY_PAYLOADS {
7157 if seen_payloads {
7158 return Err(invalid("directory names two dictionary payload blocks"));
7159 }
7160 seen_payloads = true;
7161 let count = cur.u16()? as usize;
7162 if count != fields.len() {
7163 return Err(invalid("dictionary payload block does not match the table's columns"));
7164 }
7165 dictionary_payloads = Vec::with_capacity(count);
7166 for _ in 0..count {
7167 let bytes = cur.u64()?;
7168 if bytes > size {
7169 return Err(invalid("a dictionary payload is larger than the file"));
7170 }
7171 dictionary_payloads.push(bytes);
7172 }
7173 } else {
7174 return Err(invalid("directory extension magic differs"));
7175 }
7176 }
7177 if !cur.done() {
7178 return Err(invalid("directory has trailing bytes"));
7179 }
7180 Ok(Table {
7181 name,
7182 fields,
7183 stripes,
7184 rows,
7185 dictionaries,
7186 dictionary_payloads,
7187 distincts,
7188 frequencies,
7189 pair_frequencies,
7190 frequency_texts,
7191 host_groups,
7192 clustering,
7193 generation,
7194 sections,
7195 })
7196}
7197
7198fn put_bound(out: &mut Vec<u8>, bound: Option<&Bound>) -> Result<()> {
7200 bounds::put(out, bound)
7201}
7202
7203#[derive(Debug)]
7220struct Codes;
7221
7222impl chooser::Chooser for Codes {
7223 fn name(&self) -> &'static str {
7224 "codes"
7225 }
7226
7227 fn narrow_strings(
7228 &self,
7229 _values: &[&[u8]],
7230 offered: &[string::Kind],
7231 _depth: u8,
7232 ) -> Vec<string::Kind> {
7233 offered.to_vec()
7236 }
7237
7238 fn narrow_integers(
7239 &self,
7240 _values: &[i64],
7241 offered: &[integer::Kind],
7242 depth: u8,
7243 ) -> Vec<integer::Kind> {
7244 narrowed_to(Codes::keep(depth), offered)
7247 }
7248
7249 fn considers_integer(&self, kind: integer::Kind, depth: u8) -> bool {
7250 Codes::keep(depth).contains(&kind)
7251 }
7252}
7253
7254impl Codes {
7255 fn keep(depth: u8) -> &'static [integer::Kind] {
7256 if depth == 0 {
7257 &[integer::Kind::Constant, integer::Kind::Packed, integer::Kind::Rle]
7258 } else {
7259 &[integer::Kind::Constant, integer::Kind::Packed]
7260 }
7261 }
7262}
7263
7264fn narrowed_to(keep: &[integer::Kind], offered: &[integer::Kind]) -> Vec<integer::Kind> {
7272 let narrowed: Vec<integer::Kind> =
7273 offered.iter().copied().filter(|kind| keep.contains(kind)).collect();
7274 if narrowed.is_empty() { offered.to_vec() } else { narrowed }
7275}
7276
7277#[derive(Debug)]
7289struct Fixed;
7290
7291impl chooser::Chooser for Fixed {
7292 fn name(&self) -> &'static str {
7293 "fixed"
7294 }
7295
7296 fn narrow_strings(
7297 &self,
7298 _values: &[&[u8]],
7299 offered: &[string::Kind],
7300 _depth: u8,
7301 ) -> Vec<string::Kind> {
7302 offered.to_vec()
7303 }
7304
7305 fn narrow_integers(
7306 &self,
7307 _values: &[i64],
7308 offered: &[integer::Kind],
7309 depth: u8,
7310 ) -> Vec<integer::Kind> {
7311 narrowed_to(Fixed::keep(depth), offered)
7312 }
7313
7314 fn considers_integer(&self, kind: integer::Kind, depth: u8) -> bool {
7315 Fixed::keep(depth).contains(&kind)
7316 }
7317}
7318
7319impl Fixed {
7320 fn keep(depth: u8) -> &'static [integer::Kind] {
7321 if depth == 0 {
7322 &[
7323 integer::Kind::Constant,
7324 integer::Kind::Packed,
7325 integer::Kind::Delta,
7326 integer::Kind::Rle,
7327 integer::Kind::Sparse,
7328 integer::Kind::Strided,
7329 ]
7330 } else {
7331 &[integer::Kind::Constant, integer::Kind::Packed, integer::Kind::Delta]
7332 }
7333 }
7334}
7335
7336fn widened(data: &Data) -> Option<Vec<i64>> {
7343 match data {
7344 Data::Int8(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7345 Data::UInt8(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7346 Data::Int16(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7347 Data::UInt16(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7348 Data::Int32(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7349 Data::UInt32(values) => Some(values.iter().map(|value| i64::from(*value)).collect()),
7350 Data::Int64(values) => Some(values.to_vec()),
7351 _ => None,
7352 }
7353}
7354
7355trait Narrow: Copy {
7362 const BIASED: (u32, u64);
7367
7368 fn narrow(value: i64) -> Self;
7370}
7371
7372#[allow(clippy::cast_sign_loss, reason = "a residue is a bit pattern and not a number")]
7389fn residue<T: Narrow>(value: i64) -> u64 {
7390 let (bits, bias) = T::BIASED;
7391 (value as u64).wrapping_add(bias) >> bits
7392}
7393
7394macro_rules! narrows {
7399 ($($ty:ty => $bias:expr),* $(,)?) => {$(
7400 impl Narrow for $ty {
7401 const BIASED: (u32, u64) = (<$ty>::BITS, $bias);
7402
7403 #[allow(
7404 clippy::cast_possible_truncation,
7405 clippy::cast_sign_loss,
7406 reason = "the caller has checked the bits this truncates away"
7407 )]
7408 fn narrow(value: i64) -> Self {
7409 value as Self
7410 }
7411 }
7412 )*};
7413}
7414
7415narrows! {
7416 i8 => 1 << 7,
7417 u8 => 0,
7418 i16 => 1 << 15,
7419 u16 => 0,
7420 i32 => 1 << 31,
7421 u32 => 0,
7422}
7423
7424fn fit<T: Narrow>(values: &[i64]) -> Result<Vec<T>> {
7437 let mut spilled = 0u64;
7438 for value in values {
7439 spilled |= residue::<T>(*value);
7440 }
7441 if spilled != 0 {
7442 return Err(invalid("page value is not of its type"));
7443 }
7444 Ok(values.iter().map(|value| T::narrow(*value)).collect())
7445}
7446
7447fn narrowed(ty: &LogicalType, values: Vec<i64>) -> Result<Data> {
7452 Ok(match ty {
7453 LogicalType::TinyInt => Data::Int8(fit::<i8>(&values)?.into()),
7454 LogicalType::UTinyInt => Data::UInt8(fit::<u8>(&values)?.into()),
7455 LogicalType::SmallInt => Data::Int16(fit::<i16>(&values)?.into()),
7456 LogicalType::USmallInt => Data::UInt16(fit::<u16>(&values)?.into()),
7457 LogicalType::Integer | LogicalType::Date => Data::Int32(fit::<i32>(&values)?.into()),
7458 LogicalType::UInteger => Data::UInt32(fit::<u32>(&values)?.into()),
7459 LogicalType::BigInt
7460 | LogicalType::Timestamp
7461 | LogicalType::Time
7462 | LogicalType::TimeTz
7463 | LogicalType::TimestampTz
7464 | LogicalType::TimestampS
7465 | LogicalType::TimestampMs
7466 | LogicalType::TimestampNs => Data::Int64(values.into()),
7467 LogicalType::Decimal { .. } => match ty.physical() {
7470 PhysicalType::Int16 => Data::Int16(fit::<i16>(&values)?.into()),
7471 PhysicalType::Int32 => Data::Int32(fit::<i32>(&values)?.into()),
7472 PhysicalType::Int64 => Data::Int64(values.into()),
7473 _ => return Err(invalid("cascade codec belongs to a decimal that is not an integer")),
7474 },
7475 _ => return Err(invalid("cascade codec belongs to a page that is not integers")),
7476 })
7477}
7478
7479fn plain_width(ty: &LogicalType) -> Option<usize> {
7482 Some(match ty {
7483 LogicalType::TinyInt | LogicalType::UTinyInt => 1,
7484 LogicalType::SmallInt | LogicalType::USmallInt => 2,
7485 LogicalType::Integer | LogicalType::UInteger | LogicalType::Date => 4,
7486 LogicalType::BigInt
7487 | LogicalType::Timestamp
7488 | LogicalType::Time
7489 | LogicalType::TimeTz
7490 | LogicalType::TimestampTz
7491 | LogicalType::TimestampS
7492 | LogicalType::TimestampMs
7493 | LogicalType::TimestampNs => 8,
7494 LogicalType::Decimal { .. } => match ty.physical() {
7495 PhysicalType::Int16 => 2,
7496 PhysicalType::Int32 => 4,
7497 PhysicalType::Int64 => 8,
7498 _ => return None,
7501 },
7502 _ => return None,
7503 })
7504}
7505
7506fn cascaded(
7512 flat: &Vector,
7513 ty: &LogicalType,
7514 packed: Option<&Packed<'_>>,
7515) -> Result<Option<Vec<u8>>> {
7516 let (Some(width), Some(data)) = (plain_width(ty), flat.data()) else { return Ok(None) };
7517 let Some(values) = widened(data) else { return Ok(None) };
7518 let plain = values.len().saturating_mul(width);
7519 let best = match packed {
7520 Some(packed) => plain.min(21 + size_of_val(packed.words())),
7522 None => plain,
7523 };
7524 let out = integer::encode_with(&values, &Fixed)?;
7525 Ok((out.len() < best).then_some(out))
7526}
7527
7528fn text_compressed(flat: &Vector) -> Result<Option<Vec<u8>>> {
7566 let mut values: Vec<&[u8]> = Vec::with_capacity(flat.len());
7567 let mut payload = 0_usize;
7568 for row in 0..flat.len() {
7569 let text = flat.text_at(row).unwrap_or("").as_bytes();
7570 payload = payload.saturating_add(text.len());
7571 values.push(text);
7572 }
7573 let plain = (flat.len() + 1).saturating_mul(4).saturating_add(payload);
7575 let Some(out) = string::encode_only(string::Kind::Fsst, &values)? else {
7576 return Ok(None);
7577 };
7578 Ok((out.len() < plain).then_some(out))
7579}
7580
7581fn encoded_codes(codes: &[u32]) -> Result<Option<Vec<u8>>> {
7582 let wide: Vec<i64> = codes.iter().map(|code| i64::from(*code)).collect();
7583 let coded = integer::encode_with(&wide, &Codes)?;
7584 let plain = codes.len().saturating_mul(size_of::<u32>());
7585 Ok((coded.len() < plain).then_some(coded))
7586}
7587
7588fn push_validity(out: &mut Vec<u8>, flat: &Vector) {
7591 let flag = match flat.validity() {
7592 Validity::AllValid => 0,
7593 Validity::AllInvalid => 1,
7594 Validity::Mask(_) => 2,
7595 };
7596 out.push(flag);
7597 if flag == 2 {
7598 for group in (0..flat.len()).step_by(8) {
7599 let mut bits = 0_u8;
7600 for bit in 0..8 {
7601 if group + bit < flat.len() && !flat.is_null_at(group + bit) {
7602 bits |= 1 << bit;
7603 }
7604 }
7605 out.push(bits);
7606 }
7607 }
7608}
7609
7610fn coded_page(codes: &[u32], validity: &[u8]) -> Result<Vec<u8>> {
7617 let coded = encoded_codes(codes)?;
7618 let mut out = Vec::with_capacity(
7619 1 + validity.len() + coded.as_ref().map_or(size_of_val(codes), Vec::len),
7620 );
7621 out.push(if coded.is_some() { 4 } else { 3 });
7622 out.extend_from_slice(validity);
7623 match coded {
7624 Some(coded) => out.extend_from_slice(&coded),
7625 None => {
7626 for &code in codes {
7627 put_u32(&mut out, code);
7628 }
7629 }
7630 }
7631 Ok(out)
7632}
7633
7634fn encode(vector: &Vector) -> Result<Vec<u8>> {
7637 let ty = vector.logical_type();
7638 let flat = vector.flatten()?;
7640 let mut out = Vec::new();
7641 let dictionary = if ty == &LogicalType::Varchar { string_dictionary(&flat)? } else { None };
7642 let compressed_text = if dictionary.is_none() && ty == &LogicalType::Varchar {
7643 text_compressed(&flat)?
7644 } else {
7645 None
7646 };
7647 let packed_vector = if dictionary.is_none() { Some(flat.bit_packed()?) } else { None };
7648 let packed = packed_vector.as_ref().and_then(Vector::packed_parts);
7649 let cascade = if dictionary.is_none() { cascaded(&flat, ty, packed.as_ref())? } else { None };
7653 out.push(if cascade.is_some() {
7654 5
7655 } else if dictionary.is_some() {
7656 1
7657 } else if compressed_text.is_some() {
7658 6
7659 } else if packed.is_some() {
7660 2
7661 } else {
7662 0
7663 });
7664 push_validity(&mut out, &flat);
7665 if let Some(cascade) = cascade {
7666 out.extend_from_slice(&cascade);
7667 return Ok(out);
7668 }
7669 if let Some(dictionary) = dictionary {
7670 out.extend_from_slice(&dictionary);
7671 return Ok(out);
7672 }
7673 if let Some(compressed_text) = compressed_text {
7674 out.extend_from_slice(&compressed_text);
7675 return Ok(out);
7676 }
7677 if let Some(packed) = packed {
7678 if packed.offset() != 0 {
7679 return Err(invalid("writer received a sliced packed vector"));
7680 }
7681 out.push(u8::try_from(packed.width()).map_err(|_| invalid("packed width overflow"))?);
7682 out.extend_from_slice(&packed.base().to_le_bytes());
7683 put_u32(
7684 &mut out,
7685 u32::try_from(packed.words().len()).map_err(|_| invalid("too many packed words"))?,
7686 );
7687 for word in packed.words() {
7688 put_u64(&mut out, *word);
7689 }
7690 return Ok(out);
7691 }
7692 let data = flat.data().ok_or_else(|| invalid("scalar column did not flatten"))?;
7693 match (ty, data) {
7694 (LogicalType::TinyInt, Data::Int8(values)) => {
7695 for value in &**values {
7696 out.extend_from_slice(&value.to_le_bytes());
7697 }
7698 }
7699 (LogicalType::UTinyInt, Data::UInt8(values)) => {
7700 for value in &**values {
7701 out.extend_from_slice(&value.to_le_bytes());
7702 }
7703 }
7704 (LogicalType::SmallInt, Data::Int16(values)) => {
7705 for value in &**values {
7706 out.extend_from_slice(&value.to_le_bytes());
7707 }
7708 }
7709 (LogicalType::USmallInt, Data::UInt16(values)) => {
7710 for value in &**values {
7711 out.extend_from_slice(&value.to_le_bytes());
7712 }
7713 }
7714 (LogicalType::UInteger, Data::UInt32(values)) => {
7715 for value in &**values {
7716 out.extend_from_slice(&value.to_le_bytes());
7717 }
7718 }
7719 (LogicalType::UBigInt, Data::UInt64(values)) => {
7720 for value in &**values {
7721 out.extend_from_slice(&value.to_le_bytes());
7722 }
7723 }
7724 (LogicalType::Integer | LogicalType::Date, Data::Int32(values)) => {
7725 for value in &**values {
7726 out.extend_from_slice(&value.to_le_bytes());
7727 }
7728 }
7729 (
7730 LogicalType::BigInt
7731 | LogicalType::Timestamp
7732 | LogicalType::Time
7733 | LogicalType::TimeTz
7734 | LogicalType::TimestampTz
7735 | LogicalType::TimestampS
7736 | LogicalType::TimestampMs
7737 | LogicalType::TimestampNs,
7738 Data::Int64(values),
7739 ) => {
7740 for value in &**values {
7741 out.extend_from_slice(&value.to_le_bytes());
7742 }
7743 }
7744 (LogicalType::HugeInt | LogicalType::Uuid, Data::Int128(values)) => {
7747 for value in &**values {
7748 out.extend_from_slice(&value.to_le_bytes());
7749 }
7750 }
7751 (LogicalType::UHugeInt, Data::UInt128(values)) => {
7752 for value in &**values {
7753 out.extend_from_slice(&value.to_le_bytes());
7754 }
7755 }
7756 (LogicalType::Float, Data::Float32(values)) => {
7759 for value in &**values {
7760 out.extend_from_slice(&value.to_le_bytes());
7761 }
7762 }
7763 (LogicalType::Double, Data::Float64(values)) => {
7764 for value in &**values {
7765 out.extend_from_slice(&value.to_le_bytes());
7766 }
7767 }
7768 (LogicalType::Interval, Data::Interval(values)) => {
7772 for (months, days, micros) in &**values {
7773 out.extend_from_slice(&months.to_le_bytes());
7774 out.extend_from_slice(&days.to_le_bytes());
7775 out.extend_from_slice(µs.to_le_bytes());
7776 }
7777 }
7778 (LogicalType::Boolean, Data::Bool(values)) => {
7779 for value in &**values {
7780 out.push(u8::from(*value));
7781 }
7782 }
7783 (LogicalType::Decimal { .. }, Data::Int16(values)) => {
7786 for value in &**values {
7787 out.extend_from_slice(&value.to_le_bytes());
7788 }
7789 }
7790 (LogicalType::Decimal { .. }, Data::Int32(values)) => {
7791 for value in &**values {
7792 out.extend_from_slice(&value.to_le_bytes());
7793 }
7794 }
7795 (LogicalType::Decimal { .. }, Data::Int64(values)) => {
7796 for value in &**values {
7797 out.extend_from_slice(&value.to_le_bytes());
7798 }
7799 }
7800 (LogicalType::Decimal { .. }, Data::Int128(values)) => {
7801 for value in &**values {
7802 out.extend_from_slice(&value.to_le_bytes());
7803 }
7804 }
7805 (LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit, Data::Varlen(values)) => {
7810 let mut bytes = Vec::new();
7811 put_u32(&mut out, 0);
7812 for row in 0..vector.len() {
7813 let value = values.bytes(row).ok_or_else(|| invalid("string view is invalid"))?;
7814 bytes.extend_from_slice(value);
7815 put_u32(
7816 &mut out,
7817 u32::try_from(bytes.len())
7818 .map_err(|_| invalid("string payload exceeds 4GiB"))?,
7819 );
7820 }
7821 out.extend_from_slice(&bytes);
7822 }
7823 _ => return Err(Error::not_implemented(format!("native page for {ty}"))),
7824 }
7825 Ok(out)
7826}
7827
7828fn put_varint(out: &mut Vec<u8>, mut value: u32) {
7829 while value >= 0x80 {
7830 out.push((value as u8 & 0x7f) | 0x80);
7831 value >>= 7;
7832 }
7833 out.push(value as u8);
7834}
7835
7836fn unique_codes(codes: &[u32]) -> Vec<u32> {
7838 let mut unique = codes.to_vec();
7839 unique.sort_unstable();
7840 unique.dedup();
7841 unique
7842}
7843
7844fn merged_codes(lists: Vec<Vec<u32>>) -> Vec<u32> {
7850 let mut lists = lists;
7851 while lists.len() > 1 {
7852 let mut next = Vec::with_capacity(lists.len().div_ceil(2));
7853 for pair in lists.chunks(2) {
7854 match pair {
7855 [left, right] => next.push(merged_pair(left, right)),
7856 [only] => next.push(only.clone()),
7857 _ => {}
7858 }
7859 }
7860 lists = next;
7861 }
7862 lists.pop().unwrap_or_default()
7863}
7864
7865fn merged_pair(left: &[u32], right: &[u32]) -> Vec<u32> {
7866 let mut out = Vec::with_capacity(left.len().saturating_add(right.len()));
7867 let mut at = 0;
7868 let mut to = 0;
7869 while at < left.len() && to < right.len() {
7870 match left[at].cmp(&right[to]) {
7871 Ordering::Less => {
7872 out.push(left[at]);
7873 at += 1;
7874 }
7875 Ordering::Greater => {
7876 out.push(right[to]);
7877 to += 1;
7878 }
7879 Ordering::Equal => {
7880 out.push(left[at]);
7881 at += 1;
7882 to += 1;
7883 }
7884 }
7885 }
7886 out.extend_from_slice(&left[at..]);
7887 out.extend_from_slice(&right[to..]);
7888 out
7889}
7890
7891fn merged_range(ranges: impl Iterator<Item = Range>) -> Range {
7896 let mut merged = Range::default();
7897 let mut first = true;
7898 for range in ranges {
7899 merged.nulls = merged.nulls.saturating_add(range.nulls);
7900 merged.sum = match (merged.sum.take(), range.sum) {
7904 (Some(held), Some(next)) if !first => held.checked_add(next),
7905 (_, next) if first => next,
7906 _ => None,
7907 };
7908 merged.exact = if first { range.exact } else { merged.exact && range.exact };
7909 if first {
7910 merged.low = range.low;
7911 merged.high = range.high;
7912 first = false;
7913 continue;
7914 }
7915 merged.low = match (merged.low.take(), range.low) {
7916 (Some(held), Some(next)) => Some(held.smaller(next)),
7917 _ => None,
7918 };
7919 merged.high = match (merged.high.take(), range.high) {
7920 (Some(held), Some(next)) => Some(held.larger(next)),
7921 _ => None,
7922 };
7923 }
7924 merged
7925}
7926
7927fn shortened(bound: Option<Bound>, high: bool) -> Option<Bound> {
7940 match bound {
7941 Some(Bound::Bytes(mut value)) if value.len() > PART_BOUND_BYTES => {
7942 value.truncate(PART_BOUND_BYTES);
7943 if !high {
7944 return Some(Bound::Bytes(value));
7945 }
7946 while let Some(last) = value.pop() {
7947 if last < u8::MAX {
7948 value.push(last + 1);
7949 return Some(Bound::Bytes(value));
7950 }
7951 }
7952 None
7953 }
7954 other => other,
7955 }
7956}
7957
7958fn encode_part_ranges(ranges: &[Range]) -> Result<Vec<u8>> {
7966 let mut out = Vec::new();
7967 put_u32(
7968 &mut out,
7969 u32::try_from(ranges.len()).map_err(|_| invalid("too many parts in a stripe"))?,
7970 );
7971 for range in ranges {
7972 put_bound(&mut out, shortened(range.low.clone(), false).as_ref())?;
7973 put_bound(&mut out, shortened(range.high.clone(), true).as_ref())?;
7974 put_u32(&mut out, u32::try_from(range.nulls).map_err(|_| invalid("null count overflow"))?);
7975 }
7976 Ok(out)
7977}
7978
7979fn decode_part_ranges(bytes: &[u8]) -> Result<Vec<Range>> {
7981 let mut cur = Cursor::new(bytes);
7982 let parts = cur.u32()? as usize;
7983 let mut out = Vec::new();
7984 for _ in 0..parts {
7985 let low = cur.bound()?;
7986 let high = cur.bound()?;
7987 let nulls = cur.u32()? as usize;
7988 out.push(Range { low, high, nulls, exact: false, sum: None });
7989 }
7990 Ok(out)
7991}
7992
7993fn encode_sieves<'a>(sieves: impl Iterator<Item = &'a Option<Sieve>>) -> Result<Vec<u8>> {
7994 let held: Vec<&Option<Sieve>> = sieves.collect();
7995 let mut out = Vec::new();
7996 put_u32(
7997 &mut out,
7998 u32::try_from(held.len()).map_err(|_| invalid("too many parts in a stripe"))?,
7999 );
8000 for sieve in &held {
8001 let length = sieve.as_ref().map_or(0, Sieve::len);
8002 put_u32(&mut out, u32::try_from(length).map_err(|_| invalid("sieve length overflow"))?);
8003 }
8004 for sieve in held.into_iter().flatten() {
8006 out.extend_from_slice(&sieve.to_bytes());
8007 }
8008 Ok(out)
8009}
8010
8011fn decode_sieves(bytes: &[u8]) -> Result<Vec<Option<Sieve>>> {
8017 let parts = u32::from_le_bytes(
8018 bytes
8019 .get(..4)
8020 .ok_or_else(|| invalid("sieve page is truncated"))?
8021 .try_into()
8022 .map_err(|_| invalid("sieve page is truncated"))?,
8023 ) as usize;
8024 let mut lengths = Vec::with_capacity(parts);
8025 for part in 0..parts {
8026 let at = 4 + part * 4;
8027 let field = bytes.get(at..at + 4).ok_or_else(|| invalid("sieve page is truncated"))?;
8028 lengths.push(u32::from_le_bytes(
8029 field.try_into().map_err(|_| invalid("sieve page is truncated"))?,
8030 ) as usize);
8031 }
8032 let mut at = 4 + parts * 4;
8033 let mut out = Vec::with_capacity(parts);
8034 for length in lengths {
8035 if length == 0 {
8036 out.push(None);
8037 continue;
8038 }
8039 let end = at.checked_add(length).ok_or_else(|| invalid("sieve page is truncated"))?;
8040 let field = bytes.get(at..end).ok_or_else(|| invalid("sieve page is truncated"))?;
8041 out.push(Sieve::from_bytes(field));
8042 at = end;
8043 }
8044 if at != bytes.len() {
8045 return Err(invalid("sieve page has trailing bytes"));
8046 }
8047 Ok(out)
8048}
8049
8050fn encode_membership(unique: &[u32]) -> Vec<u8> {
8056 let mut out = Vec::with_capacity(unique.len().saturating_mul(2).saturating_add(5));
8057 put_varint(&mut out, u32::try_from(unique.len()).unwrap_or(u32::MAX));
8058 let mut previous = 0;
8059 for (at, &code) in unique.iter().enumerate() {
8060 put_varint(&mut out, if at == 0 { code } else { code - previous });
8061 previous = code;
8062 }
8063 out
8064}
8065
8066fn take_varint(bytes: &[u8], at: &mut usize) -> Result<u32> {
8067 let mut value = 0_u32;
8068 for shift in (0..35).step_by(7) {
8069 let byte = *bytes.get(*at).ok_or_else(|| invalid("membership varint is truncated"))?;
8070 *at += 1;
8071 let part = u32::from(byte & 0x7f);
8072 if shift == 28 && part > 0x0f {
8073 return Err(invalid("membership varint overflow"));
8074 }
8075 value = value
8076 .checked_add(
8077 part.checked_shl(shift).ok_or_else(|| invalid("membership varint overflow"))?,
8078 )
8079 .ok_or_else(|| invalid("membership varint overflow"))?;
8080 if byte & 0x80 == 0 {
8081 return Ok(value);
8082 }
8083 }
8084 Err(invalid("membership varint is too long"))
8085}
8086
8087fn decode_membership(bytes: &[u8]) -> Result<Vec<u32>> {
8088 let mut at = 0;
8089 let count = take_varint(bytes, &mut at)? as usize;
8090 let mut codes = Vec::with_capacity(count);
8091 let mut previous = 0_u32;
8092 for index in 0..count {
8093 let delta = take_varint(bytes, &mut at)?;
8094 let code = if index == 0 {
8095 delta
8096 } else {
8097 previous.checked_add(delta).ok_or_else(|| invalid("membership code overflow"))?
8098 };
8099 if index > 0 && code <= previous {
8100 return Err(invalid("membership codes are not increasing"));
8101 }
8102 codes.push(code);
8103 previous = code;
8104 }
8105 if at != bytes.len() {
8106 return Err(invalid("membership page has trailing bytes"));
8107 }
8108 Ok(codes)
8109}
8110
8111fn string_dictionary(vector: &Vector) -> Result<Option<Vec<u8>>> {
8112 let mut by_text = HashMap::new();
8113 let mut values = Vec::new();
8114 let mut codes = Vec::with_capacity(vector.len());
8115 let mut plain_bytes = 0_usize;
8116 for row in 0..vector.len() {
8117 let text = vector.text_at(row).unwrap_or("");
8118 plain_bytes = plain_bytes.saturating_add(text.len());
8119 let code = match by_text.get(text) {
8120 Some(&code) => code,
8121 None => {
8122 let code = u32::try_from(values.len())
8123 .map_err(|_| invalid("too many dictionary values"))?;
8124 by_text.insert(text, code);
8125 values.push(text);
8126 code
8127 }
8128 };
8129 codes.push(code);
8130 }
8131 let dictionary_bytes = values.iter().map(|value| value.len()).sum::<usize>();
8132 let encoded = 8_usize
8133 .saturating_add((values.len() + 1).saturating_mul(4))
8134 .saturating_add(dictionary_bytes)
8135 .saturating_add(codes.len().saturating_mul(4));
8136 let plain = (vector.len() + 1).saturating_mul(4).saturating_add(plain_bytes);
8137 if encoded >= plain {
8138 return Ok(None);
8139 }
8140 let mut out = Vec::with_capacity(encoded);
8141 put_u32(
8142 &mut out,
8143 u32::try_from(values.len()).map_err(|_| invalid("too many dictionary values"))?,
8144 );
8145 put_u32(
8146 &mut out,
8147 u32::try_from(dictionary_bytes).map_err(|_| invalid("dictionary payload exceeds 4GiB"))?,
8148 );
8149 let mut offset = 0_u32;
8150 put_u32(&mut out, offset);
8151 for value in &values {
8152 offset = offset
8153 .checked_add(
8154 u32::try_from(value.len()).map_err(|_| invalid("dictionary value is too long"))?,
8155 )
8156 .ok_or_else(|| invalid("dictionary payload exceeds 4GiB"))?;
8157 put_u32(&mut out, offset);
8158 }
8159 for value in values {
8160 out.extend_from_slice(value.as_bytes());
8161 }
8162 for code in codes {
8163 put_u32(&mut out, code);
8164 }
8165 Ok(Some(out))
8166}
8167
8168struct EncodedDictionary {
8169 index: Vec<u8>,
8170 ranks: Vec<u8>,
8171 grams: Vec<u8>,
8172}
8173
8174fn sort_by_value<'a>(codes: &mut [u32], values: impl Fn(u32) -> &'a [u8]) {
8215 let mut work = vec![(0, codes.len(), 0)];
8216 let mut keyed: Vec<(u64, u8, u32)> = Vec::new();
8217 while let Some((from, to, depth)) = work.pop() {
8218 let part = &mut codes[from..to];
8219 keyed.clear();
8220 keyed.extend(part.iter().map(|&code| {
8221 let value = values(code);
8222 let rest = value.get(depth..).unwrap_or_default();
8223 (head(rest), rest.len().min(8) as u8, code)
8224 }));
8225 keyed.sort_unstable();
8226 for (slot, entry) in part.iter_mut().zip(keyed.iter()) {
8227 *slot = entry.2;
8228 }
8229 let mut start = 0;
8230 while start < keyed.len() {
8231 let (key, taken, _) = keyed[start];
8232 let mut end = start + 1;
8233 while end < keyed.len() && keyed[end].0 == key && keyed[end].1 == taken {
8234 end += 1;
8235 }
8236 if taken == 8 && end - start > 1 {
8237 work.push((from + start, from + end, depth + 8));
8238 }
8239 start = end;
8240 }
8241 }
8242}
8243
8244const PARALLEL_SORT_MIN: usize = 1 << 16;
8246
8247const BUCKETS_PER_WORKER: usize = 4;
8250
8251const SAMPLES_PER_BUCKET: usize = 32;
8253
8254fn sort_by_value_across<'a>(
8272 codes: &mut [u32],
8273 values: impl Fn(u32) -> &'a [u8] + Sync,
8274 workers: usize,
8275) {
8276 if workers <= 1 || codes.len() < PARALLEL_SORT_MIN {
8277 sort_by_value(codes, values);
8278 return;
8279 }
8280 let buckets = workers * BUCKETS_PER_WORKER;
8281 let wanted = buckets * SAMPLES_PER_BUCKET;
8282 let mut sample = (0..wanted).map(|at| codes[at * codes.len() / wanted]).collect::<Vec<_>>();
8283 sort_by_value(&mut sample, &values);
8284 let splitters =
8285 (1..buckets).map(|cut| values(sample[cut * sample.len() / buckets])).collect::<Vec<_>>();
8286 let values = &values;
8287 let splitters = &splitters;
8288 let per = codes.len().div_ceil(workers);
8289 let places = std::thread::scope(|scope| {
8291 codes
8292 .chunks(per)
8293 .map(|run| {
8294 scope.spawn(move || {
8295 run.iter()
8296 .map(|&code| {
8297 let value = values(code);
8298 splitters.partition_point(|splitter| *splitter <= value) as u32
8299 })
8300 .collect::<Vec<_>>()
8301 })
8302 })
8303 .collect::<Vec<_>>()
8304 .into_iter()
8305 .flat_map(|handle| {
8306 handle.join().unwrap_or_else(|panic| std::panic::resume_unwind(panic))
8307 })
8308 .collect::<Vec<_>>()
8309 });
8310 let mut starts = vec![0_usize; buckets + 1];
8311 for &place in &places {
8312 starts[place as usize + 1] += 1;
8313 }
8314 for bucket in 0..buckets {
8315 starts[bucket + 1] += starts[bucket];
8316 }
8317 let mut laid = vec![0_u32; codes.len()];
8318 let mut next = starts.clone();
8319 for (&code, &place) in codes.iter().zip(&places) {
8320 laid[next[place as usize]] = code;
8321 next[place as usize] += 1;
8322 }
8323 drop(places);
8324 let mut runs = Vec::with_capacity(buckets);
8325 let mut rest = laid.as_mut_slice();
8326 for bucket in 0..buckets {
8327 let (run, after) = rest.split_at_mut(starts[bucket + 1] - starts[bucket]);
8328 runs.push(run);
8329 rest = after;
8330 }
8331 runs.sort_by_key(|run| run.len());
8333 let queue = Mutex::new(runs);
8334 std::thread::scope(|scope| {
8335 for _ in 0..workers {
8336 scope.spawn(|| {
8337 loop {
8338 let taken =
8339 queue.lock().unwrap_or_else(std::sync::PoisonError::into_inner).pop();
8340 let Some(run) = taken else { break };
8341 sort_by_value(run, values);
8342 }
8343 });
8344 }
8345 });
8346 codes.copy_from_slice(&laid);
8347}
8348
8349fn head(bytes: &[u8]) -> u64 {
8351 let mut word = [0; 8];
8352 let take = bytes.len().min(8);
8353 word[..take].copy_from_slice(&bytes[..take]);
8354 u64::from_be_bytes(word)
8355}
8356
8357fn encode_global_dictionary(
8368 dictionary: &GlobalDictionary,
8369 order: &[(u64, u32)],
8370 places: &[Placed],
8371 scattered: bool,
8372) -> Result<EncodedDictionary> {
8373 let values = dictionary.values();
8374 if order.len() != values {
8375 return Err(invalid("global dictionary order does not cover its values"));
8376 }
8377 let blocks = values.div_ceil(TEXT_PAYLOAD_VALUES);
8378 if places.len() != blocks {
8379 return Err(invalid("global dictionary payload is not the blocks it says it is"));
8380 }
8381 if dictionary.grams.len() != blocks {
8382 return Err(invalid("global dictionary signatures do not cover its blocks"));
8383 }
8384 let (ranks, rank_ends) = encode_ranks(order, code_width(values))?;
8385 let rank_blocks = values.div_ceil(TEXT_RANK_BLOCK);
8386 let offset_bits = offset_width(&dictionary.ends);
8387 let payload_words = if scattered { 3 } else { 2 };
8388 let index_len = DICTIONARY_HEADER
8389 .checked_add(offset_bytes(values, offset_bits))
8390 .and_then(|len| len.checked_add(blocks.checked_mul(payload_words * 8)?))
8391 .and_then(|len| len.checked_add(rank_blocks.checked_mul(16)?))
8392 .and_then(|len| len.checked_add(8))
8393 .ok_or_else(|| invalid("global dictionary index length overflow"))?;
8394 let mut index = Vec::with_capacity(index_len);
8395 put_u32(
8396 &mut index,
8397 u32::try_from(values).map_err(|_| invalid("global dictionary has too many values"))?,
8398 );
8399 put_u32(&mut index, TEXT_PAYLOAD_VALUES as u32);
8400 put_u32(
8401 &mut index,
8402 u32::try_from(blocks).map_err(|_| invalid("global dictionary has too many blocks"))?,
8403 );
8404 let flag = (if scattered { DICTIONARY_SCATTERED } else { 0 }) | DICTIONARY_GRAMS;
8405 put_u32(&mut index, offset_bits as u32 | flag);
8406 encode_offsets(&dictionary.ends, offset_bits, &mut index)?;
8407 let mut end = 0_u64;
8412 for place in places {
8413 if scattered {
8414 put_u64(&mut index, place.start);
8415 put_u64(&mut index, place.length);
8416 } else {
8417 end = end
8418 .checked_add(place.length)
8419 .ok_or_else(|| invalid("global dictionary payload overflow"))?;
8420 put_u64(&mut index, end);
8421 }
8422 }
8423 for place in places {
8424 put_u64(&mut index, place.hash);
8425 }
8426 if rank_ends.len() != rank_blocks {
8429 return Err(invalid("global dictionary order is not the blocks it says it is"));
8430 }
8431 for end in &rank_ends {
8432 put_u64(&mut index, *end);
8433 }
8434 let mut at = 0_usize;
8435 for end in &rank_ends {
8436 let end = usize::try_from(*end).map_err(|_| invalid("global dictionary order overflow"))?;
8437 put_u64(&mut index, checksum(&ranks[at..end]));
8438 at = end;
8439 }
8440 let gram_len = blocks
8441 .checked_mul(TEXT_GRAM_BYTES)
8442 .ok_or_else(|| invalid("global dictionary signature count overflow"))?;
8443 let mut grams = Vec::with_capacity(gram_len);
8444 for block in &dictionary.grams {
8445 grams.extend_from_slice(block);
8446 }
8447 put_u64(&mut index, checksum(&grams));
8448 if index.len() != index_len {
8449 return Err(invalid("global dictionary index is not the length it was laid out for"));
8450 }
8451 Ok(EncodedDictionary { index, ranks, grams })
8452}
8453
8454const PAYLOAD_SAMPLE_BLOCKS: usize = 8;
8461
8462fn payload_shapes() -> Vec<chooser::Settled> {
8488 let integers = vec![integer::Kind::Packed];
8489 [
8490 vec![string::Kind::Front, string::Kind::Lz],
8491 vec![string::Kind::Lz, string::Kind::Fsst],
8492 vec![string::Kind::Lz, string::Kind::Plain],
8493 vec![string::Kind::Fsst],
8494 vec![string::Kind::Plain],
8495 ]
8496 .into_iter()
8497 .map(|strings| chooser::Settled::new(strings, integers.clone()))
8498 .collect()
8499}
8500
8501fn synced(file: &File, profile: Option<&LoadProfile>) -> Result<()> {
8523 let started = profile.map(|_| std::time::Instant::now());
8524 file.sync_all().map_err(io)?;
8525 if let (Some(profile), Some(started)) = (profile, started) {
8526 profile.waited(
8527 Stage::Publish,
8528 u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX),
8529 );
8530 }
8531 Ok(())
8532}
8533
8534fn encode_ready(dictionaries: &mut [Option<GlobalDictionary>]) -> Result<()> {
8535 for dictionary in dictionaries.iter_mut().flatten() {
8536 dictionary.settle()?;
8537 }
8538 encode_waiting(dictionaries, false)
8543}
8544
8545fn finish_dictionaries(dictionaries: &mut [Option<GlobalDictionary>]) -> Result<()> {
8552 for dictionary in dictionaries.iter_mut().flatten() {
8553 dictionary.seal_rest();
8554 }
8555 encode_waiting(dictionaries, true)
8556}
8557
8558fn encode_waiting(dictionaries: &mut [Option<GlobalDictionary>], closing: bool) -> Result<()> {
8561 let jobs = dictionaries
8562 .iter()
8563 .enumerate()
8564 .filter(|(_, held)| held.as_ref().is_some_and(|held| closing || held.shape.is_some()))
8565 .flat_map(|(column, held)| {
8566 (0..held.as_ref().map_or(0, |held| held.waiting.len())).map(move |at| (column, at))
8567 })
8568 .collect::<Vec<_>>();
8569 if jobs.is_empty() {
8570 return Ok(());
8571 }
8572 let one = |column: usize, at: usize| -> Result<(usize, usize, Vec<u8>)> {
8573 let held = dictionaries[column].as_ref().ok_or_else(|| Error::internal("no dictionary"))?;
8574 Ok((column, at, held.encode_waiting(at)?))
8575 };
8576 let workers = std::thread::available_parallelism()
8577 .map_or(1, usize::from)
8578 .min(MAX_FREQUENCY_WORKERS)
8579 .min(jobs.len());
8580 let made = if workers <= 1 {
8581 jobs.iter().map(|&(column, at)| one(column, at)).collect::<Result<Vec<_>>>()?
8582 } else {
8583 let next = AtomicUsize::new(0);
8584 let jobs = &jobs;
8585 let pieces = std::thread::scope(|scope| {
8586 (0..workers)
8587 .map(|_| {
8588 scope.spawn(|| {
8589 let mut mine = Vec::new();
8590 loop {
8591 let job = next.fetch_add(1, Atomic::Relaxed);
8592 let Some(&(column, at)) = jobs.get(job) else { break };
8593 mine.push(one(column, at)?);
8594 }
8595 Ok(mine)
8596 })
8597 })
8598 .collect::<Vec<_>>()
8599 .into_iter()
8600 .map(|handle| {
8601 handle
8602 .join()
8603 .map_err(|_| Error::internal("a dictionary encode worker panicked"))?
8604 })
8605 .collect::<Result<Vec<_>>>()
8606 })?;
8607 pieces.into_iter().flatten().collect()
8608 };
8609 let mut done: Vec<Vec<(usize, Vec<u8>)>> =
8610 (0..dictionaries.len()).map(|_| Vec::new()).collect();
8611 for (column, at, bytes) in made {
8612 done[column].push((at, bytes));
8613 }
8614 for (column, mut made) in done.into_iter().enumerate() {
8615 if made.is_empty() {
8616 continue;
8617 }
8618 let Some(held) = dictionaries[column].as_mut() else { continue };
8619 made.sort_by_key(|(at, _)| *at);
8620 let waiting = std::mem::take(&mut held.waiting);
8621 for ((block, _), (_, bytes)) in waiting.into_iter().zip(made) {
8622 if held.encoded() != block {
8623 return Err(Error::internal("a dictionary block was encoded out of order"));
8624 }
8625 held.blocks.push(bytes);
8626 }
8627 }
8628 Ok(())
8629}
8630
8631fn settle_shape(sample: &[Vec<&[u8]>]) -> Result<chooser::Settled> {
8641 let mut best: Option<(chooser::Settled, usize)> = None;
8642 for shape in payload_shapes() {
8643 let mut size = 0;
8644 for block in sample {
8645 size += string::encode_with(block, &shape)?.len();
8646 }
8647 if best.as_ref().is_none_or(|(_, smallest)| size < *smallest) {
8648 best = Some((shape, size));
8649 }
8650 }
8651 best.map(|(shape, _)| shape)
8652 .ok_or_else(|| invalid("no shape applies to a global dictionary payload"))
8653}
8654
8655fn encode_ranks(order: &[(u64, u32)], code_bits: usize) -> Result<(Vec<u8>, Vec<u64>)> {
8662 let mut out = Vec::with_capacity(order.len() * 4);
8663 let mut ends = Vec::with_capacity(order.len().div_ceil(TEXT_RANK_BLOCK));
8664 let mut heads = Vec::with_capacity(TEXT_RANK_BLOCK);
8665 let mut codes = Vec::with_capacity(TEXT_RANK_BLOCK);
8666 for block in order.chunks(TEXT_RANK_BLOCK) {
8667 let base = block.first().map_or(0, |&(head, _)| head);
8670 let span = block.last().map_or(0, |&(head, _)| head.wrapping_sub(base));
8671 let width = (u64::BITS - span.leading_zeros()) as usize;
8672 heads.clear();
8673 codes.clear();
8674 for &(head, code) in block {
8675 heads.push(head.wrapping_sub(base));
8676 codes.push(u64::from(code));
8677 }
8678 put_u64(&mut out, base);
8679 out.push(width as u8);
8680 bitpack::pack_tail(&heads, width, &mut out)
8681 .map_err(|_| invalid("global dictionary heads do not pack"))?;
8682 bitpack::pack_tail(&codes, code_bits, &mut out)
8683 .map_err(|_| invalid("global dictionary codes do not pack"))?;
8684 ends.push(out.len() as u64);
8685 }
8686 Ok((out, ends))
8687}
8688
8689fn open_global_dictionary(
8696 file: Arc<File>,
8697 page: Page,
8698 ty: &LogicalType,
8699 keep_budget: usize,
8700) -> Result<Vector> {
8701 if ty != &LogicalType::Varchar {
8702 return Err(invalid("global dictionary belongs to a non-string column"));
8703 }
8704 let mut header = [0; DICTIONARY_HEADER];
8705 read_at(&file, page.offset, &mut header)?;
8706 let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
8707 let per_block = u32::from_le_bytes(header[4..8].try_into().expect("four bytes")) as usize;
8708 let blocks = u32::from_le_bytes(header[8..12].try_into().expect("four bytes")) as usize;
8709 let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
8710 let scattered = width & DICTIONARY_SCATTERED != 0;
8711 let has_grams = width & DICTIONARY_GRAMS != 0;
8712 let offset_bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
8713 if per_block != TEXT_PAYLOAD_VALUES {
8714 return Err(invalid("global dictionary block width differs"));
8715 }
8716 if blocks != count.div_ceil(TEXT_PAYLOAD_VALUES) {
8717 return Err(invalid("global dictionary block count differs from its value count"));
8718 }
8719 if offset_bits > u32::BITS as usize {
8720 return Err(invalid("global dictionary packs offsets past a payload"));
8721 }
8722 let offset_len = offset_bytes(count, offset_bits);
8723 let ranks = count;
8728 let rank_blocks = ranks.div_ceil(TEXT_RANK_BLOCK);
8729 let payload_words = if scattered { 3 } else { 2 };
8733 let hash_len = blocks
8734 .checked_mul(payload_words * 8)
8735 .and_then(|len| len.checked_add(rank_blocks.checked_mul(16)?))
8736 .and_then(|len| len.checked_add(usize::from(has_grams) * 8))
8737 .ok_or_else(|| invalid("global dictionary block count overflow"))?;
8738 let gram_len = if has_grams {
8739 blocks
8740 .checked_mul(TEXT_GRAM_BYTES)
8741 .ok_or_else(|| invalid("global dictionary signature count overflow"))?
8742 } else {
8743 0
8744 };
8745 let index_len = DICTIONARY_HEADER
8746 .checked_add(offset_len)
8747 .and_then(|len| len.checked_add(hash_len))
8748 .ok_or_else(|| invalid("global dictionary header overflow"))?;
8749 if index_len > page.length as usize {
8750 return Err(invalid("global dictionary offset index exceeds its page"));
8751 }
8752 let mut index = vec![0; index_len];
8753 index[..DICTIONARY_HEADER].copy_from_slice(&header);
8754 read_at(&file, page.offset + DICTIONARY_HEADER as u64, &mut index[DICTIONARY_HEADER..])?;
8755 if checksum(&index) != page.hash {
8756 return Err(invalid("global dictionary index checksum differs"));
8757 }
8758 let offsets = index[DICTIONARY_HEADER..DICTIONARY_HEADER + offset_len].to_vec();
8759 let word_end = index_len - usize::from(has_grams) * 8;
8760 let gram_hash = has_grams
8761 .then(|| u64::from_le_bytes(index[word_end..index_len].try_into().expect("eight bytes")));
8762 let mut words = index[DICTIONARY_HEADER + offset_len..word_end]
8763 .chunks_exact(8)
8764 .map(|part| u64::from_le_bytes(part.try_into().expect("eight bytes")))
8765 .collect::<Vec<_>>();
8766 let mut rest = words.split_off(blocks * payload_words);
8767 let rank_hashes = rest.split_off(rank_blocks);
8768 let rank_ends = rest;
8769 if rank_ends.windows(2).any(|pair| pair[0] >= pair[1]) {
8772 return Err(invalid("global dictionary order blocks do not rise"));
8773 }
8774 let rank_len = usize::try_from(rank_ends.last().copied().unwrap_or_default())
8775 .map_err(|_| invalid("global dictionary rank overflow"))?;
8776 let body_len = index_len
8777 .checked_add(rank_len)
8778 .ok_or_else(|| invalid("global dictionary header overflow"))?;
8779 if body_len > page.length as usize {
8780 return Err(invalid("global dictionary order exceeds its page"));
8781 }
8782 let gram_end = body_len
8783 .checked_add(gram_len)
8784 .ok_or_else(|| invalid("global dictionary signature length overflow"))?;
8785 if gram_end > page.length as usize {
8786 return Err(invalid("global dictionary signatures exceed their page"));
8787 }
8788 let grams = gram_hash.map(|hash| NativeGrams {
8789 start: page.offset + body_len as u64,
8790 length: gram_len,
8791 hash,
8792 loaded: OnceLock::new(),
8793 });
8794 let hashes = words.split_off(blocks * (payload_words - 1));
8795 let (starts, lengths) = if scattered {
8796 let mut starts = Vec::with_capacity(blocks);
8797 let mut lengths = Vec::with_capacity(blocks);
8798 for pair in words.chunks_exact(2) {
8799 starts.push(pair[0]);
8800 lengths.push(pair[1]);
8801 }
8802 (starts, lengths)
8803 } else {
8804 let base = page.offset + gram_end as u64;
8808 let mut starts = Vec::with_capacity(blocks);
8809 let mut lengths = Vec::with_capacity(blocks);
8810 let mut at = 0_u64;
8811 for &end in &words {
8812 let len = end
8813 .checked_sub(at)
8814 .ok_or_else(|| invalid("global dictionary block ends before it starts"))?;
8815 starts.push(base + at);
8816 lengths.push(len);
8817 at = end;
8818 }
8819 (starts, lengths)
8820 };
8821 let stored_len = page.length as u64 - gram_end as u64;
8827 if scattered && stored_len == 0 {
8828 let size = file.metadata().map_err(io)?.len();
8829 let inside = starts.iter().zip(&lengths).all(|(&start, &len)| {
8830 start >= HEADER && start.checked_add(len).is_some_and(|end| end <= size)
8831 });
8832 if !inside {
8833 return Err(invalid("global dictionary block lies outside the file"));
8834 }
8835 } else if lengths.iter().try_fold(0_u64, |sum, len| sum.checked_add(*len)) != Some(stored_len) {
8836 return Err(invalid("global dictionary blocks do not bound the payload"));
8837 }
8838 Vector::external_text(
8839 LogicalType::Varchar,
8840 Arc::new(NativeText {
8841 file,
8842 values: count,
8843 offsets,
8844 offset_bits,
8845 value_ends: OnceLock::new(),
8846 value_lens: OnceLock::new(),
8847 ends_asked: AtomicUsize::new(0),
8848 ranks,
8849 rank_at: page.offset + index_len as u64,
8850 rank_ends,
8851 rank_hashes,
8852 rank_blocks: (0..rank_blocks).map(|_| OnceLock::new()).collect(),
8853 code_bits: code_width(count),
8854 code_ranks: OnceLock::new(),
8855 starts,
8856 lengths,
8857 hashes,
8858 grams,
8859 blocks: (0..blocks).map(|_| OnceLock::new()).collect(),
8860 keep_budget,
8861 payload_kept: AtomicUsize::new(0),
8862 swept: (0..blocks).map(|_| AtomicBool::new(false)).collect(),
8863 searched: Mutex::new(HashMap::new()),
8864 }),
8865 )
8866}
8867
8868fn page_encoding(ty: &LogicalType, rows: usize, bytes: &[u8]) -> String {
8881 fn cascade_at(rows: usize, bytes: &[u8]) -> Result<(u8, usize)> {
8883 let mut cur = Cursor::new(bytes);
8884 let codec = cur.u8()?;
8885 if cur.u8()? == 2 {
8886 cur.take(rows.div_ceil(8))?;
8887 }
8888 Ok((codec, cur.at))
8889 }
8890 let Ok((codec, at)) = cascade_at(rows, bytes) else {
8891 return "UNREADABLE".to_string();
8892 };
8893 let tail = &bytes[at..];
8894 let described = |described: Result<String>| described.unwrap_or_else(|_| "UNREADABLE".into());
8895 match codec {
8896 0 => match ty {
8897 LogicalType::Varchar | LogicalType::Blob => "PLAIN".to_string(),
8898 _ => "FIXED".to_string(),
8899 },
8900 1 => "DICT(PLAIN)".to_string(),
8901 2 => "FOR+BITPACK".to_string(),
8902 3 => "TABLE DICT".to_string(),
8903 4 => format!("TABLE DICT({})", described(integer::describe(tail))),
8904 5 => described(integer::describe(tail)),
8905 6 => described(string::describe(tail)),
8906 other => format!("CODEC {other}"),
8907 }
8908}
8909
8910fn decode_selected_stable_codes(
8915 rows: usize,
8916 bytes: &[u8],
8917 positions: &[usize],
8918 out: &mut Vec<Option<u32>>,
8919) -> Result<bool> {
8920 if positions.windows(2).any(|pair| pair[0] >= pair[1])
8921 || positions.last().is_some_and(|&position| position >= rows)
8922 {
8923 return Err(invalid("selected code positions are not sorted and in range"));
8924 }
8925 let mut cur = Cursor::new(bytes);
8926 let codec = cur.u8()?;
8927 if codec != 3 && codec != 4 {
8928 return Ok(false);
8929 }
8930 let flag = cur.u8()?;
8931 let mask = match flag {
8932 0 | 1 => None,
8933 2 => {
8934 let at = cur.at;
8935 let len = rows.div_ceil(8);
8936 cur.take(len)?;
8937 Some((at, len))
8938 }
8939 _ => return Err(invalid("page validity tag differs")),
8940 };
8941 let valid = |row: usize| match flag {
8942 0 => true,
8943 1 => false,
8944 2 => mask.is_some_and(|(at, _)| bytes[at + row / 8] >> (row % 8) & 1 == 1),
8945 _ => unreachable!("the validity tag was checked"),
8946 };
8947 if codec == 4 {
8948 let wide = integer::decode_selected(&bytes[cur.at..], positions)?;
8949 for (&row, code) in positions.iter().zip(wide) {
8950 let code = u32::try_from(code).map_err(|_| invalid("code is not a code"))?;
8951 out.push(valid(row).then_some(code));
8952 }
8953 return Ok(true);
8954 }
8955 let codes_at = cur.at;
8956 let codes_len = rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?;
8957 cur.take(codes_len)?;
8958 if cur.at != bytes.len() {
8959 return Err(invalid("global code page has trailing bytes"));
8960 }
8961 let codes = &bytes[codes_at..codes_at + codes_len];
8962 for &row in positions {
8963 let at = row.checked_mul(4).ok_or_else(|| invalid("dictionary code offset overflow"))?;
8964 let code = u32::from_le_bytes(
8965 codes[at..at + 4].try_into().map_err(|_| invalid("dictionary code is truncated"))?,
8966 );
8967 out.push(valid(row).then_some(code));
8968 }
8969 Ok(true)
8970}
8971
8972fn decode(
8973 ty: &LogicalType,
8974 rows: usize,
8975 bytes: &[u8],
8976 global: Option<Arc<Vector>>,
8977) -> Result<Vector> {
8978 let mut cur = Cursor::new(bytes);
8979 let codec = cur.u8()?;
8980 let flag = cur.u8()?;
8981 let validity = match flag {
8982 0 => Validity::AllValid,
8983 1 => Validity::AllInvalid,
8984 2 => {
8985 let mask = cur.take(rows.div_ceil(8))?;
8986 Validity::from_iter(rows, |row| mask[row / 8] >> (row % 8) & 1 == 1)
8987 }
8988 _ => return Err(invalid("page validity tag differs")),
8989 };
8990 if codec == 1 {
8991 if ty != &LogicalType::Varchar {
8992 return Err(invalid("dictionary codec belongs to a non-string page"));
8993 }
8994 let count = cur.u32()? as usize;
8995 let payload_len = cur.u32()? as usize;
8996 let offset_bytes = cur.take(
8997 (count + 1)
8998 .checked_mul(4)
8999 .ok_or_else(|| invalid("dictionary offset count overflow"))?,
9000 )?;
9001 let offsets = offset_bytes
9002 .chunks_exact(4)
9003 .map(|part| u32::from_le_bytes(part.try_into().expect("four bytes")))
9004 .collect::<Vec<_>>();
9005 let payload = cur.take(payload_len)?.to_vec();
9006 if offsets.first() != Some(&0)
9007 || offsets.last().copied().map(|last| last as usize) != Some(payload.len())
9008 || offsets.windows(2).any(|pair| pair[0] > pair[1])
9009 {
9010 return Err(invalid("dictionary offsets do not bound the payload"));
9011 }
9012 let mut strings = StringColumn::over(Buffer::from_vec(payload).into_page());
9015 for pair in offsets.windows(2) {
9016 strings.push_in_place(pair[0] as usize, (pair[1] - pair[0]) as usize)?;
9017 }
9018 let mut codes = Vec::with_capacity(rows);
9019 for _ in 0..rows {
9020 codes.push(cur.u32()?);
9021 }
9022 if codes.iter().any(|code| *code as usize >= count) {
9023 return Err(invalid("dictionary code is out of range"));
9024 }
9025 if cur.at != bytes.len() {
9026 return Err(invalid("dictionary page has trailing bytes"));
9027 }
9028 let dictionary = Vector::flat(LogicalType::Varchar, Data::Varlen(strings))?;
9029 return Ok(Vector::dictionary(codes, dictionary)?.with_validity(validity));
9030 }
9031 if codec == 3 || codec == 4 {
9032 let dictionary = global.ok_or_else(|| invalid("global code page has no dictionary"))?;
9033 let codes = if codec == 4 {
9034 let wide = integer::decode(&bytes[cur.at..])?;
9037 if wide.len() != rows {
9038 return Err(invalid("encoded code page holds the wrong number of rows"));
9039 }
9040 let mut codes = Vec::with_capacity(wide.len());
9047 let mut seen = 0_i64;
9048 for &code in &wide {
9049 seen |= code;
9050 codes.push(code as u32);
9051 }
9052 if seen < 0 || seen > i64::from(u32::MAX) {
9053 return Err(invalid("code is not a code"));
9054 }
9055 codes
9056 } else {
9057 let mut codes = Vec::with_capacity(rows);
9058 for _ in 0..rows {
9059 codes.push(cur.u32()?);
9060 }
9061 if cur.at != bytes.len() {
9062 return Err(invalid("global code page has trailing bytes"));
9063 }
9064 codes
9065 };
9066 let highest = codes.iter().copied().max();
9067 return Ok(Vector::stable_dictionary_validated(codes, dictionary, highest)?
9068 .with_validity(validity));
9069 }
9070 if codec == 6 {
9071 if ty != &LogicalType::Varchar {
9072 return Err(invalid("compressed text codec belongs to a non-string page"));
9073 }
9074 let (payload, ends) = string::decode_flat(&bytes[cur.at..])?.into_parts();
9078 if ends.len() != rows {
9079 return Err(invalid("compressed text page holds the wrong number of rows"));
9080 }
9081 let mut values = StringColumn::over(Buffer::from_vec(payload).into_page());
9084 let mut start = 0;
9085 for end in ends {
9086 let len = end
9087 .checked_sub(start)
9088 .ok_or_else(|| invalid("compressed text value ends before it starts"))?;
9089 values.push_in_place(start, len)?;
9090 start = end;
9091 }
9092 return Ok(Vector::flat(ty.clone(), Data::Varlen(values))?.with_validity(validity));
9093 }
9094 if codec == 5 {
9095 let values = integer::decode(&bytes[cur.at..])?;
9097 if values.len() != rows {
9098 return Err(invalid("cascade page holds the wrong number of rows"));
9099 }
9100 let data = narrowed(ty, values)?;
9101 return Ok(Vector::flat(ty.clone(), data)?.with_validity(validity));
9102 }
9103 if codec == 2 {
9104 let width = u32::from(cur.u8()?);
9105 let base = i128::from_le_bytes(cur.take(16)?.try_into().expect("sixteen bytes"));
9106 let count = cur.u32()? as usize;
9107 let length = count.checked_mul(8).ok_or_else(|| invalid("packed page is too long"))?;
9108 let words: Vec<u64> = cur
9109 .take(length)?
9110 .chunks_exact(8)
9111 .map(|word| u64::from_le_bytes(word.try_into().expect("eight bytes")))
9112 .collect();
9113 if cur.at != bytes.len() {
9114 return Err(invalid("packed page has trailing bytes"));
9115 }
9116 return Ok(Vector::packed(ty.clone(), words, width, base, rows)?.with_validity(validity));
9117 }
9118 if codec != 0 {
9119 return Err(invalid("page codec is unknown"));
9120 }
9121 let data = match ty {
9122 LogicalType::TinyInt => {
9123 let values = cur.take(rows)?;
9124 Data::Int8(values.iter().map(|item| *item as i8).collect::<Vec<_>>().into())
9125 }
9126 LogicalType::UTinyInt => Data::UInt8(cur.take(rows)?.to_vec().into()),
9127 LogicalType::SmallInt => {
9128 let values =
9129 cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9130 Data::Int16(
9131 values
9132 .chunks_exact(2)
9133 .map(|item| i16::from_le_bytes(item.try_into().expect("two bytes")))
9134 .collect::<Vec<_>>()
9135 .into(),
9136 )
9137 }
9138 LogicalType::USmallInt => {
9139 let values =
9140 cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9141 Data::UInt16(
9142 values
9143 .chunks_exact(2)
9144 .map(|item| u16::from_le_bytes(item.try_into().expect("two bytes")))
9145 .collect::<Vec<_>>()
9146 .into(),
9147 )
9148 }
9149 LogicalType::UInteger => {
9150 let values =
9151 cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9152 Data::UInt32(
9153 values
9154 .chunks_exact(4)
9155 .map(|item| u32::from_le_bytes(item.try_into().expect("four bytes")))
9156 .collect::<Vec<_>>()
9157 .into(),
9158 )
9159 }
9160 LogicalType::UBigInt => {
9161 let values =
9162 cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9163 Data::UInt64(
9164 values
9165 .chunks_exact(8)
9166 .map(|item| u64::from_le_bytes(item.try_into().expect("eight bytes")))
9167 .collect::<Vec<_>>()
9168 .into(),
9169 )
9170 }
9171 LogicalType::Integer | LogicalType::Date => {
9172 let values =
9173 cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9174 Data::Int32(
9175 values
9176 .chunks_exact(4)
9177 .map(|item| i32::from_le_bytes(item.try_into().expect("four bytes")))
9178 .collect::<Vec<_>>()
9179 .into(),
9180 )
9181 }
9182 LogicalType::BigInt
9183 | LogicalType::Timestamp
9184 | LogicalType::Time
9185 | LogicalType::TimeTz
9186 | LogicalType::TimestampTz
9187 | LogicalType::TimestampS
9188 | LogicalType::TimestampMs
9189 | LogicalType::TimestampNs => {
9190 let values =
9191 cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9192 Data::Int64(
9193 values
9194 .chunks_exact(8)
9195 .map(|item| i64::from_le_bytes(item.try_into().expect("eight bytes")))
9196 .collect::<Vec<_>>()
9197 .into(),
9198 )
9199 }
9200 LogicalType::HugeInt | LogicalType::Uuid => {
9201 let values =
9202 cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9203 Data::Int128(
9204 values
9205 .chunks_exact(16)
9206 .map(|item| i128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9207 .collect::<Vec<_>>()
9208 .into(),
9209 )
9210 }
9211 LogicalType::UHugeInt => {
9212 let values =
9213 cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9214 Data::UInt128(
9215 values
9216 .chunks_exact(16)
9217 .map(|item| u128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9218 .collect::<Vec<_>>()
9219 .into(),
9220 )
9221 }
9222 LogicalType::Float => {
9223 let values =
9224 cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9225 Data::Float32(
9226 values
9227 .chunks_exact(4)
9228 .map(|item| f32::from_le_bytes(item.try_into().expect("four bytes")))
9229 .collect::<Vec<_>>()
9230 .into(),
9231 )
9232 }
9233 LogicalType::Double => {
9234 let values =
9235 cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9236 Data::Float64(
9237 values
9238 .chunks_exact(8)
9239 .map(|item| f64::from_le_bytes(item.try_into().expect("eight bytes")))
9240 .collect::<Vec<_>>()
9241 .into(),
9242 )
9243 }
9244 LogicalType::Interval => {
9245 let values =
9246 cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9247 Data::Interval(
9248 values
9249 .chunks_exact(16)
9250 .map(|item| {
9251 (
9252 i32::from_le_bytes(item[..4].try_into().expect("four bytes")),
9253 i32::from_le_bytes(item[4..8].try_into().expect("four bytes")),
9254 i64::from_le_bytes(item[8..].try_into().expect("eight bytes")),
9255 )
9256 })
9257 .collect::<Vec<_>>()
9258 .into(),
9259 )
9260 }
9261 LogicalType::Boolean => {
9262 let values = cur.take(rows)?;
9263 if values.iter().any(|value| *value > 1) {
9264 return Err(invalid("boolean page has another value"));
9265 }
9266 Data::Bool(values.iter().map(|value| *value == 1).collect::<Vec<_>>().into())
9267 }
9268 LogicalType::Decimal { .. } => match ty.physical() {
9271 PhysicalType::Int16 => {
9272 let values =
9273 cur.take(rows.checked_mul(2).ok_or_else(|| invalid("page size overflow"))?)?;
9274 Data::Int16(
9275 values
9276 .chunks_exact(2)
9277 .map(|item| i16::from_le_bytes(item.try_into().expect("two bytes")))
9278 .collect::<Vec<_>>()
9279 .into(),
9280 )
9281 }
9282 PhysicalType::Int32 => {
9283 let values =
9284 cur.take(rows.checked_mul(4).ok_or_else(|| invalid("page size overflow"))?)?;
9285 Data::Int32(
9286 values
9287 .chunks_exact(4)
9288 .map(|item| i32::from_le_bytes(item.try_into().expect("four bytes")))
9289 .collect::<Vec<_>>()
9290 .into(),
9291 )
9292 }
9293 PhysicalType::Int64 => {
9294 let values =
9295 cur.take(rows.checked_mul(8).ok_or_else(|| invalid("page size overflow"))?)?;
9296 Data::Int64(
9297 values
9298 .chunks_exact(8)
9299 .map(|item| i64::from_le_bytes(item.try_into().expect("eight bytes")))
9300 .collect::<Vec<_>>()
9301 .into(),
9302 )
9303 }
9304 _ => {
9305 let values =
9306 cur.take(rows.checked_mul(16).ok_or_else(|| invalid("page size overflow"))?)?;
9307 Data::Int128(
9308 values
9309 .chunks_exact(16)
9310 .map(|item| i128::from_le_bytes(item.try_into().expect("sixteen bytes")))
9311 .collect::<Vec<_>>()
9312 .into(),
9313 )
9314 }
9315 },
9316 LogicalType::Varchar | LogicalType::Blob | LogicalType::Bit => {
9317 let offset_bytes = cur
9318 .take((rows + 1).checked_mul(4).ok_or_else(|| invalid("offset count overflow"))?)?;
9319 let offsets = offset_bytes
9320 .chunks_exact(4)
9321 .map(|part| u32::from_le_bytes(part.try_into().expect("four bytes")))
9322 .collect::<Vec<_>>();
9323 let payload = cur.take(bytes.len() - cur.at)?.to_vec();
9324 if offsets.first() != Some(&0)
9325 || offsets.last().copied().map(|last| last as usize) != Some(payload.len())
9326 || offsets.windows(2).any(|pair| pair[0] > pair[1])
9327 {
9328 return Err(invalid("string offsets do not bound the payload"));
9329 }
9330 let mut values = StringColumn::over(Buffer::from_vec(payload).into_page());
9338 let text = ty == &LogicalType::Varchar;
9339 for pair in offsets.windows(2) {
9340 let (at, len) = (pair[0] as usize, (pair[1] - pair[0]) as usize);
9341 if text {
9342 values.push_in_place(at, len)?;
9343 } else {
9344 values.push_bytes_in_place(at, len)?;
9345 }
9346 }
9347 Data::Varlen(values)
9348 }
9349 _ => return Err(Error::not_implemented(format!("native page for {ty}"))),
9350 };
9351 if cur.at != bytes.len() {
9352 return Err(invalid("page has trailing bytes"));
9353 }
9354 Ok(Vector::flat(ty.clone(), data)?.with_validity(validity))
9355}
9356
9357#[cfg(test)]
9358mod tests {
9359 use std::fs;
9360 use std::io::{Seek, SeekFrom, Write};
9361 use std::path::PathBuf;
9362 use std::time::{SystemTime, UNIX_EPOCH};
9363
9364 use rudb_common::Stat;
9365 use rudb_common::Value;
9366 use rudb_common::bounds::{Frequencies, Op, Remainder, Zones};
9367 use rudb_common::stat::Provenance;
9368
9369 use super::*;
9370
9371 #[derive(Debug)]
9374 struct TestsEverything<'a>(&'a dyn chooser::Chooser);
9375
9376 impl chooser::Chooser for TestsEverything<'_> {
9377 fn name(&self) -> &'static str {
9378 "tests everything"
9379 }
9380
9381 fn narrow_strings(
9382 &self,
9383 values: &[&[u8]],
9384 offered: &[string::Kind],
9385 depth: u8,
9386 ) -> Vec<string::Kind> {
9387 self.0.narrow_strings(values, offered, depth)
9388 }
9389
9390 fn narrow_integers(
9391 &self,
9392 values: &[i64],
9393 offered: &[integer::Kind],
9394 depth: u8,
9395 ) -> Vec<integer::Kind> {
9396 self.0.narrow_integers(values, offered, depth)
9397 }
9398 }
9399
9400 #[test]
9401 fn ruling_kinds_out_before_testing_for_them_writes_the_same_bytes() {
9402 let columns: Vec<Vec<i64>> = vec![
9403 vec![],
9404 vec![5; 1000],
9405 (0..1000).collect(),
9406 (0..1000).map(|row| 1_600_000_000_000_000 + row * 1_000_000).collect(),
9407 (0..1000).map(|row| row / 50).collect(),
9408 (0..1000).map(|row| if row % 97 == 0 { row } else { 0 }).collect(),
9409 (0..1000).map(|row| (row * 7919) % 13).collect(),
9410 (0..1000).map(|row| (row * 2_654_435_761) % 1_000_003).collect(),
9411 (0..1000).map(|row| [3, 3, 3, 9, 9, 1][row as usize % 6]).collect(),
9412 (0..1000).map(|row| i64::MIN + row % 3).collect(),
9413 ];
9414 let choosers: [&dyn chooser::Chooser; 2] = [&Fixed, &Codes];
9415 for column in &columns {
9416 for chooser in choosers {
9417 let quick = integer::encode_with(column, chooser).unwrap();
9418 let full = integer::encode_with(column, &TestsEverything(chooser)).unwrap();
9419 assert_eq!(
9420 quick,
9421 full,
9422 "{} on {:?}",
9423 chooser.name(),
9424 &column[..column.len().min(8)]
9425 );
9426 }
9427 }
9428 }
9429
9430 #[test]
9431 fn checksum_matches_fixed_vectors() {
9432 assert_eq!(checksum(b""), 0xef46_db37_51d8_e999);
9433 assert_eq!(checksum(b"a"), 0xd24e_c4f1_a98c_6e5b);
9434 assert_eq!(checksum(b"abc"), 0x44bc_2cf5_ad77_0999);
9435 }
9436
9437 #[test]
9438 fn sorting_across_threads_matches_sorting_on_one() {
9439 let mut state = 0x9e37_79b9_7f4a_7c15_u64;
9440 let mut next = move || {
9441 state ^= state << 13;
9442 state ^= state >> 7;
9443 state ^= state << 17;
9444 state
9445 };
9446 let mut values = Vec::new();
9447 for at in 0..150_000_u64 {
9448 let value = match next() % 6 {
9449 0 => Vec::new(),
9450 1 => format!("https://example.com/{}", next() % 5_000).into_bytes(),
9451 2 => format!("https://example.com/path/{at}").into_bytes(),
9452 3 => b"same".to_vec(),
9453 4 => vec![0xff; (next() % 12) as usize],
9454 _ => (0..next() % 20).map(|_| (next() % 3) as u8).collect(),
9455 };
9456 values.push(value);
9457 }
9458 let value = |code: u32| values[code as usize].as_slice();
9459 for workers in [1, 2, 3, 8, 32] {
9460 let mut one = (0..values.len() as u32).rev().collect::<Vec<_>>();
9461 let mut across = one.clone();
9462 sort_by_value(&mut one, value);
9463 sort_by_value_across(&mut across, value, workers);
9464 assert_eq!(one, across, "{workers} workers");
9465 }
9466 let mut sorted = (0..values.len() as u32).collect::<Vec<_>>();
9467 sort_by_value_across(&mut sorted, value, 8);
9468 assert!(sorted.windows(2).all(|pair| value(pair[0]) <= value(pair[1])));
9469 }
9470
9471 fn path(label: &str) -> PathBuf {
9472 let stamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("time advances").as_nanos();
9473 std::env::temp_dir().join(format!("rudb-native-{label}-{}-{stamp}.rdb", std::process::id()))
9474 }
9475
9476 fn dictionary_values(dictionary: &GlobalDictionary) -> Vec<Vec<u8>> {
9481 let (flat, bases) = dictionary.decoded(None).expect("the blocks decode");
9482 (0..dictionary.values())
9483 .map(|code| {
9484 let (from, to) = GlobalDictionary::value_span(&dictionary.ends, &bases, code);
9485 flat[from..to].to_vec()
9486 })
9487 .collect()
9488 }
9489
9490 fn attached(table: &Table) -> Vec<&Section> {
9497 table.sections().iter().filter(|held| !held.among(section::STATISTICS_KINDS)).collect()
9498 }
9499
9500 #[test]
9502 fn a_read_at_an_offset_ignores_where_another_thread_left_the_cursor() {
9503 const SPANS: usize = 64;
9504 const SPAN: usize = 512;
9505 let path = path("positional");
9506 let content: Vec<u8> =
9507 (0..SPANS).flat_map(|span| std::iter::repeat_n(span as u8, SPAN)).collect();
9508 fs::write(&path, &content).expect("the file is written");
9509 let file = Arc::new(File::open(&path).expect("the file opens"));
9510 std::thread::scope(|scope| {
9511 for _ in 0..8 {
9512 let file = Arc::clone(&file);
9513 scope.spawn(move || {
9514 for _ in 0..64 {
9515 for span in 0..SPANS {
9516 let mut bytes = [0_u8; SPAN];
9517 read_at(&file, (span * SPAN) as u64, &mut bytes)
9518 .expect("the span reads");
9519 assert!(
9520 bytes.iter().all(|byte| *byte == span as u8),
9521 "span {span} came back as {}",
9522 bytes[0],
9523 );
9524 }
9525 }
9526 });
9527 }
9528 });
9529 let mut past = [0_u8; SPAN];
9530 let end = (SPANS * SPAN) as u64;
9531 let error = read_at(&file, end, &mut past).expect_err("a read past the end is refused");
9532 assert!(error.message().contains("ends before its declared length"), "{error}");
9533 drop(file);
9534 let _ = fs::remove_file(&path);
9535 }
9536
9537 #[test]
9543 fn a_writer_puts_a_page_where_it_said_it_did_wherever_the_cursor_has_got_to() {
9544 let path = path("cursor");
9545 let mut writer = Writer::create(
9546 &path,
9547 "items",
9548 vec![
9549 Field::required("id", LogicalType::Integer),
9550 Field::new("text", LogicalType::Varchar),
9551 ],
9552 )
9553 .expect("new file");
9554 writer.append(&sample()).expect("first part");
9555 writer.file.seek(SeekFrom::Start(0)).expect("the cursor goes back to the header");
9556 writer.append(&sample()).expect("second part");
9557 writer.file.seek(SeekFrom::Start(1)).expect("and somewhere useless again");
9558 writer.finish().expect("commit");
9559 let reader = Reader::open(&path).expect("reopen from disk");
9560 assert_eq!(reader.table().rows(), 6);
9561 let ids = reader.read(0, &[0]).expect("the integer page reads back");
9562 assert_eq!(ids.value_at(0, 0), Value::Integer(4));
9563 assert_eq!(ids.value_at(2, 0), Value::Integer(-2));
9564 let text = reader.read(1, &[1]).expect("the text page reads back");
9565 assert_eq!(text.value_at(1, 0), Value::Null);
9566 assert_eq!(text.value_at(2, 0), Value::Varchar("long text after a slash".into()));
9567 let end = reader.table().stripes().iter().flat_map(|stripe| {
9570 stripe
9571 .pages
9572 .iter()
9573 .map(|page| page.offset + u64::from(page.length))
9574 .chain(std::iter::once(stripe.index.offset + u64::from(stripe.index.length)))
9575 });
9576 let last = end.fold(HEADER, u64::max);
9577 let directory = fs::metadata(&path).expect("the file is there").len();
9578 assert!(last <= directory, "a page runs to {last} in a file of {directory} bytes");
9579 fs::remove_file(path).expect("remove scratch file");
9580 }
9581
9582 fn dictionary_index_len(header: &[u8; DICTIONARY_HEADER]) -> u64 {
9588 let count = u64::from(u32::from_le_bytes(header[0..4].try_into().expect("four bytes")));
9589 let blocks = u64::from(u32::from_le_bytes(header[8..12].try_into().expect("four bytes")));
9590 let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
9591 let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
9592 let payload_words = if width & DICTIONARY_SCATTERED == 0 { 2 } else { 3 };
9593 let rank_blocks = count.div_ceil(TEXT_RANK_BLOCK as u64);
9594 DICTIONARY_HEADER as u64
9595 + offset_bytes(count as usize, bits) as u64
9596 + blocks * payload_words * 8
9597 + rank_blocks * 16
9598 + if width & DICTIONARY_GRAMS == 0 { 0 } else { 8 }
9599 }
9600
9601 fn sample() -> Chunk {
9602 Chunk::new(vec![
9603 Vector::from_values(
9604 LogicalType::Integer,
9605 &[Value::Integer(4), Value::Integer(9), Value::Integer(-2)],
9606 )
9607 .expect("integers"),
9608 Vector::from_values(
9609 LogicalType::Varchar,
9610 &[
9611 Value::Varchar("alpha".into()),
9612 Value::Null,
9613 Value::Varchar("long text after a slash".into()),
9614 ],
9615 )
9616 .expect("strings"),
9617 ])
9618 .expect("matching rows")
9619 }
9620
9621 fn sample_ids() -> Chunk {
9622 Chunk::new(vec![
9623 Vector::flat(LogicalType::Integer, Data::Int32(vec![7, 8, 9].into()))
9624 .expect("integers"),
9625 ])
9626 .expect("one column")
9627 }
9628
9629 #[test]
9630 fn the_planner_gets_the_null_count_off_the_same_directory_the_bounds_are_in() {
9631 let path = path("nulls_for_the_planner");
9634 let mut writer =
9635 Writer::create(&path, "items", vec![Field::new("a", LogicalType::Integer)])
9636 .expect("new file");
9637 let rows = Chunk::new(vec![
9638 Vector::from_values(
9639 LogicalType::Integer,
9640 &[
9641 Value::Integer(4),
9642 Value::Null,
9643 Value::Integer(9),
9644 Value::Null,
9645 Value::Integer(1),
9646 Value::Integer(2),
9647 ],
9648 )
9649 .expect("integers"),
9650 ])
9651 .expect("one column");
9652 writer.append(&rows).expect("the only part");
9653 writer.finish().expect("commit");
9654 let reader = Reader::open(&path).expect("reopen from disk");
9655 let stripes = Stripes::new(reader);
9656 let column = stripes.column("a").expect("the file has that column");
9657 assert_eq!(stripes.nulls(column), Stat::exact(2, Provenance::NullCount));
9658 assert_eq!(stripes.nulls(column + 1), Stat::Unknown);
9661 fs::remove_file(&path).expect("clean up");
9662 }
9663
9664 #[test]
9665 fn the_planner_gets_a_row_count_per_value_off_a_complete_synopsis() {
9666 let path = path("frequencies_for_the_planner");
9671 let mut writer =
9672 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9673 .expect("new file");
9674 let rows = Chunk::new(vec![
9675 Vector::from_values(
9676 LogicalType::Integer,
9677 &[
9678 Value::Integer(4),
9679 Value::Integer(4),
9680 Value::Integer(4),
9681 Value::Integer(9),
9682 Value::Integer(9),
9683 Value::Integer(1),
9684 ],
9685 )
9686 .expect("integers"),
9687 ])
9688 .expect("one column");
9689 writer.append(&rows).expect("the only part");
9690 writer.finish().expect("commit");
9691 let reader = Reader::open(&path).expect("reopen from disk");
9692 let common = Common::new(reader);
9693 assert_eq!(common.rows(), 6);
9694 let column = common.column("id").expect("the file has that column");
9695 assert_eq!(common.column("nothing"), None);
9696 assert_eq!(
9697 common.rows_with(column, &Bound::Int(4)),
9698 Stat::exact(3, Provenance::FrequencySynopsis)
9699 );
9700 assert_eq!(
9702 common.rows_with(column, &Bound::Int(7)),
9703 Stat::exact(0, Provenance::FrequencySynopsis)
9704 );
9705 assert_eq!(common.rows_with(column, &Bound::Bytes(b"four".to_vec())), Stat::Unknown);
9708 assert_eq!(common.remainder(column), None);
9711 fs::remove_file(&path).expect("clean up");
9712 }
9713
9714 #[test]
9715 fn string_frequency_estimates_do_not_open_the_global_dictionary() {
9716 let path = path("string_frequencies_for_the_planner");
9717 let mut writer =
9718 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
9719 .expect("new file");
9720 let rows = Chunk::new(vec![
9721 Vector::from_values(
9722 LogicalType::Varchar,
9723 &[
9724 Value::Varchar(String::new()),
9725 Value::Varchar("alpha".into()),
9726 Value::Varchar(String::new()),
9727 Value::Varchar("beta".into()),
9728 Value::Varchar(String::new()),
9729 ],
9730 )
9731 .expect("strings"),
9732 ])
9733 .expect("one column");
9734 writer.append(&rows).expect("the only part");
9735 writer.finish().expect("commit");
9736
9737 let reader = Reader::open(&path).expect("reopen from disk");
9738 assert_eq!(reader.reads().dictionaries, 0, "open reads only the directory");
9739 let common = Common::new(reader.clone());
9740 let column = common.column("text").expect("the file has that column");
9741 assert_eq!(
9742 common.rows_with(column, &Bound::Bytes(Vec::new())),
9743 Stat::exact(3, Provenance::FrequencySynopsis)
9744 );
9745 assert_eq!(
9746 common.rows_with(column, &Bound::Bytes(b"missing".to_vec())),
9747 Stat::exact(0, Provenance::FrequencySynopsis)
9748 );
9749 assert_eq!(
9750 reader.reads().dictionaries,
9751 0,
9752 "the bounded spellings answer without opening the dictionary index"
9753 );
9754 fs::remove_file(&path).expect("clean up");
9755 }
9756
9757 #[test]
9758 fn host_groups_certify_omitted_hosts_and_keep_exact_aggregates() {
9759 let path = path("certified_host_groups");
9760 let mut writer =
9761 Writer::create(&path, "hits", vec![Field::required("Referer", LogicalType::Varchar)])
9762 .expect("new file");
9763 let mut values = vec![Value::Varchar("http://www.example.com/a".into()); 150];
9764 values.extend(vec![Value::Varchar("https://example.com/b".into()); 70]);
9765 values.extend((0..550).map(|at| Value::Varchar(format!("https://site{at}.test/x"))));
9766 values.push(Value::Varchar(String::new()));
9767 for part in values.chunks(512) {
9768 writer
9769 .append(
9770 &Chunk::new(vec![
9771 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
9772 ])
9773 .expect("one column"),
9774 )
9775 .expect("part written");
9776 }
9777 writer.finish().expect("commit");
9778 let reader = Reader::open(&path).expect("reopen");
9779 let summary = reader.table.host_groups.as_ref().expect("bounded host metadata");
9780 assert!(summary.omitted_max < 220);
9781 assert!(reader.host_groups(0, summary.omitted_max).expect("valid column").is_none());
9782 let groups = reader.host_groups(0, 220).expect("valid column").expect("certified");
9783 let example = groups.iter().find(|entry| entry.host == "example.com").expect("leader");
9784 assert_eq!(example.count, 220);
9785 assert_eq!(example.bytes_sum, 150 * 24 + 70 * 21);
9786 assert_eq!(example.minimum, "http://www.example.com/a");
9787 assert_eq!(reader.reads().dictionaries, 0, "the directory settles the question");
9788 fs::remove_file(&path).expect("clean up");
9789 }
9790
9791 fn bare_table(sections: Vec<Section>) -> Table {
9796 Table {
9797 name: "linked".to_owned(),
9798 fields: vec![Field::required("id", LogicalType::Integer)],
9799 stripes: Vec::new(),
9800 rows: 0,
9801 dictionaries: vec![None],
9802 dictionary_payloads: Vec::new(),
9803 distincts: vec![None],
9804 frequencies: vec![None],
9805 pair_frequencies: Vec::new(),
9806 frequency_texts: Vec::new(),
9807 host_groups: None,
9808 clustering: None,
9809 generation: 1,
9810 sections,
9811 }
9812 }
9813
9814 fn a_key_map_section() -> Section {
9815 Section {
9816 kind: *section::KEY_MAP,
9817 id: 1,
9818 generation: 3,
9819 extents: 1,
9820 extent_page: HEADER,
9821 extent_bytes: section::EXTENT_BYTES as u32,
9822 hash: 0x1234_5678_9abc_def0,
9823 flags: 0,
9824 header_bytes: 24,
9825 }
9826 }
9827
9828 #[test]
9829 fn a_section_table_round_trips_through_a_directory() {
9830 let mut later = a_key_map_section();
9831 later.kind = *b"RUDBZZ9\0";
9832 later.id = 2;
9833 let table = bare_table(vec![a_key_map_section(), later]);
9834 let directory = encode_directory(&table).expect("directory");
9835 let decoded = decode_directory(&directory, 1 << 20).expect("reopen");
9836 assert_eq!(decoded.sections(), &[a_key_map_section(), later]);
9837 assert!(decoded.sections()[0].known());
9841 assert!(!decoded.sections()[1].known());
9842 }
9843
9844 #[test]
9845 fn a_directory_written_before_the_section_table_reads_as_a_table_with_none() {
9846 let directory = encode_directory(&bare_table(Vec::new())).expect("directory");
9850 let block = SECTIONS.len() + size_of::<u64>() + size_of::<u16>();
9851 let older = &directory[..directory.len() - block];
9852 let decoded = decode_directory(older, 1 << 20).expect("a directory from before sections");
9853 assert!(decoded.sections().is_empty());
9854 assert_eq!(decoded.generation(), 0, "a format 22 table recorded no generation");
9855 assert_eq!(decoded.name(), "linked");
9856 assert_eq!(decoded.fields().len(), 1, "everything before the block still decodes");
9857 }
9858
9859 #[test]
9860 fn a_file_stamped_with_the_previous_format_still_opens_and_reads() {
9861 let path = path("format_twenty_two");
9868 let mut writer =
9869 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9870 .expect("new file");
9871 let rows = Chunk::new(vec![
9872 Vector::from_values(
9873 LogicalType::Integer,
9874 &[Value::Integer(1), Value::Integer(2), Value::Integer(3)],
9875 )
9876 .expect("integers"),
9877 ])
9878 .expect("one column");
9879 writer.append(&rows).expect("the only part");
9880 writer.finish().expect("commit");
9881
9882 let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
9883 write_at(&file, 8, &22_u32.to_le_bytes()).expect("stamp the older format");
9884 drop(file);
9885
9886 let reader = Reader::open(&path).expect("a format 22 file opens unchanged");
9887 assert_eq!(reader.table().rows(), 3);
9888 assert_eq!(reader.read(0, &[0]).expect("the part still reads").len(), 3);
9893
9894 let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
9897 write_at(&file, 8, &21_u32.to_le_bytes()).expect("stamp an unreadable format");
9898 drop(file);
9899 let error = Reader::open(&path).expect_err("format 21 is not readable");
9900 assert!(error.to_string().contains("format 21"), "{error}");
9901
9902 fs::remove_file(&path).expect("clean up");
9903 }
9904
9905 #[test]
9906 fn a_section_whose_extent_table_is_outside_the_file_is_refused() {
9907 let mut past = a_key_map_section();
9912 past.extent_page = 1 << 30;
9913 let directory = encode_directory(&bare_table(vec![past])).expect("directory");
9914 let error = decode_directory(&directory, 1 << 20).expect_err("refused");
9915 assert!(error.to_string().contains("outside the file"), "{error}");
9916
9917 let mut inside_the_header = a_key_map_section();
9918 inside_the_header.extent_page = 8;
9919 let directory = encode_directory(&bare_table(vec![inside_the_header])).expect("directory");
9920 assert!(
9921 decode_directory(&directory, 1 << 20).is_err(),
9922 "a section may not overlap a header"
9923 );
9924 }
9925
9926 #[test]
9927 fn a_section_recorded_as_not_built_is_legal_and_names_no_bytes() {
9928 let not_built = Section {
9932 kind: *section::FORWARD_LINK,
9933 id: 9,
9934 generation: 3,
9935 extents: 0,
9936 extent_page: 0,
9937 extent_bytes: 0,
9938 hash: 0,
9939 flags: 0,
9940 header_bytes: 0,
9941 };
9942 let directory = encode_directory(&bare_table(vec![not_built])).expect("directory");
9943 let decoded = decode_directory(&directory, 1 << 20).expect("reopen");
9944 assert_eq!(decoded.sections(), &[not_built]);
9945
9946 let mut incoherent = not_built;
9949 incoherent.extent_bytes = 28;
9950 incoherent.extent_page = HEADER;
9951 let directory = encode_directory(&bare_table(vec![incoherent])).expect("directory");
9952 assert!(decode_directory(&directory, 1 << 20).is_err());
9953 }
9954
9955 #[test]
9956 fn a_directory_naming_more_sections_than_the_bound_is_refused() {
9957 let directory = encode_directory(&bare_table(Vec::new())).expect("directory");
9958 let mut torn = directory.clone();
9959 let count_at = torn.len() - size_of::<u16>();
9960 torn[count_at..].copy_from_slice(&u16::MAX.to_le_bytes());
9961 assert!(decode_directory(&torn, 1 << 20).is_err());
9964 }
9965
9966 fn linked_file(label: &str, rows: i32) -> PathBuf {
9968 let path = path(label);
9969 let mut writer =
9970 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
9971 .expect("new file");
9972 let values = (0..rows).map(Value::Integer).collect::<Vec<_>>();
9973 let chunk =
9974 Chunk::new(vec![Vector::from_values(LogicalType::Integer, &values).expect("integers")])
9975 .expect("one column");
9976 writer.append(&chunk).expect("the only part");
9977 writer.finish().expect("commit");
9978 path
9979 }
9980
9981 fn a_key_map_payload() -> Vec<u8> {
9982 (0..512_u32).flat_map(u32::to_le_bytes).collect()
9985 }
9986
9987 #[test]
9988 fn a_section_attached_to_a_committed_file_reads_back_byte_for_byte() {
9989 let path = linked_file("attach", 64);
9990 let payload = a_key_map_payload();
9991 let table = attach(
9992 &path,
9993 "items",
9994 &[section::Attachment {
9995 kind: *section::KEY_MAP,
9996 id: 0,
9997 flags: 2,
9998 header_bytes: 40,
9999 bytes: &payload,
10000 }],
10001 )
10002 .expect("attach a key map");
10003 assert_eq!(attached(&table).len(), 1);
10004
10005 let reader = Reader::open(&path).expect("reopen after the attach");
10006 let held = attached(reader.table());
10007 assert_eq!(held.len(), 1);
10008 assert_eq!(held[0].kind, *section::KEY_MAP);
10009 assert_eq!(held[0].flags, 2, "the form a reader must not have to guess");
10010 assert_eq!(held[0].header_bytes, 40);
10011 assert_eq!(held[0].generation, 1);
10015 assert!(held[0].usable(reader.table().generation()));
10016 assert_eq!(reader.payload(held[0]).expect("read the payload"), payload);
10017 assert_eq!(reader.extents(held[0]).expect("extent table").len(), 1);
10018
10019 fs::remove_file(&path).expect("clean up");
10020 }
10021
10022 #[test]
10023 fn attaching_a_section_answers_every_row_exactly_as_before() {
10024 let path = linked_file("attach_changes_nothing", 300);
10029 let before = Reader::open(&path).expect("open before");
10030 let rows = before.table().rows();
10031 let first = before.read(0, &[0]).expect("read before");
10032 let values = (0..rows).map(|at| first.value_at(at, 0)).collect::<Vec<_>>();
10033 let layout = before.layout().columns_total();
10034 drop(before);
10035
10036 let payload = a_key_map_payload();
10037 attach(
10038 &path,
10039 "items",
10040 &[section::Attachment {
10041 kind: *section::KEY_MAP,
10042 id: 0,
10043 flags: 0,
10044 header_bytes: 0,
10045 bytes: &payload,
10046 }],
10047 )
10048 .expect("attach");
10049
10050 let after = Reader::open(&path).expect("open after");
10051 assert_eq!(after.table().rows(), rows);
10052 let read = after.read(0, &[0]).expect("read after");
10053 for (at, value) in values.iter().enumerate() {
10054 assert_eq!(&read.value_at(at, 0), value, "row {at} moved");
10055 }
10056 assert_eq!(
10057 after.layout().columns_total(),
10058 layout,
10059 "an attach appends and does not rewrite a column page"
10060 );
10061
10062 fs::remove_file(&path).expect("clean up");
10063 }
10064
10065 #[test]
10066 fn a_rebuilt_section_replaces_the_one_it_supersedes() {
10067 let path = linked_file("attach_twice", 32);
10071 let one = a_key_map_payload();
10072 let two = vec![7_u8; 1024];
10073 let entry = |bytes| section::Attachment {
10074 kind: *section::KEY_MAP,
10075 id: 4,
10076 flags: 1,
10077 header_bytes: 0,
10078 bytes,
10079 };
10080 attach(&path, "items", &[entry(&one)]).expect("first build");
10081 attach(&path, "items", &[entry(&two)]).expect("rebuild");
10082
10083 let reader = Reader::open(&path).expect("reopen");
10084 let held = attached(reader.table());
10085 assert_eq!(held.len(), 1, "one map per column and not one per build");
10086 assert_eq!(reader.payload(held[0]).expect("payload"), two);
10087
10088 fs::remove_file(&path).expect("clean up");
10089 }
10090
10091 #[test]
10092 fn an_attach_carries_through_a_kind_it_does_not_know() {
10093 let path = linked_file("attach_unknown", 16);
10097 let payload = vec![3_u8; 96];
10098 attach(
10099 &path,
10100 "items",
10101 &[section::Attachment {
10102 kind: *b"RUDBZZ9\0",
10103 id: 1,
10104 flags: 0,
10105 header_bytes: 0,
10106 bytes: &payload,
10107 }],
10108 )
10109 .expect("a kind this build does not know still writes");
10110 let key_map = a_key_map_payload();
10111 attach(
10112 &path,
10113 "items",
10114 &[section::Attachment {
10115 kind: *section::KEY_MAP,
10116 id: 0,
10117 flags: 0,
10118 header_bytes: 0,
10119 bytes: &key_map,
10120 }],
10121 )
10122 .expect("attach beside it");
10123
10124 let reader = Reader::open(&path).expect("reopen");
10125 let held = attached(reader.table());
10126 assert_eq!(held.len(), 2, "the unfamiliar entry survived a directory rewrite");
10127 let unknown = held.iter().find(|one| !one.known()).expect("the unfamiliar one");
10128 assert_eq!(reader.payload(unknown).expect("its bytes are still there"), payload);
10129
10130 fs::remove_file(&path).expect("clean up");
10131 }
10132
10133 #[test]
10134 fn a_payload_of_nothing_is_a_relationship_recorded_as_not_built() {
10135 let path = linked_file("attach_not_built", 8);
10136 attach(
10137 &path,
10138 "items",
10139 &[section::Attachment {
10140 kind: *section::FORWARD_LINK,
10141 id: 2,
10142 flags: 0,
10143 header_bytes: 0,
10144 bytes: &[],
10145 }],
10146 )
10147 .expect("record a link that did not fit the budget");
10148
10149 let reader = Reader::open(&path).expect("reopen");
10150 let held = attached(reader.table());
10151 assert_eq!(held.len(), 1);
10152 assert_eq!(held[0].extents, 0);
10153 assert_eq!(held[0].extent_page, 0, "an entry that names no bytes points at none");
10154 assert!(reader.extents(held[0]).expect("no extent table").is_empty());
10155 assert!(reader.payload(held[0]).expect("no payload").is_empty());
10156
10157 fs::remove_file(&path).expect("clean up");
10158 }
10159
10160 #[test]
10161 fn a_payload_past_one_extent_is_split_and_joined_back() {
10162 let path = linked_file("attach_two_extents", 8);
10166 let payload = vec![0x5a_u8; section::MAX_EXTENT as usize + 1];
10167 attach(
10168 &path,
10169 "items",
10170 &[section::Attachment {
10171 kind: *section::KEY_MAP,
10172 id: 0,
10173 flags: 0,
10174 header_bytes: 0,
10175 bytes: &payload,
10176 }],
10177 )
10178 .expect("attach a payload past the bound");
10179
10180 let reader = Reader::open(&path).expect("reopen");
10181 let held = attached(reader.table());
10182 let extents = reader.extents(held[0]).expect("extent table");
10183 assert_eq!(extents.len(), 2, "one byte past the bound is two extents");
10184 assert_eq!(extents[0].length, section::MAX_EXTENT);
10185 assert_eq!(extents[1].length, 1);
10186 assert_eq!(extents[1].first, u64::from(section::MAX_EXTENT));
10187 assert_eq!(reader.extent(&extents[1]).expect("the last extent"), vec![0x5a]);
10189 assert_eq!(reader.payload(held[0]).expect("the whole payload").len(), payload.len());
10190
10191 fs::remove_file(&path).expect("clean up");
10192 }
10193
10194 #[test]
10195 fn a_torn_extent_is_refused_rather_than_decoded() {
10196 let path = linked_file("attach_torn", 8);
10197 let payload = a_key_map_payload();
10198 attach(
10199 &path,
10200 "items",
10201 &[section::Attachment {
10202 kind: *section::KEY_MAP,
10203 id: 0,
10204 flags: 0,
10205 header_bytes: 0,
10206 bytes: &payload,
10207 }],
10208 )
10209 .expect("attach");
10210
10211 let reader = Reader::open(&path).expect("reopen");
10212 let extent = reader.extents(&reader.table().sections()[0]).expect("extent table")[0];
10213 let file = OpenOptions::new().write(true).open(&path).expect("reopen to corrupt");
10214 write_at(&file, extent.offset + 7, &[0xff]).expect("flip a byte of the payload");
10215 drop(file);
10216
10217 let reader = Reader::open(&path).expect("the table still opens");
10218 let error = reader
10219 .payload(&reader.table().sections()[0])
10220 .expect_err("a corrupt payload is not handed out");
10221 assert!(error.to_string().contains("checksum"), "{error}");
10222 assert_eq!(reader.read(0, &[0]).expect("the column is untouched").width(), 1);
10225
10226 fs::remove_file(&path).expect("clean up");
10227 }
10228
10229 #[test]
10230 fn attaching_to_a_file_of_the_previous_format_is_refused_rather_than_done() {
10231 let path = linked_file("attach_old_format", 8);
10234 let file = OpenOptions::new().write(true).open(&path).expect("reopen to patch");
10235 write_at(&file, 8, &22_u32.to_le_bytes()).expect("stamp the older format");
10236 drop(file);
10237
10238 let payload = a_key_map_payload();
10239 let error = attach(
10240 &path,
10241 "items",
10242 &[section::Attachment {
10243 kind: *section::KEY_MAP,
10244 id: 0,
10245 flags: 0,
10246 header_bytes: 0,
10247 bytes: &payload,
10248 }],
10249 )
10250 .expect_err("format 22 cannot gain a section");
10251 assert!(error.to_string().contains("format 22"), "{error}");
10252 assert!(Reader::open(&path).expect("and the file is untouched").table().rows() == 8);
10253
10254 fs::remove_file(&path).expect("clean up");
10255 }
10256
10257 #[test]
10258 fn a_section_header_longer_than_its_payload_is_refused_at_the_write() {
10259 let path = linked_file("attach_bad_header", 8);
10260 let error = attach(
10261 &path,
10262 "items",
10263 &[section::Attachment {
10264 kind: *section::KEY_MAP,
10265 id: 0,
10266 flags: 0,
10267 header_bytes: 40,
10268 bytes: &[1, 2, 3],
10269 }],
10270 )
10271 .expect_err("a writer's bug stops at the write");
10272 assert!(error.to_string().contains("header is longer"), "{error}");
10273
10274 fs::remove_file(&path).expect("clean up");
10275 }
10276
10277 #[test]
10278 fn attaching_to_a_name_the_file_does_not_hold_says_so() {
10279 let path = linked_file("attach_wrong_name", 8);
10280 let error = attach(&path, "orders", &[]).expect_err("no such table");
10281 assert!(error.to_string().contains("orders"), "{error}");
10282 fs::remove_file(&path).expect("clean up");
10283 }
10284
10285 #[test]
10286 fn the_planner_gets_an_exact_count_for_a_leading_value_of_an_incomplete_synopsis() {
10287 let path = path("frequency_prefix_for_the_planner");
10294 let mut writer =
10295 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10296 .expect("new file");
10297 let mut values = vec![Value::Integer(1); 10_000];
10298 for _ in 0..10 {
10299 values.extend((0..600).map(|tail| Value::Integer(1_000 + tail)));
10300 }
10301 for part in values.chunks(8_000) {
10304 let rows = Chunk::new(vec![
10305 Vector::from_values(LogicalType::Integer, part).expect("integers"),
10306 ])
10307 .expect("one column");
10308 writer.append(&rows).expect("a part");
10309 }
10310 writer.finish().expect("commit");
10311 let reader = Reader::open(&path).expect("reopen from disk");
10312 let prefix =
10313 reader.frequency_prefix(0).expect("a readable synopsis").expect("the column has one");
10314 assert_eq!(prefix.entries.len(), 512);
10317 assert_eq!(prefix.omitted_max, 10);
10318 let common = Common::new(reader);
10319 assert_eq!(common.rows(), 16_000);
10320 let column = common.column("id").expect("the file has that column");
10321 assert_eq!(
10322 common.rows_with(column, &Bound::Int(1)),
10323 Stat::exact(10_000, Provenance::FrequencySynopsis)
10324 );
10325 assert_eq!(
10327 common.rows_with(column, &Bound::Int(1_100)),
10328 Stat::exact(10, Provenance::FrequencySynopsis)
10329 );
10330 assert_eq!(common.rows_with(column, &Bound::Int(1_550)), Stat::Unknown);
10333 assert_eq!(common.rows_with(column, &Bound::Int(9_999)), Stat::Unknown);
10336 let remainder = common.remainder(column).expect("the list is a prefix");
10340 assert_eq!(remainder, Remainder { rows: 890, listed: 512, most: 10 });
10341 assert_eq!(remainder.rows / (601 - remainder.listed), 10);
10342 fs::remove_file(&path).expect("clean up");
10343 }
10344
10345 #[test]
10347 fn a_file_holding_no_table_commits_and_opens_and_a_table_can_be_added_to_it() {
10348 let path = path("empty");
10349 Writer::empty(&path, &[]).expect("a file with nothing in it");
10350 let catalog = Catalog::open(&path).expect("the empty file opens");
10351 assert_eq!(catalog.len(), 0);
10352 assert!(catalog.is_empty());
10353 assert_eq!(catalog.names().count(), 0);
10354 let mut writer =
10357 Writer::open(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10358 .expect("a table goes into the empty file");
10359 writer.append(&sample_ids()).expect("rows");
10360 writer.finish().expect("commit");
10361 let catalog = Catalog::open(&path).expect("the file opens again");
10362 assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10363 fs::remove_file(&path).expect("clean up");
10364 }
10365
10366 #[test]
10376 fn a_committed_empty_table_gives_up_its_name_and_one_with_rows_does_not() {
10377 let path = path("empty-name");
10378 let field = || vec![Field::required("id", LogicalType::Integer)];
10379 Writer::create(&path, "items", field()).expect("new file").finish().expect("commit");
10380 let catalog = Catalog::open(&path).expect("the file opens");
10381 assert_eq!(catalog.rows().collect::<Vec<_>>(), vec![("items", 0)]);
10382
10383 let mut writer = Writer::open(&path, "items", field()).expect("the empty name is free");
10384 writer.append(&sample_ids()).expect("rows");
10385 writer.finish().expect("commit");
10386 let catalog = Catalog::open(&path).expect("the file opens again");
10387 assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10389 let held = catalog.rows().collect::<Vec<_>>();
10390 assert_eq!(held.len(), 1);
10391 assert!(held[0].1 > 0, "the rows that were appended are the ones the catalog counts");
10392
10393 let error = Writer::open(&path, "items", field()).expect_err("a name with rows is taken");
10395 assert!(error.to_string().contains("same name"), "{error}");
10396 fs::remove_file(&path).expect("clean up");
10397 }
10398
10399 fn sample_view(name: &str) -> ViewEntry {
10401 ViewEntry {
10402 name: name.to_string(),
10403 sql: "SELECT id FROM items WHERE id > 0".to_string(),
10404 statement: format!("CREATE VIEW {name} AS SELECT id FROM items WHERE (id > 0);"),
10405 aliases: vec!["n".to_string()],
10406 columns: vec![Field::new("n", LogicalType::Integer)],
10407 }
10408 }
10409
10410 #[test]
10411 fn a_view_written_into_the_catalog_comes_back_whole() {
10412 let path = path("views");
10413 let mut writer =
10414 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10415 .expect("new file");
10416 writer.append(&sample_ids()).expect("rows");
10417 writer.with_views(vec![sample_view("v")]).finish().expect("commit");
10418 let catalog = Catalog::open(&path).expect("reopen");
10419 assert_eq!(catalog.views().cloned().collect::<Vec<_>>(), vec![sample_view("v")]);
10420 assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10423 fs::remove_file(&path).expect("clean up");
10424 }
10425
10426 #[test]
10428 fn appending_a_table_carries_the_views_forward() {
10429 let path = path("viewscarry");
10430 let mut writer =
10431 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10432 .expect("new file");
10433 writer.append(&sample_ids()).expect("rows");
10434 writer.with_views(vec![sample_view("v")]).finish().expect("commit");
10435 let mut writer =
10436 Writer::open(&path, "other", vec![Field::required("id", LogicalType::Integer)])
10437 .expect("a second table");
10438 writer.append(&sample_ids()).expect("rows");
10439 writer.finish().expect("commit");
10440 let catalog = Catalog::open(&path).expect("reopen");
10441 assert_eq!(catalog.views().count(), 1);
10442 assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items", "other"]);
10443 fs::remove_file(&path).expect("clean up");
10444 }
10445
10446 #[test]
10448 fn restating_the_views_leaves_every_table_where_it_was() {
10449 let path = path("restate");
10450 let mut writer =
10451 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
10452 .expect("new file");
10453 writer.append(&sample_ids()).expect("rows");
10454 writer.finish().expect("commit");
10455 let before = fs::metadata(&path).expect("the file is there").len();
10456 Writer::restate(&path, &[sample_view("v"), sample_view("w")]).expect("two views");
10457 let catalog = Catalog::open(&path).expect("reopen");
10458 assert_eq!(catalog.views().count(), 2);
10459 assert_eq!(catalog.names().collect::<Vec<_>>(), vec!["items"]);
10460 let after = fs::metadata(&path).expect("the file is there").len();
10463 assert!(after > before, "a generation was written");
10464 assert!(after - before < before, "the table was not written again");
10465 let reader = Catalog::open(&path).expect("reopen").table("items").expect("the table");
10468 assert_eq!(reader.table().rows, 3);
10469 Writer::restate(&path, &[]).expect("no views at all");
10472 assert_eq!(Catalog::open(&path).expect("reopen").views().count(), 0);
10473 fs::remove_file(&path).expect("clean up");
10474 }
10475
10476 #[test]
10478 fn a_view_named_after_a_table_is_refused_when_the_catalog_is_read() {
10479 let bytes = encode_catalog(
10480 &[Entry {
10481 name: "items".to_string(),
10482 fields: vec![Field::required("id", LogicalType::Integer)],
10483 rows: 1,
10484 directory: Page { offset: HEADER, length: 8, hash: 0 },
10485 }],
10486 &[sample_view("items")],
10487 )
10488 .expect("it encodes, because encoding does not look");
10489 let error = decode_catalog(&bytes, HEADER + 8).expect_err("and decoding does");
10490 assert!(error.to_string().contains("same name"), "{error}");
10491 }
10492
10493 #[test]
10494 fn committed_file_reopens_and_reads_only_requested_columns() {
10495 let path = path("reopen");
10496 let mut writer = Writer::create(
10497 &path,
10498 "items",
10499 vec![
10500 Field::required("id", LogicalType::Integer),
10501 Field::new("text", LogicalType::Varchar),
10502 ],
10503 )
10504 .expect("new file");
10505 writer.append(&sample()).expect("first part");
10506 writer.append(&sample()).expect("second part");
10507 writer.finish().expect("commit");
10508 let reader = Reader::open(&path).expect("reopen from disk");
10509 assert_eq!(reader.table().rows(), 6);
10510 assert_eq!(reader.table().stripes().len(), 1);
10513 assert_eq!(reader.parts(), 2);
10514 assert_eq!(reader.part_rows(0), 3);
10515 assert_eq!(reader.part_rows(1), 3);
10516 let text = reader.read(1, &[1]).expect("only text page");
10517 assert_eq!(text.width(), 1);
10518 assert_eq!(text.value_at(1, 0), Value::Null);
10519 assert_eq!(text.value_at(2, 0), Value::Varchar("long text after a slash".into()));
10520 let sparse = reader.read_sparse(1, &[1]).expect("one part without its whole page");
10521 assert_eq!(sparse.width(), 1);
10522 assert_eq!(sparse.value_at(1, 0), Value::Null);
10523 assert_eq!(sparse.value_at(2, 0), Value::Varchar("long text after a slash".into()));
10524 assert!(!reader.skips_codes(0, 1, &[0]).expect("alpha is in the stripe"));
10525 assert!(!reader.skips_codes(0, 1, &[2]).expect("long text is in the stripe"));
10526 assert!(reader.skips_codes(0, 1, &[3]).expect("unknown code is absent"));
10527 let count = reader.read(0, &[]).expect("no page is needed for count");
10528 assert_eq!(count.len(), 3);
10529 assert!(reader.skips(0, &[Probe { column: 0, op: Op::Greater, value: Bound::Int(100) }]));
10530 assert!(!reader.skips(0, &[Probe { column: 0, op: Op::Greater, value: Bound::Int(0) }]));
10531 let integers = reader.top_frequencies(0, 1).expect("valid integer synopsis").expect("kept");
10532 assert_eq!(
10533 integers,
10534 vec![(Value::Integer(-2), 2), (Value::Integer(4), 2), (Value::Integer(9), 2),]
10535 );
10536 let strings = reader.top_frequencies(1, 1).expect("valid string synopsis").expect("kept");
10537 assert_eq!(strings.len(), 3);
10538 assert!(strings.contains(&(Value::Null, 2)));
10539 assert!(strings.contains(&(Value::Varchar("alpha".into()), 2)));
10540 assert!(strings.contains(&(Value::Varchar("long text after a slash".into()), 2)));
10541 fs::remove_file(path).expect("remove scratch file");
10542 }
10543
10544 #[test]
10552 fn runs_handed_over_out_of_order_still_read_back_in_source_order() {
10553 let path = path("interleaved-runs");
10554 let mut writer =
10555 Writer::create(&path, "interleaved", vec![Field::new("v", LogicalType::BigInt)])
10556 .expect("new file");
10557 for morsel in [2_u64, 0, 3, 1] {
10558 let parts = (0..4_u64)
10559 .map(|chunk| {
10560 let first = i64::try_from(morsel * 32 + chunk * 8).expect("small");
10561 let values =
10562 (0..8_i64).map(|row| Value::BigInt(first + row)).collect::<Vec<_>>();
10563 let column =
10564 Vector::from_values(LogicalType::BigInt, &values).expect("a column");
10565 ((morsel, chunk), Chunk::new(vec![column]).expect("one column"))
10566 })
10567 .collect::<Vec<_>>();
10568 writer.append_stripe(parts).expect("a stripe");
10569 }
10570 writer.finish().expect("commit");
10571
10572 let reader = Reader::open(&path).expect("valid directory");
10573 assert_eq!(reader.table().stripes().len(), 4, "a run is a stripe of its own");
10574 assert_eq!(reader.table().rows(), 128);
10575 for part in 0..16_usize {
10576 let read = reader.read(part, &[0]).expect("a part back");
10577 for row in 0..8_usize {
10578 let want = i64::try_from(part * 8 + row).expect("small");
10579 assert_eq!(read.value_at(row, 0), Value::BigInt(want), "part {part} row {row}");
10580 }
10581 }
10582 fs::remove_file(path).expect("remove scratch file");
10583 }
10584
10585 #[test]
10588 fn runs_that_overlap_each_other_are_refused_at_commit() {
10589 let path = path("overlapping-runs");
10590 let mut writer =
10591 Writer::create(&path, "overlapping", vec![Field::new("v", LogicalType::BigInt)])
10592 .expect("new file");
10593 let one = |order: (u64, u64)| {
10594 let column =
10595 Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)]).expect("a column");
10596 (order, Chunk::new(vec![column]).expect("one column"))
10597 };
10598 writer.append_stripe(vec![one((0, 0)), one((0, 2))]).expect("a stripe");
10601 writer.append_stripe(vec![one((0, 1))]).expect("a stripe");
10602 let error = writer.finish().expect_err("the runs overlap");
10603 assert!(error.message().contains("source order"), "{error}");
10604 fs::remove_file(path).expect("remove scratch file");
10605 }
10606
10607 #[test]
10610 fn a_run_longer_than_a_stripe_is_refused() {
10611 let path = path("overlong-run");
10612 let mut writer =
10613 Writer::create(&path, "overlong", vec![Field::new("v", LogicalType::BigInt)])
10614 .expect("new file");
10615 let parts = (0..=STRIPE_PARTS)
10616 .map(|at| {
10617 let column = Vector::from_values(LogicalType::BigInt, &[Value::BigInt(1)])
10618 .expect("a column");
10619 let chunk = Chunk::new(vec![column]).expect("one column");
10620 ((0, u64::try_from(at).expect("small")), chunk)
10621 })
10622 .collect::<Vec<_>>();
10623 let error = writer.append_stripe(parts).expect_err("one part too many");
10624 assert!(error.message().contains("more parts than it holds"), "{error}");
10625 fs::remove_file(path).expect("remove scratch file");
10626 }
10627
10628 #[test]
10634 fn parts_past_the_stripe_bound_start_a_new_stripe() {
10635 let path = path("stripe-bound");
10636 let mut writer = Writer::create(
10637 &path,
10638 "items",
10639 vec![
10640 Field::required("id", LogicalType::Integer),
10641 Field::new("text", LogicalType::Varchar),
10642 ],
10643 )
10644 .expect("new file");
10645 let parts = STRIPE_PARTS * 2 + 3;
10646 for part in 0..parts {
10647 let id = part as i32;
10648 let chunk = Chunk::new(vec![
10649 Vector::from_values(
10650 LogicalType::Integer,
10651 &[Value::Integer(id), Value::Integer(-id)],
10652 )
10653 .expect("integers"),
10654 Vector::from_values(
10655 LogicalType::Varchar,
10656 &[Value::Varchar(format!("value {part}")), Value::Null],
10657 )
10658 .expect("strings"),
10659 ])
10660 .expect("matching rows");
10661 writer.append(&chunk).expect("one part");
10662 }
10663 writer.finish().expect("commit");
10664
10665 let reader = Reader::open(&path).expect("reopen from disk");
10666 assert_eq!(reader.parts(), parts);
10667 assert_eq!(reader.table().rows(), parts * 2);
10668 assert_eq!(reader.table().stripes().len(), parts.div_ceil(STRIPE_PARTS));
10669 assert_eq!(reader.table().stripes()[0].parts(), STRIPE_PARTS);
10670 assert_eq!(reader.table().stripes()[0].rows(), STRIPE_PARTS * 2);
10671 assert_eq!(reader.table().stripes()[2].parts(), 3);
10672 for part in (0..parts).rev() {
10675 let dense = reader.read(part, &[0, 1]).expect("a whole page read");
10676 let sparse = reader.read_sparse(part, &[0, 1]).expect("one part read");
10677 for chunk in [&dense, &sparse] {
10678 assert_eq!(chunk.len(), 2, "part {part} has its own row count");
10679 assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
10680 assert_eq!(chunk.value_at(1, 0), Value::Integer(-(part as i32)));
10681 assert_eq!(chunk.value_at(0, 1), Value::Varchar(format!("value {part}")));
10682 assert_eq!(chunk.value_at(1, 1), Value::Null);
10683 }
10684 }
10685 let above = [Probe { column: 0, op: Op::Greater, value: Bound::Int(100) }];
10688 assert!(reader.skips(0, &above), "the first stripe stops at 63");
10689 assert!(!reader.skips(STRIPE_PARTS * 2, &above), "the third stripe reaches 130");
10690 fs::remove_file(path).expect("remove scratch file");
10691 }
10692
10693 fn scattered(n: i64) -> i64 {
10695 n.wrapping_mul(-7_046_029_254_386_353_131)
10696 }
10697
10698 #[test]
10704 fn a_part_is_skipped_when_its_sieve_does_not_hold_the_constant() {
10705 let path = path("sieve-skip");
10706 let mut writer =
10707 Writer::create(&path, "hits", vec![Field::required("id", LogicalType::BigInt)])
10708 .expect("new file");
10709 let parts = STRIPE_PARTS + 3;
10710 let per_part = 128;
10714 for part in 0..parts {
10715 let held: Vec<Value> = (0..per_part)
10716 .map(|row| Value::BigInt(scattered((part * per_part + row) as i64)))
10717 .collect();
10718 let chunk =
10719 Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10720 .expect("one column");
10721 writer.append(&chunk).expect("one part");
10722 }
10723 writer.finish().expect("commit");
10724
10725 let reader = Reader::open(&path).expect("reopen from disk");
10726 let probe = |value: i64| Probe {
10727 column: 0,
10728 op: Op::Equal,
10729 value: Bound::Int(i128::from(scattered(value))),
10730 };
10731 for wanted in [0_i64, (per_part + 1) as i64, (parts * per_part - 1) as i64] {
10732 let tests = [probe(wanted)];
10733 let kept: Vec<usize> = (0..parts).filter(|&part| !reader.skips(part, &tests)).collect();
10734 let home = wanted as usize / per_part;
10735 assert!(kept.contains(&home), "the part holding {wanted} is read");
10736 assert!(kept.len() <= 2, "{wanted} keeps {kept:?}, which is more than one stray part");
10740 }
10741 let absent = [probe((parts * per_part) as i64 + 1)];
10742 let kept = (0..parts).filter(|&part| !reader.skips(part, &absent)).count();
10743 assert!(kept <= 1, "{kept} parts of {parts} kept a value no part holds");
10744 let tests = [probe(0)];
10747 assert!(
10748 reader.table().stripes().iter().all(|stripe| !stripe.zone.skips(&tests)),
10749 "the bounds rule out no stripe at all"
10750 );
10751 fs::remove_file(path).expect("remove scratch file");
10752 }
10753
10754 #[test]
10760 fn a_part_is_skipped_when_its_own_bounds_rule_out_a_comparison_the_stripe_keeps() {
10761 let path = path("part-range-skip");
10762 let mut writer =
10763 Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10764 .expect("new file");
10765 let parts = STRIPE_PARTS + 3;
10766 let per_part = 128;
10767 for part in 0..parts {
10768 let held: Vec<Value> = (0..per_part)
10772 .map(|row| {
10773 Value::BigInt((part * 1_000) as i64 + (scattered(row as i64).rem_euclid(900)))
10774 })
10775 .collect();
10776 let chunk =
10777 Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10778 .expect("one column");
10779 writer.append(&chunk).expect("one part");
10780 }
10781 writer.finish().expect("commit");
10782
10783 let reader = Reader::open(&path).expect("reopen from disk");
10784 let under = [Probe { column: 0, op: Op::Less, value: Bound::Int(3_000) }];
10785 let kept: Vec<usize> = (0..parts).filter(|&part| !reader.skips(part, &under)).collect();
10786 assert_eq!(kept, vec![0, 1, 2], "only the three parts that start under three thousand");
10787 assert!(!reader.stripe_skips(0, &under), "the stripe reaches from zero and keeps itself");
10789 fs::remove_file(path).expect("remove scratch file");
10790 }
10791
10792 #[test]
10796 fn a_part_is_waved_through_when_its_own_bounds_pass_a_comparison_the_stripe_cannot() {
10797 let path = path("part-range-certain");
10798 let mut writer =
10799 Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10800 .expect("new file");
10801 let parts = STRIPE_PARTS + 3;
10802 let per_part = 128;
10803 for part in 0..parts {
10804 let held: Vec<Value> = (0..per_part)
10805 .map(|row| {
10806 Value::BigInt((part * 1_000) as i64 + (scattered(row as i64).rem_euclid(900)))
10807 })
10808 .collect();
10809 let chunk =
10810 Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
10811 .expect("one column");
10812 writer.append(&chunk).expect("one part");
10813 }
10814 writer.finish().expect("commit");
10815
10816 let reader = Reader::open(&path).expect("reopen from disk");
10817 let under = [Probe { column: 0, op: Op::Less, value: Bound::Int(3_000) }];
10818 let waved: Vec<usize> = (0..parts).filter(|&part| reader.certain(part, &under)).collect();
10819 assert_eq!(waved, vec![0, 1, 2], "the three parts that end under three thousand");
10820 assert!(!reader.stripe_skips(0, &under), "the stripe straddles the comparison");
10823 fs::remove_file(path).expect("remove scratch file");
10824 }
10825
10826 #[test]
10829 fn a_stripe_of_one_part_writes_no_range_page_and_a_stripe_of_many_does() {
10830 for (parts, wanted) in [(1_usize, false), (STRIPE_PARTS, true)] {
10831 let path = path("part-range-page");
10832 let mut writer =
10833 Writer::create(&path, "hits", vec![Field::required("at", LogicalType::BigInt)])
10834 .expect("new file");
10835 for part in 0..parts {
10836 let held: Vec<Value> = (0..128)
10837 .map(|row| {
10838 Value::BigInt((part * 1_000) as i64 + scattered(row as i64).rem_euclid(900))
10839 })
10840 .collect();
10841 let chunk = Chunk::new(vec![
10842 Vector::from_values(LogicalType::BigInt, &held).expect("numbers"),
10843 ])
10844 .expect("one column");
10845 writer.append(&chunk).expect("one part");
10846 }
10847 writer.finish().expect("commit");
10848 let reader = Reader::open(&path).expect("reopen from disk");
10849 let bytes = reader.layout().columns[0].part_ranges;
10850 assert_eq!(bytes > 0, wanted, "{parts} parts wrote {bytes} bytes of ranges");
10851 fs::remove_file(path).expect("remove scratch file");
10852 }
10853 }
10854
10855 #[test]
10858 fn a_string_end_that_is_cut_down_still_covers_the_value_it_came_from() {
10859 let long = vec![b'a'; PART_BOUND_BYTES * 2];
10860 let low = shortened(Some(Bound::Bytes(long.clone())), false).expect("a low end");
10861 let high = shortened(Some(Bound::Bytes(long.clone())), true).expect("a high end");
10862 let Bound::Bytes(low) = low else { panic!("a string stays a string") };
10863 let Bound::Bytes(high) = high else { panic!("a string stays a string") };
10864 assert!(low.len() <= PART_BOUND_BYTES && high.len() <= PART_BOUND_BYTES);
10865 assert!(low.as_slice() <= long.as_slice(), "the low end is at or under the value");
10866 assert!(high.as_slice() >= long.as_slice(), "the high end is at or over the value");
10867 }
10868
10869 #[test]
10872 fn a_string_end_with_no_room_to_step_up_gives_up_the_bound() {
10873 let long = vec![u8::MAX; PART_BOUND_BYTES * 2];
10874 assert_eq!(shortened(Some(Bound::Bytes(long.clone())), true), None);
10875 let low = shortened(Some(Bound::Bytes(long)), false).expect("a low end is still a prefix");
10876 assert_eq!(low, Bound::Bytes(vec![u8::MAX; PART_BOUND_BYTES]));
10877 }
10878
10879 #[test]
10891 fn what_a_column_is_stored_as_follows_the_order_the_rows_were_written_in() {
10892 let parts = 4;
10893 let per_part = 1024;
10894 let rows = parts * per_part;
10895 let written = |name: &str, keys: &[i64]| {
10896 let path = path(name);
10897 let fields = vec![Field::required("key", LogicalType::BigInt)];
10898 let mut writer = Writer::create(&path, "keys", fields).expect("new file");
10899 for part in 0..parts {
10900 let values: Vec<Value> = keys[part * per_part..(part + 1) * per_part]
10901 .iter()
10902 .map(|key| Value::BigInt(*key))
10903 .collect();
10904 let chunk = Chunk::new(vec![
10905 Vector::from_values(LogicalType::BigInt, &values).expect("numbers"),
10906 ])
10907 .expect("one column");
10908 writer.append(&chunk).expect("one part");
10909 }
10910 writer.finish().expect("commit");
10911 path
10912 };
10913 let climbing = |step: &dyn Fn(usize) -> i64| {
10916 let mut key = 0;
10917 (0..rows)
10918 .map(|row| {
10919 key += step(row);
10920 key
10921 })
10922 .collect::<Vec<i64>>()
10923 };
10924 let ascending = climbing(&|row| (row % 3) as i64);
10925 let sparse = climbing(&|row| ((row * 2_654_435_761) % 4096) as i64);
10929 let near_path = written("stored-near", &ascending);
10930 let far_path = written("stored-far", &sparse);
10931
10932 let one = Reader::open(&near_path).expect("reopen from disk");
10933 let other = Reader::open(&far_path).expect("reopen from disk");
10934 let near = one.stored(0).expect("the column is stored");
10935 let far = other.stored(0).expect("the column is stored");
10936 assert_eq!(near.len(), parts, "one row per part");
10937 assert_eq!(far.len(), parts);
10938 let total = |stored: &[StoredPart]| stored.iter().map(|part| part.bytes).sum::<u64>();
10941 assert_eq!(total(&near), one.layout().columns[0].pages);
10942 assert_eq!(total(&far), other.layout().columns[0].pages);
10943 assert!(
10944 total(&near) * 2 < total(&far),
10945 "the sparse keys cost more, {} against {}",
10946 total(&far),
10947 total(&near)
10948 );
10949 for (at, part) in near.iter().enumerate() {
10951 assert_eq!(part.part, at);
10952 assert_eq!(part.row, at * per_part);
10953 assert_eq!(part.rows, per_part);
10954 let held = &ascending[at * per_part..(at + 1) * per_part];
10955 assert_eq!(part.low, Some(Value::BigInt(held[0])));
10956 assert_eq!(part.high, Some(Value::BigInt(held[per_part - 1])));
10957 assert_eq!(part.nulls, Some(0));
10958 }
10959 assert!(near[0].encoding.contains("DELTA"), "{}", near[0].encoding);
10962 assert!(far[0].encoding.contains("DELTA"), "{}", far[0].encoding);
10963 assert_ne!(near[0].encoding, far[0].encoding);
10964 fs::remove_file(near_path).expect("remove scratch file");
10965 fs::remove_file(far_path).expect("remove scratch file");
10966 }
10967
10968 #[test]
10978 fn a_sieve_larger_than_the_part_it_indexes_is_not_written() {
10979 let path = path("sieve-pays");
10980 let fields = vec![
10981 Field::required("spread", LogicalType::BigInt),
10982 Field::required("repeated", LogicalType::BigInt),
10983 ];
10984 let mut writer = Writer::create(&path, "hits", fields).expect("new file");
10985 let parts = 3;
10986 let per_part = 1024;
10987 for part in 0..parts {
10988 let base = (part * per_part) as i64;
10989 let spread: Vec<Value> =
10990 (0..per_part).map(|row| Value::BigInt(scattered(base + row as i64))).collect();
10991 let repeated: Vec<Value> =
10992 (0..per_part).map(|row| Value::BigInt(scattered((row / 256) as i64))).collect();
10993 let chunk = Chunk::new(vec![
10994 Vector::from_values(LogicalType::BigInt, &spread).expect("numbers"),
10995 Vector::from_values(LogicalType::BigInt, &repeated).expect("numbers"),
10996 ])
10997 .expect("two columns");
10998 writer.append(&chunk).expect("one part");
10999 }
11000 writer.finish().expect("commit");
11001
11002 let reader = Reader::open(&path).expect("reopen from disk");
11003 let layout = reader.layout();
11004 let spread = &layout.columns[0];
11005 let repeated = &layout.columns[1];
11006 assert!(spread.sieves > 0, "a column whose parts are worth a filter keeps one");
11007 assert_eq!(
11008 repeated.sieves, 0,
11009 "a column whose filter costs more than its parts keeps none"
11010 );
11011 for column in &layout.columns {
11014 assert!(
11015 column.sieves < column.pages,
11016 "{} spends {} on sieves over {} of data",
11017 column.name,
11018 column.sieves,
11019 column.pages
11020 );
11021 }
11022 let absent = [Probe {
11024 column: 0,
11025 op: Op::Equal,
11026 value: Bound::Int(i128::from(scattered((parts * per_part) as i64 + 1))),
11027 }];
11028 assert!((0..parts).all(|part| reader.skips(part, &absent)), "no part holds it");
11029 fs::remove_file(path).expect("remove scratch file");
11030 }
11031
11032 #[test]
11038 fn a_damaged_sieve_page_is_read_through_rather_than_refused() {
11039 let path = path("sieve-damaged");
11040 let mut writer =
11041 Writer::create(&path, "hits", vec![Field::required("id", LogicalType::BigInt)])
11042 .expect("new file");
11043 let rows = 128;
11044 let held: Vec<Value> = (0..rows).map(|row| Value::BigInt(scattered(row))).collect();
11045 let chunk =
11046 Chunk::new(vec![Vector::from_values(LogicalType::BigInt, &held).expect("numbers")])
11047 .expect("one column");
11048 writer.append(&chunk).expect("one part");
11049 writer.finish().expect("commit");
11050
11051 let page = Reader::open(&path).expect("reopen").table.stripes[0]
11052 .sieves
11053 .get(0)
11054 .expect("a sieve page");
11055 let mut file = OpenOptions::new().write(true).open(&path).expect("open the sieve page");
11056 file.seek(SeekFrom::Start(page.offset + u64::from(page.length) - 1)).expect("seek");
11057 file.write_all(&[0xff]).expect("damage one byte");
11058 drop(file);
11059
11060 let reader = Reader::open(&path).expect("reopen the damaged file");
11061 let absent =
11062 [Probe { column: 0, op: Op::Equal, value: Bound::Int(i128::from(scattered(99))) }];
11063 assert!(!reader.skips(0, &absent), "a sieve that cannot be read skips nothing");
11064 assert_eq!(
11065 reader.read(0, &[0]).expect("the rows are untouched").len(),
11066 usize::try_from(rows).expect("a small count")
11067 );
11068 fs::remove_file(path).expect("remove scratch file");
11069 }
11070
11071 #[test]
11082 fn workers_that_want_the_same_stripe_read_it_once() {
11083 let path = path("single-flight");
11084 let mut writer =
11085 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11086 .expect("new file");
11087 for part in 0..STRIPE_PARTS {
11088 let id = part as i32;
11089 let chunk = Chunk::new(vec![
11090 Vector::from_values(
11091 LogicalType::Integer,
11092 &[Value::Integer(id), Value::Integer(-id)],
11093 )
11094 .expect("integers"),
11095 ])
11096 .expect("matching rows");
11097 writer.append(&chunk).expect("one part");
11098 }
11099 writer.finish().expect("commit");
11100
11101 let reader = Reader::open(&path).expect("reopen from disk");
11102 assert_eq!(reader.table().stripes().len(), 1, "one stripe is the point of the test");
11103 let barrier = std::sync::Barrier::new(8);
11104 std::thread::scope(|scope| {
11105 for worker in 0..8 {
11106 let reader = &reader;
11107 let barrier = &barrier;
11108 scope.spawn(move || {
11109 barrier.wait();
11110 for part in (worker..STRIPE_PARTS).step_by(8) {
11111 let chunk = reader.read(part, &[0]).expect("a whole page read");
11112 assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11113 assert_eq!(chunk.value_at(1, 0), Value::Integer(-(part as i32)));
11114 }
11115 });
11116 }
11117 });
11118 assert_eq!(reader.pages.load(Atomic::Relaxed), 1, "one stripe, one page read, whoever won");
11119 fs::remove_file(path).expect("remove scratch file");
11120 }
11121
11122 #[test]
11135 fn opening_costs_the_same_over_a_thousand_times_the_rows() {
11136 let opened = |label: &str, rows_per_part: i32| {
11137 let path = path(label);
11138 let mut writer =
11139 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11140 .expect("new file");
11141 for part in 0..STRIPE_PARTS * 3 {
11142 let values = (0..rows_per_part)
11146 .map(|row| {
11147 Value::Integer((part as i32 * rows_per_part + row).wrapping_mul(2_654_435))
11148 })
11149 .collect::<Vec<_>>();
11150 let chunk = Chunk::new(vec![
11151 Vector::from_values(LogicalType::Integer, &values).expect("integers"),
11152 ])
11153 .expect("matching rows");
11154 writer.append(&chunk).expect("one part");
11155 }
11156 writer.finish().expect("commit");
11157 let reader = Reader::open(&path).expect("reopen from disk");
11158 let size = fs::metadata(&path).expect("the file is there").len();
11159 let out = (reader.reads(), reader.table().stripes().len(), size);
11160 fs::remove_file(path).expect("remove scratch file");
11161 out
11162 };
11163
11164 let (thin, thin_stripes, thin_size) = opened("open-thin", 1);
11165 let (fat, fat_stripes, fat_size) = opened("open-fat", 1000);
11166 assert_eq!(
11167 thin_stripes, fat_stripes,
11168 "the same stripe count is what makes this a fair ask"
11169 );
11170 assert!(
11171 fat_size > thin_size * 50,
11172 "the fat file has to actually be larger, and it is {fat_size} against {thin_size}"
11173 );
11174
11175 assert_eq!(thin.opening.reads, fat.opening.reads, "the same reads either way");
11176 assert_eq!(thin.pages, 0, "opening read a page");
11177 assert_eq!(fat.pages, 0, "opening read a page");
11178 assert_eq!(thin.indexes, 0, "opening read an index");
11179 assert_eq!(fat.indexes, 0, "opening read an index");
11180 assert!(
11183 fat.opening.bytes < thin.opening.bytes * 2,
11184 "opening the thin file read {} bytes and the fat one read {}",
11185 thin.opening.bytes,
11186 fat.opening.bytes
11187 );
11188 }
11189
11190 #[test]
11198 fn two_opens_of_one_file_cost_the_same_and_the_second_is_not_cheaper() {
11199 let path = path("open-twice");
11200 let mut writer =
11201 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11202 .expect("new file");
11203 for part in 0..STRIPE_PARTS * 3 {
11204 let chunk = Chunk::new(vec![
11205 Vector::from_values(LogicalType::Integer, &[Value::Integer(part as i32)])
11206 .expect("integers"),
11207 ])
11208 .expect("matching rows");
11209 writer.append(&chunk).expect("one part");
11210 }
11211 writer.finish().expect("commit");
11212
11213 let first = Reader::open(&path).expect("open");
11214 for part in 0..first.parts() {
11217 first.read(part, &[0]).expect("a part");
11218 }
11219 assert!(first.reads().pages > 0, "the scan has to have read something");
11220 let second = Reader::open(&path).expect("open again");
11221
11222 assert_eq!(first.reads().opening, second.reads().opening);
11223 assert_eq!(
11224 second.reads().pages,
11225 0,
11226 "the second open read a page off the back of the first"
11227 );
11228 assert_eq!(second.reads().indexes, 0, "the second open read an index it inherited");
11229 fs::remove_file(path).expect("remove scratch file");
11230 }
11231
11232 #[test]
11240 fn an_index_is_read_once_per_stripe_however_often_the_page_is_evicted() {
11241 let path = path("index-cache");
11242 let mut writer =
11243 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11244 .expect("new file");
11245 let parts = STRIPE_PARTS * (CACHED_STRIPES_PER_COLUMN + 2);
11246 for part in 0..parts {
11247 let id = part as i32;
11248 let chunk = Chunk::new(vec![
11249 Vector::from_values(LogicalType::Integer, &[Value::Integer(id)]).expect("integers"),
11250 ])
11251 .expect("matching rows");
11252 writer.append(&chunk).expect("one part");
11253 }
11254 writer.finish().expect("commit");
11255
11256 let reader = Reader::open(&path).expect("reopen from disk");
11257 let stripes = reader.table().stripes().len();
11258 assert!(stripes > CACHED_STRIPES_PER_COLUMN, "the page cache has to be too small for this");
11259 for _ in 0..2 {
11261 for part in 0..parts {
11262 let chunk = reader.read(part, &[0]).expect("a part");
11263 assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11264 }
11265 }
11266 assert_eq!(reader.indexes.load(Atomic::Relaxed), stripes, "one index read per stripe");
11267 assert!(
11268 reader.pages.load(Atomic::Relaxed) > stripes,
11269 "the pages are the ones that get read again, which is what makes the index count mean \
11270 something"
11271 );
11272 fs::remove_file(path).expect("remove scratch file");
11273 }
11274
11275 #[test]
11284 fn a_worker_per_stripe_reads_its_page_once_when_the_cache_was_told_to_expect_it() {
11285 let workers = CACHED_STRIPES_PER_COLUMN + 4;
11286 let path = path("stripe-per-worker");
11287 let mut writer =
11288 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11289 .expect("new file");
11290 for part in 0..STRIPE_PARTS * workers {
11291 let chunk = Chunk::new(vec![
11292 Vector::from_values(LogicalType::Integer, &[Value::Integer(part as i32)])
11293 .expect("integers"),
11294 ])
11295 .expect("matching rows");
11296 writer.append(&chunk).expect("one part");
11297 }
11298 writer.finish().expect("commit");
11299
11300 let read = |told: bool| {
11301 let reader = Reader::open(&path).expect("reopen from disk");
11302 assert_eq!(reader.table().stripes().len(), workers, "a stripe per worker");
11303 if told {
11304 reader.keep_stripes(workers);
11305 }
11306 let barrier = std::sync::Barrier::new(workers);
11307 std::thread::scope(|scope| {
11308 for (worker, run) in reader.stripe_parts().into_iter().enumerate() {
11309 let reader = &reader;
11310 let barrier = &barrier;
11311 scope.spawn(move || {
11312 for part in run {
11313 barrier.wait();
11314 let chunk = reader.read(part, &[0]).expect("a part of my own stripe");
11315 assert_eq!(chunk.value_at(0, 0), Value::Integer(part as i32));
11316 }
11317 assert!(worker < workers);
11318 });
11319 }
11320 });
11321 reader.pages.load(Atomic::Relaxed)
11322 };
11323
11324 assert_eq!(read(true), workers, "one page read per stripe and no more");
11325 assert!(read(false) > workers, "a cache that small is read again on every part");
11326 fs::remove_file(path).expect("remove scratch file");
11327 }
11328
11329 #[test]
11334 fn a_damaged_index_page_is_an_error() {
11335 let path = path("damaged-index");
11336 let mut writer =
11337 Writer::create(&path, "items", vec![Field::required("id", LogicalType::Integer)])
11338 .expect("new file");
11339 writer.append(&sample_ids()).expect("first part");
11340 writer.append(&sample_ids()).expect("second part");
11341 writer.finish().expect("commit");
11342
11343 let reader = Reader::open(&path).expect("valid directory");
11344 let index = reader.table.stripes[0].index;
11345 let mut byte = [0; 1];
11346 read_at(&reader.file, index.offset, &mut byte).expect("the first part length");
11347 let mut file = OpenOptions::new().write(true).open(&path).expect("open index page");
11348 file.seek(SeekFrom::Start(index.offset)).expect("index start");
11349 file.write_all(&[!byte[0]]).expect("damage the first part length");
11350 let error = reader.read(1, &[0]).expect_err("a damaged index must not be used");
11351 assert!(error.message().contains("index page section checksum differs"), "{error}");
11352 fs::remove_file(path).expect("remove scratch file");
11353 }
11354
11355 #[test]
11362 fn every_integer_width_round_trips_through_a_page() {
11363 let path = path("integer-widths");
11364 let columns = [
11365 (LogicalType::TinyInt, vec![Value::TinyInt(i8::MIN), Value::TinyInt(i8::MAX)]),
11366 (LogicalType::UTinyInt, vec![Value::UTinyInt(0), Value::UTinyInt(u8::MAX)]),
11367 (LogicalType::SmallInt, vec![Value::SmallInt(i16::MIN), Value::SmallInt(i16::MAX)]),
11368 (LogicalType::USmallInt, vec![Value::USmallInt(0), Value::USmallInt(u16::MAX)]),
11369 (LogicalType::Integer, vec![Value::Integer(i32::MIN), Value::Integer(i32::MAX)]),
11370 (LogicalType::UInteger, vec![Value::UInteger(0), Value::UInteger(u32::MAX)]),
11371 (LogicalType::BigInt, vec![Value::BigInt(i64::MIN), Value::BigInt(i64::MAX)]),
11372 (LogicalType::UBigInt, vec![Value::UBigInt(0), Value::UBigInt(u64::MAX)]),
11373 ];
11374 let fields = columns
11375 .iter()
11376 .enumerate()
11377 .map(|(at, (ty, _))| Field::required(format!("c{at}"), ty.clone()))
11378 .collect::<Vec<_>>();
11379 let vectors = columns
11380 .iter()
11381 .map(|(ty, values)| Vector::from_values(ty.clone(), values).expect("a vector"))
11382 .collect::<Vec<_>>();
11383 let mut writer = Writer::create(&path, "widths", fields).expect("new file");
11384 writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11385 writer.finish().expect("commit");
11386
11387 let reader = Reader::open(&path).expect("reopen from disk");
11388 let wanted = (0..columns.len()).collect::<Vec<_>>();
11389 let read = reader.read(0, &wanted).expect("every column");
11390 assert_eq!(read.len(), 2);
11391 for (at, (ty, values)) in columns.iter().enumerate() {
11393 assert_eq!(read.value_at(0, at), values[0], "the low end of {ty}");
11394 assert_eq!(read.value_at(1, at), values[1], "the high end of {ty}");
11395 }
11396 fs::remove_file(path).expect("remove scratch file");
11397 }
11398
11399 #[test]
11410 fn every_other_type_the_format_knows_round_trips_through_a_page() {
11411 let path = path("other-types");
11412 let columns = [
11413 (LogicalType::Float, vec![Value::Float(f32::MIN), Value::Float(-0.0)]),
11414 (LogicalType::Double, vec![Value::Double(f64::MIN), Value::Double(f64::MAX)]),
11415 (LogicalType::HugeInt, vec![Value::HugeInt(i128::MIN), Value::HugeInt(i128::MAX)]),
11416 (LogicalType::UHugeInt, vec![Value::UHugeInt(0), Value::UHugeInt(u128::MAX)]),
11417 (LogicalType::Time, vec![Value::Time(0), Value::Time(86_399_999_999)]),
11418 (LogicalType::TimeTz, vec![Value::TimeTz(-50_400_000_000), Value::TimeTz(0)]),
11419 (
11420 LogicalType::TimestampTz,
11421 vec![Value::TimestampTz(i64::MIN + 1), Value::TimestampTz(i64::MAX)],
11422 ),
11423 (
11424 LogicalType::Interval,
11425 vec![
11426 Value::Interval { months: i32::MIN, days: i32::MAX, micros: i64::MIN },
11427 Value::Interval { months: 13, days: -1, micros: 1 },
11428 ],
11429 ),
11430 (
11431 LogicalType::Blob,
11432 vec![Value::Blob(vec![0, 0xff, 0x80, 0xfe]), Value::Blob(Vec::new())],
11433 ),
11434 ];
11435 let fields = columns
11436 .iter()
11437 .enumerate()
11438 .map(|(at, (ty, _))| Field::required(format!("c{at}"), ty.clone()))
11439 .collect::<Vec<_>>();
11440 let vectors = columns
11441 .iter()
11442 .map(|(ty, values)| Vector::from_values(ty.clone(), values).expect("a vector"))
11443 .collect::<Vec<_>>();
11444 let mut writer = Writer::create(&path, "others", fields).expect("new file");
11445 writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11446 writer.finish().expect("commit");
11447
11448 let reader = Reader::open(&path).expect("reopen from disk");
11449 let wanted = (0..columns.len()).collect::<Vec<_>>();
11450 let read = reader.read(0, &wanted).expect("every column");
11451 assert_eq!(read.len(), 2);
11452 for (at, (ty, values)) in columns.iter().enumerate() {
11453 assert_eq!(read.value_at(0, at), values[0], "the low end of {ty}");
11454 assert_eq!(read.value_at(1, at), values[1], "the high end of {ty}");
11455 }
11456 let Value::Float(zero) = read.value_at(1, 0) else { panic!("a float stays a float") };
11459 assert!(zero.is_sign_negative(), "a negative zero came back as {zero}");
11460
11461 fs::remove_file(path).expect("remove scratch file");
11462 }
11463
11464 #[test]
11470 fn a_nan_survives_being_written_down() {
11471 let path = path("nan");
11472 let nan = Vector::from_values(LogicalType::Double, &[Value::Double(f64::NAN)])
11473 .expect("a NaN vector");
11474 let mut writer =
11475 Writer::create(&path, "nan", vec![Field::required("d", LogicalType::Double)])
11476 .expect("new file");
11477 writer.append(&Chunk::new(vec![nan]).expect("one column")).expect("one stripe");
11478 writer.finish().expect("commit");
11479 let read = Reader::open(&path).expect("reopen").read(0, &[0]).expect("the column");
11480 let Value::Double(back) = read.value_at(0, 0) else { panic!("a double stays a double") };
11481 assert!(back.is_nan(), "a NaN came back as {back}");
11482 fs::remove_file(path).expect("remove scratch file");
11483 }
11484
11485 #[test]
11492 fn a_uuid_and_a_bit_string_come_back_as_the_bits_that_went_in() {
11493 let path = path("uuid-and-bit");
11494 let uuids = vec![0_i128, i128::MIN, -1];
11495 let mut bits = StringColumn::new();
11496 for value in [&b"\x02\xff"[..], &b""[..], &b"\x00\x01\x02\x03\x04\x05"[..]] {
11497 bits.push_bytes(value);
11498 }
11499 let expected = bits.clone();
11500 let fields =
11501 vec![Field::required("u", LogicalType::Uuid), Field::required("b", LogicalType::Bit)];
11502 let vectors = vec![
11503 Vector::flat(LogicalType::Uuid, Data::Int128(uuids.clone().into())).expect("uuids"),
11504 Vector::flat(LogicalType::Bit, Data::Varlen(bits)).expect("bit strings"),
11505 ];
11506 let mut writer = Writer::create(&path, "ids", fields).expect("new file");
11507 writer.append(&Chunk::new(vectors).expect("matching rows")).expect("one stripe");
11508 writer.finish().expect("commit");
11509
11510 let reader = Reader::open(&path).expect("reopen from disk");
11511 let read = reader.read(0, &[0, 1]).expect("both columns").flatten().expect("flat");
11512 let Some(Data::Int128(back)) = read.column(0).expect("the uuids").data() else {
11513 panic!("a uuid column is the 128 bit lane")
11514 };
11515 assert_eq!(back.as_slice(), uuids.as_slice());
11516 let Some(Data::Varlen(back)) = read.column(1).expect("the bits").data() else {
11517 panic!("a bit column is bytes")
11518 };
11519 for row in 0..expected.len() {
11520 assert_eq!(back.bytes(row), expected.bytes(row), "row {row} of the bit column");
11521 }
11522 fs::remove_file(path).expect("remove scratch file");
11523 }
11524
11525 #[test]
11526 fn numeric_frequency_candidates_keep_bounded_row_ordinals() {
11527 let path = path("frequency-ordinals");
11528 let mut writer =
11529 Writer::create(&path, "items", vec![Field::required("id", LogicalType::BigInt)])
11530 .expect("new file");
11531 let mut values = Vec::new();
11532 for leader in 0..10_i64 {
11533 values.extend(std::iter::repeat_n(leader, 100));
11534 }
11535 values.extend(1_000_i64..41_000);
11536 for part in values.chunks(1_024) {
11537 let vector = Vector::flat(LogicalType::BigInt, Data::Int64(part.to_vec().into()))
11538 .expect("big integers");
11539 writer.append(&Chunk::new(vec![vector]).expect("one column")).expect("one stripe");
11540 }
11541 writer.finish().expect("commit");
11542
11543 let reader = Reader::open(&path).expect("reopen from disk");
11544 let occurrences =
11545 reader.frequency_occurrences(0).expect("valid metadata").expect("bounded ordinals");
11546 assert!(occurrences.omitted_max < 100);
11547 assert!(occurrences.ordinals.len() <= FREQUENCY_ORDINALS);
11548 assert_eq!(occurrences.anchor_indices.len(), occurrences.ordinals.len());
11549 assert!(occurrences.ordinals.windows(2).all(|pair| pair[0] < pair[1]));
11550 assert_eq!(&occurrences.ordinals[..1_000], &(0_u64..1_000).collect::<Vec<_>>());
11551 assert_eq!(
11552 &occurrences.anchor_indices[..1_000]
11553 .iter()
11554 .map(|&entry| occurrences.anchors[entry as usize].clone())
11555 .collect::<Vec<_>>(),
11556 &(0_i64..10)
11557 .flat_map(|leader| std::iter::repeat_n(Value::BigInt(leader), 100))
11558 .collect::<Vec<_>>()
11559 );
11560 fs::remove_file(path).expect("remove scratch file");
11561 }
11562
11563 #[test]
11564 fn numeric_frequencies_count_nulls_and_values_past_the_top_of_bigint() {
11565 let path = path("frequency-bits");
11570 let mut writer = Writer::create(
11571 &path,
11572 "items",
11573 vec![Field::new("u", LogicalType::UBigInt), Field::new("s", LogicalType::BigInt)],
11574 )
11575 .expect("new file");
11576 let mut rows = Vec::new();
11577 let mut leaders = Vec::new();
11578 for leader in 0..10_u64 {
11579 let count = 300 - leader * 10;
11580 let (unsigned, signed) = if leader == 0 {
11581 (Value::Null, Value::Null)
11582 } else {
11583 (Value::UBigInt(u64::MAX - leader), Value::BigInt(-(leader as i64)))
11584 };
11585 rows.extend(std::iter::repeat_n((unsigned.clone(), signed.clone()), count as usize));
11586 leaders.push(((unsigned, count), (signed, count)));
11587 }
11588 rows.extend((1_000..41_000_u64).map(|id| (Value::UBigInt(id), Value::BigInt(id as i64))));
11589 for part in rows.chunks(1_024) {
11590 let unsigned = part.iter().map(|(value, _)| value.clone()).collect::<Vec<_>>();
11591 let signed = part.iter().map(|(_, value)| value.clone()).collect::<Vec<_>>();
11592 let chunk = Chunk::new(vec![
11593 Vector::from_values(LogicalType::UBigInt, &unsigned).expect("unsigned"),
11594 Vector::from_values(LogicalType::BigInt, &signed).expect("signed"),
11595 ])
11596 .expect("matching columns");
11597 writer.append(&chunk).expect("rows");
11598 }
11599 writer.finish().expect("commit");
11600
11601 let reader = Reader::open(&path).expect("reopen from disk");
11602 for column in 0..2 {
11603 let prefix =
11604 reader.frequency_prefix(column).expect("valid metadata").expect("a synopsis");
11605 let wanted = leaders
11606 .iter()
11607 .map(|(unsigned, signed)| if column == 0 { unsigned } else { signed })
11608 .cloned()
11609 .collect::<Vec<_>>();
11610 assert_eq!(&prefix.entries[..10], &wanted[..], "column {column}");
11611 assert!(prefix.omitted_max < 210, "column {column}");
11612 assert_eq!(
11613 reader.distinct_values(column).expect("valid metadata"),
11614 Some(9 + 40_000),
11615 "column {column}"
11616 );
11617 }
11618 fs::remove_file(path).expect("remove scratch file");
11619 }
11620
11621 #[test]
11622 fn numeric_string_pair_leaders_are_certified_in_the_directory() {
11623 let path = path("pair-frequencies");
11624 let mut pairs = Vec::new();
11625 pairs.extend(std::iter::repeat_n((1_i64, "alpha".to_string()), 100));
11626 pairs.extend(std::iter::repeat_n((1_i64, "beta".to_string()), 50));
11627 pairs.extend(std::iter::repeat_n((2_i64, "gamma".to_string()), 40));
11628 pairs.extend((1_000_i64..1_600).map(|id| (id, format!("tail {id}"))));
11629 let mut writer = Writer::create(
11630 &path,
11631 "items",
11632 vec![
11633 Field::required("id", LogicalType::BigInt),
11634 Field::required("phrase", LogicalType::Varchar),
11635 ],
11636 )
11637 .expect("new file");
11638 for part in pairs.chunks(1_024) {
11639 let ids = part.iter().map(|(id, _)| Value::BigInt(*id)).collect::<Vec<_>>();
11640 let phrases =
11641 part.iter().map(|(_, phrase)| Value::Varchar(phrase.clone())).collect::<Vec<_>>();
11642 writer
11643 .append(
11644 &Chunk::new(vec![
11645 Vector::from_values(LogicalType::BigInt, &ids).expect("ids"),
11646 Vector::from_values(LogicalType::Varchar, &phrases).expect("phrases"),
11647 ])
11648 .expect("matching columns"),
11649 )
11650 .expect("rows");
11651 }
11652 writer.finish().expect("commit");
11653
11654 let reader = Reader::open(&path).expect("reopen from disk");
11655 let leaders = reader
11656 .top_pair_frequencies(0, 1, 2)
11657 .expect("valid pair metadata")
11658 .expect("the top two beat the omitted tail");
11659 assert!(
11660 leaders.contains(&(vec![Value::BigInt(1), Value::Varchar("alpha".to_string())], 100,))
11661 );
11662 assert!(
11663 leaders.contains(&(vec![Value::BigInt(1), Value::Varchar("beta".to_string())], 50,))
11664 );
11665 fs::remove_file(path).expect("remove scratch file");
11666 }
11667
11668 #[test]
11674 fn a_file_from_another_format_says_which_format_it_is() {
11675 let older = path("older-format");
11676 let mut writer =
11677 Writer::create(&older, "items", vec![Field::new("id", LogicalType::Integer)])
11678 .expect("new file");
11679 let chunk = Chunk::new(vec![
11680 Vector::flat(LogicalType::Integer, Data::Int32(vec![1, 2, 3].into()))
11681 .expect("integers"),
11682 ])
11683 .expect("chunk");
11684 writer.append(&chunk).expect("page written");
11685 writer.finish().expect("commit");
11686
11687 let unreadable =
11691 READABLE.iter().copied().min().expect("at least one format is readable") - 1;
11692 let mut file = OpenOptions::new().write(true).open(&older).expect("open for the header");
11693 file.seek(SeekFrom::Start(8)).expect("the version follows the magic");
11694 file.write_all(&unreadable.to_le_bytes()).expect("write an older version");
11695 drop(file);
11696 let complaint = Reader::open(&older).expect_err("an older format is refused").to_string();
11697 assert!(complaint.contains(&format!("format {unreadable}")), "{complaint}");
11698 assert!(complaint.contains(&format!("format {FORMAT}")), "{complaint}");
11699
11700 let mut file = OpenOptions::new().write(true).open(&older).expect("open for the header");
11701 file.seek(SeekFrom::Start(0)).expect("the magic is first");
11702 file.write_all(b"NOTRUDB!").expect("write another engine's magic");
11703 drop(file);
11704 let complaint = Reader::open(&older).expect_err("a foreign file is refused").to_string();
11705 assert!(complaint.contains("magic"), "{complaint}");
11706 assert!(!complaint.contains("format"), "a version has nothing to do with it: {complaint}");
11707 fs::remove_file(older).expect("remove scratch file");
11708 }
11709
11710 #[test]
11711 fn an_unfinished_or_damaged_file_does_not_answer_with_partial_rows() {
11712 let unfinished = path("unfinished");
11713 let mut writer =
11714 Writer::create(&unfinished, "items", vec![Field::new("id", LogicalType::Integer)])
11715 .expect("new file");
11716 let chunk = Chunk::new(vec![
11717 Vector::flat(LogicalType::Integer, Data::Int32(vec![1, 2, 3].into()))
11718 .expect("integers"),
11719 ])
11720 .expect("chunk");
11721 writer.append(&chunk).expect("page written");
11722 drop(writer);
11723 assert!(Reader::open(&unfinished).is_err(), "no directory was committed");
11724 fs::remove_file(unfinished).expect("remove scratch file");
11725
11726 let damaged = path("damaged");
11727 let mut writer =
11728 Writer::create(&damaged, "items", vec![Field::new("id", LogicalType::Integer)])
11729 .expect("new file");
11730 writer.append(&chunk).expect("page written");
11731 writer.finish().expect("commit");
11732 let reader = Reader::open(&damaged).expect("valid directory");
11733 let mut file =
11734 OpenOptions::new().write(true).open(&damaged).expect("open for a damaged page");
11735 file.seek(SeekFrom::Start(HEADER + 1)).expect("inside first page");
11736 file.write_all(&[255]).expect("damage one byte");
11737 assert!(reader.read(0, &[0]).is_err(), "page checksum rejects corruption");
11738 fs::remove_file(damaged).expect("remove scratch file");
11739 }
11740
11741 #[test]
11742 fn damaged_lazy_dictionary_payload_is_an_error() {
11743 let path = path("damaged-dictionary");
11744 let mut writer = Writer::create(
11745 &path,
11746 "items",
11747 vec![
11748 Field::required("id", LogicalType::Integer),
11749 Field::new("text", LogicalType::Varchar),
11750 ],
11751 )
11752 .expect("new file");
11753 writer.append(&sample()).expect("stripe written");
11754 writer.finish().expect("commit");
11755
11756 let reader = Reader::open(&path).expect("valid directory");
11757 let dictionary = reader.table.dictionaries[1].expect("string dictionary page");
11758 let mut header = [0; DICTIONARY_HEADER];
11761 read_at(&reader.file, dictionary.offset, &mut header).expect("dictionary header");
11762 let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
11765 let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
11766 assert_ne!(width & DICTIONARY_SCATTERED, 0, "the blocks say where they are");
11767 let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
11768 let mut start = [0; 8];
11769 let at = dictionary.offset + (DICTIONARY_HEADER + offset_bytes(count, bits)) as u64;
11770 read_at(&reader.file, at, &mut start).expect("the first block's start");
11771 let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
11772 file.seek(SeekFrom::Start(u64::from_le_bytes(start))).expect("inside dictionary payload");
11773 file.write_all(&[255]).expect("damage dictionary payload");
11774
11775 let chunk = reader.read(0, &[1]).expect("code page and dictionary index remain valid");
11776 let error =
11777 chunk.validate_external().expect_err("payload corruption must reach the caller");
11778 assert!(error.message().contains("payload checksum differs"), "{error}");
11779 fs::remove_file(path).expect("remove scratch file");
11780 }
11781
11782 #[test]
11792 fn a_column_of_all_different_values_is_written_without_a_dictionary() {
11793 let path = path("dictionary-decide");
11794 let rows = 20_000;
11795 let unique =
11797 |row: usize| format!("{row:09} a value that appears exactly once in the table");
11798 let repeated = |row: usize| unique(row / 40);
11800 let mut writer = Writer::create(
11801 &path,
11802 "items",
11803 vec![
11804 Field::required("unique", LogicalType::Varchar),
11805 Field::required("repeated", LogicalType::Varchar),
11806 ],
11807 )
11808 .expect("new file");
11809 for part in (0..rows).step_by(1_000) {
11810 let span = part..(part + 1_000).min(rows);
11811 let left = span.clone().map(|row| Value::Varchar(unique(row))).collect::<Vec<_>>();
11812 let right = span.map(|row| Value::Varchar(repeated(row))).collect::<Vec<_>>();
11813 writer
11814 .append(
11815 &Chunk::new(vec![
11816 Vector::from_values(LogicalType::Varchar, &left).expect("strings"),
11817 Vector::from_values(LogicalType::Varchar, &right).expect("strings"),
11818 ])
11819 .expect("two columns"),
11820 )
11821 .expect("a part");
11822 }
11823 writer.finish().expect("commit");
11824
11825 let reader = Reader::open(&path).expect("reopen from disk");
11826 assert!(
11827 reader.table.dictionaries[0].is_none(),
11828 "a column with no repeats has nothing to say twice"
11829 );
11830 assert!(
11831 reader.table.dictionaries[1].is_some(),
11832 "a column whose values come round again keeps its dictionary"
11833 );
11834 let mut first = 0;
11835 for part in 0..reader.parts() {
11836 let chunk = reader.read(part, &[0, 1]).expect("a part");
11837 for row in 0..chunk.len() {
11838 assert_eq!(chunk.value_at(row, 0), Value::Varchar(unique(first + row)));
11839 assert_eq!(chunk.value_at(row, 1), Value::Varchar(repeated(first + row)));
11840 }
11841 first += chunk.len();
11842 }
11843 assert_eq!(first, rows, "every row was read back");
11844 let raw = (0..rows).map(|row| unique(row).len()).sum::<usize>();
11845 let size = fs::metadata(&path).expect("the file is there").len() as usize;
11846 assert!(size < raw, "a column without a dictionary is still encoded: {size} against {raw}");
11847 fs::remove_file(path).expect("remove scratch file");
11848 }
11849
11850 #[test]
11863 fn a_dictionary_over_many_blocks_checks_every_block_of_it() {
11864 let path = path("dictionary-blocks");
11865 let value = |row: usize| {
11866 let row = row.saturating_sub(8_000);
11867 format!("{row:07} a value long enough to be worth a payload block")
11868 };
11869 let parts = 40;
11870 let per_part = 1000;
11871 let mut writer =
11872 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
11873 .expect("new file");
11874 for part in 0..parts {
11875 let values = (0..per_part)
11876 .map(|row| Value::Varchar(value(part * per_part + row)))
11877 .collect::<Vec<_>>();
11878 let chunk = Chunk::new(vec![
11879 Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
11880 ])
11881 .expect("matching rows");
11882 writer.append(&chunk).expect("a part");
11883 }
11884 writer.finish().expect("commit");
11885
11886 let reader = Reader::open(&path).expect("reopen from disk");
11887 let dictionary = reader.table.dictionaries[0].expect("string dictionary page");
11888 assert!(
11889 parts * per_part > TEXT_PAYLOAD_VALUES * 4,
11890 "the dictionary has to be several blocks for this to be testing anything"
11891 );
11892 for part in [0, parts - 1] {
11893 let chunk = reader.read(part, &[0]).expect("a part");
11894 chunk.validate_external().expect("every payload block checks out");
11895 assert_eq!(chunk.value_at(0, 0), Value::Varchar(value(part * per_part)));
11896 }
11897
11898 let mut header = [0; DICTIONARY_HEADER];
11900 read_at(&reader.file, dictionary.offset, &mut header).expect("dictionary header");
11901 let count = u32::from_le_bytes(header[0..4].try_into().expect("four bytes")) as usize;
11902 let blocks = u32::from_le_bytes(header[8..12].try_into().expect("four bytes")) as usize;
11903 let width = u32::from_le_bytes(header[12..16].try_into().expect("four bytes"));
11904 let bits = (width & !(DICTIONARY_SCATTERED | DICTIONARY_GRAMS)) as usize;
11905 let mut place = [0; 16];
11906 let at = DICTIONARY_HEADER + offset_bytes(count, bits) + (blocks - 1) * 16;
11907 read_at(&reader.file, dictionary.offset + at as u64, &mut place).expect("its place");
11908 let start = u64::from_le_bytes(place[..8].try_into().expect("eight bytes"));
11909 let length = u64::from_le_bytes(place[8..].try_into().expect("eight bytes"));
11910 let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
11911 file.seek(SeekFrom::Start(start + length - 4)).expect("the last bytes of the last block");
11912 file.write_all(&[255]).expect("damage the last payload block");
11913 let reader = Reader::open(&path).expect("the directory and the index are untouched");
11914 let chunk = reader.read(parts - 1, &[0]).expect("the code page remains valid");
11915 let error = chunk.validate_external().expect_err("the damage must reach the caller");
11916 assert!(error.message().contains("payload checksum differs"), "{error}");
11917 fs::remove_file(path).expect("remove scratch file");
11918 }
11919
11920 #[test]
11934 fn values_of_different_lengths_read_back_out_of_packed_offsets() {
11935 let path = path("dictionary-offsets");
11936 let value = |row: usize| {
11937 let row = row % 5_000;
11938 if row % 511 == 3 { String::new() } else { "x".repeat(row % 97) + &format!("{row:05}") }
11939 };
11940 let rows = 6_000;
11941 let mut writer =
11942 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
11943 .expect("new file");
11944 let values = (0..rows).map(|row| Value::Varchar(value(row))).collect::<Vec<_>>();
11945 for part in values.chunks(1_000) {
11946 let chunk =
11947 Chunk::new(vec![Vector::from_values(LogicalType::Varchar, part).expect("strings")])
11948 .expect("matching rows");
11949 writer.append(&chunk).expect("a part");
11950 }
11951 writer.finish().expect("commit");
11952
11953 let reader = Reader::open(&path).expect("reopen from disk");
11954 assert!(
11955 rows > TEXT_PAYLOAD_VALUES * 4,
11956 "the dictionary has to be several blocks for this to be testing anything"
11957 );
11958 for part in 0..rows / 1_000 {
11959 let chunk = reader.read(part, &[0]).expect("a part");
11960 for row in 0..1_000 {
11961 let row = part * 1_000 + row;
11962 assert_eq!(
11963 chunk.value_at(row % 1_000, 0),
11964 Value::Varchar(value(row)),
11965 "value {row}"
11966 );
11967 }
11968 }
11969 for _ in 0..2 {
11972 for part in 0..rows / 1_000 {
11973 let chunk = reader.read(part, &[0]).expect("a part");
11974 let mut lens = vec![0_i64; 1_000];
11975 let column = chunk.column(0).expect("one column");
11976 assert!(column.try_bytes_lens(&mut lens).expect("lengths"), "a stored column");
11977 for (row, &len) in lens.iter().enumerate() {
11978 let row = part * 1_000 + row;
11979 assert_eq!(len as usize, value(row).len(), "the length of value {row}");
11980 }
11981 }
11982 }
11983 fs::remove_file(path).expect("remove scratch file");
11984 }
11985
11986 #[test]
11988 fn lengths_restart_at_each_block_and_refuse_ends_that_go_backwards() {
11989 let mut ends: Vec<u32> = (1..=TEXT_PAYLOAD_VALUES as u32).map(|at| at * 2).collect();
11990 ends.extend([3, 3, 10]);
11991 let lens = lengths_of(&ends).expect("ordered ends");
11992 assert!(lens[..TEXT_PAYLOAD_VALUES].iter().all(|&len| len == 2));
11993 assert_eq!(&lens[TEXT_PAYLOAD_VALUES..], &[3, 0, 7]);
11994 ends.push(9);
11995 assert_eq!(lengths_of(&ends), None);
11996 }
11997
11998 #[test]
12010 fn a_global_dictionary_is_opened_once_however_many_workers_ask_at_once() {
12011 let path = path("dictionary-once");
12012 let parts = 8;
12013 let per_part = 500;
12014 let value =
12015 |row: usize| format!("{row:07} a value long enough to be worth a payload block");
12016 let mut writer =
12017 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12018 .expect("new file");
12019 for part in 0..parts {
12020 let values = (0..per_part)
12021 .map(|row| Value::Varchar(value(part * per_part + row)))
12022 .collect::<Vec<_>>();
12023 let chunk = Chunk::new(vec![
12024 Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
12025 ])
12026 .expect("matching rows");
12027 writer.append(&chunk).expect("a part");
12028 }
12029 writer.finish().expect("commit");
12030
12031 let reader = Reader::open(&path).expect("reopen from disk");
12032 assert!(reader.table.dictionaries[0].is_some(), "the column has to have one to share");
12033 assert_eq!(reader.reads().dictionaries, 0, "opening the file does not open a dictionary");
12034
12035 let workers = 16;
12036 let gate = std::sync::Barrier::new(workers);
12037 std::thread::scope(|scope| {
12038 for worker in 0..workers {
12039 let reader = reader.clone();
12040 let gate = &gate;
12041 scope.spawn(move || {
12042 gate.wait();
12043 let chunk = reader.read(worker % parts, &[0]).expect("a part");
12044 assert_eq!(
12045 chunk.value_at(0, 0),
12046 Value::Varchar(value((worker % parts) * per_part))
12047 );
12048 });
12049 }
12050 });
12051
12052 assert_eq!(reader.reads().dictionaries, 1, "sixteen workers, one dictionary, one open");
12053 fs::remove_file(path).expect("remove scratch file");
12054 }
12055
12056 #[test]
12061 fn a_damaged_sorted_order_is_an_error() {
12062 let path = path("damaged-order");
12063 let mut writer = Writer::create(
12064 &path,
12065 "items",
12066 vec![
12067 Field::required("id", LogicalType::Integer),
12068 Field::new("text", LogicalType::Varchar),
12069 ],
12070 )
12071 .expect("new file");
12072 writer.append(&sample()).expect("stripe written");
12073 writer.finish().expect("commit");
12074
12075 let reader = Reader::open(&path).expect("valid directory");
12076 let page = reader.table.dictionaries[1].expect("string dictionary page");
12077 let mut header = [0; DICTIONARY_HEADER];
12078 read_at(&reader.file, page.offset, &mut header).expect("dictionary header");
12079 let index_len = dictionary_index_len(&header);
12080 let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
12081 file.seek(SeekFrom::Start(page.offset + index_len)).expect("the first head");
12082 file.write_all(&[255]).expect("damage the order");
12083
12084 let dictionary = reader.dictionary(1).expect("read").expect("a string column has one");
12085 let error = dictionary.compare_rank(0, b"anything").expect_err("a damaged order is caught");
12086 assert!(error.message().contains("rank checksum differs"), "{error}");
12087 fs::remove_file(path).expect("remove scratch file");
12088 }
12089
12090 #[test]
12094 fn a_global_dictionary_carries_the_sorted_order_of_its_values() {
12095 let spellings = ["overlong1z", "b", "", "overlong1a", "overlong", "ab", "a", "overlong1"];
12098 let path = path("dictionary-order");
12099 let mut writer =
12100 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12101 .expect("new file");
12102 writer
12103 .append(
12104 &Chunk::new(vec![
12105 Vector::from_values(
12106 LogicalType::Varchar,
12107 &spellings.map(|text| Value::Varchar(text.into())),
12108 )
12109 .expect("strings"),
12110 ])
12111 .expect("one column"),
12112 )
12113 .expect("stripe written");
12114 writer.finish().expect("commit");
12115
12116 let reader = Reader::open(&path).expect("valid directory");
12117 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12118 let count = dictionary.ranks().expect("a v10 file stores one");
12119 assert_eq!(count, spellings.len(), "every distinct value has a rank");
12120 let order = (0..count)
12121 .map(|rank| dictionary.code_at_rank(rank).expect("a code"))
12122 .collect::<Vec<_>>();
12123 let mut seen = order.clone();
12124 seen.sort_unstable();
12125 assert_eq!(seen, (0..spellings.len() as u32).collect::<Vec<_>>(), "a permutation of codes");
12126
12127 let ranked = order
12128 .iter()
12129 .map(|&code| {
12130 dictionary.try_bytes_at(code as usize).expect("read").expect("a value").to_vec()
12131 })
12132 .collect::<Vec<_>>();
12133 let mut expected = spellings.map(|text| text.as_bytes().to_vec()).to_vec();
12134 expected.sort();
12135 assert_eq!(ranked, expected, "rank order is value order");
12136
12137 for (rank, value) in expected.iter().enumerate() {
12140 assert_eq!(
12141 dictionary.compare_rank(rank, value).expect("compare"),
12142 Ordering::Equal,
12143 "rank {rank} is its own value"
12144 );
12145 if rank > 0 {
12146 assert_eq!(
12147 dictionary.compare_rank(rank - 1, value).expect("compare"),
12148 Ordering::Less,
12149 "rank {rank} follows the one before it"
12150 );
12151 }
12152 }
12153 fs::remove_file(path).expect("remove scratch file");
12154 }
12155
12156 #[test]
12164 fn a_large_dictionary_ranks_in_value_order() {
12165 let path = path("dictionary-large-rank");
12166 let value = |row: u64| {
12167 let mixed = row.wrapping_mul(0x9e37_79b9_7f4a_7c15) >> 40;
12168 match row % 3 {
12169 0 => format!("https://example.com/a/long/shared/path/{mixed:08}"),
12170 1 => format!("{mixed}"),
12171 _ => format!("x{}", row % 1000).repeat(1 + (row % 4) as usize) + &row.to_string(),
12172 }
12173 };
12174 let distinct = 70_000;
12175 let parts = 4 * distinct / 1000;
12176 let per_part = 1000;
12177 let mut writer =
12178 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12179 .expect("new file");
12180 for part in 0..parts {
12181 let values = (0..per_part)
12182 .map(|row| Value::Varchar(value((part * per_part + row) / 4)))
12183 .collect::<Vec<_>>();
12184 let chunk = Chunk::new(vec![
12185 Vector::from_values(LogicalType::Varchar, &values).expect("strings"),
12186 ])
12187 .expect("matching rows");
12188 writer.append(&chunk).expect("a part");
12189 }
12190 writer.finish().expect("commit");
12191
12192 let reader = Reader::open(&path).expect("reopen from disk");
12193 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12194 let count = dictionary.ranks().expect("a ranked dictionary");
12195 assert_eq!(count, distinct as usize, "every distinct value has a rank");
12196 assert!(count >= PARALLEL_SORT_MIN, "too few values to be sorted on more than one thread");
12197 let ranked = (0..count)
12198 .map(|rank| {
12199 let code = dictionary.code_at_rank(rank).expect("a code");
12200 dictionary.try_bytes_at(code as usize).expect("read").expect("a value").to_vec()
12201 })
12202 .collect::<Vec<_>>();
12203 let mut expected = (0..distinct).map(|row| value(row).into_bytes()).collect::<Vec<_>>();
12204 expected.sort();
12205 assert_eq!(ranked, expected, "rank order is value order");
12206 fs::remove_file(path).expect("remove scratch file");
12207 }
12208
12209 #[test]
12222 fn a_directory_read_a_window_at_a_time_is_the_directory_read_whole() {
12223 let path = path("windowed-directory");
12224 let fields = vec![
12225 Field::required("id", LogicalType::BigInt),
12226 Field::required("word", LogicalType::Varchar),
12227 Field::new("score", LogicalType::Double),
12228 ];
12229 let mut writer = Writer::create(&path, "items", fields).expect("new file");
12230 for part in 0..70_i64 {
12231 let ids = (0..100).map(|row| Value::BigInt(part * 100 + row % 7)).collect::<Vec<_>>();
12232 let words = (0..100)
12233 .map(|row| Value::Varchar(format!("word {}", row % 13)))
12234 .collect::<Vec<_>>();
12235 let scores = (0..100)
12236 .map(|row| if row % 4 == 0 { Value::Null } else { Value::Double(row as f64) })
12237 .collect::<Vec<_>>();
12238 let chunk = Chunk::new(vec![
12239 Vector::from_values(LogicalType::BigInt, &ids).expect("integers"),
12240 Vector::from_values(LogicalType::Varchar, &words).expect("strings"),
12241 Vector::from_values(LogicalType::Double, &scores).expect("doubles"),
12242 ])
12243 .expect("three columns");
12244 writer.append(&chunk).expect("a part");
12245 }
12246 writer.finish().expect("commit");
12247
12248 let catalog = Catalog::open(&path).expect("reopen");
12249 let entry = catalog.entries.first().expect("one table").directory;
12250 let (offset, length) = (entry.offset, entry.length as usize);
12251 let mut bytes = vec![0; length];
12252 read_at(&catalog.file, offset, &mut bytes).expect("the directory");
12253 assert_eq!(file_checksum(&catalog.file, offset, length).expect("checksum"), entry.hash);
12254 let whole = decode_directory(&bytes, catalog.size).expect("whole");
12255 assert!(whole.stripes.len() > 1, "the table should span stripes");
12256 for size in [1, 7, 33, 4_096] {
12257 let mut cursor = Cursor::over(&catalog.file, offset, length);
12258 cursor.window.as_mut().expect("a window").size = size;
12259 let windowed = read_directory(cursor, catalog.size, Some(offset)).expect("windowed");
12260 assert_eq!(format!("{:?}", windowed.stripes), format!("{:?}", whole.stripes));
12261 assert_eq!(format!("{:?}", windowed.fields), format!("{:?}", whole.fields));
12262 let mut stored = 0;
12263 for (column, (left, held)) in
12264 windowed.frequencies.iter().zip(&whole.frequencies).enumerate()
12265 {
12266 match (left, held) {
12267 (None, None) => {}
12268 (
12269 Some(super::Frequencies::Stored { span, values }),
12270 Some(super::Frequencies::Held(summary)),
12271 ) => {
12272 let mut one = vec![0; span.length as usize];
12273 read_at(&catalog.file, span.offset, &mut one).expect("a synopsis");
12274 let read = decode_summary(
12275 &mut Cursor::new(&one),
12276 &whole.fields[column],
12277 whole.rows,
12278 *values,
12279 )
12280 .expect("a valid synopsis")
12281 .expect("one is there");
12282 assert_eq!(format!("{read:?}"), format!("{summary:?}"));
12283 stored += 1;
12284 }
12285 other => panic!("column {column} came back as {other:?}"),
12286 }
12287 }
12288 assert!(stored >= 2, "only {stored} synopses were left in the file");
12289 }
12290 let reader = catalog.table("items").expect("the table");
12291 assert!(reader.frequency_summaries[1].get().is_none());
12292 assert!(reader.top_frequencies(1, 1).expect("a readable synopsis").is_some());
12293 let first = reader.frequency_summaries[1].get().expect("decoded synopsis");
12294 let clone = reader.clone();
12295 assert!(clone.top_frequencies(1, 1).expect("cached synopsis").is_some());
12296 assert!(Arc::ptr_eq(first, clone.frequency_summaries[1].get().expect("same synopsis")));
12297 fs::remove_file(path).expect("remove scratch file");
12298 }
12299
12300 #[test]
12301 fn a_checksum_carried_across_reads_is_the_checksum_of_the_whole() {
12302 let path = path("file-checksum");
12303 let bytes = (0..200_000_u32)
12304 .map(|at| (at.wrapping_mul(2_654_435_761) >> 13) as u8)
12305 .collect::<Vec<_>>();
12306 fs::write(&path, &bytes).expect("scratch file");
12307 let file = File::open(&path).expect("open");
12308 for (offset, length) in [
12309 (0, 0),
12310 (3, 1),
12311 (5, 31),
12312 (0, 32),
12313 (9, 33),
12314 (1, 65_536),
12315 (7, 65_567),
12316 (0, 200_000),
12317 (11, 131_101),
12318 ] {
12319 let whole = checksum(&bytes[offset..offset + length]);
12320 assert_eq!(
12321 file_checksum(&file, offset as u64, length).expect("read"),
12322 whole,
12323 "{offset} {length}"
12324 );
12325 }
12326 fs::remove_file(path).expect("remove scratch file");
12327 }
12328
12329 #[test]
12330 fn a_string_synopsis_is_read_without_keeping_the_dictionary_blocks() {
12331 let path = path("synopsis-keeps-no-block");
12332 let spelled = |index: usize| Value::Varchar(format!("phrase {index:05}"));
12333 let mut values = (0..3_000).map(spelled).collect::<Vec<_>>();
12334 for _ in 0..3 {
12335 values.extend((0..3_000).step_by(5).map(spelled));
12336 }
12337 let mut writer =
12338 Writer::create(&path, "items", vec![Field::required("text", LogicalType::Varchar)])
12339 .expect("new file");
12340 for part in values.chunks(1_024) {
12341 writer
12342 .append(
12343 &Chunk::new(vec![
12344 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12345 ])
12346 .expect("one column"),
12347 )
12348 .expect("a part");
12349 }
12350 writer.finish().expect("commit");
12351
12352 let reader = Reader::open(&path).expect("reopen from disk");
12353 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12354 let resting = dictionary.footprint();
12355 let prefix = reader.frequency_prefix(0).expect("a readable synopsis").expect("one");
12356 assert_eq!(prefix.entries.len(), 512);
12357 for (value, count) in &prefix.entries {
12358 let Value::Varchar(text) = value else { panic!("a string column gave {value:?}") };
12359 let index = text["phrase ".len()..].parse::<usize>().expect("a spelled number");
12360 assert_eq!((index % 5, *count), (0, 4), "{text} came back with {count}");
12361 }
12362 assert_eq!(dictionary.footprint(), resting, "reading the synopsis kept a decoded block");
12363 let again = reader.frequency_prefix(0).expect("a readable synopsis").expect("one");
12364 assert_eq!(again.entries, prefix.entries);
12365 fs::remove_file(path).expect("remove scratch file");
12366 }
12367
12368 #[test]
12378 fn a_dictionary_sweep_reads_every_value_and_keeps_it_under_the_budget() {
12379 let path = path("dictionary-sweep");
12380 let spellings = (0..2_500)
12383 .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12384 .collect::<Vec<_>>();
12385 let mut writer =
12386 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12387 .expect("new file");
12388 for part in spellings.chunks(1_024) {
12391 writer
12392 .append(
12393 &Chunk::new(vec![
12394 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12395 ])
12396 .expect("one column"),
12397 )
12398 .expect("stripe written");
12399 }
12400 writer.finish().expect("commit");
12401
12402 let reader = Reader::open(&path).expect("valid directory");
12403 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12404 assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12405 for first in [0, TEXT_PAYLOAD_VALUES, TEXT_PAYLOAD_VALUES * 2] {
12406 assert!(dictionary.text_block_might_contain(first, b"value").expect("signature"));
12407 assert!(!dictionary.text_block_might_contain(first, b"google").expect("signature"));
12408 }
12409
12410 let resting = dictionary.footprint();
12411 let sweep = || {
12412 let mut swept: Vec<Vec<u8>> = Vec::new();
12413 let mut at = 0;
12414 let mut calls = 0;
12415 while at < dictionary.len() {
12416 let stopped = dictionary
12417 .sweep_text(at, dictionary.len(), &mut |index: usize, text: &[u8]| {
12418 assert_eq!(index, swept.len(), "a sweep hands its values over in order");
12419 swept.push(text.to_vec());
12420 Ok(())
12421 })
12422 .expect("a sweep reads");
12423 assert!(stopped > at, "a sweep moves");
12424 at = stopped;
12425 calls += 1;
12426 }
12427 assert_eq!(calls, 3, "a sweep hands over one block at a time");
12428 swept
12429 };
12430 let swept = sweep();
12431 assert_eq!(dictionary.footprint(), resting, "a first sweep keeps nothing it decoded");
12432 assert_eq!(sweep(), swept, "a second sweep reads what the first did");
12433 let after = dictionary.footprint();
12434 assert!(after > resting, "a second sweep under the budget keeps what it decoded");
12435
12436 let read = (0..dictionary.len())
12437 .map(|code| dictionary.try_bytes_at(code).expect("read").expect("a value").to_vec())
12438 .collect::<Vec<_>>();
12439 assert_eq!(swept, read, "a sweep answers what a point read answers");
12440 let grown = dictionary.footprint() - after;
12444 assert!(
12445 grown == 0 || grown == dictionary.len() * size_of::<u32>(),
12446 "a point read of a kept block decodes nothing, and {grown} bytes grew"
12447 );
12448 fs::remove_file(path).expect("remove scratch file");
12449 }
12450
12451 #[test]
12452 fn a_damaged_substring_signature_is_checked_only_when_used() {
12453 let path = path("damaged-substring-signature");
12454 let mut writer =
12455 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12456 .expect("new file");
12457 let rows = [Value::Varchar("google".into()), Value::Varchar("example".into())];
12458 writer
12459 .append(
12460 &Chunk::new(vec![
12461 Vector::from_values(LogicalType::Varchar, &rows).expect("strings"),
12462 ])
12463 .expect("one column"),
12464 )
12465 .expect("stripe written");
12466 writer.finish().expect("commit");
12467
12468 let reader = Reader::open(&path).expect("valid directory");
12469 let page = reader.table.dictionaries[0].expect("string dictionary page");
12470 let mut file = OpenOptions::new().write(true).open(&path).expect("open dictionary page");
12471 file.seek(SeekFrom::Start(page.offset + u64::from(page.length) - 1))
12472 .expect("last signature byte");
12473 file.write_all(&[255]).expect("damage signature");
12474 let reader = Reader::open(&path).expect("the directory is still valid");
12475 let dictionary = reader.dictionary(0).expect("index is still valid").expect("dictionary");
12476 let error = dictionary
12477 .text_block_might_contain(0, b"goog")
12478 .expect_err("a used signature checks its own checksum");
12479 assert!(error.message().contains("substring signatures checksum differs"), "{error}");
12480 fs::remove_file(path).expect("remove scratch file");
12481 }
12482
12483 #[test]
12494 fn a_sweep_over_a_block_with_a_short_second_run_reads_what_a_point_read_reads() {
12495 let path = path("dictionary-sweep-short-run");
12496 let spellings = (0..2_800)
12497 .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12498 .collect::<Vec<_>>();
12499 let mut writer =
12500 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12501 .expect("new file");
12502 for part in spellings.chunks(1_024) {
12503 writer
12504 .append(
12505 &Chunk::new(vec![
12506 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12507 ])
12508 .expect("one column"),
12509 )
12510 .expect("stripe written");
12511 }
12512 writer.finish().expect("commit");
12513
12514 let reader = Reader::open(&path).expect("valid directory");
12515 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12516 assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12517 let last = dictionary.len() % TEXT_PAYLOAD_VALUES;
12518 assert!(last > TEXT_OFFSET_RUN, "the last block has to reach into a second run of offsets");
12519 assert!(last < TEXT_PAYLOAD_VALUES, "and that second run has to be short of a whole one");
12520
12521 let mut swept: Vec<Vec<u8>> = Vec::new();
12522 let mut at = 0;
12523 while at < dictionary.len() {
12524 let stopped = dictionary
12525 .sweep_text(at, dictionary.len(), &mut |index: usize, text: &[u8]| {
12526 assert_eq!(index, swept.len(), "a sweep hands its values over in order");
12527 swept.push(text.to_vec());
12528 Ok(())
12529 })
12530 .expect("a sweep reads");
12531 assert!(stopped > at, "a sweep moves");
12532 at = stopped;
12533 }
12534 let read = (0..dictionary.len())
12535 .map(|code| dictionary.try_bytes_at(code).expect("read").expect("a value").to_vec())
12536 .collect::<Vec<_>>();
12537 assert_eq!(swept, read, "a sweep answers what a point read answers");
12538 fs::remove_file(path).expect("remove scratch file");
12539 }
12540
12541 #[test]
12550 fn the_unpacked_ends_answer_what_the_packed_ends_answer() {
12551 let path = path("dictionary-unpacked-ends");
12552 let spellings = (0..2_800)
12553 .map(|index| Value::Varchar(format!("value {index:08} {}", "x".repeat(index % 40))))
12554 .collect::<Vec<_>>();
12555 let mut writer =
12556 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12557 .expect("new file");
12558 for part in spellings.chunks(1_024) {
12559 writer
12560 .append(
12561 &Chunk::new(vec![
12562 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12563 ])
12564 .expect("one column"),
12565 )
12566 .expect("stripe written");
12567 }
12568 writer.finish().expect("commit");
12569
12570 let reader = Reader::open(&path).expect("valid directory");
12571 let dictionary = reader.dictionary(0).expect("read").expect("a string column has one");
12572 assert_eq!(dictionary.len(), spellings.len(), "every value is distinct");
12573 let wanted = (0..spellings.len())
12574 .map(|index| format!("value {index:08} {}", "x".repeat(index % 40)).into_bytes())
12575 .collect::<Vec<_>>();
12576
12577 let pass = |what: &str| {
12578 for (index, value) in wanted.iter().enumerate() {
12579 let len = dictionary.try_bytes_len_at(index).expect("read").expect("a value");
12580 assert_eq!(len, value.len(), "{what} has the wrong length at {index}");
12581 let bytes = dictionary.try_bytes_at(index).expect("read").expect("a value");
12582 assert_eq!(bytes, value.as_slice(), "{what} has the wrong value at {index}");
12583 }
12584 };
12585 pass("the first pass");
12586 pass("the second pass");
12587
12588 let lens = wanted.iter().map(|value| value.len() as i64).collect::<Vec<_>>();
12592 let mut whole = vec![0i64; wanted.len()];
12593 assert!(dictionary.try_bytes_lens(&mut whole).expect("read"), "the text answers whole");
12594 assert_eq!(whole, lens, "a vector of lengths answers what a length at a time answers");
12595 let codes = (0..4_000_u32).map(|row| (7 * (4_000 - row)) % 2_800).collect::<Vec<_>>();
12596 let coded = Vector::dictionary_over(codes.clone(), dictionary).expect("codes in range");
12597 let mut through = vec![0i64; codes.len()];
12598 assert!(coded.try_bytes_lens(&mut through).expect("read"), "the codes answer whole");
12599 for (row, &code) in codes.iter().enumerate() {
12600 assert_eq!(through[row], lens[code as usize], "row {row} reads code {code}");
12601 let one = coded.try_bytes_len_at(row).expect("read").expect("a value");
12602 assert_eq!(through[row], one as i64, "row {row} a row at a time");
12603 }
12604
12605 let fresh = Reader::open(&path).expect("valid directory");
12608 let untouched = fresh.dictionary(0).expect("read").expect("a string column has one");
12609 let few = vec![2_799_u32, 0, 1_024, 1_023, 511, 512];
12610 let coded = Vector::dictionary_over(few.clone(), untouched).expect("in range");
12611 let mut short = vec![0i64; few.len()];
12612 assert!(coded.try_bytes_lens(&mut short).expect("read"), "the codes answer whole");
12613 let expected = few.iter().map(|&code| lens[code as usize]).collect::<Vec<_>>();
12614 assert_eq!(short, expected, "the packed ends answer what the table answers");
12615 fs::remove_file(path).expect("remove scratch file");
12616 }
12617
12618 #[test]
12628 fn narrowing_a_page_takes_what_fits_and_refuses_what_does_not() {
12629 assert_eq!(fit::<i8>(&[]).expect("an empty page fits anything"), Vec::<i8>::new());
12630 assert_eq!(fit::<i8>(&[-128, 0, 127]).expect("the edges fit"), vec![-128_i8, 0, 127]);
12631 fit::<i8>(&[128]).expect_err("one past the top does not fit");
12632 fit::<i8>(&[-129]).expect_err("one past the bottom does not fit");
12633 assert_eq!(fit::<u8>(&[0, 255]).expect("the edges fit"), vec![0_u8, 255]);
12634 fit::<u8>(&[256]).expect_err("one past the top does not fit");
12635 fit::<u8>(&[-1]).expect_err("a negative does not fit an unsigned page");
12636 assert_eq!(
12637 fit::<i16>(&[-32_768, 0, 32_767]).expect("the edges fit"),
12638 vec![-32_768_i16, 0, 32_767]
12639 );
12640 fit::<i16>(&[32_768]).expect_err("one past the top does not fit");
12641 fit::<i16>(&[-32_769]).expect_err("one past the bottom does not fit");
12642 assert_eq!(fit::<u16>(&[0, 65_535]).expect("the edges fit"), vec![0_u16, 65_535]);
12643 fit::<u16>(&[65_536]).expect_err("one past the top does not fit");
12644 fit::<u16>(&[-1]).expect_err("a negative does not fit an unsigned page");
12645 assert_eq!(
12646 fit::<i32>(&[i64::from(i32::MIN), 0, i64::from(i32::MAX)]).expect("the edges fit"),
12647 vec![i32::MIN, 0, i32::MAX]
12648 );
12649 fit::<i32>(&[i64::from(i32::MAX) + 1]).expect_err("one past the top does not fit");
12650 fit::<i32>(&[i64::from(i32::MIN) - 1]).expect_err("one past the bottom does not fit");
12651 assert_eq!(
12652 fit::<u32>(&[0, 4_294_967_295]).expect("the edges fit"),
12653 vec![0_u32, 4_294_967_295]
12654 );
12655 fit::<u32>(&[4_294_967_296]).expect_err("one past the top does not fit");
12656 fit::<u32>(&[-1]).expect_err("a negative does not fit an unsigned page");
12657
12658 fit::<i8>(&[0, 1, 2, 128, 3]).expect_err("one bad value spoils the page");
12661 }
12662
12663 #[test]
12670 fn the_residue_agrees_with_a_checked_conversion_everywhere() {
12671 for value in -70_000_i64..70_000 {
12672 assert_eq!(fit::<i8>(&[value]).is_ok(), i8::try_from(value).is_ok(), "{value} as i8");
12673 assert_eq!(fit::<u8>(&[value]).is_ok(), u8::try_from(value).is_ok(), "{value} as u8");
12674 assert_eq!(fit::<i16>(&[value]).is_ok(), i16::try_from(value).is_ok(), "{value} i16");
12675 assert_eq!(fit::<u16>(&[value]).is_ok(), u16::try_from(value).is_ok(), "{value} u16");
12676 }
12677 let wide = [i64::MIN, i64::MIN + 1, i64::from(i32::MIN), 0, i64::from(u32::MAX), i64::MAX];
12678 for edge in wide {
12679 for step in -2_i64..=2 {
12680 let value = edge.saturating_add(step);
12681 assert_eq!(
12682 fit::<i32>(&[value]).is_ok(),
12683 i32::try_from(value).is_ok(),
12684 "{value} as i32"
12685 );
12686 assert_eq!(
12687 fit::<u32>(&[value]).is_ok(),
12688 u32::try_from(value).is_ok(),
12689 "{value} as u32"
12690 );
12691 }
12692 }
12693 }
12694
12695 #[test]
12710 fn a_dictionary_reads_the_same_whether_its_blocks_say_where_they_are() {
12711 let spellings = (0..3_000)
12712 .map(|index| format!("value {index:08} {}", "y".repeat(index % 40)))
12713 .collect::<Vec<_>>();
12714 let mut read = Vec::new();
12715 for layout in ["outside", "inside", "behind"] {
12716 let mut dictionary = GlobalDictionary::new();
12717 for text in &spellings {
12718 dictionary.code(text).expect("a code for every spelling");
12719 }
12720 dictionary.finish_blocks().expect("the last block encodes");
12721 let order = dictionary.ranked(None).expect("a sorted order");
12722 let laid = |from: u64| {
12724 let mut at = from;
12725 dictionary
12726 .blocks
12727 .iter()
12728 .map(|block| {
12729 let place =
12730 Placed { start: at, length: block.len() as u64, hash: checksum(block) };
12731 at += block.len() as u64;
12732 place
12733 })
12734 .collect::<Vec<_>>()
12735 };
12736 let payload = dictionary.blocks.concat();
12737 let scattered = layout != "behind";
12738 let (bytes, encoded, offset, length) = if layout == "outside" {
12739 let mut bytes = vec![0; HEADER as usize];
12740 bytes.extend_from_slice(&payload);
12741 let encoded = encode_global_dictionary(&dictionary, &order, &laid(HEADER), true)
12742 .expect("an encoding");
12743 let offset = bytes.len() as u64;
12744 bytes.extend_from_slice(&encoded.index);
12745 bytes.extend_from_slice(&encoded.ranks);
12746 bytes.extend_from_slice(&encoded.grams);
12747 let length = encoded.index.len() + encoded.ranks.len() + encoded.grams.len();
12748 (bytes, encoded, offset, length)
12749 } else {
12750 let first = encode_global_dictionary(&dictionary, &order, &laid(0), scattered)
12753 .expect("an encoding");
12754 let body = (first.index.len() + first.ranks.len() + first.grams.len()) as u64;
12755 let encoded = encode_global_dictionary(&dictionary, &order, &laid(body), scattered)
12756 .expect("an encoding");
12757 let mut bytes = encoded.index.clone();
12758 bytes.extend_from_slice(&encoded.ranks);
12759 bytes.extend_from_slice(&encoded.grams);
12760 bytes.extend_from_slice(&payload);
12761 let length = bytes.len();
12762 (bytes, encoded, 0, length)
12763 };
12764 let path = path(&format!("blocks-{layout}"));
12765 fs::write(&path, &bytes).expect("the dictionary is written on its own");
12766 let file = Arc::new(File::open(&path).expect("it opens again"));
12767 let page = Page {
12768 offset,
12769 length: u32::try_from(length).expect("a test dictionary is small"),
12770 hash: checksum(&encoded.index),
12771 };
12772 let opened =
12773 open_global_dictionary(file, page, &LogicalType::Varchar, TEXT_KEEP_BUDGET)
12774 .expect("a dictionary laid out either way opens");
12775 let mut swept: Vec<Vec<u8>> = Vec::new();
12776 let mut at = 0;
12777 while at < opened.len() {
12778 at = opened
12779 .sweep_text(at, opened.len(), &mut |_index: usize, text: &[u8]| {
12780 swept.push(text.to_vec());
12781 Ok(())
12782 })
12783 .expect("a sweep reads");
12784 }
12785 fs::remove_file(&path).expect("clean up");
12786 read.push(swept);
12787 }
12788 let wanted =
12789 spellings.iter().map(|text| text.as_bytes().to_vec()).collect::<Vec<Vec<u8>>>();
12790 assert_eq!(read[0], wanted, "the blocks outside the page hold the values");
12791 assert_eq!(read[1], read[0], "the blocks inside the page hold the same values");
12792 assert_eq!(read[2], read[0], "the blocks behind one another hold the same values");
12793 }
12794
12795 #[test]
12803 fn a_dictionary_at_its_budget_sweeps_without_keeping() {
12804 let path = path("dictionary-budget");
12805 let spellings = (0..2_500)
12806 .map(|index| Value::Varchar(format!("value {index:08} {}", "y".repeat(index % 40))))
12807 .collect::<Vec<_>>();
12808 let mut writer =
12809 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12810 .expect("new file");
12811 for part in spellings.chunks(1_024) {
12812 writer
12813 .append(
12814 &Chunk::new(vec![
12815 Vector::from_values(LogicalType::Varchar, part).expect("strings"),
12816 ])
12817 .expect("one column"),
12818 )
12819 .expect("stripe written");
12820 }
12821 writer.finish().expect("commit");
12822
12823 let reader = Reader::open(&path).expect("valid directory");
12824 let page = reader.table.dictionaries[0].expect("a string column has one");
12825 let file = Arc::clone(&reader.file);
12826 let starved = open_global_dictionary(file, page, &LogicalType::Varchar, 0)
12827 .expect("a dictionary opens whatever it may keep");
12828
12829 let resting = starved.footprint();
12830 let mut swept: Vec<Vec<u8>> = Vec::new();
12831 let mut at = 0;
12832 while at < starved.len() {
12833 at = starved
12834 .sweep_text(at, starved.len(), &mut |_index: usize, text: &[u8]| {
12835 swept.push(text.to_vec());
12836 Ok(())
12837 })
12838 .expect("a sweep reads");
12839 }
12840 assert_eq!(swept.len(), spellings.len(), "a starved sweep still reads every value");
12841 assert_eq!(starved.footprint(), resting, "and keeps no block it decoded");
12842
12843 let generous = reader.dictionary(0).expect("read").expect("a string column has one");
12844 let read = (0..generous.len())
12845 .map(|code| generous.try_bytes_at(code).expect("read").expect("a value").to_vec())
12846 .collect::<Vec<_>>();
12847 assert_eq!(swept, read, "a starved sweep answers what a point read answers");
12848 fs::remove_file(path).expect("remove scratch file");
12849 }
12850
12851 #[test]
12852 fn damaged_membership_cannot_skip_a_string_page() {
12853 let path = path("damaged-membership");
12854 let mut writer = Writer::create(
12855 &path,
12856 "items",
12857 vec![
12858 Field::required("id", LogicalType::Integer),
12859 Field::new("text", LogicalType::Varchar),
12860 ],
12861 )
12862 .expect("new file");
12863 writer.append(&sample()).expect("stripe written");
12864 writer.finish().expect("commit");
12865
12866 let reader = Reader::open(&path).expect("valid directory");
12867 let membership = reader.table.stripes[0].memberships.get(1).expect("string membership");
12868 let mut file = OpenOptions::new().write(true).open(&path).expect("open membership page");
12869 file.seek(SeekFrom::Start(membership.offset)).expect("membership start");
12870 file.write_all(&[255]).expect("damage membership");
12871 let error = reader.skips_codes(0, 1, &[3]).expect_err("corruption must not skip rows");
12872 assert!(error.message().contains("membership page checksum differs"), "{error}");
12873 fs::remove_file(path).expect("remove scratch file");
12874 }
12875
12876 #[test]
12877 fn membership_delta_stream_is_sorted_exact_and_bounded() {
12878 let unique = unique_codes(&[900, 4, 4, 72, 9, u32::MAX]);
12879 assert_eq!(unique, [4, 9, 72, 900, u32::MAX]);
12880 let encoded = encode_membership(&unique);
12881 assert_eq!(
12882 decode_membership(&encoded).expect("valid membership"),
12883 [4, 9, 72, 900, u32::MAX]
12884 );
12885 let merged = merged_codes(vec![vec![4, 900], vec![9, 900, u32::MAX], vec![72]]);
12888 assert_eq!(merged, [4, 9, 72, 900, u32::MAX]);
12889 assert_eq!(
12890 decode_membership(&encode_membership(&merged)).expect("valid membership"),
12891 unique
12892 );
12893 assert!(decode_membership(&[1, 0x80]).is_err(), "a truncated varint is invalid");
12894 assert!(
12895 decode_membership(&[1, 0xff, 0xff, 0xff, 0xff, 0x10]).is_err(),
12896 "a value past u32 is invalid"
12897 );
12898 }
12899
12900 #[test]
12901 fn a_global_dictionary_may_be_larger_than_one_column_page() {
12902 let dictionary = Page {
12903 offset: HEADER,
12904 length: u32::try_from(MAX_PAGE + 1).expect("the page bound fits on disk"),
12905 hash: 0,
12906 };
12907 let table = Table {
12908 name: "items".to_owned(),
12909 fields: vec![Field::new("text", LogicalType::Varchar)],
12910 stripes: Vec::new(),
12911 rows: 0,
12912 dictionaries: vec![Some(dictionary)],
12913 dictionary_payloads: Vec::new(),
12914 distincts: vec![None],
12915 frequencies: vec![None],
12916 pair_frequencies: Vec::new(),
12917 frequency_texts: Vec::new(),
12918 host_groups: None,
12919 clustering: None,
12920 generation: 1,
12921 sections: Vec::new(),
12922 };
12923 let directory = encode_directory(&table).expect("directory");
12924 let file_size = dictionary.offset + u64::from(dictionary.length) + 1;
12925
12926 let decoded = decode_directory(&directory, file_size).expect("large lazy dictionary");
12927 assert_eq!(decoded.dictionaries[0].expect("dictionary").length, dictionary.length);
12928 }
12929
12930 #[test]
12931 fn a_column_with_one_value_everywhere_costs_almost_nothing_a_row() {
12932 let path = path("constant-codes");
12933 let mut writer =
12934 Writer::create(&path, "items", vec![Field::new("text", LogicalType::Varchar)])
12935 .expect("new file");
12936 let empty = vec![Value::Varchar(String::new()); 1024];
12937 for _ in 0..4 {
12938 let column = Vector::from_values(LogicalType::Varchar, &empty).expect("strings");
12939 writer.append(&Chunk::new(vec![column]).expect("one column")).expect("a part");
12940 }
12941 writer.finish().expect("commit");
12942
12943 let reader = Reader::open(&path).expect("valid directory");
12944 let pages = reader.layout().columns.first().expect("one column").pages;
12945 assert!(pages < 256, "{pages} bytes of pages for 4,096 rows of one value");
12949 let read = reader.read(3, &[0]).expect("the last part back");
12950 assert_eq!(read.value_at(0, 0), Value::Varchar(String::new()));
12951 assert_eq!(read.value_at(1023, 0), Value::Varchar(String::new()));
12952 fs::remove_file(path).expect("remove scratch file");
12953 }
12954
12955 #[test]
12956 fn a_cascade_value_too_wide_for_its_column_is_refused_rather_than_cut() {
12957 let over = vec![i64::from(i32::MAX) + 1];
12960 let error = narrowed(&LogicalType::Integer, over).expect_err("a page that disagrees");
12961 assert!(format!("{error}").contains("not of its type"), "{error}");
12962 assert!(narrowed(&LogicalType::BigInt, vec![i64::MIN]).is_ok(), "bigint holds all of i64");
12963 assert!(narrowed(&LogicalType::Varchar, vec![0]).is_err(), "strings are not integers");
12964 }
12965
12966 #[test]
12967 fn a_code_stream_the_cascade_cannot_shrink_is_left_alone() {
12968 let mut state: u32 = 0x9e37_79b9;
12972 let spread: Vec<u32> = (0..1024)
12973 .map(|_| {
12974 state ^= state << 13;
12975 state ^= state >> 17;
12976 state ^= state << 5;
12977 state
12978 })
12979 .collect();
12980 assert_eq!(encoded_codes(&spread).expect("no failure"), None);
12981 let near: Vec<u32> = (0..1024).collect();
12982 let coded = encoded_codes(&near).expect("no failure").expect("counting up is packable");
12983 assert!(coded.len() < near.len() * 4, "{} bytes for a run of 1,024", coded.len());
12984 }
12985
12986 #[test]
12992 fn two_writes_of_the_same_rows_give_the_same_bytes() {
12993 fn written(path: &PathBuf) {
12994 let fields = (0..40)
12995 .map(|column| {
12996 let ty =
12997 if column % 4 == 0 { LogicalType::Varchar } else { LogicalType::BigInt };
12998 Field::new(format!("c{column}"), ty)
12999 })
13000 .collect::<Vec<_>>();
13001 let mut writer = Writer::create(path, "wide", fields).expect("new file");
13002 for part in 0..70_u64 {
13003 let columns = (0..40)
13004 .map(|column| {
13005 let values = (0..64_u64)
13006 .map(|row| {
13007 let seed = part.wrapping_mul(31).wrapping_add(row);
13008 if column % 4 == 0 {
13009 Value::Varchar(format!("v{}", seed % 17))
13010 } else {
13011 Value::BigInt(i64::try_from(seed % 97).expect("small"))
13012 }
13013 })
13014 .collect::<Vec<_>>();
13015 let ty = if column % 4 == 0 {
13016 LogicalType::Varchar
13017 } else {
13018 LogicalType::BigInt
13019 };
13020 Vector::from_values(ty, &values).expect("a column")
13021 })
13022 .collect::<Vec<_>>();
13023 writer.append(&Chunk::new(columns).expect("forty columns")).expect("a part");
13024 }
13025 writer.finish().expect("commit");
13026 }
13027
13028 let first = path("repeatable-one");
13029 let second = path("repeatable-two");
13030 written(&first);
13031 written(&second);
13032 let left = fs::read(&first).expect("the first file");
13033 let right = fs::read(&second).expect("the second file");
13034 assert_eq!(left.len(), right.len(), "two writes of the same rows differ in length");
13035 assert!(left == right, "two writes of the same rows differ in their bytes");
13036
13037 let reader = Reader::open(&first).expect("valid directory");
13040 assert_eq!(reader.table().rows(), 70 * 64);
13041 let read = reader.read(0, &[0, 1]).expect("the first part back");
13042 assert_eq!(read.value_at(0, 0), Value::Varchar("v0".to_owned()));
13043 assert_eq!(read.value_at(0, 1), Value::BigInt(0));
13044 fs::remove_file(first).expect("remove scratch file");
13045 fs::remove_file(second).expect("remove scratch file");
13046 }
13047
13048 fn three_tables(path: &PathBuf) {
13050 let writer = Writer::create(
13051 path,
13052 "region",
13053 vec![
13054 Field::new("r_key", LogicalType::Integer),
13055 Field::new("r_name", LogicalType::Varchar),
13056 ],
13057 )
13058 .expect("new file");
13059 let mut writer = writer;
13060 writer
13061 .append(
13062 &Chunk::new(vec![
13063 Vector::from_values(
13064 LogicalType::Integer,
13065 &[Value::Integer(0), Value::Integer(1)],
13066 )
13067 .expect("keys"),
13068 Vector::from_values(
13069 LogicalType::Varchar,
13070 &[Value::Varchar("AFRICA".to_owned()), Value::Varchar("ASIA".to_owned())],
13071 )
13072 .expect("names"),
13073 ])
13074 .expect("two columns"),
13075 )
13076 .expect("a part");
13077 let mut writer = writer
13078 .next("empty", vec![Field::new("nothing", LogicalType::BigInt)])
13079 .expect("a second table");
13080 writer
13081 .append(
13082 &Chunk::new(vec![
13083 Vector::from_values(LogicalType::BigInt, &[Value::BigInt(7)]).expect("a row"),
13084 ])
13085 .expect("one column"),
13086 )
13087 .expect("a part");
13088 let mut writer =
13089 writer.next("wide", vec![Field::new("n", LogicalType::BigInt)]).expect("a third table");
13090 for part in 0..70_i64 {
13091 let values = (0..64).map(|row| Value::BigInt(part * 64 + row)).collect::<Vec<_>>();
13092 writer
13093 .append(
13094 &Chunk::new(vec![
13095 Vector::from_values(LogicalType::BigInt, &values).expect("a column"),
13096 ])
13097 .expect("one column"),
13098 )
13099 .expect("a part");
13100 }
13101 writer.finish().expect("commit");
13102 }
13103
13104 #[test]
13105 fn three_tables_in_one_file_read_back_by_name() {
13106 let file = path("three-tables");
13107 three_tables(&file);
13108 let catalog = Catalog::open(&file).expect("a committed catalog");
13109 assert_eq!(catalog.names().collect::<Vec<_>>(), ["region", "empty", "wide"]);
13110
13111 let region = catalog.table("region").expect("the first table");
13112 assert_eq!(region.table().rows(), 2);
13113 assert_eq!(
13114 region.read(0, &[1]).expect("names").value_at(1, 0),
13115 Value::Varchar("ASIA".to_owned())
13116 );
13117
13118 let wide = catalog.table("wide").expect("the third table");
13119 assert_eq!(wide.table().rows(), 70 * 64);
13120 assert_eq!(wide.read(0, &[0]).expect("the first part").value_at(0, 0), Value::BigInt(0));
13121
13122 let empty = catalog.table("empty").expect("the second table");
13125 assert_eq!(empty.table().rows(), 1);
13126 assert_eq!(empty.read(0, &[0]).expect("the row").value_at(0, 0), Value::BigInt(7));
13127
13128 fs::remove_file(file).expect("remove scratch file");
13129 }
13130
13131 #[test]
13132 fn a_name_the_file_does_not_hold_is_an_error_rather_than_the_first_table() {
13133 let file = path("three-tables-missing");
13134 three_tables(&file);
13135 let catalog = Catalog::open(&file).expect("a committed catalog");
13136 let error = catalog.table("nation").expect_err("no such table");
13137 assert!(error.message().contains("nation"), "{}", error.message());
13138 fs::remove_file(file).expect("remove scratch file");
13139 }
13140
13141 #[test]
13142 fn a_file_of_three_tables_will_not_open_as_one() {
13143 let file = path("three-tables-unnamed");
13144 three_tables(&file);
13145 let error = Reader::open(&file).expect_err("more than one table");
13146 assert!(error.message().contains("more than one table"), "{}", error.message());
13147 fs::remove_file(file).expect("remove scratch file");
13148 }
13149
13150 #[test]
13152 fn decimals_of_every_storage_width_round_trip() {
13153 let file = path("decimals");
13154 let widths = [(4_u8, 2_u8), (9, 2), (18, 4), (38, 6)];
13155 let fields = widths
13156 .iter()
13157 .enumerate()
13158 .map(|(index, (width, scale))| {
13159 Field::new(
13160 format!("d{index}"),
13161 LogicalType::decimal(*width, *scale).expect("a decimal type"),
13162 )
13163 })
13164 .collect::<Vec<_>>();
13165 let mut writer = Writer::create(&file, "money", fields).expect("new file");
13166 let rows: [i128; 3] = [-1234, 0, 999];
13167 let columns = widths
13168 .iter()
13169 .map(|(width, scale)| {
13170 let values = rows
13171 .iter()
13172 .map(|unscaled| Value::Decimal {
13173 unscaled: *unscaled,
13174 width: *width,
13175 scale: *scale,
13176 })
13177 .collect::<Vec<_>>();
13178 Vector::from_values(
13179 LogicalType::decimal(*width, *scale).expect("a decimal type"),
13180 &values,
13181 )
13182 .expect("a decimal column")
13183 })
13184 .collect::<Vec<_>>();
13185 writer.append(&Chunk::new(columns).expect("four columns")).expect("a part");
13186 writer.finish().expect("commit");
13187
13188 let reader = Reader::open(&file).expect("a committed file");
13189 for (index, (width, scale)) in widths.iter().enumerate() {
13190 assert_eq!(
13191 reader.table().fields()[index].ty,
13192 LogicalType::decimal(*width, *scale).expect("a decimal type"),
13193 "column {index} came back as another type"
13194 );
13195 let column = reader.read(0, &[index]).expect("the column");
13196 for (row, unscaled) in rows.iter().enumerate() {
13197 assert_eq!(
13198 column.value_at(row, 0),
13199 Value::Decimal { unscaled: *unscaled, width: *width, scale: *scale },
13200 "column {index} row {row}"
13201 );
13202 }
13203 }
13204 fs::remove_file(file).expect("remove scratch file");
13205 }
13206
13207 #[test]
13208 fn two_tables_of_one_name_are_refused_before_anything_is_committed() {
13209 let file = path("two-of-a-name");
13210 let writer = Writer::create(&file, "t", vec![Field::new("a", LogicalType::BigInt)])
13211 .expect("new file");
13212 let error = writer
13213 .next("t", vec![Field::new("a", LogicalType::BigInt)])
13214 .expect_err("the same name twice");
13215 assert!(error.message().contains("same name"), "{}", error.message());
13216 fs::remove_file(file).expect("remove scratch file");
13217 }
13218
13219 #[test]
13220 fn opening_the_catalog_reads_no_table_directory() {
13221 let file = path("catalog-only");
13222 three_tables(&file);
13223 let catalog = Catalog::open(&file).expect("a committed catalog");
13224 assert_eq!(catalog.opening.reads, 2, "opening the catalog read more than the slot");
13227 assert_eq!(catalog.names().len(), 3);
13228 fs::remove_file(file).expect("remove scratch file");
13229 }
13230
13231 #[test]
13242 fn the_checksum_answers_what_it_has_always_answered() {
13243 let bytes: Vec<u8> =
13244 (0..1000_u32).map(|at| (at.wrapping_mul(31).wrapping_add(7) % 251) as u8).collect();
13245 for (length, expected) in [
13246 (0, 0xef46_db37_51d8_e999),
13247 (1, 0xa96c_7f0c_e858_bbb7),
13248 (3, 0x56e6_9576_32a4_87f9),
13249 (4, 0xc60d_15b1_e3ff_8f04),
13250 (5, 0x8088_1585_8624_dd4e),
13251 (7, 0xafbe_fc3d_6c6f_9a8e),
13252 (8, 0x3da5_c7aa_2696_83e0),
13253 (9, 0x465e_c429_b13c_3892),
13254 (15, 0xdee8_9d8a_065a_6233),
13255 (16, 0x1330_489a_7767_9c80),
13256 (31, 0x3391_303d_485e_846e),
13257 (32, 0x40b7_aff7_5d45_bbc8),
13258 (33, 0x4997_cae4_951c_17a5),
13259 (39, 0x5807_28fd_5c14_5739),
13260 (40, 0xf95c_f6f5_c08a_3d3b),
13261 (63, 0x2944_b4da_fc69_b206),
13262 (64, 0xbb76_f6ef_19bd_5a1b),
13263 (65, 0x814e_0c65_4a9f_d640),
13264 (127, 0x00de_aab1_31cf_f89b),
13265 (1000, 0x9e33_00c1_cde3_c58d),
13266 ] {
13267 assert_eq!(checksum(&bytes[..length]), expected, "the checksum of {length} bytes");
13268 }
13269 assert_eq!(checksum(b"the quick brown fox jumps over the lazy dog"), 0xed71_4233_c5a9_a792);
13270 }
13271 #[test]
13278 fn a_declared_order_comes_back_out_of_the_file() {
13279 let path = path("clustered");
13280 let shipped = vec![
13281 Field::new("key", LogicalType::BigInt),
13282 Field::new("line", LogicalType::Integer),
13283 Field::new("shipdate", LogicalType::Date),
13284 ];
13285 let plain = vec![Field::new("a", LogicalType::Integer)];
13286 let stage_zero = Clustering::new(vec![2, 0, 1], Width::Month, &shipped).expect("valid");
13287
13288 let mut writer = Writer::create(&path, "lineitem", shipped)
13289 .expect("new file")
13290 .declare(stage_zero.clone())
13291 .expect("the columns are the table's");
13292 let column = |ty: LogicalType, values: &[Value]| {
13293 Vector::from_values(ty, values).expect("the values match the type")
13294 };
13295 writer
13296 .append(
13297 &Chunk::new(vec![
13298 column(
13299 LogicalType::BigInt,
13300 &[Value::BigInt(0), Value::BigInt(1), Value::BigInt(2), Value::BigInt(3)],
13301 ),
13302 column(
13303 LogicalType::Integer,
13304 &[
13305 Value::Integer(1),
13306 Value::Integer(1),
13307 Value::Integer(1),
13308 Value::Integer(1),
13309 ],
13310 ),
13311 column(
13312 LogicalType::Date,
13313 &[Value::Date(0), Value::Date(1), Value::Date(2), Value::Date(3)],
13314 ),
13315 ])
13316 .expect("three columns"),
13317 )
13318 .expect("four rows");
13319 let mut writer = writer.next("nation", plain).expect("a second table");
13320 writer
13321 .append(
13322 &Chunk::new(vec![column(LogicalType::Integer, &[Value::Integer(7)])])
13323 .expect("one column"),
13324 )
13325 .expect("one row");
13326 writer.finish().expect("commit");
13327
13328 let catalog = Catalog::open(&path).expect("reopen");
13329 let lineitem = catalog.table("lineitem").expect("the clustered table");
13330 assert_eq!(lineitem.table().clustering(), Some(&stage_zero));
13331 let nation = catalog.table("nation").expect("the plain table");
13332 assert_eq!(nation.table().clustering(), None, "nobody declared one here");
13333
13334 assert_eq!(lineitem.table().rows(), 4);
13337 assert_eq!(nation.table().rows(), 1);
13338 fs::remove_file(&path).ok();
13339 }
13340
13341 #[test]
13343 fn a_declaration_off_the_end_of_the_table_never_reaches_the_file() {
13344 let path = path("clustered-bad");
13345 let writer = Writer::create(&path, "items", vec![Field::new("a", LogicalType::Integer)])
13346 .expect("new file");
13347 let four =
13348 (0..4).map(|at| Field::new(format!("c{at}"), LogicalType::Integer)).collect::<Vec<_>>();
13349 let wrong = Clustering::new(vec![3], Width::Exact, &four).expect("valid against four");
13350 assert!(writer.declare(wrong).is_err(), "the table has one column, not four");
13351 fs::remove_file(&path).ok();
13352 }
13353
13354 #[test]
13362 fn the_dictionary_order_is_the_byte_order_however_deep_the_values_agree() {
13363 let mut values = vec![String::new(), "http://".to_owned()];
13364 for host in 0..7 {
13365 for path in 0..30 {
13366 values.push(format!("http://example{host}.test/page/{path:04}/index.html"));
13367 values.push(format!("http://example{host}.test/page/{path:04}"));
13368 }
13369 }
13370 values.push("http://example0.test/page/0000/index.htmlx".to_owned());
13371
13372 let mut dictionary = GlobalDictionary::new();
13373 for value in &values {
13374 dictionary.code(value).expect("a code for every value");
13375 }
13376 dictionary.finish_blocks().expect("the last block encodes");
13377 let ranked = dictionary.ranked(None).expect("a sorted order");
13378 assert_eq!(ranked.len(), values.len(), "one entry a distinct value");
13379
13380 let spellings = dictionary_values(&dictionary);
13381 let seen = ranked
13382 .iter()
13383 .map(|&(_, code)| {
13384 String::from_utf8(spellings[code as usize].clone()).expect("text in, text out")
13385 })
13386 .collect::<Vec<_>>();
13387 let mut wanted = values.clone();
13388 wanted.sort_unstable();
13389 assert_eq!(seen, wanted, "the order is the order the bytes give");
13390
13391 for &(carried, code) in &ranked {
13392 let value = &spellings[code as usize];
13393 assert_eq!(carried, head(value), "the head belongs to the value it is filed with");
13394 }
13395 }
13396
13397 #[test]
13402 fn the_commonest_entries_are_the_ones_a_full_sort_would_have_kept() {
13403 let entry =
13404 |value: u32, count: u64| FrequencyEntry { value: FrequencyValue::Code(value), count };
13405 let mut all = (0..FREQUENCY_ENTRIES as u32 * 3)
13406 .map(|code| entry(code, u64::from(code % 7) + 1))
13407 .collect::<Vec<_>>();
13408 all.push(FrequencyEntry { value: FrequencyValue::Null, count: 4 });
13409
13410 let mut sorted = all.clone();
13411 sorted.sort_unstable_by(|left, right| {
13412 right.count.cmp(&left.count).then_with(|| frequency_order(left.value, right.value))
13413 });
13414 let wanted_omitted = sorted[FREQUENCY_ENTRIES].count;
13415 sorted.truncate(FREQUENCY_ENTRIES);
13416
13417 let mut picked = all.clone();
13418 let omitted = keep_most_frequent(&mut picked);
13419 assert_eq!(omitted, wanted_omitted, "the largest count that did not make the cut");
13420 assert_eq!(picked.len(), FREQUENCY_ENTRIES, "the cut is where it says it is");
13421 assert!(
13422 picked
13423 .iter()
13424 .zip(&sorted)
13425 .all(|(one, two)| one.value == two.value && one.count == two.count),
13426 "the same entries in the same order"
13427 );
13428
13429 let mut short = all[..FREQUENCY_ENTRIES - 1].to_vec();
13430 let omitted = keep_most_frequent(&mut short);
13431 assert_eq!(omitted, 0, "nothing is omitted when everything fits");
13432 assert!(short.windows(2).all(|pair| pair[0].count >= pair[1].count), "still in order");
13433 }
13434
13435 #[test]
13437 fn a_short_dictionary_sorts_without_a_bucketing_pass() {
13438 let empty = GlobalDictionary::new();
13439 assert!(empty.ranked(None).expect("an empty order").is_empty(), "nothing in, nothing out");
13440
13441 let mut dictionary = GlobalDictionary::new();
13442 for value in ["pear", "apple", "", "apples", "app"] {
13443 dictionary.code(value).expect("a code for every value");
13444 }
13445 dictionary.finish_blocks().expect("the one block encodes");
13446 let spellings = dictionary_values(&dictionary);
13447 let seen = dictionary
13448 .ranked(None)
13449 .expect("a sorted order")
13450 .iter()
13451 .map(|&(_, code)| spellings[code as usize].clone())
13452 .collect::<Vec<_>>();
13453 let wanted: Vec<Vec<u8>> =
13454 [&b""[..], b"app", b"apple", b"apples", b"pear"].iter().map(|v| v.to_vec()).collect();
13455 assert_eq!(seen, wanted, "shorter first where one runs out inside another");
13456 }
13457}