1use uqa_core::{ArrayValue, Value};
10use uqa_sql::ast::{
11 BinaryOp, FrameMode, FunctionBinding, FunctionDispatch, InternalColumnRef, NullsOrder,
12};
13use uqa_sql::expr::{
14 cast_value_with_type_resolution, eval_binary_values, eval_binary_values_with_integer_width,
15 eval_bound_builtin_function_call, eval_function_call, integer_width_for_literal,
16 integer_width_for_type, negate_value, truthy, EngineHook, EvalContext, IntegerWidth, RowLookup,
17};
18use uqa_sql::{ResultRow, SQLError, SQLParam};
19
20use crate::batch::{OwnedPhysicalRow, PhysicalRow, RowSchema};
21
22pub type SubqueryId = usize;
24
25#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
26pub enum ScalarExpr {
27 Star,
28 QualifiedStar(String),
29 Default,
30 Column(String),
31 Position(usize),
33 InternalColumn(InternalColumnRef),
36 QualifiedColumn {
37 qualifier: String,
38 column: String,
39 },
40 Literal(Value),
41 Param(usize),
42 Func {
43 name: String,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 binding: Option<FunctionBinding>,
46 args: Vec<Self>,
47 distinct: bool,
48 order_by: Vec<ScalarOrder>,
49 filter: Option<Box<Self>>,
50 },
51 Array(Vec<Self>),
52 Row(Vec<Self>),
53 Binary {
54 op: BinaryOp,
55 lhs: Box<Self>,
56 rhs: Box<Self>,
57 },
58 UnaryMinus(Box<Self>),
59 Not(Box<Self>),
60 And(Vec<Self>),
61 Or(Vec<Self>),
62 IsNull {
63 expr: Box<Self>,
64 negated: bool,
65 },
66 Between {
67 expr: Box<Self>,
68 low: Box<Self>,
69 high: Box<Self>,
70 },
71 InList {
72 expr: Box<Self>,
73 list: Vec<Self>,
74 negated: bool,
75 },
76 WindowCall {
77 name: String,
78 args: Vec<Self>,
79 spec: ScalarWindowSpec,
80 },
81 Case {
82 base: Option<Box<Self>>,
83 when: Vec<(Self, Self)>,
84 else_branch: Option<Box<Self>>,
85 },
86 Cast {
87 expr: Box<Self>,
88 ty: String,
89 },
90 ScalarSubquery(SubqueryId),
91 Exists {
92 subquery: SubqueryId,
93 negated: bool,
94 },
95 InSubquery {
96 expr: Box<Self>,
97 subquery: SubqueryId,
98 negated: bool,
99 },
100}
101
102#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
103pub struct ScalarOrder {
104 pub expr: ScalarExpr,
105 pub descending: bool,
106 pub nulls: Option<NullsOrder>,
107}
108
109#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
110pub struct ScalarWindowSpec {
111 pub partition_by: Vec<ScalarExpr>,
112 pub order_by: Vec<ScalarOrder>,
113 pub frame: Option<ScalarWindowFrame>,
114}
115
116#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
117pub struct ScalarWindowFrame {
118 pub mode: FrameMode,
119 pub start: ScalarFrameBound,
120 pub end: ScalarFrameBound,
121}
122
123#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
124pub enum ScalarFrameBound {
125 UnboundedPreceding,
126 UnboundedFollowing,
127 CurrentRow,
128 Preceding(Box<ScalarExpr>),
129 Following(Box<ScalarExpr>),
130}
131
132impl ScalarExpr {
133 #[must_use]
134 pub fn qualified_column(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
135 Self::QualifiedColumn {
136 qualifier: qualifier.into(),
137 column: column.into(),
138 }
139 }
140
141 pub fn collect_columns(&self, output: &mut std::collections::BTreeSet<String>) -> bool {
145 match self {
146 Self::Column(name) | Self::QualifiedColumn { column: name, .. } => {
147 output.insert(name.clone());
148 true
149 }
150 Self::Literal(_) | Self::Param(_) | Self::InternalColumn(_) => true,
151 Self::Func {
152 args,
153 order_by,
154 filter,
155 ..
156 } => {
157 args.iter().all(|arg| arg.collect_columns(output))
158 && order_by
159 .iter()
160 .all(|order| order.expr.collect_columns(output))
161 && filter
162 .as_deref()
163 .is_none_or(|filter| filter.collect_columns(output))
164 }
165 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
166 items.iter().all(|item| item.collect_columns(output))
167 }
168 Self::Binary { lhs, rhs, .. } => {
169 lhs.collect_columns(output) && rhs.collect_columns(output)
170 }
171 Self::UnaryMinus(expr)
172 | Self::Not(expr)
173 | Self::IsNull { expr, .. }
174 | Self::Cast { expr, .. } => expr.collect_columns(output),
175 Self::Between { expr, low, high } => {
176 expr.collect_columns(output)
177 && low.collect_columns(output)
178 && high.collect_columns(output)
179 }
180 Self::InList { expr, list, .. } => {
181 expr.collect_columns(output) && list.iter().all(|item| item.collect_columns(output))
182 }
183 Self::Case {
184 base,
185 when,
186 else_branch,
187 } => {
188 base.as_deref()
189 .is_none_or(|base| base.collect_columns(output))
190 && when.iter().all(|(condition, result)| {
191 condition.collect_columns(output) && result.collect_columns(output)
192 })
193 && else_branch
194 .as_deref()
195 .is_none_or(|branch| branch.collect_columns(output))
196 }
197 Self::Default
198 | Self::Star
199 | Self::QualifiedStar(_)
200 | Self::Position(_)
201 | Self::WindowCall { .. }
202 | Self::ScalarSubquery(_)
203 | Self::Exists { .. }
204 | Self::InSubquery { .. } => false,
205 }
206 }
207
208 #[must_use]
209 pub fn contains_window(&self) -> bool {
210 match self {
211 Self::WindowCall { .. } => true,
212 Self::Func {
213 args,
214 order_by,
215 filter,
216 ..
217 } => {
218 args.iter().any(Self::contains_window)
219 || order_by.iter().any(|order| order.expr.contains_window())
220 || filter.as_deref().is_some_and(Self::contains_window)
221 }
222 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
223 items.iter().any(Self::contains_window)
224 }
225 Self::Binary { lhs, rhs, .. } => lhs.contains_window() || rhs.contains_window(),
226 Self::UnaryMinus(expr)
227 | Self::Not(expr)
228 | Self::IsNull { expr, .. }
229 | Self::Cast { expr, .. }
230 | Self::InSubquery { expr, .. } => expr.contains_window(),
231 Self::Between { expr, low, high } => {
232 expr.contains_window() || low.contains_window() || high.contains_window()
233 }
234 Self::InList { expr, list, .. } => {
235 expr.contains_window() || list.iter().any(Self::contains_window)
236 }
237 Self::Case {
238 base,
239 when,
240 else_branch,
241 } => {
242 base.as_deref().is_some_and(Self::contains_window)
243 || when.iter().any(|(condition, result)| {
244 condition.contains_window() || result.contains_window()
245 })
246 || else_branch.as_deref().is_some_and(Self::contains_window)
247 }
248 Self::Default
249 | Self::Star
250 | Self::QualifiedStar(_)
251 | Self::Column(_)
252 | Self::QualifiedColumn { .. }
253 | Self::Position(_)
254 | Self::InternalColumn(_)
255 | Self::Literal(_)
256 | Self::Param(_)
257 | Self::ScalarSubquery(_)
258 | Self::Exists { .. } => false,
259 }
260 }
261
262 #[must_use]
263 pub fn contains_subquery(&self) -> bool {
264 match self {
265 Self::ScalarSubquery(_) | Self::Exists { .. } | Self::InSubquery { .. } => true,
266 Self::Func {
267 args,
268 order_by,
269 filter,
270 ..
271 } => {
272 args.iter().any(Self::contains_subquery)
273 || order_by.iter().any(|order| order.expr.contains_subquery())
274 || filter.as_deref().is_some_and(Self::contains_subquery)
275 }
276 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
277 items.iter().any(Self::contains_subquery)
278 }
279 Self::Binary { lhs, rhs, .. } => lhs.contains_subquery() || rhs.contains_subquery(),
280 Self::UnaryMinus(expr)
281 | Self::Not(expr)
282 | Self::IsNull { expr, .. }
283 | Self::Cast { expr, .. } => expr.contains_subquery(),
284 Self::Between { expr, low, high } => {
285 expr.contains_subquery() || low.contains_subquery() || high.contains_subquery()
286 }
287 Self::InList { expr, list, .. } => {
288 expr.contains_subquery() || list.iter().any(Self::contains_subquery)
289 }
290 Self::WindowCall { args, spec, .. } => {
291 args.iter().any(Self::contains_subquery)
292 || spec.partition_by.iter().any(Self::contains_subquery)
293 || spec
294 .order_by
295 .iter()
296 .any(|order| order.expr.contains_subquery())
297 }
298 Self::Case {
299 base,
300 when,
301 else_branch,
302 } => {
303 base.as_deref().is_some_and(Self::contains_subquery)
304 || when.iter().any(|(condition, result)| {
305 condition.contains_subquery() || result.contains_subquery()
306 })
307 || else_branch.as_deref().is_some_and(Self::contains_subquery)
308 }
309 Self::Default
310 | Self::Star
311 | Self::QualifiedStar(_)
312 | Self::Column(_)
313 | Self::QualifiedColumn { .. }
314 | Self::Position(_)
315 | Self::InternalColumn(_)
316 | Self::Literal(_)
317 | Self::Param(_) => false,
318 }
319 }
320
321 #[must_use]
322 pub fn contains_parameter(&self) -> bool {
323 match self {
324 Self::Param(_) => true,
325 Self::Func {
326 args,
327 order_by,
328 filter,
329 ..
330 } => {
331 args.iter().any(Self::contains_parameter)
332 || order_by.iter().any(|order| order.expr.contains_parameter())
333 || filter.as_deref().is_some_and(Self::contains_parameter)
334 }
335 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
336 items.iter().any(Self::contains_parameter)
337 }
338 Self::Binary { lhs, rhs, .. } => lhs.contains_parameter() || rhs.contains_parameter(),
339 Self::UnaryMinus(expr)
340 | Self::Not(expr)
341 | Self::IsNull { expr, .. }
342 | Self::Cast { expr, .. }
343 | Self::InSubquery { expr, .. } => expr.contains_parameter(),
344 Self::Between { expr, low, high } => {
345 expr.contains_parameter() || low.contains_parameter() || high.contains_parameter()
346 }
347 Self::InList { expr, list, .. } => {
348 expr.contains_parameter() || list.iter().any(Self::contains_parameter)
349 }
350 Self::WindowCall { args, spec, .. } => {
351 args.iter().any(Self::contains_parameter)
352 || spec.partition_by.iter().any(Self::contains_parameter)
353 || spec
354 .order_by
355 .iter()
356 .any(|order| order.expr.contains_parameter())
357 || spec.frame.as_ref().is_some_and(|frame| {
358 scalar_frame_bound_contains_parameter(&frame.start)
359 || scalar_frame_bound_contains_parameter(&frame.end)
360 })
361 }
362 Self::Case {
363 base,
364 when,
365 else_branch,
366 } => {
367 base.as_deref().is_some_and(Self::contains_parameter)
368 || when.iter().any(|(condition, result)| {
369 condition.contains_parameter() || result.contains_parameter()
370 })
371 || else_branch.as_deref().is_some_and(Self::contains_parameter)
372 }
373 Self::Default
374 | Self::Star
375 | Self::QualifiedStar(_)
376 | Self::Column(_)
377 | Self::QualifiedColumn { .. }
378 | Self::Position(_)
379 | Self::InternalColumn(_)
380 | Self::Literal(_)
381 | Self::ScalarSubquery(_)
382 | Self::Exists { .. } => false,
383 }
384 }
385
386 #[must_use]
387 pub fn contains_aggregate(&self, is_aggregate: &dyn Fn(&str) -> bool) -> bool {
388 match self {
389 Self::Func {
390 name,
391 args,
392 order_by,
393 filter,
394 ..
395 } => {
396 is_aggregate(name)
397 || args
398 .iter()
399 .any(|expression| expression.contains_aggregate(is_aggregate))
400 || order_by
401 .iter()
402 .any(|order| order.expr.contains_aggregate(is_aggregate))
403 || filter
404 .as_deref()
405 .is_some_and(|expression| expression.contains_aggregate(is_aggregate))
406 }
407 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => items
408 .iter()
409 .any(|expression| expression.contains_aggregate(is_aggregate)),
410 Self::Binary { lhs, rhs, .. } => {
411 lhs.contains_aggregate(is_aggregate) || rhs.contains_aggregate(is_aggregate)
412 }
413 Self::UnaryMinus(expr)
414 | Self::Not(expr)
415 | Self::IsNull { expr, .. }
416 | Self::Cast { expr, .. }
417 | Self::InSubquery { expr, .. } => expr.contains_aggregate(is_aggregate),
418 Self::Between { expr, low, high } => {
419 expr.contains_aggregate(is_aggregate)
420 || low.contains_aggregate(is_aggregate)
421 || high.contains_aggregate(is_aggregate)
422 }
423 Self::InList { expr, list, .. } => {
424 expr.contains_aggregate(is_aggregate)
425 || list
426 .iter()
427 .any(|item| item.contains_aggregate(is_aggregate))
428 }
429 Self::Case {
430 base,
431 when,
432 else_branch,
433 } => {
434 base.as_deref()
435 .is_some_and(|expression| expression.contains_aggregate(is_aggregate))
436 || when.iter().any(|(condition, result)| {
437 condition.contains_aggregate(is_aggregate)
438 || result.contains_aggregate(is_aggregate)
439 })
440 || else_branch
441 .as_deref()
442 .is_some_and(|expression| expression.contains_aggregate(is_aggregate))
443 }
444 Self::Default
445 | Self::Star
446 | Self::QualifiedStar(_)
447 | Self::Column(_)
448 | Self::QualifiedColumn { .. }
449 | Self::Position(_)
450 | Self::InternalColumn(_)
451 | Self::Literal(_)
452 | Self::Param(_)
453 | Self::ScalarSubquery(_)
454 | Self::Exists { .. }
455 | Self::WindowCall { .. } => false,
456 }
457 }
458}
459
460fn scalar_frame_bound_contains_parameter(bound: &ScalarFrameBound) -> bool {
461 match bound {
462 ScalarFrameBound::Preceding(expression) | ScalarFrameBound::Following(expression) => {
463 expression.contains_parameter()
464 }
465 ScalarFrameBound::UnboundedPreceding
466 | ScalarFrameBound::UnboundedFollowing
467 | ScalarFrameBound::CurrentRow => false,
468 }
469}
470
471pub trait ScalarSubqueryRunner {
475 fn execute_subquery(
476 &self,
477 subquery: SubqueryId,
478 outer_row: Option<&dyn RowLookup>,
479 params: &[SQLParam],
480 ) -> Result<SubqueryResult, SQLError>;
481
482 fn execute_subquery_physical(
483 &self,
484 subquery: SubqueryId,
485 outer_schema: &RowSchema,
486 outer_row: &PhysicalRow,
487 params: &[SQLParam],
488 ) -> Result<SubqueryResult, SQLError> {
489 let outer = outer_schema.view(outer_row);
490 self.execute_subquery(subquery, Some(&outer), params)
491 }
492
493 fn scalar_subquery_value(
494 &self,
495 subquery: SubqueryId,
496 outer_row: Option<&dyn RowLookup>,
497 params: &[SQLParam],
498 ) -> Result<Value, SQLError> {
499 self.execute_subquery(subquery, outer_row, params)?
500 .into_scalar_value()
501 }
502
503 fn scalar_subquery_value_physical(
504 &self,
505 subquery: SubqueryId,
506 outer_schema: &RowSchema,
507 outer_row: &PhysicalRow,
508 params: &[SQLParam],
509 ) -> Result<Value, SQLError> {
510 self.execute_subquery_physical(subquery, outer_schema, outer_row, params)?
511 .into_scalar_value()
512 }
513
514 fn subquery_exists(
515 &self,
516 subquery: SubqueryId,
517 outer_row: Option<&dyn RowLookup>,
518 params: &[SQLParam],
519 ) -> Result<bool, SQLError> {
520 self.execute_subquery(subquery, outer_row, params)?
521 .into_exists()
522 }
523
524 fn subquery_exists_physical(
525 &self,
526 subquery: SubqueryId,
527 outer_schema: &RowSchema,
528 outer_row: &PhysicalRow,
529 params: &[SQLParam],
530 ) -> Result<bool, SQLError> {
531 self.execute_subquery_physical(subquery, outer_schema, outer_row, params)?
532 .into_exists()
533 }
534
535 fn subquery_contains(
536 &self,
537 subquery: SubqueryId,
538 needle: &Value,
539 outer_row: Option<&dyn RowLookup>,
540 params: &[SQLParam],
541 ) -> Result<Option<bool>, SQLError> {
542 self.execute_subquery(subquery, outer_row, params)?
543 .contains(needle)
544 }
545
546 fn subquery_contains_physical(
547 &self,
548 subquery: SubqueryId,
549 needle: &Value,
550 outer_schema: &RowSchema,
551 outer_row: &PhysicalRow,
552 params: &[SQLParam],
553 ) -> Result<Option<bool>, SQLError> {
554 self.execute_subquery_physical(subquery, outer_schema, outer_row, params)?
555 .contains(needle)
556 }
557}
558
559pub struct SubqueryResult {
563 pub columns: Vec<String>,
564 pub rows: Box<dyn Iterator<Item = Result<OwnedPhysicalRow, SQLError>> + Send>,
565}
566
567impl SubqueryResult {
568 pub fn from_rows(columns: Vec<String>, rows: Vec<ResultRow>) -> Self {
569 let schema = RowSchema::new(columns.clone());
570 Self {
571 columns,
572 rows: Box::new(rows.into_iter().map(move |row| {
573 Ok(OwnedPhysicalRow::new(
574 schema.clone(),
575 PhysicalRow::from_result_row(&schema, row),
576 ))
577 })),
578 }
579 }
580
581 pub fn into_scalar_value(mut self) -> Result<Value, SQLError> {
582 let Some(first_row) = self.rows.next().transpose()? else {
583 return Ok(Value::Null);
584 };
585 if self.rows.next().transpose()?.is_some() {
586 return Err(SQLError::TypeMismatch(
587 "scalar subquery returned more than one row".into(),
588 ));
589 }
590 if self.columns.is_empty() {
591 return Err(SQLError::TypeMismatch(
592 "scalar subquery returned no columns".into(),
593 ));
594 }
595 Ok(first_row
596 .positional_column(0)
597 .cloned()
598 .unwrap_or(Value::Null))
599 }
600
601 pub fn into_exists(mut self) -> Result<bool, SQLError> {
602 Ok(self.rows.next().transpose()?.is_some())
603 }
604
605 pub fn contains(self, needle: &Value) -> Result<Option<bool>, SQLError> {
606 if self.columns.is_empty() {
607 return Ok(Some(false));
608 }
609 let mut saw_row = false;
610 let mut saw_null = false;
611 for row in self.rows {
612 let row = row?;
613 saw_row = true;
614 match row.positional_column(0) {
615 Some(Value::Null) | None => saw_null = true,
616 Some(value) if !matches!(needle, Value::Null) && value == needle => {
617 return Ok(Some(true));
618 }
619 Some(_) => {}
620 }
621 }
622 Ok(if !saw_row {
623 Some(false)
624 } else if matches!(needle, Value::Null) || saw_null {
625 None
626 } else {
627 Some(false)
628 })
629 }
630}
631
632pub struct ScalarEvalContext<'a> {
633 row: Option<&'a ResultRow>,
634 row_lookup: Option<&'a dyn RowLookup>,
635 row_schema: Option<&'a RowSchema>,
636 params: &'a [SQLParam],
637 function_hook: Option<&'a dyn EngineHook>,
638 subquery_runner: Option<&'a dyn ScalarSubqueryRunner>,
639 physical_outer_row: Option<(&'a RowSchema, &'a PhysicalRow)>,
640}
641
642impl<'a> ScalarEvalContext<'a> {
643 #[must_use]
644 pub fn new(row: Option<&'a ResultRow>, params: &'a [SQLParam]) -> Self {
645 Self {
646 row,
647 row_lookup: row.map(|row| row as &dyn RowLookup),
648 row_schema: None,
649 params,
650 function_hook: None,
651 subquery_runner: None,
652 physical_outer_row: None,
653 }
654 }
655
656 #[must_use]
657 pub fn from_row_lookup(row: &'a dyn RowLookup, params: &'a [SQLParam]) -> Self {
658 Self {
659 row: None,
660 row_lookup: Some(row),
661 row_schema: None,
662 params,
663 function_hook: None,
664 subquery_runner: None,
665 physical_outer_row: None,
666 }
667 }
668
669 #[must_use]
670 pub fn with_function_hook(mut self, hook: &'a dyn EngineHook) -> Self {
671 self.function_hook = Some(hook);
672 self
673 }
674
675 #[must_use]
676 pub fn with_row_schema(mut self, schema: &'a RowSchema) -> Self {
677 self.row_schema = Some(schema);
678 self
679 }
680
681 #[must_use]
682 pub fn with_subquery_runner(mut self, runner: &'a dyn ScalarSubqueryRunner) -> Self {
683 self.subquery_runner = Some(runner);
684 self
685 }
686
687 #[must_use]
688 pub fn with_physical_outer_row(mut self, schema: &'a RowSchema, row: &'a PhysicalRow) -> Self {
689 self.physical_outer_row = Some((schema, row));
690 self
691 }
692
693 fn sql_context(&self) -> EvalContext<'_> {
694 let context = self.row_lookup.map_or_else(
695 || EvalContext::new(self.row, self.params),
696 |row| EvalContext::from_row_lookup(row, self.params),
697 );
698 match self.function_hook {
699 Some(hook) => context.with_engine(hook),
700 None => context,
701 }
702 }
703
704 fn outer_row(&self) -> Option<&dyn RowLookup> {
705 self.row_lookup
706 }
707}
708
709pub fn eval_scalar(
712 expression: &ScalarExpr,
713 context: &ScalarEvalContext<'_>,
714) -> Result<Value, SQLError> {
715 match expression {
716 ScalarExpr::Default => Err(SQLError::Internal(
717 "DEFAULT reached scalar expression evaluation without a mutation target".into(),
718 )),
719 ScalarExpr::Star | ScalarExpr::QualifiedStar(_) => {
720 Err(SQLError::Internal("`*` cannot be evaluated".into()))
721 }
722 ScalarExpr::Column(name) => context.sql_context().column_value(name),
723 ScalarExpr::Position(position) => context
724 .row_lookup
725 .and_then(|row| row.positional_column(*position))
726 .cloned()
727 .ok_or_else(|| {
728 SQLError::Internal(format!(
729 "bound physical column position {position} is unavailable"
730 ))
731 }),
732 ScalarExpr::InternalColumn(column) => context
733 .row_lookup
734 .and_then(|row| row.internal_column(*column))
735 .cloned()
736 .ok_or_else(|| {
737 SQLError::Internal(format!(
738 "internal relation attribute {column:?} is unavailable"
739 ))
740 }),
741 ScalarExpr::QualifiedColumn { qualifier, column } => context
742 .sql_context()
743 .qualified_column_value(qualifier, column),
744 ScalarExpr::Literal(value) => Ok(value.clone()),
745 ScalarExpr::Param(index) => eval_parameter(*index, context.params),
746 ScalarExpr::Func {
747 name,
748 binding,
749 args,
750 ..
751 } => {
752 let arguments = eval_call_arguments(args, context)?;
753 if let Some(binding) = binding {
754 if let Some(uqa_sql::ast::FunctionResolutionError::UndefinedFunction {
755 signature,
756 }) = binding.resolution_error.as_ref()
757 {
758 return Err(SQLError::Routine {
759 sqlstate: "42883".into(),
760 message: format!("function {signature} does not exist"),
761 });
762 }
763 if binding.builtin {
764 if let Some(result) = context
765 .function_hook
766 .and_then(|hook| hook.call_bound_builtin_function(binding, &arguments))
767 {
768 return result;
769 }
770 return eval_bound_builtin_function_call(
771 binding,
772 arguments,
773 &context.sql_context(),
774 );
775 }
776 let sql_context = context.sql_context();
777 let engine = sql_context.engine.ok_or_else(|| {
778 SQLError::Unsupported(
779 "bound user function requires a logical engine session".into(),
780 )
781 })?;
782 engine
783 .call_bound_user_function(binding, &arguments)
784 .unwrap_or_else(|| Err(SQLError::UnknownFunction(binding.name.clone())))
785 } else {
786 eval_function_call(name, arguments, &context.sql_context())
787 }
788 }
789 ScalarExpr::Array(items) => items
790 .iter()
791 .map(|item| eval_scalar(item, context))
792 .collect::<Result<Vec<_>, _>>()
793 .and_then(|items| {
794 ArrayValue::try_new(items).map(Value::Array).ok_or_else(|| {
795 SQLError::TypeMismatch(
796 "multidimensional arrays must have matching dimensions".into(),
797 )
798 })
799 }),
800 ScalarExpr::Row(items) => items
801 .iter()
802 .map(|item| eval_scalar(item, context))
803 .collect::<Result<Vec<_>, _>>()
804 .map(Value::Row),
805 ScalarExpr::Binary { op, lhs, rhs } => {
806 let left = eval_scalar(lhs, context)?;
807 let right = eval_scalar(rhs, context)?;
808 eval_binary_values_with_integer_width(
809 *op,
810 &left,
811 &right,
812 scalar_integer_binary_width(lhs, rhs),
813 )
814 }
815 ScalarExpr::UnaryMinus(inner) => {
816 let source_ty = scalar_source_type(inner, context);
817 let value = eval_scalar(inner, context)?;
818 negate_value(&value, source_ty.as_deref())
819 }
820 ScalarExpr::Not(inner) => {
821 let value = eval_scalar(inner, context)?;
822 if matches!(value, Value::Null) {
823 Ok(Value::Null)
824 } else {
825 Ok(Value::Bool(!truthy(&value)))
826 }
827 }
828 ScalarExpr::And(items) => eval_and(items, context),
829 ScalarExpr::Or(items) => eval_or(items, context),
830 ScalarExpr::IsNull { expr, negated } => {
831 let is_null = matches!(eval_scalar(expr, context)?, Value::Null);
832 Ok(Value::Bool(if *negated { !is_null } else { is_null }))
833 }
834 ScalarExpr::Between { expr, low, high } => eval_between(expr, low, high, context),
835 ScalarExpr::InList {
836 expr,
837 list,
838 negated,
839 } => eval_in_list(expr, list, *negated, context),
840 ScalarExpr::WindowCall { name, .. } => Err(SQLError::Unsupported(format!(
841 "window function `{name}` must be evaluated by the window-aware executor"
842 ))),
843 ScalarExpr::Case {
844 base,
845 when,
846 else_branch,
847 } => eval_case(base.as_deref(), when, else_branch.as_deref(), context),
848 ScalarExpr::Cast { expr, ty } => {
849 let source_ty = scalar_source_type(expr, context);
850 let value = eval_scalar(expr, context)?;
851 cast_value_with_type_resolution(&value, source_ty.as_deref(), ty, context.function_hook)
852 }
853 ScalarExpr::ScalarSubquery(subquery) => execute_scalar_subquery(*subquery, context),
854 ScalarExpr::Exists { subquery, negated } => {
855 let exists = execute_exists_subquery(*subquery, context)?;
856 Ok(Value::Bool(if *negated { !exists } else { exists }))
857 }
858 ScalarExpr::InSubquery {
859 expr,
860 subquery,
861 negated,
862 } => {
863 let needle = eval_scalar(expr, context)?;
864 let found = execute_in_subquery(*subquery, &needle, context)?;
865 Ok(found.map_or(Value::Null, |found| {
866 Value::Bool(if *negated { !found } else { found })
867 }))
868 }
869 }
870}
871
872fn eval_parameter(index: usize, params: &[SQLParam]) -> Result<Value, SQLError> {
873 match index
874 .checked_sub(1)
875 .and_then(|parameter_index| params.get(parameter_index))
876 {
877 Some(SQLParam::Scalar(value) | SQLParam::TypedScalar { value, .. }) => Ok(value.clone()),
878 Some(SQLParam::Vector(vector)) => Ok(Value::List(
879 vector
880 .iter()
881 .map(|value| Value::Float(f64::from(*value)))
882 .collect(),
883 )),
884 Some(SQLParam::Tensor(vectors)) => Ok(Value::List(
885 vectors
886 .iter()
887 .map(|vector| {
888 Value::List(
889 vector
890 .iter()
891 .map(|value| Value::Float(f64::from(*value)))
892 .collect(),
893 )
894 })
895 .collect(),
896 )),
897 None => Err(SQLError::MissingParam(index)),
898 }
899}
900
901pub fn eval_call_arguments(
902 arguments: &[ScalarExpr],
903 context: &ScalarEvalContext<'_>,
904) -> Result<Vec<(Option<String>, Value)>, SQLError> {
905 scalar_call_arguments(arguments)?
906 .into_iter()
907 .map(|argument| {
908 Ok((
909 argument.name.map(str::to_string),
910 eval_scalar(argument.value, context)?,
911 ))
912 })
913 .collect()
914}
915
916#[doc(hidden)]
918#[derive(Debug, Clone, Copy, PartialEq)]
919pub struct ScalarCallArgument<'a> {
920 pub name: Option<&'a str>,
921 pub value: &'a ScalarExpr,
922 pub explicit_variadic: bool,
923}
924
925#[doc(hidden)]
927pub fn scalar_call_arguments(
928 arguments: &[ScalarExpr],
929) -> Result<Vec<ScalarCallArgument<'_>>, SQLError> {
930 let mut decoded = Vec::with_capacity(arguments.len());
931 for argument in arguments {
932 decoded.push(scalar_call_argument(argument)?);
933 }
934 validate_scalar_call_arguments(&decoded)?;
935 Ok(decoded)
936}
937
938#[doc(hidden)]
940pub fn validate_scalar_call_arguments(
941 arguments: &[ScalarCallArgument<'_>],
942) -> Result<bool, SQLError> {
943 let variadic_positions = arguments
944 .iter()
945 .enumerate()
946 .filter_map(|(position, argument)| argument.explicit_variadic.then_some(position))
947 .collect::<Vec<_>>();
948 if variadic_positions.len() > 1 {
949 return Err(malformed_call_argument(
950 "call contains more than one explicit VARIADIC argument",
951 ));
952 }
953 if variadic_positions
954 .first()
955 .is_some_and(|position| position + 1 != arguments.len())
956 {
957 return Err(malformed_call_argument(
958 "explicit VARIADIC argument must be the final call argument",
959 ));
960 }
961 Ok(!variadic_positions.is_empty())
962}
963
964#[doc(hidden)]
966pub fn scalar_call_argument(expression: &ScalarExpr) -> Result<ScalarCallArgument<'_>, SQLError> {
967 let ScalarExpr::Func {
968 name,
969 args,
970 binding,
971 distinct,
972 order_by,
973 filter,
974 } = expression
975 else {
976 return Ok(ScalarCallArgument {
977 name: None,
978 value: expression,
979 explicit_variadic: false,
980 });
981 };
982 if binding.as_ref().and_then(|binding| binding.dispatch)
983 == Some(FunctionDispatch::NamedArgument)
984 {
985 validate_marker_shape(
986 binding.as_ref(),
987 FunctionDispatch::NamedArgument,
988 *distinct,
989 order_by,
990 filter.as_deref(),
991 name,
992 )?;
993 let [ScalarExpr::Literal(Value::Str(argument_name)), value] = args.as_slice() else {
994 return Err(malformed_call_argument(
995 "named argument marker must contain a string name and one value",
996 ));
997 };
998 let (value, explicit_variadic) = direct_variadic_argument(value)?;
999 if !explicit_variadic
1000 && matches!(
1001 value,
1002 ScalarExpr::Func { binding, .. }
1003 if binding.as_ref().and_then(|binding| binding.dispatch)
1004 == Some(FunctionDispatch::NamedArgument)
1005 )
1006 {
1007 return Err(malformed_call_argument(
1008 "call argument contains nested syntax markers",
1009 ));
1010 }
1011 return Ok(ScalarCallArgument {
1012 name: Some(argument_name),
1013 value,
1014 explicit_variadic,
1015 });
1016 }
1017 let (value, explicit_variadic) = direct_variadic_argument(expression)?;
1018 Ok(ScalarCallArgument {
1019 name: None,
1020 value,
1021 explicit_variadic,
1022 })
1023}
1024
1025fn direct_variadic_argument(expression: &ScalarExpr) -> Result<(&ScalarExpr, bool), SQLError> {
1026 let ScalarExpr::Func {
1027 name,
1028 args,
1029 binding,
1030 distinct,
1031 order_by,
1032 filter,
1033 } = expression
1034 else {
1035 return Ok((expression, false));
1036 };
1037 if binding.as_ref().and_then(|binding| binding.dispatch)
1038 != Some(FunctionDispatch::VariadicArgument)
1039 {
1040 return Ok((expression, false));
1041 }
1042 validate_marker_shape(
1043 binding.as_ref(),
1044 FunctionDispatch::VariadicArgument,
1045 *distinct,
1046 order_by,
1047 filter.as_deref(),
1048 name,
1049 )?;
1050 let [value] = args.as_slice() else {
1051 return Err(malformed_call_argument(
1052 "VARIADIC argument marker must contain exactly one value",
1053 ));
1054 };
1055 if matches!(
1056 value,
1057 ScalarExpr::Func { binding, .. }
1058 if matches!(
1059 binding.as_ref().and_then(|binding| binding.dispatch),
1060 Some(FunctionDispatch::VariadicArgument | FunctionDispatch::NamedArgument)
1061 )
1062 ) {
1063 return Err(malformed_call_argument(
1064 "call argument contains nested syntax markers",
1065 ));
1066 }
1067 Ok((value, true))
1068}
1069
1070fn validate_marker_shape(
1071 binding: Option<&FunctionBinding>,
1072 expected_dispatch: FunctionDispatch,
1073 distinct: bool,
1074 order_by: &[ScalarOrder],
1075 filter: Option<&ScalarExpr>,
1076 name: &str,
1077) -> Result<(), SQLError> {
1078 if binding.is_none_or(|binding| {
1079 !binding.builtin
1080 || binding.dispatch != Some(expected_dispatch)
1081 || !binding.argument_types.is_empty()
1082 || binding.invocation.is_some()
1083 || binding.resolution_error.is_some()
1084 }) || distinct
1085 || !order_by.is_empty()
1086 || filter.is_some()
1087 {
1088 return Err(malformed_call_argument(&format!(
1089 "{name} syntax marker contains function-call metadata"
1090 )));
1091 }
1092 Ok(())
1093}
1094
1095fn malformed_call_argument(message: &str) -> SQLError {
1096 SQLError::Internal(format!("malformed call argument: {message}"))
1097}
1098
1099fn eval_and(items: &[ScalarExpr], context: &ScalarEvalContext<'_>) -> Result<Value, SQLError> {
1100 let mut saw_null = false;
1101 for item in items {
1102 let value = eval_scalar(item, context)?;
1103 if matches!(value, Value::Null) {
1104 saw_null = true;
1105 } else if !truthy(&value) {
1106 return Ok(Value::Bool(false));
1107 }
1108 }
1109 Ok(if saw_null {
1110 Value::Null
1111 } else {
1112 Value::Bool(true)
1113 })
1114}
1115
1116fn eval_or(items: &[ScalarExpr], context: &ScalarEvalContext<'_>) -> Result<Value, SQLError> {
1117 let mut saw_null = false;
1118 for item in items {
1119 let value = eval_scalar(item, context)?;
1120 if matches!(value, Value::Null) {
1121 saw_null = true;
1122 } else if truthy(&value) {
1123 return Ok(Value::Bool(true));
1124 }
1125 }
1126 Ok(if saw_null {
1127 Value::Null
1128 } else {
1129 Value::Bool(false)
1130 })
1131}
1132
1133fn eval_between(
1134 expression: &ScalarExpr,
1135 low: &ScalarExpr,
1136 high: &ScalarExpr,
1137 context: &ScalarEvalContext<'_>,
1138) -> Result<Value, SQLError> {
1139 let value = eval_scalar(expression, context)?;
1140 let low = eval_scalar(low, context)?;
1141 let high = eval_scalar(high, context)?;
1142 let greater_equal = eval_binary_values(BinaryOp::GreaterEqual, &value, &low)?;
1143 let less_equal = eval_binary_values(BinaryOp::LessEqual, &value, &high)?;
1144 match (greater_equal, less_equal) {
1145 (Value::Bool(false), _) | (_, Value::Bool(false)) => Ok(Value::Bool(false)),
1146 (Value::Bool(true), Value::Bool(true)) => Ok(Value::Bool(true)),
1147 _ => Ok(Value::Null),
1148 }
1149}
1150
1151fn eval_in_list(
1152 expression: &ScalarExpr,
1153 list: &[ScalarExpr],
1154 negated: bool,
1155 context: &ScalarEvalContext<'_>,
1156) -> Result<Value, SQLError> {
1157 let needle = eval_scalar(expression, context)?;
1158 let mut saw_null = matches!(needle, Value::Null);
1159 for item in list {
1160 let candidate = eval_scalar(item, context)?;
1161 match eval_binary_values(BinaryOp::Equal, &needle, &candidate)? {
1162 Value::Bool(true) => return Ok(Value::Bool(!negated)),
1163 Value::Null => saw_null = true,
1164 _ => {}
1165 }
1166 }
1167 Ok(if saw_null {
1168 Value::Null
1169 } else {
1170 Value::Bool(negated)
1171 })
1172}
1173
1174fn eval_case(
1175 base: Option<&ScalarExpr>,
1176 branches: &[(ScalarExpr, ScalarExpr)],
1177 else_branch: Option<&ScalarExpr>,
1178 context: &ScalarEvalContext<'_>,
1179) -> Result<Value, SQLError> {
1180 let base = base
1181 .map(|expression| eval_scalar(expression, context))
1182 .transpose()?;
1183 for (condition, result) in branches {
1184 let condition = eval_scalar(condition, context)?;
1185 let matched = match &base {
1186 Some(base) => matches!(
1187 eval_binary_values(BinaryOp::Equal, base, &condition)?,
1188 Value::Bool(true)
1189 ),
1190 None => truthy(&condition),
1191 };
1192 if matched {
1193 return eval_scalar(result, context);
1194 }
1195 }
1196 else_branch.map_or(Ok(Value::Null), |expression| {
1197 eval_scalar(expression, context)
1198 })
1199}
1200
1201fn execute_scalar_subquery(
1202 subquery: SubqueryId,
1203 context: &ScalarEvalContext<'_>,
1204) -> Result<Value, SQLError> {
1205 let runner = context
1206 .subquery_runner
1207 .ok_or_else(|| SQLError::Unsupported("physical subquery requires a plan runner".into()))?;
1208 match context.physical_outer_row {
1209 Some((schema, row)) => {
1210 runner.scalar_subquery_value_physical(subquery, schema, row, context.params)
1211 }
1212 None => runner.scalar_subquery_value(subquery, context.outer_row(), context.params),
1213 }
1214}
1215
1216fn execute_exists_subquery(
1217 subquery: SubqueryId,
1218 context: &ScalarEvalContext<'_>,
1219) -> Result<bool, SQLError> {
1220 let runner = context
1221 .subquery_runner
1222 .ok_or_else(|| SQLError::Unsupported("physical subquery requires a plan runner".into()))?;
1223 match context.physical_outer_row {
1224 Some((schema, row)) => {
1225 runner.subquery_exists_physical(subquery, schema, row, context.params)
1226 }
1227 None => runner.subquery_exists(subquery, context.outer_row(), context.params),
1228 }
1229}
1230
1231fn execute_in_subquery(
1232 subquery: SubqueryId,
1233 needle: &Value,
1234 context: &ScalarEvalContext<'_>,
1235) -> Result<Option<bool>, SQLError> {
1236 let runner = context
1237 .subquery_runner
1238 .ok_or_else(|| SQLError::Unsupported("physical subquery requires a plan runner".into()))?;
1239 match context.physical_outer_row {
1240 Some((schema, row)) => {
1241 runner.subquery_contains_physical(subquery, needle, schema, row, context.params)
1242 }
1243 None => runner.subquery_contains(subquery, needle, context.outer_row(), context.params),
1244 }
1245}
1246
1247fn scalar_source_type(expression: &ScalarExpr, context: &ScalarEvalContext<'_>) -> Option<String> {
1248 match expression {
1249 ScalarExpr::Cast { ty, .. } => return Some(ty.clone()),
1250 ScalarExpr::UnaryMinus(inner) => return scalar_source_type(inner, context),
1251 ScalarExpr::Literal(Value::Int(value)) if i32::try_from(*value).is_ok() => {
1252 return Some("integer".into());
1253 }
1254 ScalarExpr::Literal(Value::Int(_)) => return Some("bigint".into()),
1255 ScalarExpr::Literal(Value::Bytes(_)) => return Some("bytea".into()),
1256 ScalarExpr::Literal(Value::Str(_) | Value::FixedChar(_)) => return None,
1257 _ => {}
1258 }
1259 context
1260 .row_schema
1261 .and_then(|schema| {
1262 crate::scalar_type(expression, schema, context.params)
1263 .ok()
1264 .flatten()
1265 })
1266 .map(|ty| ty.sql_name())
1267}
1268
1269fn scalar_integer_width(expression: &ScalarExpr) -> Option<IntegerWidth> {
1270 match expression {
1271 ScalarExpr::Literal(Value::Int(value)) => Some(integer_width_for_literal(*value)),
1272 ScalarExpr::Cast { ty, .. } => integer_width_for_type(ty),
1273 ScalarExpr::UnaryMinus(inner) => scalar_integer_width(inner),
1274 ScalarExpr::Binary {
1275 op: BinaryOp::Add | BinaryOp::Subtract | BinaryOp::Multiply | BinaryOp::Divide,
1276 lhs,
1277 rhs,
1278 } => Some(scalar_integer_width(lhs)?.max(scalar_integer_width(rhs)?)),
1279 _ => None,
1280 }
1281}
1282
1283pub(crate) fn scalar_integer_binary_width(
1284 lhs: &ScalarExpr,
1285 rhs: &ScalarExpr,
1286) -> Option<IntegerWidth> {
1287 Some(scalar_integer_width(lhs)?.max(scalar_integer_width(rhs)?))
1288}
1289
1290#[cfg(test)]
1291mod tests {
1292 use super::{
1293 eval_call_arguments, eval_scalar, scalar_call_arguments, ScalarEvalContext, ScalarExpr,
1294 };
1295 use crate::{PhysicalRow, RowSchema};
1296 use uqa_core::Value;
1297 use uqa_sql::ast::{BinaryOp, ColumnType, FunctionBinding, FunctionDispatch};
1298 use uqa_sql::{SQLError, SQLParam};
1299
1300 #[test]
1301 fn arithmetic_does_not_require_parser_ast() {
1302 let expression = ScalarExpr::Binary {
1303 op: BinaryOp::Multiply,
1304 lhs: Box::new(ScalarExpr::Literal(Value::Int(7))),
1305 rhs: Box::new(ScalarExpr::Literal(Value::Int(3))),
1306 };
1307 assert_eq!(
1308 eval_scalar(&expression, &ScalarEvalContext::new(None, &[])).unwrap(),
1309 Value::Int(21)
1310 );
1311 }
1312
1313 #[test]
1314 fn cast_uses_the_input_schema_declared_source_type() {
1315 let schema = RowSchema::with_types(vec!["support".into()], vec![Some(ColumnType::Regproc)]);
1316 let row = PhysicalRow::from_values(vec![Value::Int(0)]);
1317 let view = schema.view(&row);
1318 let expression = ScalarExpr::Cast {
1319 expr: Box::new(ScalarExpr::Column("support".into())),
1320 ty: "text".into(),
1321 };
1322 assert_eq!(
1323 eval_scalar(
1324 &expression,
1325 &ScalarEvalContext::from_row_lookup(&view, &[]).with_row_schema(&schema),
1326 )
1327 .unwrap(),
1328 Value::Str("-".into())
1329 );
1330 }
1331
1332 #[test]
1333 fn cast_preserves_unknown_type_for_string_literals() {
1334 let expression = ScalarExpr::Cast {
1335 expr: Box::new(ScalarExpr::Literal(Value::Str("[1,5)".into()))),
1336 ty: "int4range".into(),
1337 };
1338 assert_eq!(
1339 eval_scalar(&expression, &ScalarEvalContext::new(None, &[])).unwrap(),
1340 Value::Str("[1,5)".into())
1341 );
1342 }
1343
1344 #[test]
1345 fn parameter_zero_is_not_aliased_to_parameter_one() {
1346 let params = [SQLParam::Scalar(Value::Str("secret".into()))];
1347 assert!(matches!(
1348 eval_scalar(
1349 &ScalarExpr::Param(0),
1350 &ScalarEvalContext::new(None, ¶ms)
1351 ),
1352 Err(SQLError::MissingParam(0))
1353 ));
1354 }
1355
1356 #[test]
1357 fn typed_scalar_parameter_evaluates_like_scalar() {
1358 let params = [SQLParam::typed_scalar(
1359 Value::Int(7),
1360 ColumnType::SmallInteger,
1361 )];
1362 assert_eq!(
1363 eval_scalar(
1364 &ScalarExpr::Param(1),
1365 &ScalarEvalContext::new(None, ¶ms)
1366 )
1367 .unwrap(),
1368 Value::Int(7)
1369 );
1370 }
1371
1372 #[test]
1373 fn parameter_detection_descends_into_nested_expressions() {
1374 let expression = ScalarExpr::Func {
1375 name: "knn_match".into(),
1376 binding: None,
1377 args: vec![
1378 ScalarExpr::Column("embedding".into()),
1379 ScalarExpr::Array(vec![ScalarExpr::Param(1)]),
1380 ScalarExpr::Literal(Value::Int(3)),
1381 ],
1382 distinct: false,
1383 order_by: Vec::new(),
1384 filter: None,
1385 };
1386
1387 assert!(expression.contains_parameter());
1388 assert!(!ScalarExpr::Literal(Value::Int(3)).contains_parameter());
1389 }
1390
1391 #[test]
1392 fn explicit_variadic_call_argument_is_transparent_to_runtime_evaluation() {
1393 let arguments = vec![marker(
1394 FunctionDispatch::NamedArgument,
1395 vec![
1396 ScalarExpr::Literal(Value::Str("items".into())),
1397 marker(
1398 FunctionDispatch::VariadicArgument,
1399 vec![ScalarExpr::Literal(Value::Int(42))],
1400 ),
1401 ],
1402 )];
1403
1404 let decoded = scalar_call_arguments(&arguments).unwrap();
1405 assert_eq!(decoded[0].name, Some("items"));
1406 assert!(decoded[0].explicit_variadic);
1407 assert_eq!(decoded[0].value, &ScalarExpr::Literal(Value::Int(42)));
1408 assert_eq!(
1409 eval_call_arguments(&arguments, &ScalarEvalContext::new(None, &[])).unwrap(),
1410 vec![(Some("items".into()), Value::Int(42))]
1411 );
1412 }
1413
1414 #[test]
1415 fn call_argument_markers_reject_duplicates_and_malformed_nesting() {
1416 let duplicate = vec![
1417 marker(
1418 FunctionDispatch::VariadicArgument,
1419 vec![ScalarExpr::Literal(Value::Int(1))],
1420 ),
1421 marker(
1422 FunctionDispatch::VariadicArgument,
1423 vec![ScalarExpr::Literal(Value::Int(2))],
1424 ),
1425 ];
1426 assert!(matches!(
1427 scalar_call_arguments(&duplicate),
1428 Err(SQLError::Internal(message)) if message.contains("more than one")
1429 ));
1430
1431 let nested = vec![marker(
1432 FunctionDispatch::VariadicArgument,
1433 vec![marker(
1434 FunctionDispatch::VariadicArgument,
1435 vec![ScalarExpr::Literal(Value::Int(1))],
1436 )],
1437 )];
1438 assert!(matches!(
1439 scalar_call_arguments(&nested),
1440 Err(SQLError::Internal(message)) if message.contains("nested")
1441 ));
1442 }
1443
1444 fn marker(dispatch: FunctionDispatch, args: Vec<ScalarExpr>) -> ScalarExpr {
1445 let binding = FunctionBinding::dispatched(dispatch);
1446 ScalarExpr::Func {
1447 name: binding.name.clone(),
1448 binding: Some(binding),
1449 args,
1450 distinct: false,
1451 order_by: Vec::new(),
1452 filter: None,
1453 }
1454 }
1455}