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 format_type_name(&self, ty: &ColumnType) -> Result<String, SQLError> {
22        self.routines.format_type_name(ty)
23    }
24
25    fn has_untyped_function(&self, name: &str) -> bool {
26        self.routines.has_untyped_function(name)
27    }
28
29    fn resolve_type_name(&self, name: &str) -> Result<Option<ColumnType>, SQLError> {
30        self.routines.resolve_type_name(name)
31    }
32
33    fn resolve_collation_name(&self, name: &str) -> Result<Option<u32>, SQLError> {
34        self.routines.resolve_collation_name(name)
35    }
36
37    fn enum_labels(&self) -> Option<&dyn crate::expr::enums::EnumLabelCatalog> {
38        self.routines.enum_labels()
39    }
40
41    fn composite_types(&self) -> Option<&dyn crate::expr::composites::CompositeTypeCatalog> {
42        self.routines.composite_types()
43    }
44
45    fn resolve_function_type(
46        &self,
47        name: &str,
48        binding: Option<&FunctionBinding>,
49        argument_names: &[Option<String>],
50        argument_types: &[Option<ColumnType>],
51        explicit_variadic: bool,
52    ) -> Result<Option<ColumnType>, SQLError> {
53        self.routines.resolve_function_type(
54            name,
55            binding,
56            argument_names,
57            argument_types,
58            explicit_variadic,
59        )
60    }
61
62    fn resolve_function_overload(
63        &self,
64        name: &str,
65        binding: Option<&FunctionBinding>,
66        argument_names: &[Option<String>],
67        argument_types: &[Option<ColumnType>],
68        explicit_variadic: bool,
69    ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
70        self.routines.resolve_function_overload(
71            name,
72            binding,
73            argument_names,
74            argument_types,
75            explicit_variadic,
76        )
77    }
78
79    fn is_scalar_function_binding(&self, binding: &FunctionBinding) -> Result<bool, SQLError> {
80        self.routines.is_scalar_function_binding(binding)
81    }
82
83    fn resolve_function_overload_with_builtins(
84        &self,
85        name: &str,
86        binding: Option<&FunctionBinding>,
87        argument_names: &[Option<String>],
88        argument_types: &[Option<ColumnType>],
89        explicit_variadic: bool,
90        builtins: &[BuiltinFunctionOverload],
91    ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
92        self.routines.resolve_function_overload_with_builtins(
93            name,
94            binding,
95            argument_names,
96            argument_types,
97            explicit_variadic,
98            builtins,
99        )
100    }
101
102    fn resolve_scalar_subquery_type(
103        &self,
104        subquery: crate::SubqueryId,
105        outer_schema: &crate::RowSchema,
106        params: &[SQLParam],
107    ) -> Result<Option<ColumnType>, SQLError> {
108        let plan = self.scope.scalar_subqueries.get(subquery).ok_or_else(|| {
109            SQLError::Internal(format!(
110                "physical scalar subquery slot {subquery} is out of bounds"
111            ))
112        })?;
113        let output = crate::binding::bind_query_plan_schema(
114            self.routines,
115            plan,
116            params,
117            &self.scope.context(),
118            Some(outer_schema),
119        )?;
120        Ok(output.column_type(0).cloned())
121    }
122}