Skip to main content

uqa_sql/binding/
scoped_types.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Catalog-backed function and scalar-subquery typing within an owned analysis scope.
8
9use super::snapshot::BindingSnapshot;
10use crate::{
11    ast::FunctionBinding, routines::RoutineResolution, BuiltinFunctionOverload, ColumnType,
12    FunctionTypeResolver, ResolvedFunctionOverload, SQLError, SQLParam,
13};
14
15pub struct BindingTypeResolver<'a> {
16    pub routines: &'a dyn RoutineResolution,
17    pub scope: &'a BindingSnapshot,
18}
19
20impl FunctionTypeResolver for BindingTypeResolver<'_> {
21    fn has_untyped_function(&self, name: &str) -> bool {
22        self.routines.has_untyped_function(name)
23    }
24
25    fn resolve_type_name(&self, name: &str) -> Result<Option<ColumnType>, SQLError> {
26        self.routines.resolve_type_name(name)
27    }
28
29    fn resolve_function_type(
30        &self,
31        name: &str,
32        binding: Option<&FunctionBinding>,
33        argument_names: &[Option<String>],
34        argument_types: &[Option<ColumnType>],
35        explicit_variadic: bool,
36    ) -> Result<Option<ColumnType>, SQLError> {
37        self.routines.resolve_function_type(
38            name,
39            binding,
40            argument_names,
41            argument_types,
42            explicit_variadic,
43        )
44    }
45
46    fn resolve_function_overload(
47        &self,
48        name: &str,
49        binding: Option<&FunctionBinding>,
50        argument_names: &[Option<String>],
51        argument_types: &[Option<ColumnType>],
52        explicit_variadic: bool,
53    ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
54        self.routines.resolve_function_overload(
55            name,
56            binding,
57            argument_names,
58            argument_types,
59            explicit_variadic,
60        )
61    }
62
63    fn is_scalar_function_binding(&self, binding: &FunctionBinding) -> Result<bool, SQLError> {
64        self.routines.is_scalar_function_binding(binding)
65    }
66
67    fn resolve_function_overload_with_builtins(
68        &self,
69        name: &str,
70        binding: Option<&FunctionBinding>,
71        argument_names: &[Option<String>],
72        argument_types: &[Option<ColumnType>],
73        explicit_variadic: bool,
74        builtins: &[BuiltinFunctionOverload],
75    ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
76        self.routines.resolve_function_overload_with_builtins(
77            name,
78            binding,
79            argument_names,
80            argument_types,
81            explicit_variadic,
82            builtins,
83        )
84    }
85
86    fn resolve_scalar_subquery_type(
87        &self,
88        subquery: crate::SubqueryId,
89        outer_schema: &crate::RowSchema,
90        params: &[SQLParam],
91    ) -> Result<Option<ColumnType>, SQLError> {
92        let plan = self.scope.scalar_subqueries.get(subquery).ok_or_else(|| {
93            SQLError::Internal(format!(
94                "physical scalar subquery slot {subquery} is out of bounds"
95            ))
96        })?;
97        let output = crate::binding::bind_query_plan_schema(
98            self.routines,
99            plan,
100            params,
101            &self.scope.context(),
102            Some(outer_schema),
103        )?;
104        Ok(output.column_type(0).cloned())
105    }
106}