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}
39impl RetrievalConstants<'_> {
40 fn without_parameters(&self) -> RetrievalConstants<'_> {
41 RetrievalConstants {
42 params: &[],
43 evaluate: self.evaluate,
44 }
45 }
46}
47pub trait RetrievalArguments: GraphNameCatalog {
49 fn evaluate_argument(
50 &self,
51 expression: &ScalarExpr,
52 params: &[SQLParam],
53 ) -> Result<Value, SQLError>;
54}
55
56use calls::{
57 bind_operator_argument, checked_retrieval_call_tree_present, lower_bayesian_match_with_prior,
58 lower_calibrated_vector_match, lower_multi_field_match, lower_operator_arg, lower_signal_arg,
59 lower_staged_retrieval, try_lower_fts_match, try_lower_knn_match, try_lower_text_match,
60 validate_checked_retrieval_call_tree, validate_operator_function_arity,
61 validate_probability_signal_contract,
62};
63use constants::{
64 const_bool, const_f64, const_f64_vector, const_gating, const_optional_string, const_string,
65 const_temporal_bound, const_usize, const_value, const_vector, named_arg_expr,
66};
67use fusion::{
68 lower_bayesian_evidence_fusion, lower_learned_fusion, lower_positive_evidence_pool,
69 try_lower_attention_fusion,
70};
71use graph::lower_graph_function;
72use predicates::{column_name, lower_comparison, lower_document_boolean, lower_function};
73
74enum OptionalStringConstant {
75 Null,
76 Value(String),
77}
78impl OptionalStringConstant {
79 fn into_option(self) -> Option<String> {
80 match self {
81 Self::Null => None,
82 Self::Value(value) => Some(value),
83 }
84 }
85}
86
87pub fn lower_where(expr: &ScalarExpr, constants: &RetrievalConstants<'_>) -> Option<RetrievalExpr> {
89 match expr {
90 ScalarExpr::And(parts) => {
91 let mut out: Vec<RetrievalExpr> = Vec::with_capacity(parts.len());
92 for p in parts {
93 out.push(lower_where(p, constants)?);
94 }
95 Some(lower_document_boolean(out, false))
96 }
97 ScalarExpr::Or(parts) => {
98 let mut out: Vec<RetrievalExpr> = Vec::with_capacity(parts.len());
99 for p in parts {
100 out.push(lower_where(p, constants)?);
101 }
102 Some(lower_document_boolean(out, true))
103 }
104 ScalarExpr::Not(inner) if crate::semantics::expr_is_null_free(inner) => Some(
111 RetrievalExpr::Complement(Box::new(lower_where(inner, constants)?)),
112 ),
113 ScalarExpr::Func { name, args, .. } => lower_function(name, args, constants),
114 ScalarExpr::Binary { op, lhs, rhs } => lower_comparison(*op, lhs, rhs, constants),
115 ScalarExpr::IsNull { expr, negated } => {
116 let field = column_name(expr)?;
117 let predicate = if *negated {
118 Predicate::IsNotNull
119 } else {
120 Predicate::IsNull
121 };
122 Some(RetrievalExpr::Filter {
123 field,
124 predicate,
125 source: None,
126 })
127 }
128 ScalarExpr::Between { expr, low, high } => {
129 let field = column_name(expr)?;
130 let lo = const_value(low, constants)?;
131 let hi = const_value(high, constants)?;
132 Some(RetrievalExpr::Filter {
133 field,
134 predicate: Predicate::Between { low: lo, high: hi },
135 source: None,
136 })
137 }
138 ScalarExpr::InList {
139 expr,
140 list,
141 negated,
142 } => {
143 let field = column_name(expr)?;
144 let mut set: BTreeSet<Value> = BTreeSet::new();
145 let mut has_null = false;
146 for v in list {
147 let value = const_value(v, constants)?;
148 if matches!(value, Value::Null) {
149 has_null = true;
150 continue;
151 }
152 set.insert(value);
153 }
154 if *negated {
155 if has_null {
159 return Some(RetrievalExpr::Empty);
160 }
161 let filter = RetrievalExpr::Filter {
162 field: field.clone(),
163 predicate: Predicate::InSet(set),
164 source: None,
165 };
166 let not_null = RetrievalExpr::Filter {
167 field,
168 predicate: Predicate::IsNotNull,
169 source: None,
170 };
171 return Some(RetrievalExpr::Intersect(vec![
172 RetrievalExpr::Complement(Box::new(filter)),
173 not_null,
174 ]));
175 }
176 Some(RetrievalExpr::Filter {
177 field,
178 predicate: Predicate::InSet(set),
179 source: None,
180 })
181 }
182 _ => None,
183 }
184}