1use super::{
10 apply_attribute_change, type_contains_composite, AttributeChange, CompositeTypeCatalog,
11};
12use crate::{
13 ast::{
14 ColumnDef, ColumnType, Expr, PartitionBound, PartitionRangeDatum, PartitionSpec, Statement,
15 TableCheck,
16 },
17 plan::{QueryPlan, UnifiedPlan},
18 type_resolution::FunctionTypeResolver,
19 SQLError, ScalarExpr,
20};
21use uqa_core::Value;
22
23pub struct CompositeConstantChange<'a> {
24 pub target: u32,
25 pub change: &'a AttributeChange,
26 pub catalog: &'a dyn CompositeTypeCatalog,
27 pub types: &'a dyn FunctionTypeResolver,
28 pub rename: Option<&'a crate::binding::composite_rename::CompositeFieldRename<'a>>,
29}
30
31impl CompositeConstantChange<'_> {
32 pub fn value(&self, value: &mut Value, ty: &ColumnType) -> Result<bool, SQLError> {
33 if matches!(value, Value::Null) || !type_contains_composite(ty, self.target, self.catalog)?
34 {
35 return Ok(false);
36 }
37 *value = apply_attribute_change(
38 std::mem::replace(value, Value::Null),
39 ty,
40 self.target,
41 self.change,
42 self.catalog,
43 )?;
44 Ok(true)
45 }
46
47 fn typed_value(
48 &self,
49 value: &mut Value,
50 name: &str,
51 source: &mut Option<Box<super::CompositeConstantSource>>,
52 ) -> Result<bool, SQLError> {
53 let Some(ty) = self.types.resolve_type_name(name)? else {
54 return if crate::ast::UserTypeIdentity::parse(name).is_some() {
55 Err(SQLError::Internal(format!(
56 "stored constant type {name} disappeared"
57 )))
58 } else {
59 Ok(false)
60 };
61 };
62 if !matches!(value, Value::Null) && type_contains_composite(&ty, self.target, self.catalog)?
63 {
64 if source.is_none() {
65 *source = Some(Box::new(super::CompositeConstantSource::capture(
66 value,
67 &ty,
68 Some(self.catalog),
69 self.types.enum_labels(),
70 )?));
71 }
72 source
73 .as_mut()
74 .expect("captured composite source")
75 .retain_enum_oids(self.types.enum_labels())?;
76 if let AttributeChange::Type { name, to, .. } = self.change {
77 *value = source
78 .as_ref()
79 .expect("captured composite source")
80 .project_type_change(&ty, self.target, name, to, self.catalog)?;
81 return Ok(true);
82 }
83 }
84 self.value(value, &ty)
85 }
86
87 fn syntax_node(&self, node: &mut Expr) -> Result<bool, SQLError> {
88 match node {
89 Expr::CompositeRow { binding, .. } => {
90 super::constructor::retain_argument_types(binding, self.types, Some(self.catalog))
91 }
92 Expr::TypedLiteral {
93 value,
94 ty,
95 composite_source,
96 ..
97 } => self.typed_value(value, ty, composite_source),
98 _ => Ok(false),
99 }
100 }
101
102 fn scalar_node(&self, node: &mut ScalarExpr) -> Result<bool, SQLError> {
103 match node {
104 ScalarExpr::CompositeRow { binding, .. } => {
105 super::constructor::retain_argument_types(binding, self.types, Some(self.catalog))
106 }
107 ScalarExpr::TypedLiteral {
108 value,
109 ty,
110 composite_source,
111 ..
112 } => self.typed_value(value, ty, composite_source),
113 _ => Ok(false),
114 }
115 }
116
117 pub fn expression(&self, expression: &mut Expr) -> Result<bool, SQLError> {
118 self.expression_in_schema(expression, &crate::RowSchema::default())
119 }
120
121 pub fn expression_in_schema(
122 &self,
123 expression: &mut Expr,
124 schema: &crate::RowSchema,
125 ) -> Result<bool, SQLError> {
126 let mut changed = self
127 .rename
128 .map_or(Ok(false), |rename| rename.expression(expression, schema))?;
129 crate::catalog::stored_ast::visit_stored_expression(expression, &mut |node| {
130 changed |= self.syntax_node(node)?;
131 Ok(())
132 })?;
133 Ok(changed)
134 }
135
136 pub fn statement(&self, statement: &mut Statement) -> Result<bool, SQLError> {
137 self.statement_in_scope(statement, None, None)
138 }
139
140 pub fn statement_in_scope(
141 &self,
142 statement: &mut Statement,
143 definition: Option<&crate::ast::CreateFunction>,
144 outer: Option<&crate::RowSchema>,
145 ) -> Result<bool, SQLError> {
146 let mut changed = self.rename.map_or(Ok(false), |rename| {
147 rename.statement(statement, definition, outer)
148 })?;
149 crate::catalog::stored_ast::visit_stored_statement_expressions(statement, &mut |node| {
150 changed |= self.syntax_node(node)?;
151 Ok(())
152 })?;
153 Ok(changed)
154 }
155
156 pub fn query(&self, query: &mut QueryPlan) -> Result<bool, SQLError> {
157 let changed = self
158 .rename
159 .map_or(Ok(false), |rename| rename.query(query))?;
160 self.plan_nodes(|visit| query.rewrite_scalar_expressions(visit))
161 .map(|values| values || changed)
162 }
163
164 pub fn plan(&self, plan: &mut UnifiedPlan) -> Result<bool, SQLError> {
165 let changed = self.rename.is_some_and(|rename| rename.bound_plan(plan));
166 self.plan_nodes(|visit| plan.rewrite_scalar_expressions(visit))
167 .map(|values| values || changed)
168 }
169
170 pub fn expression_plan(
171 &self,
172 plan: &mut crate::plan::ExpressionPlan,
173 ) -> Result<bool, SQLError> {
174 let mut changed = self
175 .plan_nodes(|visit| crate::plan::rewrite_scalar_expression(&mut plan.scalar, visit))?;
176 for query in &mut plan.subqueries {
177 changed |= self.query(query)?;
178 }
179 Ok(changed)
180 }
181
182 fn plan_nodes(
183 &self,
184 visit: impl FnOnce(&mut dyn FnMut(&mut ScalarExpr)),
185 ) -> Result<bool, SQLError> {
186 let mut changed = false;
187 let mut failure = None;
188 visit(&mut |node| {
189 if failure.is_some() {
190 return;
191 }
192 match self.scalar_node(node) {
193 Ok(value) => changed |= value,
194 Err(error) => failure = Some(error),
195 }
196 });
197 failure.map_or(Ok(changed), Err)
198 }
199
200 pub fn columns(&self, columns: &mut [ColumnDef]) -> Result<bool, SQLError> {
201 let schema = crate::RowSchema::with_types(
202 columns.iter().map(|column| column.name.clone()).collect(),
203 columns
204 .iter()
205 .map(|column| Some(column.ty.clone()))
206 .collect(),
207 );
208 let mut changed = false;
209 for column in columns {
210 for expression in column.default.iter_mut().chain(column.check.iter_mut()) {
211 changed |= self.expression_in_schema(expression, &schema)?;
212 }
213 if let Some(generated) = &mut column.generated {
214 changed |= self.expression_in_schema(&mut generated.expression, &schema)?;
215 }
216 }
217 Ok(changed)
218 }
219
220 pub fn checks(
221 &self,
222 checks: &mut [TableCheck],
223 columns: &[ColumnDef],
224 ) -> Result<bool, SQLError> {
225 let schema = crate::RowSchema::with_types(
226 columns.iter().map(|column| column.name.clone()).collect(),
227 columns
228 .iter()
229 .map(|column| Some(column.ty.clone()))
230 .collect(),
231 );
232 let mut changed = false;
233 for check in checks {
234 changed |= self.expression_in_schema(&mut check.expr, &schema)?;
235 if let Some(partition) = &mut check.partition_constraint {
236 changed |= self.bound(&mut partition.bound, &partition.spec, columns)?;
237 changed |= self.spec(&mut partition.spec)?;
238 }
239 }
240 Ok(changed)
241 }
242
243 pub fn spec(&self, spec: &mut PartitionSpec) -> Result<bool, SQLError> {
244 let mut changed = false;
245 for key in &mut spec.keys {
246 changed |= self.expression(key)?;
247 }
248 Ok(changed)
249 }
250
251 pub fn bound(
252 &self,
253 bound: &mut PartitionBound,
254 spec: &PartitionSpec,
255 columns: &[ColumnDef],
256 ) -> Result<bool, SQLError> {
257 let mut changed = false;
258 for (position, key) in spec.keys.iter().enumerate() {
259 let ty = crate::semantics::partition::partition_key_type(self.types, key, columns)?;
260 match bound {
261 PartitionBound::List(values) => {
262 for expression in values {
263 changed |= self.bound_value(expression, &ty)?;
264 }
265 }
266 PartitionBound::Range { lower, upper } => {
267 for point in [lower.get_mut(position), upper.get_mut(position)]
268 .into_iter()
269 .flatten()
270 {
271 if let PartitionRangeDatum::Value(expression) = point {
272 changed |= self.bound_value(expression, &ty)?;
273 }
274 }
275 }
276 PartitionBound::Default | PartitionBound::Hash { .. } => {}
277 }
278 }
279 Ok(changed)
280 }
281
282 fn bound_value(&self, expression: &mut Expr, ty: &ColumnType) -> Result<bool, SQLError> {
283 match expression {
284 Expr::Literal(value) | Expr::TypedLiteral { value, .. } => self.value(value, ty),
285 _ => Err(SQLError::Internal(
286 "partition bound datum was not evaluated".into(),
287 )),
288 }
289 }
290}
291
292#[cfg(test)]
293mod tests;