1use crate::{ColumnType, RowSchema, SQLError, SQLParam};
10use uqa_core::Value;
11pub mod arguments;
12pub mod composites;
13pub mod definition;
14pub mod dependencies;
15pub mod entry;
16pub mod planning;
17
18pub fn declared_parameter_types(
19 resolver: &dyn crate::FunctionTypeResolver,
20 logical_plan: &mut crate::plan::UnifiedPlan,
21 declared: &[ColumnType],
22) -> Result<Vec<Option<ColumnType>>, SQLError> {
23 let mut parameter_types = declared
24 .iter()
25 .map(|ty| match ty {
26 crate::ast::ColumnType::Named(name) if is_unknown_type(name)? => Ok(None),
27 _ => crate::type_resolution::resolve_declared_column_type(resolver, ty).map(Some),
28 })
29 .collect::<Result<Vec<_>, _>>()?;
30 logical_plan.rewrite_scalar_expressions(&mut |expression| {
31 if let crate::ScalarExpr::Param(index) = expression {
32 parameter_types.resize(parameter_types.len().max(*index), None);
33 }
34 });
35 Ok(parameter_types)
36}
37
38fn is_unknown_type(name: &str) -> Result<bool, crate::SQLError> {
39 Ok(crate::parse_regtype_name(name)?.is_some_and(|parsed| {
40 parsed.array_dimensions == 0
41 && !parsed.has_type_modifiers
42 && match parsed.names.as_slice() {
43 [local] => local == "unknown",
44 [schema, local] => schema == "pg_catalog" && local == "unknown",
45 _ => false,
46 }
47 }))
48}
49
50pub fn analyze_prepared_plan(
51 routines: &dyn crate::routines::RoutineResolution,
52 plan: &crate::plan::UnifiedPlan,
53 parameter_types: &[Option<ColumnType>],
54 scope: &crate::binding::context::BindingContext<'_>,
55) -> Result<Option<RowSchema>, SQLError> {
56 let params = analysis_parameters(parameter_types);
57 match plan {
58 crate::plan::UnifiedPlan::Query(query) => {
59 crate::binding::analyze_query_plan_schema(routines, query, ¶ms, scope, None)
60 .map(Some)
61 }
62 crate::plan::UnifiedPlan::Command(command) => {
63 crate::binding::analyze_prepared_command_schema(routines, command, ¶ms, scope)
64 }
65 }
66}
67
68pub(crate) fn analysis_parameters(parameter_types: &[Option<ColumnType>]) -> Vec<SQLParam> {
69 parameter_types
70 .iter()
71 .map(|ty| match ty {
72 Some(ty) => SQLParam::typed_scalar(Value::Null, ty.clone()),
73 None => SQLParam::Scalar(Value::Null),
74 })
75 .collect()
76}
77
78pub fn prepared_result_schema_matches(left: Option<&RowSchema>, right: Option<&RowSchema>) -> bool {
79 match (left, right) {
80 (None, None) => true,
81 (Some(left), Some(right)) => {
82 left.columns() == right.columns()
83 && left.column_types().len() == right.column_types().len()
84 && left
85 .column_types()
86 .iter()
87 .zip(right.column_types())
88 .all(|(left, right)| {
89 prepared_type_identity(left.as_ref())
90 == prepared_type_identity(right.as_ref())
91 })
92 }
93 _ => false,
94 }
95}
96
97fn prepared_type_identity(ty: Option<&ColumnType>) -> Option<(u32, i32)> {
98 ty.map(|ty| {
99 if let ColumnType::Domain { oid, .. } = ty {
100 (*oid, -1)
101 } else {
102 let metadata = crate::catalog::result_type::postgres_result_type(ty);
103 (metadata.type_oid, metadata.type_modifier)
104 }
105 })
106}
107
108pub fn statement_error(sqlstate: &str, name: &str, reason: &str) -> SQLError {
109 error(sqlstate, format!("prepared statement \"{name}\" {reason}"))
110}
111
112pub fn execute_parameter_types(
113 name: &str,
114 types: Option<Vec<Option<ColumnType>>>,
115 argument_count: usize,
116) -> Result<Vec<Option<ColumnType>>, SQLError> {
117 let types = types.ok_or_else(|| statement_error("26000", name, "does not exist"))?;
118 if types.is_empty() {
120 return Ok(Vec::new());
121 }
122 if argument_count != types.len() {
123 return Err(error(
124 "42601",
125 format!("wrong number of parameters for prepared statement \"{name}\""),
126 ));
127 }
128 Ok(types)
129}
130
131pub(super) fn error(sqlstate: &str, message: impl Into<String>) -> SQLError {
132 SQLError::Routine {
133 sqlstate: sqlstate.into(),
134 message: message.into(),
135 }
136}
137
138#[cfg(test)]
139mod tests;