uqa_sql/prepared/
arguments.rs1use super::error;
10use crate::{ast::FunctionVolatility, plan::ExpressionPlan, ColumnType, SQLError, ScalarExpr};
11
12pub struct ArgumentValidationContext<'a> {
13 pub aggregates: &'a dyn crate::plan::AggregateClassifier,
14 pub volatility: &'a dyn crate::semantics::volatility::VolatilityCatalog,
15 pub cast_type: &'a dyn Fn(&str) -> Option<ColumnType>,
16}
17
18pub fn validate_assignment_type(
19 index: usize,
20 source: &ColumnType,
21 target: &ColumnType,
22) -> Result<(), SQLError> {
23 if !crate::assignment_type_compatible(source, target) {
24 return Err(error(
25 "42804",
26 format!(
27 "parameter ${} of type {} cannot be coerced to the expected type {}",
28 index + 1,
29 source.sql_name(),
30 target.sql_name()
31 ),
32 ));
33 }
34 Ok(())
35}
36
37pub fn parameter_base_type(mut ty: &ColumnType) -> &ColumnType {
38 while let ColumnType::Domain { base, .. } = ty {
39 ty = base;
40 }
41 ty
42}
43
44pub fn contains_domain(ty: &ColumnType) -> bool {
45 match ty {
46 ColumnType::Domain { .. } => true,
47 ColumnType::Array(element) => contains_domain(element),
48 _ => false,
49 }
50}
51
52pub fn immutable_argument(
53 context: &ArgumentValidationContext<'_>,
54 expression: &ScalarExpr,
55) -> bool {
56 let mut immutable = true;
57 expression.visit(&mut |expression| match expression {
58 ScalarExpr::Param(_)
59 | ScalarExpr::Column(_)
60 | ScalarExpr::QualifiedColumn { .. }
61 | ScalarExpr::InternalColumn(_)
62 | ScalarExpr::Position(_) => immutable = false,
63 ScalarExpr::Func {
64 name,
65 binding,
66 args,
67 ..
68 } => {
69 immutable &= crate::semantics::volatility::function_volatility_with_binding(
70 context.volatility,
71 name,
72 binding.as_ref(),
73 args.len(),
74 ) == FunctionVolatility::Immutable;
75 }
76 ScalarExpr::Cast { ty, .. } => {
77 immutable &= !(context.cast_type)(ty)
78 .as_ref()
79 .is_some_and(contains_domain);
80 }
81 _ => {}
82 });
83 immutable
84}
85
86pub fn validate_argument(
87 aggregates: &dyn crate::plan::AggregateClassifier,
88 argument: &ExpressionPlan,
89) -> Result<(), SQLError> {
90 if !argument.subqueries.is_empty() {
91 return Err(error("0A000", "cannot use subquery in EXECUTE parameter"));
92 }
93 if argument.scalar.contains_window() {
94 return Err(error(
95 "42P20",
96 "window functions are not allowed in EXECUTE parameters",
97 ));
98 }
99 if crate::semantics::aggregates::contains_aggregate(aggregates, &argument.scalar) {
100 return Err(error(
101 "42803",
102 "aggregate functions are not allowed in EXECUTE parameters",
103 ));
104 }
105 Ok(())
106}