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