uqa_sql/retrieval/
description.rs1use super::{calls, column_name, RetrievalConstants, RetrievalExpr};
10use crate::{registry::FunctionKind, SQLError, ScalarExpr};
11
12#[cfg(test)]
13mod tests;
14
15pub struct VectorCallDescription {
16 pub field: Option<String>,
17 pub qualifier: Option<String>,
18 pub calibrated: bool,
19 pub bound: Option<RetrievalExpr>,
20 pub invalid_arguments: bool,
21}
22
23pub fn describe_vector_call(
24 expression: &ScalarExpr,
25 constants: &RetrievalConstants<'_>,
26 can_evaluate: &dyn Fn(&ScalarExpr) -> bool,
27) -> Result<Option<VectorCallDescription>, SQLError> {
28 let ScalarExpr::Func { name, args, .. } = expression else {
29 return Ok(None);
30 };
31 let calibrated = match crate::registry::lookup(name) {
32 Some(FunctionKind::KNNMatch) => false,
33 Some(FunctionKind::CalibratedVectorMatch) => true,
34 _ => return Ok(None),
35 };
36 calls::validate_operator_function_arity(name, args.len())?;
37 let field = column_name(&args[0]).or_else(|| {
38 (calibrated && can_evaluate(&args[0]))
39 .then(|| calls::field_name_arg(&args[0], constants))
40 .flatten()
41 });
42 let qualifier = match &args[0] {
43 ScalarExpr::QualifiedColumn { qualifier, .. } => Some(qualifier.clone()),
44 _ => None,
45 };
46 let available = field.is_some() && args.iter().skip(1).all(can_evaluate);
47 let bound = if available {
48 if calibrated {
49 calls::try_lower_calibrated_vector_match(args, constants).ok()
50 } else {
51 calls::try_lower_knn_match(args, constants).ok()
52 }
53 } else {
54 None
55 };
56 Ok(Some(VectorCallDescription {
57 field,
58 qualifier,
59 calibrated,
60 invalid_arguments: available && bound.is_none(),
61 bound,
62 }))
63}