1pub mod attributes;
10pub mod body_parameters;
11pub mod body_validation;
12pub mod compilation;
13pub mod configuration;
14pub mod declaration;
15mod defaults;
16pub mod definition_output;
17pub mod dependencies;
18pub mod inlining;
19pub mod lifecycle;
20pub mod merge_columns;
21pub mod privilege_inquiry;
22pub mod regclass;
23pub mod registration;
24pub mod resolution;
25pub mod result_check;
26pub mod security;
27
28use crate::ast::{
29 ColumnType, CreateFunction, FunctionBinding, FunctionReturns, RoutineInvocationBinding,
30};
31use crate::plan::UnifiedPlan;
32use crate::type_resolution::{
33 canonical_routine_type_name, BuiltinFunctionOverload, FunctionTypeResolver,
34 RankedFunctionMatch, ResolvedFunctionOverload,
35};
36use crate::SQLError;
37use std::sync::{Arc, OnceLock};
38
39#[derive(Clone)]
41pub struct SQLUserFunction {
42 pub def: CreateFunction,
43 pub body: RoutineBody,
44 version: OnceLock<u64>,
45}
46
47impl SQLUserFunction {
48 #[must_use]
49 pub const fn new(def: CreateFunction, body: RoutineBody) -> Self {
50 Self {
51 def,
52 body,
53 version: OnceLock::new(),
54 }
55 }
56
57 #[must_use]
59 pub fn catalog_revision(&self) -> Option<[u8; 16]> {
60 self.def.catalog_revision.or(self.def.object_id)
61 }
62
63 pub fn definition_version(&self) -> Result<u64, SQLError> {
65 if let Some(version) = self.version.get() {
66 return Ok(*version);
67 }
68 let encoded = serde_json::to_vec(&self.def).map_err(|error| {
69 SQLError::Internal(format!(
70 "encode routine `{}` definition: {error}",
71 self.def.name
72 ))
73 })?;
74 let version = encoded
76 .iter()
77 .fold(0xcbf2_9ce4_8422_2325_u64, |hash, byte| {
78 (hash ^ u64::from(*byte)).wrapping_mul(0x0000_0100_0000_01b3)
79 });
80 Ok(*self.version.get_or_init(|| version))
81 }
82}
83
84#[derive(Clone)]
87pub enum RoutineBody {
88 Bound(Arc<CompiledFunctionBody>),
90 Source,
92}
93
94pub fn analyzable_routine_body(
96 resolution: &(impl RoutineResolution + ?Sized),
97 function: &SQLUserFunction,
98) -> Result<Option<Arc<CompiledFunctionBody>>, SQLError> {
99 match resolution.routine_analysis_body(function) {
100 Ok(body) => Ok(Some(body)),
101 Err(SQLError::Internal(message)) => Err(SQLError::Internal(message)),
102 Err(_) => Ok(None),
103 }
104}
105
106#[allow(clippy::upper_case_acronyms)]
109#[derive(Clone)]
110pub enum CompiledFunctionBody {
111 PLpgSQL(crate::plpgsql::PLpgSQLFunction),
112 SQL(Vec<UnifiedPlan>),
113}
114pub fn is_routine_namespace_lookup_error(error: &SQLError) -> bool {
115 matches!(
116 error,
117 SQLError::Routine { sqlstate, message }
118 if sqlstate == "3F000"
119 || (sqlstate == "42501"
120 && message.starts_with("permission denied for schema "))
121 )
122}
123
124pub trait RoutineResolution: FunctionTypeResolver {
126 fn has_registered_scalar_function(&self, _name: &str) -> bool {
127 false
128 }
129
130 fn routine_body(
132 &self,
133 function: &SQLUserFunction,
134 ) -> Result<Arc<CompiledFunctionBody>, SQLError> {
135 match &function.body {
136 RoutineBody::Bound(body) => Ok(Arc::clone(body)),
137 RoutineBody::Source => Err(SQLError::Internal(format!(
138 "routine `{}` has no session to compile its body",
139 function.def.name
140 ))),
141 }
142 }
143
144 fn routine_analysis_body(
148 &self,
149 function: &SQLUserFunction,
150 ) -> Result<Arc<CompiledFunctionBody>, SQLError> {
151 self.routine_body(function)
152 }
153
154 fn has_registered_table_function(&self, _name: &str) -> bool {
155 false
156 }
157
158 fn has_registered_aggregate_function(&self, _name: &str) -> bool {
159 false
160 }
161
162 fn lookup_visible_sql_functions(
163 &self,
164 _name: &str,
165 ) -> Result<Option<Vec<Arc<SQLUserFunction>>>, SQLError> {
166 Ok(None)
167 }
168
169 fn lookup_visible_sql_functions_for_analysis(
171 &self,
172 name: &str,
173 ) -> Result<Option<Vec<Arc<SQLUserFunction>>>, SQLError> {
174 self.lookup_visible_sql_functions(name)
175 }
176
177 fn lookup_bound_sql_functions(&self, _name: &str) -> Option<Vec<Arc<SQLUserFunction>>> {
179 None
180 }
181
182 fn lookup_bound_sql_functions_by_binding(
183 &self,
184 _binding: &FunctionBinding,
185 ) -> Option<Vec<Arc<SQLUserFunction>>> {
186 None
187 }
188
189 fn resolve_static_sql_function(
190 &self,
191 _name: &str,
192 _binding: Option<&FunctionBinding>,
193 _argument_names: &[Option<String>],
194 _argument_types: &[Option<ColumnType>],
195 _explicit_variadic: bool,
196 ) -> Result<Option<Arc<SQLUserFunction>>, SQLError> {
197 Ok(None)
198 }
199
200 fn resolve_static_sql_function_match(
201 &self,
202 _name: &str,
203 _binding: Option<&FunctionBinding>,
204 _argument_names: &[Option<String>],
205 _argument_types: &[Option<ColumnType>],
206 _explicit_variadic: bool,
207 ) -> Result<Option<StaticFunctionMatch>, SQLError> {
208 Ok(None)
209 }
210
211 fn resolve_table_function_overload_with_builtins(
212 &self,
213 _name: &str,
214 _binding: Option<&FunctionBinding>,
215 _argument_names: &[Option<String>],
216 _argument_types: &[Option<ColumnType>],
217 _explicit_variadic: bool,
218 _builtins: &[BuiltinFunctionOverload],
219 ) -> Result<Option<ResolvedFunctionOverload>, SQLError> {
220 Ok(None)
221 }
222}
223
224pub fn routine_signature_types(def: &CreateFunction) -> Vec<String> {
225 def.identity_params()
226 .iter()
227 .map(|parameter| canonical_routine_type_name(¶meter.type_name))
228 .collect()
229}
230
231pub fn routine_returns_anonymous_record(def: &CreateFunction) -> bool {
232 def.output_params().is_empty()
233 && matches!(
234 &def.returns,
235 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name }
236 if canonical_routine_type_name(type_name) == "record"
237 )
238}
239
240pub struct StaticFunctionMatch {
241 pub function: Arc<SQLUserFunction>,
242 pub invocation: Box<RoutineInvocationBinding>,
243 pub argument_types: Vec<String>,
244 pub raw_exact_matches: usize,
245 pub exact_matches: usize,
246 pub preferred_matches: usize,
247 pub variadic_expansion: bool,
248 pub default_error: Option<crate::type_resolution::RoutineSignatureMatchError>,
249}
250
251impl StaticFunctionMatch {
252 pub fn binding(&self) -> FunctionBinding {
253 FunctionBinding {
254 object_id: self.function.def.object_id,
255 name: self.function.def.name.clone(),
256 argument_types: routine_signature_types(&self.function.def),
257 builtin: false,
258 dispatch: None,
259 invocation: Some(self.invocation.clone()),
260 resolution_error: None,
261 }
262 }
263}
264
265impl RankedFunctionMatch for StaticFunctionMatch {
266 fn argument_types(&self) -> &[String] {
267 &self.argument_types
268 }
269
270 fn raw_exact_matches(&self) -> usize {
271 self.raw_exact_matches
272 }
273
274 fn exact_matches(&self) -> usize {
275 self.exact_matches
276 }
277
278 fn preferred_matches(&self) -> usize {
279 self.preferred_matches
280 }
281
282 fn is_variadic_expansion(&self) -> bool {
283 self.variadic_expansion
284 }
285}
286
287pub fn builtin_routine_support_oid(name: &str) -> Option<i64> {
288 Some(match name.strip_prefix("pg_catalog.").unwrap_or(name) {
289 "textlike_support" => 1023,
290 "texticregexeq_support" => 1024,
291 "texticlike_support" => 1025,
292 "network_subset_support" => 1173,
293 "textregexeq_support" => 1364,
294 "varchar_support" => 3097,
295 "numeric_support" => 3157,
296 _ => return None,
297 })
298}
299
300pub fn function_binding_matches(binding: &FunctionBinding, target: &FunctionBinding) -> bool {
301 if binding.builtin || target.builtin {
302 return false;
303 }
304 match (binding.object_id, target.object_id) {
305 (Some(binding), Some(target)) => binding == target,
306 (None, None) => {
307 binding.name == target.name && binding.argument_types == target.argument_types
308 }
309 _ => false,
310 }
311}
312
313pub fn routine_local_name(name: &str) -> Result<String, SQLError> {
314 uqa_core::RelationIdentity::from_legacy_name(name)
315 .map(|relation| relation.name)
316 .map_err(|error| SQLError::Internal(format!("invalid routine name `{name}`: {error}")))
317}
318
319pub fn routine_kind(def: &CreateFunction) -> &'static str {
320 if def.is_procedure {
321 "procedure"
322 } else {
323 "function"
324 }
325}
326
327pub mod anonymous_block;
328pub mod call;
329pub mod invocation;