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}
29
30impl CompositeConstantChange<'_> {
31 pub fn value(&self, value: &mut Value, ty: &ColumnType) -> Result<bool, SQLError> {
32 if matches!(value, Value::Null) || !type_contains_composite(ty, self.target, self.catalog)?
33 {
34 return Ok(false);
35 }
36 *value = apply_attribute_change(
37 std::mem::replace(value, Value::Null),
38 ty,
39 self.target,
40 self.change,
41 self.catalog,
42 )?;
43 Ok(true)
44 }
45
46 fn typed_value(&self, value: &mut Value, name: &str) -> Result<bool, SQLError> {
47 let Some(ty) = self.types.resolve_type_name(name)? else {
48 return if crate::ast::UserTypeIdentity::parse(name).is_some() {
49 Err(SQLError::Internal(format!(
50 "stored constant type {name} disappeared"
51 )))
52 } else {
53 Ok(false)
54 };
55 };
56 self.value(value, &ty)
57 }
58
59 fn syntax_node(&self, node: &mut Expr) -> Result<bool, SQLError> {
60 match node {
61 Expr::TypedLiteral { value, ty } => self.typed_value(value, ty),
62 _ => Ok(false),
63 }
64 }
65
66 fn scalar_node(&self, node: &mut ScalarExpr) -> Result<bool, SQLError> {
67 match node {
68 ScalarExpr::TypedLiteral { value, ty, .. } => self.typed_value(value, ty),
69 _ => Ok(false),
70 }
71 }
72
73 pub fn expression(&self, expression: &mut Expr) -> Result<bool, SQLError> {
74 let mut changed = false;
75 crate::catalog::stored_ast::visit_stored_expression(expression, &mut |node| {
76 changed |= self.syntax_node(node)?;
77 Ok(())
78 })?;
79 Ok(changed)
80 }
81
82 pub fn statement(&self, statement: &mut Statement) -> Result<bool, SQLError> {
83 let mut changed = false;
84 crate::catalog::stored_ast::visit_stored_statement_expressions(statement, &mut |node| {
85 changed |= self.syntax_node(node)?;
86 Ok(())
87 })?;
88 Ok(changed)
89 }
90
91 pub fn query(&self, query: &mut QueryPlan) -> Result<bool, SQLError> {
92 self.plan_nodes(|visit| query.rewrite_scalar_expressions(visit))
93 }
94
95 pub fn plan(&self, plan: &mut UnifiedPlan) -> Result<bool, SQLError> {
96 self.plan_nodes(|visit| plan.rewrite_scalar_expressions(visit))
97 }
98
99 pub fn expression_plan(
100 &self,
101 plan: &mut crate::plan::ExpressionPlan,
102 ) -> Result<bool, SQLError> {
103 let mut changed = self
104 .plan_nodes(|visit| crate::plan::rewrite_scalar_expression(&mut plan.scalar, visit))?;
105 for query in &mut plan.subqueries {
106 changed |= self.query(query)?;
107 }
108 Ok(changed)
109 }
110
111 fn plan_nodes(
112 &self,
113 visit: impl FnOnce(&mut dyn FnMut(&mut ScalarExpr)),
114 ) -> Result<bool, SQLError> {
115 let mut changed = false;
116 let mut failure = None;
117 visit(&mut |node| {
118 if failure.is_some() {
119 return;
120 }
121 match self.scalar_node(node) {
122 Ok(value) => changed |= value,
123 Err(error) => failure = Some(error),
124 }
125 });
126 failure.map_or(Ok(changed), Err)
127 }
128
129 pub fn columns(&self, columns: &mut [ColumnDef]) -> Result<bool, SQLError> {
130 let mut changed = false;
131 for column in columns {
132 for expression in column.default.iter_mut().chain(column.check.iter_mut()) {
133 changed |= self.expression(expression)?;
134 }
135 if let Some(generated) = &mut column.generated {
136 changed |= self.expression(&mut generated.expression)?;
137 }
138 }
139 Ok(changed)
140 }
141
142 pub fn checks(
143 &self,
144 checks: &mut [TableCheck],
145 columns: &[ColumnDef],
146 ) -> Result<bool, SQLError> {
147 let mut changed = false;
148 for check in checks {
149 changed |= self.expression(&mut check.expr)?;
150 if let Some(partition) = &mut check.partition_constraint {
151 changed |= self.bound(&mut partition.bound, &partition.spec, columns)?;
152 changed |= self.spec(&mut partition.spec)?;
153 }
154 }
155 Ok(changed)
156 }
157
158 pub fn spec(&self, spec: &mut PartitionSpec) -> Result<bool, SQLError> {
159 let mut changed = false;
160 for key in &mut spec.keys {
161 changed |= self.expression(key)?;
162 }
163 Ok(changed)
164 }
165
166 pub fn bound(
167 &self,
168 bound: &mut PartitionBound,
169 spec: &PartitionSpec,
170 columns: &[ColumnDef],
171 ) -> Result<bool, SQLError> {
172 let mut changed = false;
173 for (position, key) in spec.keys.iter().enumerate() {
174 let ty = crate::semantics::partition::partition_key_type(self.types, key, columns)?;
175 match bound {
176 PartitionBound::List(values) => {
177 for expression in values {
178 changed |= self.bound_value(expression, &ty)?;
179 }
180 }
181 PartitionBound::Range { lower, upper } => {
182 for point in [lower.get_mut(position), upper.get_mut(position)]
183 .into_iter()
184 .flatten()
185 {
186 if let PartitionRangeDatum::Value(expression) = point {
187 changed |= self.bound_value(expression, &ty)?;
188 }
189 }
190 }
191 PartitionBound::Default | PartitionBound::Hash { .. } => {}
192 }
193 }
194 Ok(changed)
195 }
196
197 fn bound_value(&self, expression: &mut Expr, ty: &ColumnType) -> Result<bool, SQLError> {
198 match expression {
199 Expr::Literal(value) | Expr::TypedLiteral { value, .. } => self.value(value, ty),
200 _ => Err(SQLError::Internal(
201 "partition bound datum was not evaluated".into(),
202 )),
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests;