1use crate::ast::{ColumnType, Expr};
10use crate::{RowSchema, SQLError};
11use uqa_core::Value;
12
13use super::SchemaBindingContext;
14
15pub fn validate_default_expression(
17 context: &SchemaBindingContext<'_, '_>,
18 expression: &mut Expr,
19 target: &ColumnType,
20 column: &str,
21) -> Result<bool, SQLError> {
22 let plan = crate::plan::ExpressionPlan::lower(expression.clone());
23 if !plan.subqueries.is_empty() {
24 return Err(default_error(
25 "0A000",
26 "cannot use subquery in DEFAULT expression",
27 ));
28 }
29 if crate::semantics::windows::expr_has_window(&plan.scalar) {
30 return Err(default_error(
31 "42P20",
32 "window functions are not allowed in DEFAULT expressions",
33 ));
34 }
35 if crate::semantics::aggregates::contains_aggregate(context.catalog, &plan.scalar) {
36 return Err(default_error(
37 "42803",
38 "aggregate functions are not allowed in DEFAULT expressions",
39 ));
40 }
41 if crate::semantics::aggregates::expr_references_columns(&plan.scalar) {
42 return Err(default_error(
43 "0A000",
44 "cannot use column reference in DEFAULT expression",
45 ));
46 }
47 if crate::semantics::sets::validation::expression_may_return_set(
48 context.catalog,
49 context.catalog,
50 &plan.scalar,
51 &RowSchema::default(),
52 &[],
53 )? {
54 return Err(default_error(
55 "0A000",
56 "set-returning functions are not allowed in DEFAULT expressions",
57 ));
58 }
59 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
60 context.catalog,
61 expression,
62 Some(target),
63 )?;
64 if !cook_constant(context, expression, target)? {
65 return Ok(false);
66 }
67 let original = crate::plan::ExpressionPlan::lower_with(expression.clone(), &|name: &str| {
70 context.catalog.has_registered_aggregate_function(name)
71 });
72 let mut bound = original.clone();
73 let source = crate::binding::analyze_default_inputs(
74 context.catalog,
75 context.catalog,
76 context.catalog,
77 &mut bound,
78 context.binding,
79 )?;
80 if let Some(source) = source {
81 check_assignable(&source, target, column, "default expression")?;
82 }
83 crate::binding::bind_expression_plan_routines_for_storage(
84 context.catalog,
85 &mut bound,
86 &[],
87 context.binding,
88 &RowSchema::default(),
89 )?;
90 let sites = crate::binding::syntax_sites::expression_syntax_sites(&original, &bound)?;
91 crate::catalog::stored_ast::bind_stored_expression_sites(expression, &sites)?;
92 Ok(true)
93}
94
95pub fn check_assignable(
97 source: &ColumnType,
98 target: &ColumnType,
99 column: &str,
100 expression: &str,
101) -> Result<(), SQLError> {
102 if crate::type_resolution::assignment_type_compatible(source, target) {
103 return Ok(());
104 }
105 Err(SQLError::Diagnostic {
106 sqlstate: "42804".into(),
107 message: format!(
108 "column \"{column}\" is of type {} but {expression} is of type {}",
109 target.regtype_name(),
110 source.regtype_name()
111 ),
112 detail: None,
113 hint: Some("You will need to rewrite or cast the expression.".into()),
114 })
115}
116
117pub fn cook_constant(
119 context: &SchemaBindingContext<'_, '_>,
120 expression: &mut Expr,
121 target: &ColumnType,
122) -> Result<bool, SQLError> {
123 match expression {
124 Expr::Literal(Value::Null) => return Ok(false),
125 Expr::TypedLiteral {
126 value: Value::Null, ..
127 } => return Ok(false),
128 Expr::Literal(Value::Str(_)) => {
129 crate::catalog::stored_ast::read_unknown_stored_literal(
130 crate::FunctionTypeResolver::enum_labels(context.catalog),
131 crate::FunctionTypeResolver::catalog_input_functions(context.catalog),
132 expression,
133 target,
134 false,
135 )?;
136 }
137 Expr::Cast { expr, ty, .. }
138 if matches!(expr.as_ref(), Expr::Literal(Value::Str(_) | Value::Null)) =>
139 {
140 let cast_type = crate::expr::EngineHook::resolve_type_name(context.catalog, ty)
142 .map_err(SQLError::Internal)?
143 .map_or_else(|| ColumnType::from_sql_name(ty), Ok)?;
144 let domain_cast = matches!(cast_type, ColumnType::Domain { .. });
145 if matches!(expr.as_ref(), Expr::Literal(Value::Null)) && !domain_cast {
146 return Ok(false);
147 }
148 if crate::type_resolution::catalog_input_type(&cast_type) {
149 return Ok(true);
151 }
152 crate::catalog::stored_ast::read_unknown_stored_literal(
153 crate::FunctionTypeResolver::enum_labels(context.catalog),
154 crate::FunctionTypeResolver::catalog_input_functions(context.catalog),
155 expr,
156 &cast_type,
157 true,
158 )?;
159 if domain_cast {
160 return Ok(true);
163 }
164 let cooked = std::mem::replace(expr.as_mut(), Expr::Literal(Value::Null));
165 *expression = cooked;
166 }
167 _ => {}
168 }
169 Ok(true)
170}
171
172pub fn bind_stored_schema_expression(
174 context: &SchemaBindingContext<'_, '_>,
175 expression: &mut Expr,
176 typed_expression: Expr,
177) -> Result<bool, SQLError> {
178 let lowered = crate::plan::ExpressionPlan::lower_with(typed_expression, &|name: &str| {
179 context.catalog.has_registered_aggregate_function(name)
180 });
181 let mut plan = lowered.clone();
182 crate::binding::analyze_stored_expression_inputs(
183 context.catalog,
184 &mut plan,
185 context.binding,
186 &RowSchema::default(),
187 )?;
188 crate::binding::bind_expression_plan_routines_for_storage(
189 context.catalog,
190 &mut plan,
191 &[],
192 context.binding,
193 &RowSchema::default(),
194 )?;
195 let sites = crate::binding::syntax_sites::expression_syntax_sites(&lowered, &plan)?;
196 let changed = crate::catalog::stored_ast::bind_stored_expression_sites(expression, &sites)?;
197 let before_inputs = expression.clone();
200 crate::schema::dependencies::oid_alias::read_oid_alias_constants(context.catalog, expression)?;
201 Ok(changed || *expression != before_inputs)
202}
203
204fn default_error(sqlstate: &str, message: &str) -> SQLError {
205 SQLError::Routine {
206 sqlstate: sqlstate.into(),
207 message: message.into(),
208 }
209}