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