1mod binding;
10mod calls;
11mod constants;
12mod description;
13mod fusion;
14mod graph;
15mod ir;
16mod joins;
17mod predicates;
18use crate::semantics::graph_functions::{
19 default_graph_name as default_operator_graph, GraphNameCatalog,
20};
21use crate::{ast::BinaryOp, SQLError, SQLParam, ScalarExpr};
22pub use binding::{lower_sql_function_bound, lower_where_bound};
23pub use description::{describe_vector_call, VectorCallDescription};
24pub use ir::{AttentionSpec, MultiStageEntry, RetrievalExpr, TextScoringMode};
25pub use joins::lower_operator_join_table_function;
26use std::collections::BTreeSet;
27use uqa_core::{
28 retrieval::{Direction as DeepGraphDirection, ExternalPriorMode, GatingSpec, MultiStageCutoff},
29 Predicate, Value,
30};
31type BindingResult<T> = Result<T, SQLError>;
32
33pub type ConstantEvaluator<'a> = dyn Fn(&ScalarExpr, &[SQLParam]) -> Result<Value, SQLError> + 'a;
35pub struct RetrievalConstants<'a> {
36 pub params: &'a [SQLParam],
37 pub evaluate: &'a ConstantEvaluator<'a>,
38 pub stores: &'a dyn Fn(&str) -> bool,
40}
41impl RetrievalConstants<'_> {
42 fn without_parameters(&self) -> RetrievalConstants<'_> {
43 RetrievalConstants {
44 params: &[],
45 evaluate: self.evaluate,
46 stores: self.stores,
47 }
48 }
49}
50
51#[must_use]
53pub fn names_engine_pseudo_column(expression: &ScalarExpr) -> bool {
54 let mut names = false;
55 expression.visit(&mut |node| {
56 if let ScalarExpr::Column(column) | ScalarExpr::QualifiedColumn { column, .. } = node {
57 names |= crate::semantics::is_engine_pseudo_column(column);
58 }
59 });
60 names
61}
62pub trait RetrievalArguments: GraphNameCatalog {
64 fn evaluate_argument(
65 &self,
66 expression: &ScalarExpr,
67 params: &[SQLParam],
68 ) -> Result<Value, SQLError>;
69}
70
71use calls::{
72 bind_operator_argument, checked_retrieval_call_tree_present, lower_bayesian_match_with_prior,
73 lower_calibrated_vector_match, lower_multi_field_match, lower_operator_arg, lower_signal_arg,
74 lower_staged_retrieval, try_lower_fts_match, try_lower_knn_match, try_lower_text_match,
75 validate_checked_retrieval_call_tree, validate_operator_function_arity,
76 validate_probability_signal_contract,
77};
78use constants::{
79 const_bool, const_f64, const_f64_vector, const_gating, const_optional_string, const_string,
80 const_temporal_bound, const_usize, const_value, const_vector, named_arg_expr,
81};
82use fusion::{
83 lower_bayesian_evidence_fusion, lower_learned_fusion, lower_positive_evidence_pool,
84 try_lower_attention_fusion,
85};
86use graph::lower_graph_function;
87use predicates::{column_name, lower_comparison, lower_document_boolean, lower_function};
88
89enum OptionalStringConstant {
90 Null,
91 Value(String),
92}
93impl OptionalStringConstant {
94 fn into_option(self) -> Option<String> {
95 match self {
96 Self::Null => None,
97 Self::Value(value) => Some(value),
98 }
99 }
100}
101
102pub fn lower_where(expr: &ScalarExpr, constants: &RetrievalConstants<'_>) -> Option<RetrievalExpr> {
104 match expr {
105 ScalarExpr::And(parts) => {
106 let mut out: Vec<RetrievalExpr> = Vec::with_capacity(parts.len());
107 for p in parts {
108 out.push(lower_where(p, constants)?);
109 }
110 Some(lower_document_boolean(out, false))
111 }
112 ScalarExpr::Or(parts) => {
113 let mut out: Vec<RetrievalExpr> = Vec::with_capacity(parts.len());
114 for p in parts {
115 out.push(lower_where(p, constants)?);
116 }
117 Some(lower_document_boolean(out, true))
118 }
119 ScalarExpr::Not(inner) if crate::semantics::expr_is_null_free(inner) => Some(
126 RetrievalExpr::Complement(Box::new(lower_where(inner, constants)?)),
127 ),
128 ScalarExpr::Func { name, args, .. } => lower_function(name, args, constants),
129 ScalarExpr::Binary { op, lhs, rhs } => lower_comparison(*op, lhs, rhs, constants),
130 ScalarExpr::IsNull { expr, negated } => {
131 let field = predicates::filter_field(expr, constants)?;
132 let predicate = if *negated {
133 Predicate::IsNotNull
134 } else {
135 Predicate::IsNull
136 };
137 Some(RetrievalExpr::Filter {
138 field,
139 predicate,
140 source: None,
141 })
142 }
143 ScalarExpr::Between { expr, low, high } => {
144 let field = predicates::filter_field(expr, constants)?;
145 let lo = const_value(low, constants)?;
146 let hi = const_value(high, constants)?;
147 Some(RetrievalExpr::Filter {
148 field,
149 predicate: Predicate::Between { low: lo, high: hi },
150 source: None,
151 })
152 }
153 ScalarExpr::InList {
154 expr,
155 list,
156 negated,
157 } => {
158 let field = predicates::filter_field(expr, constants)?;
159 let mut set: BTreeSet<Value> = BTreeSet::new();
160 let mut has_null = false;
161 for v in list {
162 let value = const_value(v, constants)?;
163 if matches!(value, Value::Null) {
164 has_null = true;
165 continue;
166 }
167 set.insert(value);
168 }
169 if *negated {
170 if has_null {
174 return Some(RetrievalExpr::Empty);
175 }
176 let filter = RetrievalExpr::Filter {
177 field: field.clone(),
178 predicate: Predicate::InSet(set),
179 source: None,
180 };
181 let not_null = RetrievalExpr::Filter {
182 field,
183 predicate: Predicate::IsNotNull,
184 source: None,
185 };
186 return Some(RetrievalExpr::Intersect(vec![
187 RetrievalExpr::Complement(Box::new(filter)),
188 not_null,
189 ]));
190 }
191 Some(RetrievalExpr::Filter {
192 field,
193 predicate: Predicate::InSet(set),
194 source: None,
195 })
196 }
197 _ => None,
198 }
199}