1use super::{BindingContext, SchemaScope};
10use crate::ast::{ColumnType, FunctionDispatch};
11use crate::catalog::dependencies::ObjectAddress;
12use crate::plan::QueryPlan;
13use crate::routines::RoutineResolution;
14use crate::schema::ScalarTypeSchema;
15use crate::type_resolution::{scalar_type_with_resolver, FunctionTypeResolver};
16use crate::{RowSchema, SQLError, SQLParam, ScalarExpr};
17use uqa_core::Value;
18
19pub fn expression_composite_dependencies(
21 expression: &ScalarExpr,
22 schema: &dyn ScalarTypeSchema,
23 params: &[SQLParam],
24 types: &dyn FunctionTypeResolver,
25) -> Result<Vec<ObjectAddress>, SQLError> {
26 let mut references = Vec::new();
27 let mut failure = None;
28 expression.visit(&mut |node| {
29 if failure.is_some() {
30 return;
31 }
32 let ScalarExpr::Func { binding, args, .. } = node else {
33 return;
34 };
35 if binding.as_ref().and_then(|binding| binding.dispatch)
36 != Some(FunctionDispatch::FieldSelect)
37 {
38 return;
39 }
40 let [base, ScalarExpr::Literal(Value::Str(field))] = args.as_slice() else {
41 return;
42 };
43 let result = (|| {
44 let Some(mut ty) = scalar_type_with_resolver(base, schema, params, types)? else {
45 return Ok(None);
46 };
47 while let ColumnType::Domain { base, .. } = ty {
48 ty = *base;
49 }
50 let ColumnType::Composite(reference) = ty else {
51 return Ok(None);
52 };
53 let descriptor =
54 crate::expr::composites::descriptor(types.composite_types(), reference.oid)?;
55 Ok(descriptor.attribute(field).map(|(_, attribute)| {
56 ObjectAddress::column(descriptor.relation_oid, i32::from(attribute.number))
57 }))
58 })();
59 match result {
60 Ok(Some(reference)) => references.push(reference),
61 Ok(None) => {}
62 Err(error) => failure = Some(error),
63 }
64 });
65 failure.map_or(Ok(references), Err)
66}
67
68pub fn query_composite_dependencies(
70 routines: &dyn RoutineResolution,
71 query: &QueryPlan,
72 params: &[SQLParam],
73 context: &BindingContext<'_>,
74 outer: Option<&RowSchema>,
75) -> Result<Vec<ObjectAddress>, SQLError> {
76 let mut contains_field = false;
77 query.visit_scalar_expressions(&mut |expression| {
78 expression.visit(&mut |node| {
79 contains_field |= matches!(node, ScalarExpr::Func { binding: Some(binding), .. }
80 if binding.dispatch == Some(FunctionDispatch::FieldSelect));
81 });
82 });
83 if !contains_field {
84 return Ok(Vec::new());
85 }
86 let mut scope = SchemaScope::for_analysis(context)?;
87 scope.composite_dependencies = Some(Vec::new());
88 scope.bind_query_routines_for_storage(routines, &mut query.clone(), params, outer)?;
89 Ok(scope.composite_dependencies.unwrap_or_default())
90}
91
92pub fn expression_plan_composite_dependencies(
94 routines: &dyn RoutineResolution,
95 plan: &crate::plan::ExpressionPlan,
96 params: &[SQLParam],
97 context: &BindingContext<'_>,
98 schema: &RowSchema,
99) -> Result<Vec<ObjectAddress>, SQLError> {
100 let mut scope = SchemaScope::for_analysis(context)?;
101 scope.composite_dependencies = Some(Vec::new());
102 scope.preserve_syntax_shape = true;
103 scope.stored_expression_outer = Some(schema.clone());
104 let mut plan = plan.clone();
105 for subquery in &mut plan.subqueries {
106 scope.bind_query_routines_for_storage(routines, subquery, params, Some(schema))?;
107 }
108 scope.bind_scalar_routines_for_storage(
109 routines,
110 &mut plan.scalar,
111 schema,
112 &plan.subqueries,
113 params,
114 )?;
115 Ok(scope.composite_dependencies.unwrap_or_default())
116}
117
118impl SchemaScope {
119 pub(super) fn record_composite_dependencies(
120 &mut self,
121 routines: &dyn RoutineResolution,
122 expression: &ScalarExpr,
123 schema: &RowSchema,
124 subqueries: &[QueryPlan],
125 params: &[SQLParam],
126 ) -> Result<(), SQLError> {
127 if self.composite_dependencies.is_none() {
128 return Ok(());
129 }
130 let resolver = self.query_function_type_resolver_for_subqueries(
131 routines,
132 std::slice::from_ref(expression),
133 schema,
134 subqueries,
135 params,
136 )?;
137 let references = expression_composite_dependencies(expression, schema, params, &resolver)?;
138 if let Some(dependencies) = &mut self.composite_dependencies {
139 dependencies.extend(references);
140 }
141 Ok(())
142 }
143}
144
145pub fn routine_statement_composite_dependencies(
147 routines: &dyn RoutineResolution,
148 definition: &crate::ast::CreateFunction,
149 statement: &crate::ast::Statement,
150 context: &BindingContext<'_>,
151) -> Result<Vec<ObjectAddress>, SQLError> {
152 let mut plan = crate::plan::UnifiedPlan::lower_with(statement.clone(), &|name: &str| {
153 routines.has_registered_aggregate_function(name)
154 });
155 let mut contains_field = false;
156 plan.rewrite_scalar_expressions(&mut |node| {
157 contains_field |= matches!(node, ScalarExpr::Func { binding: Some(binding), .. }
158 if binding.dispatch == Some(FunctionDispatch::FieldSelect));
159 });
160 if !contains_field {
161 return Ok(Vec::new());
162 }
163 super::stored_routines::mark_catalog_statement_relations_bound(&mut plan)?;
164 let params = crate::routines::body_parameters::sql_body_parameters(definition)
165 .into_iter()
166 .map(|parameter| {
167 routines.resolve_type_name(¶meter.type_name).map(|ty| {
168 ty.map_or_else(
169 || SQLParam::scalar(Value::Null),
170 |ty| SQLParam::typed_scalar(Value::Null, ty),
171 )
172 })
173 })
174 .collect::<Result<Vec<_>, _>>()?;
175 let parameters =
176 crate::routines::body_parameters::sql_body_parameter_scope(definition, ¶ms)?;
177 let mut scope = SchemaScope::for_analysis(context)?;
178 scope.routine_parameters = Some(parameters.clone());
179 scope.composite_dependencies = Some(Vec::new());
180 scope.preserve_syntax_shape = true;
181 scope.bind_statement_parameters(routines, &mut plan, ¶ms, Some(parameters.schema()))?;
182 Ok(scope.composite_dependencies.unwrap_or_default())
183}
184
185pub fn statement_composite_dependencies(
187 routines: &dyn RoutineResolution,
188 statement: &crate::ast::Statement,
189 context: &BindingContext<'_>,
190 outer: &RowSchema,
191) -> Result<Vec<ObjectAddress>, SQLError> {
192 let mut plan = crate::plan::UnifiedPlan::lower_with(statement.clone(), &|name: &str| {
193 routines.has_registered_aggregate_function(name)
194 });
195 let mut contains_field = false;
196 plan.rewrite_scalar_expressions(&mut |node| {
197 contains_field |= matches!(node, ScalarExpr::Func { binding: Some(binding), .. }
198 if binding.dispatch == Some(FunctionDispatch::FieldSelect));
199 });
200 if !contains_field {
201 return Ok(Vec::new());
202 }
203 super::stored_routines::mark_catalog_statement_relations_bound(&mut plan)?;
204 let mut scope = SchemaScope::for_analysis(context)?;
205 scope.composite_dependencies = Some(Vec::new());
206 scope.preserve_syntax_shape = true;
207 scope.bind_statement_parameters(routines, &mut plan, &[], Some(outer))?;
208 Ok(scope.composite_dependencies.unwrap_or_default())
209}
210
211#[cfg(test)]
212mod tests;