uqa_execution/distinct/
mod.rs1mod encoding;
15mod memory;
16mod spill;
17
18use std::path::{Path, PathBuf};
19
20use crate::{
21 Batch, ExecError, ExecResult, PhysicalOperator, RowSchema, ScalarExpr,
22 SharedExpressionEvaluator,
23};
24
25use encoding::encode_key_borrowed;
26pub use encoding::{canonical_row_key, hash_canonical_row, try_pack_compact_text_pair};
27pub(crate) use encoding::{encode_key, encode_non_null_key, EncodedKey};
28pub use memory::{CanonicalRowHashSet, ExactRowSet};
29pub(crate) use spill::SeenKeySet;
30#[cfg(test)]
31use spill::{stable_hash, DISK_BUCKETS};
32
33pub const DEFAULT_DISTINCT_WORK_MEM_BYTES: usize = 64 * 1024 * 1024;
37
38pub struct Distinct<'a> {
44 child: Box<dyn PhysicalOperator + 'a>,
45 keys: Option<Vec<ScalarExpr>>,
46 evaluator: Option<SharedExpressionEvaluator<'a>>,
47 schema: RowSchema,
48 work_mem_bytes: usize,
49 spill_directory: Option<PathBuf>,
50 seen: SeenKeySet,
51}
52
53impl<'a> Distinct<'a> {
54 pub fn all(child: Box<dyn PhysicalOperator + 'a>) -> Self {
57 Self::all_with_work_mem(child, DEFAULT_DISTINCT_WORK_MEM_BYTES)
58 }
59
60 pub fn all_with_work_mem(child: Box<dyn PhysicalOperator + 'a>, work_mem_bytes: usize) -> Self {
62 let schema = child.row_schema().clone();
63 Self {
64 child,
65 keys: None,
66 evaluator: None,
67 schema,
68 work_mem_bytes,
69 spill_directory: None,
70 seen: SeenKeySet::new(work_mem_bytes, None),
71 }
72 }
73
74 pub fn on(
77 child: Box<dyn PhysicalOperator + 'a>,
78 keys: Vec<ScalarExpr>,
79 evaluator: SharedExpressionEvaluator<'a>,
80 ) -> Self {
81 Self::on_with_work_mem(child, keys, evaluator, DEFAULT_DISTINCT_WORK_MEM_BYTES)
82 }
83
84 pub fn on_with_work_mem(
86 child: Box<dyn PhysicalOperator + 'a>,
87 keys: Vec<ScalarExpr>,
88 evaluator: SharedExpressionEvaluator<'a>,
89 work_mem_bytes: usize,
90 ) -> Self {
91 let schema = child.row_schema().clone();
92 Self {
93 child,
94 keys: Some(keys),
95 evaluator: Some(evaluator),
96 schema,
97 work_mem_bytes,
98 spill_directory: None,
99 seen: SeenKeySet::new(work_mem_bytes, None),
100 }
101 }
102
103 pub fn with_spill_directory(mut self, directory: impl Into<PathBuf>) -> Self {
107 self.spill_directory = Some(directory.into());
108 self.reset_seen();
109 self
110 }
111
112 pub fn has_spilled(&self) -> bool {
114 self.seen.has_spilled()
115 }
116
117 pub fn in_memory_key_bytes(&self) -> usize {
119 self.seen.in_memory_bytes()
120 }
121
122 pub fn spill_path(&self) -> Option<&Path> {
124 self.seen.spill_path()
125 }
126
127 fn reset_seen(&mut self) {
128 self.seen = SeenKeySet::new(self.work_mem_bytes, self.spill_directory.clone());
129 }
130
131 fn key(&self, schema: &RowSchema, row: &crate::PhysicalRow) -> ExecResult<Vec<u8>> {
132 if let Some(keys) = self.keys.as_ref() {
133 let evaluator = self.evaluator.as_ref().ok_or_else(|| {
134 ExecError::Other("DISTINCT ON evaluator is not configured".into())
135 })?;
136 let values = keys
137 .iter()
138 .map(|expression| evaluator.evaluate_physical(expression, schema, row))
139 .collect::<ExecResult<Vec<_>>>()?;
140 return encode_key(&values);
141 }
142 let row = schema.view(row);
143 encode_key_borrowed((0..self.schema.len()).map(|index| row.value_at(index)))
144 }
145}
146
147impl PhysicalOperator for Distinct<'_> {
148 fn row_schema(&self) -> &RowSchema {
149 &self.schema
150 }
151
152 fn open(&mut self) -> ExecResult<()> {
153 self.reset_seen();
154 self.child.open()
155 }
156
157 fn next(&mut self) -> ExecResult<Option<Batch>> {
158 loop {
159 let Some(batch) = self.child.next()? else {
160 return Ok(None);
161 };
162 if batch.schema != self.schema {
163 return Err(ExecError::Other(format!(
164 "DISTINCT input schema mismatch: expected {:?}, got {:?}",
165 self.schema, batch.schema
166 )));
167 }
168 let mut rows = Vec::with_capacity(batch.rows.len());
169 for row in batch.rows {
170 let key = self.key(&batch.schema, &row)?;
171 if self.seen.insert(key)? {
172 rows.push(row.without_lock_origins());
173 }
174 }
175 if !rows.is_empty() {
176 return Ok(Some(Batch::from_physical_rows(self.schema.clone(), rows)));
177 }
178 }
179 }
180
181 fn close(&mut self) -> ExecResult<()> {
182 self.reset_seen();
183 self.child.close()
184 }
185}
186
187#[cfg(test)]
188mod tests;