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