uqa_execution/distinct/
memory.rs1use std::collections::HashMap;
10use std::path::PathBuf;
11
12use smallvec::SmallVec;
13use uqa_core::Value;
14use uqa_sql::ResultRow;
15
16use crate::{ExecResult, PhysicalRow, RowSchema};
17
18use super::encoding::{encode_key, encode_key_borrowed, hash_canonical_row};
19use super::spill::SeenKeySet;
20
21pub struct CanonicalRowHashSet {
29 pub(super) rows: Vec<SmallVec<[Value; 2]>>,
30 index: HashMap<u64, SmallVec<[usize; 1]>, ahash::RandomState>,
31}
32
33impl CanonicalRowHashSet {
34 #[must_use]
35 pub fn new() -> Self {
36 Self {
37 rows: Vec::new(),
38 index: HashMap::with_hasher(ahash::RandomState::new()),
39 }
40 }
41
42 pub fn insert_borrowed(&mut self, values: &[&Value]) -> ExecResult<bool> {
45 let hash = hash_canonical_row(self.index.hasher(), values.iter().copied().map(Some))?;
46 if self.matching_borrowed(hash, values) {
47 return Ok(false);
48 }
49
50 let row = values
51 .iter()
52 .map(|value| (*value).clone())
53 .collect::<SmallVec<[Value; 2]>>();
54 let row_index = self.rows.len();
55 self.rows.push(row);
56 self.index.entry(hash).or_default().push(row_index);
57 Ok(true)
58 }
59
60 pub fn insert_values(&mut self, values: &[Value]) -> ExecResult<bool> {
63 let hash = hash_canonical_row(self.index.hasher(), values.iter().map(Some))?;
64 if self.matching_values(hash, values) {
65 return Ok(false);
66 }
67
68 let row_index = self.rows.len();
69 self.rows.push(values.iter().cloned().collect());
70 self.index.entry(hash).or_default().push(row_index);
71 Ok(true)
72 }
73
74 pub fn contains_borrowed(&self, values: &[&Value]) -> ExecResult<bool> {
77 let hash = hash_canonical_row(self.index.hasher(), values.iter().copied().map(Some))?;
78 Ok(self.matching_borrowed(hash, values))
79 }
80
81 pub fn contains_values(&self, values: &[Value]) -> ExecResult<bool> {
83 let hash = hash_canonical_row(self.index.hasher(), values.iter().map(Some))?;
84 Ok(self.matching_values(hash, values))
85 }
86
87 fn matching_borrowed(&self, hash: u64, values: &[&Value]) -> bool {
88 self.index.get(&hash).is_some_and(|bucket| {
89 bucket.iter().copied().any(|index| {
90 let stored = &self.rows[index];
91 stored.len() == values.len()
92 && stored
93 .iter()
94 .zip(values)
95 .all(|(stored, value)| stored == *value)
96 })
97 })
98 }
99
100 fn matching_values(&self, hash: u64, values: &[Value]) -> bool {
101 self.index.get(&hash).is_some_and(|bucket| {
102 bucket
103 .iter()
104 .copied()
105 .any(|index| self.rows[index].as_slice() == values)
106 })
107 }
108}
109
110impl Default for CanonicalRowHashSet {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116pub struct ExactRowSet {
124 seen: SeenKeySet,
125}
126
127impl ExactRowSet {
128 pub fn new(work_mem_bytes: usize) -> Self {
129 Self {
130 seen: SeenKeySet::new(work_mem_bytes, None),
131 }
132 }
133
134 pub fn with_spill_directory(work_mem_bytes: usize, directory: impl Into<PathBuf>) -> Self {
135 Self {
136 seen: SeenKeySet::new(work_mem_bytes, Some(directory.into())),
137 }
138 }
139
140 pub fn insert_row(&mut self, row: &ResultRow, schema: &[String]) -> ExecResult<bool> {
143 self.seen.insert(row_key(row, schema)?)
144 }
145
146 pub fn contains_row(&mut self, row: &ResultRow, schema: &[String]) -> ExecResult<bool> {
147 self.seen.contains(&row_key(row, schema)?)
148 }
149
150 pub fn insert_values(&mut self, values: &[Value]) -> ExecResult<bool> {
154 self.seen.insert(encode_key(values)?)
155 }
156
157 pub fn contains_values(&mut self, values: &[Value]) -> ExecResult<bool> {
160 self.seen.contains(&encode_key(values)?)
161 }
162
163 pub fn insert_physical(&mut self, row: &PhysicalRow, schema: &RowSchema) -> ExecResult<bool> {
165 let view = schema.view(row);
166 self.seen.insert(encode_key_borrowed(
167 (0..schema.len()).map(|position| view.value_at(position)),
168 )?)
169 }
170
171 pub fn contains_physical(&mut self, row: &PhysicalRow, schema: &RowSchema) -> ExecResult<bool> {
173 let view = schema.view(row);
174 self.seen.contains(&encode_key_borrowed(
175 (0..schema.len()).map(|position| view.value_at(position)),
176 )?)
177 }
178
179 pub fn has_spilled(&self) -> bool {
180 self.seen.has_spilled()
181 }
182
183 pub fn in_memory_key_bytes(&self) -> usize {
184 self.seen.in_memory_bytes()
185 }
186}
187
188fn row_key(row: &ResultRow, schema: &[String]) -> ExecResult<Vec<u8>> {
189 encode_key_borrowed(schema.iter().map(|column| row.get(column)))
190}