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