1pub mod compilation;
10pub mod configuration;
11pub mod declaration;
12pub mod dependencies;
13pub mod lifecycle;
14pub mod merge_columns;
15pub mod regclass;
16pub mod registration;
17pub mod resolution;
18pub mod security;
19
20use crate::ast::{
21 ColumnType, CreateFunction, FunctionBinding, FunctionReturns, RoutineInvocationBinding,
22};
23use crate::plan::UnifiedPlan;
24use crate::type_resolution::{
25 canonical_routine_type_name, BuiltinFunctionOverload, FunctionTypeResolver,
26 RankedFunctionMatch, ResolvedFunctionOverload,
27};
28use crate::SQLError;
29use std::sync::Arc;
30
31#[derive(Clone)]
34pub struct SQLUserFunction {
35 pub def: CreateFunction,
36 pub compiled: CompiledFunctionBody,
37}
38
39#[allow(clippy::upper_case_acronyms)]
42#[derive(Clone)]
43pub enum CompiledFunctionBody {
44 PLpgSQL(crate::plpgsql::PLpgSQLFunction),
45 SQL(Vec<UnifiedPlan>),
46}
47pub fn is_routine_namespace_lookup_error(error: &SQLError) -> bool {
48 matches!(
49 error,
50 SQLError::Routine { sqlstate, message }
51 if sqlstate == "3F000"
52 || (sqlstate == "42501"
53 && message.starts_with("permission denied for schema "))
54 )
55}
56
57pub trait RoutineResolution: FunctionTypeResolver {
59 fn has_registered_scalar_function(&self, _name: &str) -> bool {
60 false
61 }
62
63 fn has_registered_table_function(&self, _name: &str) -> bool {
64 false
65 }
66
67 fn has_registered_aggregate_function(&self, _name: &str) -> bool {
68 false
69 }
70
71 fn lookup_visible_sql_functions(
72 &self,
73 _name: &str,
74 ) -> Result<Option<Vec<Arc<SQLUserFunction>>>, SQLError> {
75 Ok(None)
76 }
77
78 fn lookup_visible_sql_functions_for_analysis(
80 &self,
81 name: &str,
82 ) -> Result<Option<Vec<Arc<SQLUserFunction>>>, SQLError> {
83 self.lookup_visible_sql_functions(name)
84 }
85
86 fn lookup_bound_sql_functions(&self, _name: &str) -> Option<Vec<Arc<SQLUserFunction>>> {
88 None
89 }
90
91 fn lookup_bound_sql_functions_by_binding(
92 &self,
93 _binding: &FunctionBinding,
94 ) -> Option<Vec<Arc<SQLUserFunction>>> {
95 None
96 }
97
98 fn resolve_static_sql_function(
99 &self,
100 _name: &str,
101 _binding: Option<&FunctionBinding>,
102 _argument_names: &[Option<String>],
103 _argument_types: &[Option<ColumnType>],
104 _explicit_variadic: bool,
105 ) -> Result<Option<Arc<SQLUserFunction>>, SQLError> {
106 Ok(None)
107 }
108
109 fn resolve_static_sql_function_match(
110 &self,
111 _name: &str,
112 _binding: Option<&FunctionBinding>,
113 _argument_names: &[Option<String>],
114 _argument_types: &[Option<ColumnType>],
115 _explicit_variadic: bool,
116 ) -> Result<Option<StaticFunctionMatch>, SQLError> {
117 Ok(None)
118 }
119
120 fn resolve_table_function_overload_with_builtins(
121 &self,
122 _name: &str,
123 _binding: Option<&FunctionBinding>,
124 _argument_names: &[Option<String>],
125 _argument_types: &[Option<ColumnType>],
126 _explicit_variadic: bool,
127 _builtins: &[BuiltinFunctionOverload],
128 ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
129 Ok(None)
130 }
131}
132
133pub fn routine_signature_types(def: &CreateFunction) -> Vec<String> {
134 def.identity_params()
135 .iter()
136 .map(|parameter| canonical_routine_type_name(¶meter.type_name))
137 .collect()
138}
139
140pub fn routine_returns_anonymous_record(def: &CreateFunction) -> bool {
141 def.output_params().is_empty()
142 && matches!(
143 &def.returns,
144 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name }
145 if canonical_routine_type_name(type_name) == "record"
146 )
147}
148
149pub struct StaticFunctionMatch {
150 pub function: Arc<SQLUserFunction>,
151 pub invocation: Box<RoutineInvocationBinding>,
152 pub argument_types: Vec<String>,
153 pub raw_exact_matches: usize,
154 pub exact_matches: usize,
155 pub preferred_matches: usize,
156 pub variadic_expansion: bool,
157}
158
159impl StaticFunctionMatch {
160 pub fn binding(&self) -> FunctionBinding {
161 FunctionBinding {
162 object_id: self.function.def.object_id,
163 name: self.function.def.name.clone(),
164 argument_types: routine_signature_types(&self.function.def),
165 builtin: false,
166 dispatch: None,
167 invocation: Some(self.invocation.clone()),
168 resolution_error: None,
169 }
170 }
171}
172
173impl RankedFunctionMatch for StaticFunctionMatch {
174 fn argument_types(&self) -> &[String] {
175 &self.argument_types
176 }
177
178 fn raw_exact_matches(&self) -> usize {
179 self.raw_exact_matches
180 }
181
182 fn exact_matches(&self) -> usize {
183 self.exact_matches
184 }
185
186 fn preferred_matches(&self) -> usize {
187 self.preferred_matches
188 }
189
190 fn is_variadic_expansion(&self) -> bool {
191 self.variadic_expansion
192 }
193}
194
195pub fn builtin_routine_support_oid(name: &str) -> Option<i64> {
196 Some(match name.strip_prefix("pg_catalog.").unwrap_or(name) {
197 "textlike_support" => 1023,
198 "texticregexeq_support" => 1024,
199 "texticlike_support" => 1025,
200 "network_subset_support" => 1173,
201 "textregexeq_support" => 1364,
202 "varchar_support" => 3097,
203 "numeric_support" => 3157,
204 _ => return None,
205 })
206}
207
208pub fn function_binding_matches(binding: &FunctionBinding, target: &FunctionBinding) -> bool {
209 if binding.builtin || target.builtin {
210 return false;
211 }
212 match (binding.object_id, target.object_id) {
213 (Some(binding), Some(target)) => binding == target,
214 (None, None) => {
215 binding.name == target.name && binding.argument_types == target.argument_types
216 }
217 _ => false,
218 }
219}
220
221pub fn routine_local_name(name: &str) -> Result<String, SQLError> {
222 uqa_core::RelationIdentity::from_legacy_name(name)
223 .map(|relation| relation.name)
224 .map_err(|error| SQLError::Internal(format!("invalid routine name `{name}`: {error}")))
225}
226
227pub fn routine_kind(def: &CreateFunction) -> &'static str {
228 if def.is_procedure {
229 "procedure"
230 } else {
231 "function"
232 }
233}
234
235pub mod anonymous_block;
236pub mod call;
237pub mod invocation;