linkrs_query/executor/streaming/subquery/
executor.rs1use std::collections::{HashMap, HashSet};
4use std::sync::Arc;
5
6use crate::executor::expression::evaluator::expression_evaluator::ExpressionEvaluator;
7use crate::executor::expression::ExpressionError;
8use crate::executor::streaming::context::BorrowedRowContext;
9use crate::executor::streaming::instance::QueryBindings;
10use crate::executor::streaming::plan::materializer::PhysicalPlanMaterializer;
11use crate::executor::streaming::runtime::ExecutionRuntime;
12use crate::executor::streaming::slot::SlotLayout;
13use linkrs_core::types::expr::SubqueryBody;
14use linkrs_core::Value;
15
16use super::runner::{GroupJoinSpec, SubqueryCache, SubqueryRunner, SubqueryRunnerSpec};
17
18#[derive(Debug)]
25pub struct SubqueryExecutor {
26 pub runtime: Arc<ExecutionRuntime>,
29 pub bindings: Arc<QueryBindings>,
31 pub runners: HashMap<u64, SubqueryRunner>,
33}
34
35impl SubqueryExecutor {
36 pub fn from_specs(
40 runtime: Arc<ExecutionRuntime>,
41 bindings: Arc<QueryBindings>,
42 specs: &[SubqueryRunnerSpec],
43 ) -> Self {
44 let runners = specs
45 .iter()
46 .map(|spec| (spec.id, SubqueryRunner::from_spec(spec)))
47 .collect();
48 Self {
49 runtime,
50 bindings,
51 runners,
52 }
53 }
54
55 pub fn is_empty(&self) -> bool {
57 self.runners.is_empty()
58 }
59
60 pub fn execute_exists(
67 &self,
68 body: &SubqueryBody,
69 layout: Arc<SlotLayout>,
70 row: Vec<Value>,
71 ) -> Result<bool, ExpressionError> {
72 let runner = self.runner(body)?;
73 if let Some(spec) = &runner.group_join {
74 return self
75 .run_group_join_lookup(runner, spec, layout, row)
76 .map(|found| found.is_some());
77 }
78 if !runner.correlated {
79 let mut cache = runner.cache.lock();
80 if let Some(SubqueryCache::Exists(value)) = &*cache {
81 return Ok(*value);
82 }
83 let exists = self.run_exists(runner, layout, row)?;
84 *cache = Some(SubqueryCache::Exists(exists));
85 return Ok(exists);
86 }
87 self.run_exists(runner, layout, row)
88 }
89
90 pub fn execute_contains(
97 &self,
98 body: &SubqueryBody,
99 layout: Arc<SlotLayout>,
100 row: Vec<Value>,
101 value: &Value,
102 ) -> Result<bool, ExpressionError> {
103 if value.is_null() {
104 return Ok(false);
105 }
106 let runner = self.runner(body)?;
107 if let Some(spec) = &runner.group_join {
108 let found = self.run_group_join_lookup(runner, spec, layout, row)?;
109 return Ok(matches!(found, Some(v) if !v.is_null() && &v == value));
110 }
111 if !runner.correlated {
112 let mut cache = runner.cache.lock();
113 if let Some(SubqueryCache::Contains(set)) = &*cache {
114 return Ok(set.contains(value));
115 }
116 let set: HashSet<Value> = self
117 .run_values(runner, layout, row)?
118 .into_iter()
119 .filter(|v| !v.is_null())
120 .collect();
121 let found = set.contains(value);
122 *cache = Some(SubqueryCache::Contains(set));
123 return Ok(found);
124 }
125 self.run_contains(runner, layout, row, value)
126 }
127
128 pub fn execute_scalar(
136 &self,
137 body: &SubqueryBody,
138 layout: Arc<SlotLayout>,
139 row: Vec<Value>,
140 ) -> Result<Value, ExpressionError> {
141 let runner = self.runner(body)?;
142 if let Some(spec) = &runner.group_join {
143 let found = self.run_group_join_lookup(runner, spec, layout, row)?;
144 return Ok(found.unwrap_or(Value::Null(linkrs_core::NullType::Null)));
145 }
146 if !runner.correlated {
147 let mut cache = runner.cache.lock();
148 if let Some(SubqueryCache::Scalar(value)) = &*cache {
149 return Ok(value.clone());
150 }
151 let value = self.run_scalar(runner, layout, row)?;
152 *cache = Some(SubqueryCache::Scalar(value.clone()));
153 return Ok(value);
154 }
155 self.run_scalar(runner, layout, row)
156 }
157
158 fn run_scalar(
160 &self,
161 runner: &SubqueryRunner,
162 layout: Arc<SlotLayout>,
163 row: Vec<Value>,
164 ) -> Result<Value, ExpressionError> {
165 let values = self.run_values(runner, layout, row)?;
166 Ok(values
167 .into_iter()
168 .next()
169 .unwrap_or(Value::Null(linkrs_core::NullType::Null)))
170 }
171
172 fn runner(&self, body: &SubqueryBody) -> Result<&SubqueryRunner, ExpressionError> {
173 self.runners.get(&body.id).ok_or_else(|| {
174 ExpressionError::type_error(
175 "Subquery execution not supported in this context".to_string(),
176 )
177 })
178 }
179
180 fn run_group_join_lookup(
187 &self,
188 runner: &SubqueryRunner,
189 spec: &GroupJoinSpec,
190 layout: Arc<SlotLayout>,
191 row: Vec<Value>,
192 ) -> Result<Option<Value>, ExpressionError> {
193 let mut ctx = BorrowedRowContext::new(&row, layout);
194 let mut key = Vec::with_capacity(spec.hash_keys.len());
195 for expr in &spec.hash_keys {
196 let value = ExpressionEvaluator::evaluate(expr, &mut ctx)?;
197 if value.is_null() {
198 return Ok(None);
199 }
200 key.push(value);
201 }
202 self.ensure_group_table(runner, spec)?;
203 let table = runner.group_table.lock();
204 Ok(table.as_ref().and_then(|t| t.get(&key).cloned()))
205 }
206
207 fn ensure_group_table(
211 &self,
212 runner: &SubqueryRunner,
213 spec: &GroupJoinSpec,
214 ) -> Result<(), ExpressionError> {
215 if runner.group_table.lock().is_some() {
216 return Ok(());
217 }
218 let (mut exec, _) = PhysicalPlanMaterializer::materialize(&runner.plan, &self.bindings)
219 .map_err(|e| {
220 ExpressionError::type_error(format!(
221 "Group-Join subquery plan materialization failed: {}",
222 e
223 ))
224 })?;
225 exec.set_chunk_size(self.bindings.chunk_size);
226 exec.set_runtime(Some(self.runtime.clone()));
227 exec.open()
228 .map_err(|e| ExpressionError::type_error(format!("Subquery open failed: {}", e)))?;
229
230 let mut table: HashMap<Vec<Value>, Value> = HashMap::new();
231 while let Some(mut chunk) = exec
232 .advance()
233 .map_err(|e| ExpressionError::type_error(format!("Subquery execution failed: {}", e)))?
234 {
235 chunk.normalize_for_opaque("Subquery");
236 for row in chunk.rows {
237 if row.len() <= spec.key_columns {
238 continue;
239 }
240 let key: Vec<Value> = row[..spec.key_columns].to_vec();
241 if key.iter().any(Value::is_null) {
243 continue;
244 }
245 table.insert(key, row[spec.key_columns].clone());
246 }
247 }
248 *runner.group_table.lock() = Some(table);
249 Ok(())
250 }
251
252 fn run_exists(
254 &self,
255 runner: &SubqueryRunner,
256 layout: Arc<SlotLayout>,
257 row: Vec<Value>,
258 ) -> Result<bool, ExpressionError> {
259 runner.with_executor(&self.runtime, &self.bindings, layout, row, |exec| loop {
260 match exec.advance()? {
261 Some(chunk) => {
264 if chunk.visible_count() > 0 {
265 return Ok(true);
266 }
267 }
268 None => return Ok(false),
269 }
270 })
271 }
272
273 fn run_contains(
276 &self,
277 runner: &SubqueryRunner,
278 layout: Arc<SlotLayout>,
279 row: Vec<Value>,
280 value: &Value,
281 ) -> Result<bool, ExpressionError> {
282 runner.with_executor(&self.runtime, &self.bindings, layout, row, |exec| loop {
283 match exec.advance()? {
284 Some(chunk) => {
285 for chunk_row in chunk.visible_rows() {
286 if let Some(candidate) = chunk_row.first() {
287 if !candidate.is_null() && candidate == value {
288 return Ok(true);
289 }
290 }
291 }
292 }
293 None => return Ok(false),
294 }
295 })
296 }
297
298 fn run_values(
300 &self,
301 runner: &SubqueryRunner,
302 layout: Arc<SlotLayout>,
303 row: Vec<Value>,
304 ) -> Result<Vec<Value>, ExpressionError> {
305 runner.with_executor(&self.runtime, &self.bindings, layout, row, |exec| {
306 let mut values = Vec::new();
307 while let Some(mut chunk) = exec.advance()? {
308 chunk.normalize_for_opaque("Subquery");
309 for chunk_row in chunk.rows {
310 if let Some(value) = chunk_row.first() {
311 values.push(value.clone());
312 }
313 }
314 }
315 Ok(values)
316 })
317 }
318}