1use serde::{Deserialize, Serialize};
8
9use super::RangeSubtype;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct FunctionBinding {
13 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub object_id: Option<[u8; 16]>,
16 pub name: String,
17 pub argument_types: Vec<String>,
18 #[serde(default)]
19 pub builtin: bool,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub dispatch: Option<FunctionDispatch>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub invocation: Option<Box<RoutineInvocationBinding>>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub resolution_error: Option<FunctionResolutionError>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub enum FunctionResolutionError {
34 UndefinedFunction { signature: String },
35 Operator(Box<OperatorResolutionError>),
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct OperatorResolutionError {
40 pub sqlstate: String,
41 pub message: String,
42}
43
44impl FunctionResolutionError {
45 #[must_use]
46 pub fn sql_error(&self) -> crate::SQLError {
47 let (sqlstate, message) = match self {
48 Self::UndefinedFunction { signature } => (
49 "42883".to_string(),
50 format!("function {signature} does not exist"),
51 ),
52 Self::Operator(error) => (error.sqlstate.clone(), error.message.clone()),
53 };
54 crate::SQLError::Routine { sqlstate, message }
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60pub enum FunctionDispatch {
61 NumericOperator(NumericOperator),
62 NamedArgument,
63 VariadicArgument,
64 ArraySubscripts,
65 ArraySlices,
66 Subscript,
67 Slice,
68 AnyOperator,
69 AllOperator,
70 IsDistinct,
71 BetweenSymmetric,
72 ToBinInt4,
73 ToBinInt8,
74 ToHexInt4,
75 ToHexInt8,
76 ToOctInt4,
77 ToOctInt8,
78 RandomInt4Range,
79 RandomInt8Range,
80 RandomNumericRange,
81 ArraySortJson,
82 JsonExtract {
83 as_text: bool,
84 path: bool,
85 },
86 Range {
87 operation: RangeFunctionOperation,
88 subtype: RangeSubtype,
89 multirange: bool,
90 },
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
95pub enum RangeFunctionOperation {
96 Lower,
97 Upper,
98 IsEmpty,
99 LowerInclusive,
100 UpperInclusive,
101 LowerInfinite,
102 UpperInfinite,
103 Merge,
104 Multirange,
105 Overlap,
106 Contains,
107 ContainedBy,
108 Adjacent,
109}
110
111impl FunctionDispatch {
112 #[must_use]
114 pub const fn label(self) -> &'static str {
115 match self {
116 Self::NumericOperator(operator) => operator.symbol(),
117 Self::NamedArgument => "named argument",
118 Self::VariadicArgument => "VARIADIC argument",
119 Self::ArraySubscripts | Self::Subscript => "subscript",
120 Self::ArraySlices | Self::Slice => "slice",
121 Self::AnyOperator => "ANY operator",
122 Self::AllOperator => "ALL operator",
123 Self::IsDistinct => "IS DISTINCT FROM",
124 Self::BetweenSymmetric => "BETWEEN SYMMETRIC",
125 Self::ToBinInt4 | Self::ToBinInt8 => "pg_catalog.to_bin",
126 Self::ToHexInt4 | Self::ToHexInt8 => "pg_catalog.to_hex",
127 Self::ToOctInt4 | Self::ToOctInt8 => "pg_catalog.to_oct",
128 Self::RandomInt4Range | Self::RandomInt8Range | Self::RandomNumericRange => {
129 "pg_catalog.random"
130 }
131 Self::ArraySortJson => "pg_catalog.array_sort",
132 Self::JsonExtract { as_text: false, .. } => "JSON extraction operator",
133 Self::JsonExtract { as_text: true, .. } => "JSON text extraction operator",
134 Self::Range { operation, .. } => operation.label(),
135 }
136 }
137
138 #[must_use]
139 pub const fn is_call_argument_marker(self) -> bool {
140 matches!(self, Self::NamedArgument | Self::VariadicArgument)
141 }
142
143 #[doc(hidden)]
145 #[must_use]
146 pub fn from_legacy_serialized_name(name: &str) -> Option<Self> {
147 let fixed = match name {
148 "__named_arg" => Self::NamedArgument,
149 "__variadic_arg" => Self::VariadicArgument,
150 "__array_subscripts" => Self::ArraySubscripts,
151 "__array_slices" => Self::ArraySlices,
152 "__subscript" => Self::Subscript,
153 "__slice" => Self::Slice,
154 "__any_op" => Self::AnyOperator,
155 "__all_op" => Self::AllOperator,
156 "__is_distinct" => Self::IsDistinct,
157 "__between_symmetric" => Self::BetweenSymmetric,
158 "__to_bin_int4" => Self::ToBinInt4,
159 "__to_bin_int8" => Self::ToBinInt8,
160 "__to_hex_int4" => Self::ToHexInt4,
161 "__to_hex_int8" => Self::ToHexInt8,
162 "__to_oct_int4" => Self::ToOctInt4,
163 "__to_oct_int8" => Self::ToOctInt8,
164 "__random_int4_range" => Self::RandomInt4Range,
165 "__random_int8_range" => Self::RandomInt8Range,
166 "__random_numeric_range" => Self::RandomNumericRange,
167 "__array_sort_json" => Self::ArraySortJson,
168 _ => return Self::legacy_range_dispatch(name),
169 };
170 Some(fixed)
171 }
172
173 fn legacy_range_dispatch(name: &str) -> Option<Self> {
174 let encoded = name.strip_prefix("__range_")?;
175 let subtypes = [
176 RangeSubtype::Integer,
177 RangeSubtype::BigInteger,
178 RangeSubtype::Numeric,
179 RangeSubtype::Date,
180 RangeSubtype::Timestamp,
181 RangeSubtype::TimestampTz,
182 ];
183 for subtype in subtypes {
184 for (type_name, multirange) in [
185 (subtype.multirange_name(), true),
186 (subtype.range_name(), false),
187 ] {
188 let Some(operation) = encoded.strip_suffix(type_name) else {
189 continue;
190 };
191 let operation = match operation.trim_end_matches('_') {
192 "lower" => RangeFunctionOperation::Lower,
193 "upper" => RangeFunctionOperation::Upper,
194 "isempty" => RangeFunctionOperation::IsEmpty,
195 "lower_inc" => RangeFunctionOperation::LowerInclusive,
196 "upper_inc" => RangeFunctionOperation::UpperInclusive,
197 "lower_inf" => RangeFunctionOperation::LowerInfinite,
198 "upper_inf" => RangeFunctionOperation::UpperInfinite,
199 "merge" => RangeFunctionOperation::Merge,
200 "multirange" => RangeFunctionOperation::Multirange,
201 "overlap" => RangeFunctionOperation::Overlap,
202 "contains" => RangeFunctionOperation::Contains,
203 "contained_by" => RangeFunctionOperation::ContainedBy,
204 "adjacent" => RangeFunctionOperation::Adjacent,
205 _ => continue,
206 };
207 return Some(Self::Range {
208 operation,
209 subtype,
210 multirange,
211 });
212 }
213 }
214 None
215 }
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
220pub enum NumericOperator {
221 Modulo,
222 Power,
223 Plus,
224 SquareRoot,
225 CubeRoot,
226 Absolute,
227}
228
229impl NumericOperator {
230 #[must_use]
231 pub const fn symbol(self) -> &'static str {
232 match self {
233 Self::Modulo => "%",
234 Self::Power => "^",
235 Self::Plus => "+",
236 Self::SquareRoot => "|/",
237 Self::CubeRoot => "||/",
238 Self::Absolute => "@",
239 }
240 }
241
242 #[must_use]
243 pub const fn arity(self) -> usize {
244 match self {
245 Self::Modulo | Self::Power => 2,
246 Self::Plus | Self::SquareRoot | Self::CubeRoot | Self::Absolute => 1,
247 }
248 }
249}
250
251impl RangeFunctionOperation {
252 #[must_use]
253 pub const fn label(self) -> &'static str {
254 match self {
255 Self::Lower => "pg_catalog.lower",
256 Self::Upper => "pg_catalog.upper",
257 Self::IsEmpty => "pg_catalog.isempty",
258 Self::LowerInclusive => "pg_catalog.lower_inc",
259 Self::UpperInclusive => "pg_catalog.upper_inc",
260 Self::LowerInfinite => "pg_catalog.lower_inf",
261 Self::UpperInfinite => "pg_catalog.upper_inf",
262 Self::Merge => "pg_catalog.range_merge",
263 Self::Multirange => "pg_catalog.multirange",
264 Self::Overlap => "range overlap operator",
265 Self::Contains => "range contains operator",
266 Self::ContainedBy => "range contained-by operator",
267 Self::Adjacent => "range adjacent operator",
268 }
269 }
270}
271
272#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
274pub struct RoutineInvocationBinding {
275 pub argument_positions: Vec<usize>,
277 pub argument_targets: Vec<String>,
279 #[serde(default, skip_serializing_if = "Vec::is_empty")]
281 pub argument_sources: Vec<Option<String>>,
282 pub parameter_types: Vec<String>,
284 pub return_type: Option<String>,
286 pub variadic_mode: RoutineVariadicMode,
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
292pub enum RoutineVariadicMode {
293 #[default]
295 None,
296 Expanded {
298 parameter_index: usize,
300 },
301 Explicit {
303 parameter_index: usize,
305 },
306}
307
308impl FunctionBinding {
309 #[must_use]
311 pub fn polymorphic_builtin_syntax(name: &str) -> Self {
312 assert!(Self::is_polymorphic_builtin_syntax_name(name));
313 Self {
314 object_id: None,
315 name: name.into(),
316 argument_types: Vec::new(),
317 builtin: true,
318 dispatch: None,
319 invocation: None,
320 resolution_error: None,
321 }
322 }
323
324 #[must_use]
326 pub fn dispatched(dispatch: FunctionDispatch) -> Self {
327 Self::dispatched_with_control(
328 dispatch,
329 &uqa_core::memory::ProductionControl::uncontrolled(),
330 )
331 .expect("ordinary dispatch constructor cannot be cancelled or limited")
332 .into_uncontrolled()
333 .expect("ordinary dispatch owner")
334 }
335
336 pub fn dispatched_with_control(
337 dispatch: FunctionDispatch,
338 control: &uqa_core::memory::ProductionControl<'_>,
339 ) -> Result<uqa_core::memory::Produced<Self>, uqa_core::ValueRetentionError> {
340 let (name, memory) = control.copy_text(dispatch.label())?.into_parts();
341 control.finish(
342 Self {
343 object_id: None,
344 name,
345 argument_types: Vec::new(),
346 builtin: true,
347 dispatch: Some(dispatch),
348 invocation: None,
349 resolution_error: None,
350 },
351 memory,
352 )
353 }
354
355 #[must_use]
357 pub fn undefined_function(name: impl Into<String>, signature: impl Into<String>) -> Self {
358 Self {
359 object_id: None,
360 name: name.into(),
361 argument_types: Vec::new(),
362 builtin: false,
363 dispatch: None,
364 invocation: None,
365 resolution_error: Some(FunctionResolutionError::UndefinedFunction {
366 signature: signature.into(),
367 }),
368 }
369 }
370
371 #[doc(hidden)]
373 pub fn upgrade_legacy_serialized_dispatch(
374 display_name: &mut String,
375 binding: &mut Option<Self>,
376 ) -> bool {
377 if binding
378 .as_ref()
379 .is_some_and(|binding| binding.dispatch.is_some() || !binding.builtin)
380 {
381 return false;
382 }
383 let Some(dispatch) = FunctionDispatch::from_legacy_serialized_name(display_name) else {
384 return false;
385 };
386 if let Some(binding) = binding {
387 binding.dispatch = Some(dispatch);
388 display_name.clone_from(&binding.name);
389 } else {
390 let upgraded = Self::dispatched(dispatch);
391 display_name.clone_from(&upgraded.name);
392 *binding = Some(upgraded);
393 }
394 true
395 }
396
397 #[must_use]
399 pub fn is_polymorphic_builtin_syntax(&self) -> bool {
400 self.builtin
401 && self.argument_types.is_empty()
402 && Self::is_polymorphic_builtin_syntax_name(&self.name)
403 }
404
405 #[must_use]
407 pub fn is_polymorphic_builtin_syntax_name(name: &str) -> bool {
408 matches!(name, "coalesce" | "greatest" | "least" | "nullif")
409 }
410}
411
412pub type GeneratedFunctionDependency = FunctionBinding;