Skip to main content

reinhardt_query/types/
function.rs

1//! Function type definitions
2//!
3//! This module provides types for function-related DDL operations:
4//!
5//! - [`FunctionDef`]: Function definition for CREATE FUNCTION
6//! - [`FunctionParameter`]: Function parameter definition (name, type, mode)
7//! - [`FunctionLanguage`]: Programming language for function body
8//! - [`FunctionBehavior`]: Function volatility category
9//! - [`FunctionSecurity`]: Security context for function execution
10//!
11//! # Examples
12//!
13//! ```rust
14//! use reinhardt_query::types::function::{FunctionDef, FunctionLanguage, FunctionBehavior};
15//!
16//! // CREATE FUNCTION my_func() RETURNS integer LANGUAGE SQL AS 'SELECT 1'
17//! let func = FunctionDef::new("my_func")
18//!     .returns("integer")
19//!     .language(FunctionLanguage::Sql)
20//!     .body("SELECT 1");
21//!
22//! // CREATE OR REPLACE FUNCTION my_func(a integer, b text)
23//! // RETURNS integer LANGUAGE PLPGSQL AS '...'
24//! let func = FunctionDef::new("my_func")
25//!     .or_replace(true)
26//!     .add_parameter("a", "integer")
27//!     .add_parameter("b", "text")
28//!     .returns("integer")
29//!     .language(FunctionLanguage::PlPgSql)
30//!     .behavior(FunctionBehavior::Immutable)
31//!     .body("BEGIN RETURN a + LENGTH(b); END;");
32//! ```
33
34use crate::types::{DynIden, IntoIden};
35
36/// Function definition for CREATE FUNCTION
37///
38/// This struct represents a function definition, including its name,
39/// parameters, return type, language, behavior, security, and body.
40///
41/// # Examples
42///
43/// ```rust
44/// use reinhardt_query::types::function::{FunctionDef, FunctionLanguage};
45///
46/// // CREATE FUNCTION my_func() RETURNS integer
47/// let func = FunctionDef::new("my_func")
48///     .returns("integer")
49///     .language(FunctionLanguage::Sql)
50///     .body("SELECT 1");
51/// ```
52#[derive(Debug, Clone)]
53#[allow(dead_code)]
54pub struct FunctionDef {
55	pub(crate) name: DynIden,
56	pub(crate) or_replace: bool,
57	pub(crate) parameters: Vec<FunctionParameter>,
58	pub(crate) returns: Option<String>,
59	pub(crate) language: Option<FunctionLanguage>,
60	pub(crate) behavior: Option<FunctionBehavior>,
61	pub(crate) security: Option<FunctionSecurity>,
62	pub(crate) body: Option<String>,
63}
64
65/// Function parameter definition
66///
67/// Represents a parameter in a function signature with optional name, type, and mode.
68///
69/// # Examples
70///
71/// ```rust
72/// use reinhardt_query::types::function::{FunctionParameter, ParameterMode};
73///
74/// // IN parameter with name
75/// let param = FunctionParameter::new()
76///     .name("my_param")
77///     .param_type("integer")
78///     .mode(ParameterMode::In);
79///
80/// // OUT parameter without name
81/// let param = FunctionParameter::new()
82///     .param_type("text")
83///     .mode(ParameterMode::Out);
84/// ```
85#[derive(Debug, Clone)]
86#[allow(dead_code)]
87pub struct FunctionParameter {
88	pub(crate) name: Option<DynIden>,
89	pub(crate) param_type: Option<String>,
90	pub(crate) mode: Option<ParameterMode>,
91	pub(crate) default_value: Option<String>,
92}
93
94/// Parameter mode (IN, OUT, INOUT, VARIADIC)
95///
96/// Specifies the direction and behavior of a function parameter.
97#[derive(Debug, Clone, PartialEq, Eq)]
98#[allow(dead_code)]
99pub enum ParameterMode {
100	/// IN - Input parameter (default)
101	In,
102	/// OUT - Output parameter
103	Out,
104	/// INOUT - Input/output parameter
105	InOut,
106	/// VARIADIC - Variable number of arguments
107	Variadic,
108}
109
110/// Programming language for function body
111///
112/// Specifies the language in which the function is written.
113///
114/// # Examples
115///
116/// ```rust
117/// use reinhardt_query::types::function::FunctionLanguage;
118///
119/// let lang = FunctionLanguage::Sql;
120/// let lang = FunctionLanguage::PlPgSql;
121/// let lang = FunctionLanguage::C;
122/// let lang = FunctionLanguage::Custom("plpython3u".to_string());
123/// ```
124#[derive(Debug, Clone, PartialEq, Eq)]
125#[allow(dead_code)]
126pub enum FunctionLanguage {
127	/// SQL language
128	Sql,
129	/// PL/pgSQL (PostgreSQL)
130	PlPgSql,
131	/// C language
132	C,
133	/// Custom language (extension)
134	///
135	/// # Security Note
136	///
137	/// Only use with trusted language names. Do not use with user input.
138	Custom(String),
139}
140
141/// Function volatility category
142///
143/// Specifies whether the function modifies the database or depends on database state.
144///
145/// # Examples
146///
147/// ```rust
148/// use reinhardt_query::types::function::FunctionBehavior;
149///
150/// let behavior = FunctionBehavior::Immutable; // Result depends only on arguments
151/// let behavior = FunctionBehavior::Stable;     // Result depends on DB state (same within transaction)
152/// let behavior = FunctionBehavior::Volatile;   // Result may change (default)
153/// ```
154#[derive(Debug, Clone, PartialEq, Eq)]
155#[allow(dead_code)]
156pub enum FunctionBehavior {
157	/// IMMUTABLE - Result depends only on arguments, never changes
158	Immutable,
159	/// STABLE - Result depends on database state, but stable within a transaction
160	Stable,
161	/// VOLATILE - Result may change even within a transaction (default)
162	Volatile,
163}
164
165/// Security context for function execution
166///
167/// Specifies whether the function executes with the privileges of the definer or invoker.
168///
169/// # Examples
170///
171/// ```rust
172/// use reinhardt_query::types::function::FunctionSecurity;
173///
174/// let security = FunctionSecurity::Definer; // Execute with definer's privileges
175/// let security = FunctionSecurity::Invoker; // Execute with invoker's privileges (default)
176/// ```
177#[derive(Debug, Clone, PartialEq, Eq)]
178#[allow(dead_code)]
179pub enum FunctionSecurity {
180	/// SECURITY DEFINER - Execute with privileges of function definer
181	Definer,
182	/// SECURITY INVOKER - Execute with privileges of function caller (default)
183	Invoker,
184}
185
186impl FunctionDef {
187	/// Create a new function definition
188	///
189	/// # Examples
190	///
191	/// ```rust
192	/// use reinhardt_query::types::function::FunctionDef;
193	///
194	/// let func = FunctionDef::new("my_func");
195	/// ```
196	pub fn new<N: IntoIden>(name: N) -> Self {
197		Self {
198			name: name.into_iden(),
199			or_replace: false,
200			parameters: Vec::new(),
201			returns: None,
202			language: None,
203			behavior: None,
204			security: None,
205			body: None,
206		}
207	}
208
209	/// Set OR REPLACE clause
210	///
211	/// # Examples
212	///
213	/// ```rust
214	/// use reinhardt_query::types::function::FunctionDef;
215	///
216	/// let func = FunctionDef::new("my_func")
217	///     .or_replace(true);
218	/// ```
219	pub fn or_replace(mut self, or_replace: bool) -> Self {
220		self.or_replace = or_replace;
221		self
222	}
223
224	/// Add a function parameter
225	///
226	/// # Examples
227	///
228	/// ```rust
229	/// use reinhardt_query::types::function::FunctionDef;
230	///
231	/// let func = FunctionDef::new("my_func")
232	///     .add_parameter("param1", "integer")
233	///     .add_parameter("param2", "text");
234	/// ```
235	pub fn add_parameter<N: IntoIden, T: Into<String>>(mut self, name: N, param_type: T) -> Self {
236		self.parameters.push(FunctionParameter {
237			name: Some(name.into_iden()),
238			param_type: Some(param_type.into()),
239			mode: None,
240			default_value: None,
241		});
242		self
243	}
244
245	/// Add a function parameter with full specification
246	///
247	/// # Examples
248	///
249	/// ```rust
250	/// use reinhardt_query::types::function::{FunctionDef, FunctionParameter, ParameterMode};
251	///
252	/// let param = FunctionParameter::new()
253	///     .name("my_param")
254	///     .param_type("integer")
255	///     .mode(ParameterMode::InOut);
256	///
257	/// let func = FunctionDef::new("my_func")
258	///     .add_parameter_spec(param);
259	/// ```
260	pub fn add_parameter_spec(mut self, param: FunctionParameter) -> Self {
261		self.parameters.push(param);
262		self
263	}
264
265	/// Set RETURNS type
266	///
267	/// # Examples
268	///
269	/// ```rust
270	/// use reinhardt_query::types::function::FunctionDef;
271	///
272	/// let func = FunctionDef::new("my_func")
273	///     .returns("integer");
274	/// ```
275	pub fn returns<T: Into<String>>(mut self, returns: T) -> Self {
276		self.returns = Some(returns.into());
277		self
278	}
279
280	/// Set LANGUAGE
281	///
282	/// # Examples
283	///
284	/// ```rust
285	/// use reinhardt_query::types::function::{FunctionDef, FunctionLanguage};
286	///
287	/// let func = FunctionDef::new("my_func")
288	///     .language(FunctionLanguage::PlPgSql);
289	/// ```
290	pub fn language(mut self, language: FunctionLanguage) -> Self {
291		self.language = Some(language);
292		self
293	}
294
295	/// Set function behavior (IMMUTABLE/STABLE/VOLATILE)
296	///
297	/// # Examples
298	///
299	/// ```rust
300	/// use reinhardt_query::types::function::{FunctionDef, FunctionBehavior};
301	///
302	/// let func = FunctionDef::new("my_func")
303	///     .behavior(FunctionBehavior::Immutable);
304	/// ```
305	pub fn behavior(mut self, behavior: FunctionBehavior) -> Self {
306		self.behavior = Some(behavior);
307		self
308	}
309
310	/// Set security context (DEFINER/INVOKER)
311	///
312	/// # Examples
313	///
314	/// ```rust
315	/// use reinhardt_query::types::function::{FunctionDef, FunctionSecurity};
316	///
317	/// let func = FunctionDef::new("my_func")
318	///     .security(FunctionSecurity::Definer);
319	/// ```
320	pub fn security(mut self, security: FunctionSecurity) -> Self {
321		self.security = Some(security);
322		self
323	}
324
325	/// Set function body (AS clause)
326	///
327	/// # Examples
328	///
329	/// ```rust
330	/// use reinhardt_query::types::function::FunctionDef;
331	///
332	/// let func = FunctionDef::new("my_func")
333	///     .body("SELECT 1");
334	/// ```
335	pub fn body<B: Into<String>>(mut self, body: B) -> Self {
336		self.body = Some(body.into());
337		self
338	}
339}
340
341impl FunctionParameter {
342	/// Create a new function parameter
343	///
344	/// # Examples
345	///
346	/// ```rust
347	/// use reinhardt_query::types::function::FunctionParameter;
348	///
349	/// let param = FunctionParameter::new();
350	/// ```
351	pub fn new() -> Self {
352		Self {
353			name: None,
354			param_type: None,
355			mode: None,
356			default_value: None,
357		}
358	}
359
360	/// Set parameter name
361	///
362	/// # Examples
363	///
364	/// ```rust
365	/// use reinhardt_query::types::function::FunctionParameter;
366	///
367	/// let param = FunctionParameter::new()
368	///     .name("my_param");
369	/// ```
370	pub fn name<N: IntoIden>(mut self, name: N) -> Self {
371		self.name = Some(name.into_iden());
372		self
373	}
374
375	/// Set parameter type
376	///
377	/// # Examples
378	///
379	/// ```rust
380	/// use reinhardt_query::types::function::FunctionParameter;
381	///
382	/// let param = FunctionParameter::new()
383	///     .param_type("integer");
384	/// ```
385	pub fn param_type<T: Into<String>>(mut self, param_type: T) -> Self {
386		self.param_type = Some(param_type.into());
387		self
388	}
389
390	/// Set parameter mode
391	///
392	/// # Examples
393	///
394	/// ```rust
395	/// use reinhardt_query::types::function::{FunctionParameter, ParameterMode};
396	///
397	/// let param = FunctionParameter::new()
398	///     .mode(ParameterMode::InOut);
399	/// ```
400	pub fn mode(mut self, mode: ParameterMode) -> Self {
401		self.mode = Some(mode);
402		self
403	}
404
405	/// Set parameter default value
406	///
407	/// # Examples
408	///
409	/// ```rust
410	/// use reinhardt_query::types::function::FunctionParameter;
411	///
412	/// let param = FunctionParameter::new()
413	///     .default_value("42");
414	/// ```
415	pub fn default_value<V: Into<String>>(mut self, value: V) -> Self {
416		self.default_value = Some(value.into());
417		self
418	}
419}
420
421impl Default for FunctionParameter {
422	fn default() -> Self {
423		Self::new()
424	}
425}
426
427#[cfg(test)]
428mod tests {
429	use super::*;
430	use rstest::*;
431
432	// FunctionDef tests
433	#[rstest]
434	fn test_function_def_basic() {
435		let func = FunctionDef::new("my_func");
436		assert_eq!(func.name.to_string(), "my_func");
437		assert!(!func.or_replace);
438		assert!(func.parameters.is_empty());
439		assert!(func.returns.is_none());
440		assert!(func.language.is_none());
441		assert!(func.behavior.is_none());
442		assert!(func.security.is_none());
443		assert!(func.body.is_none());
444	}
445
446	#[rstest]
447	fn test_function_def_or_replace() {
448		let func = FunctionDef::new("my_func").or_replace(true);
449		assert_eq!(func.name.to_string(), "my_func");
450		assert!(func.or_replace);
451	}
452
453	#[rstest]
454	fn test_function_def_add_parameter() {
455		let func = FunctionDef::new("my_func").add_parameter("param1", "integer");
456		assert_eq!(func.parameters.len(), 1);
457		assert_eq!(
458			func.parameters[0].name.as_ref().unwrap().to_string(),
459			"param1"
460		);
461		assert_eq!(func.parameters[0].param_type.as_ref().unwrap(), "integer");
462	}
463
464	#[rstest]
465	fn test_function_def_multiple_parameters() {
466		let func = FunctionDef::new("my_func")
467			.add_parameter("param1", "integer")
468			.add_parameter("param2", "text");
469		assert_eq!(func.parameters.len(), 2);
470		assert_eq!(
471			func.parameters[0].name.as_ref().unwrap().to_string(),
472			"param1"
473		);
474		assert_eq!(func.parameters[0].param_type.as_ref().unwrap(), "integer");
475		assert_eq!(
476			func.parameters[1].name.as_ref().unwrap().to_string(),
477			"param2"
478		);
479		assert_eq!(func.parameters[1].param_type.as_ref().unwrap(), "text");
480	}
481
482	#[rstest]
483	fn test_function_def_returns() {
484		let func = FunctionDef::new("my_func").returns("integer");
485		assert_eq!(func.returns.as_ref().unwrap(), "integer");
486	}
487
488	#[rstest]
489	fn test_function_def_language_sql() {
490		let func = FunctionDef::new("my_func").language(FunctionLanguage::Sql);
491		assert_eq!(func.language, Some(FunctionLanguage::Sql));
492	}
493
494	#[rstest]
495	fn test_function_def_language_plpgsql() {
496		let func = FunctionDef::new("my_func").language(FunctionLanguage::PlPgSql);
497		assert_eq!(func.language, Some(FunctionLanguage::PlPgSql));
498	}
499
500	#[rstest]
501	fn test_function_def_behavior_immutable() {
502		let func = FunctionDef::new("my_func").behavior(FunctionBehavior::Immutable);
503		assert_eq!(func.behavior, Some(FunctionBehavior::Immutable));
504	}
505
506	#[rstest]
507	fn test_function_def_behavior_stable() {
508		let func = FunctionDef::new("my_func").behavior(FunctionBehavior::Stable);
509		assert_eq!(func.behavior, Some(FunctionBehavior::Stable));
510	}
511
512	#[rstest]
513	fn test_function_def_behavior_volatile() {
514		let func = FunctionDef::new("my_func").behavior(FunctionBehavior::Volatile);
515		assert_eq!(func.behavior, Some(FunctionBehavior::Volatile));
516	}
517
518	#[rstest]
519	fn test_function_def_security_definer() {
520		let func = FunctionDef::new("my_func").security(FunctionSecurity::Definer);
521		assert_eq!(func.security, Some(FunctionSecurity::Definer));
522	}
523
524	#[rstest]
525	fn test_function_def_security_invoker() {
526		let func = FunctionDef::new("my_func").security(FunctionSecurity::Invoker);
527		assert_eq!(func.security, Some(FunctionSecurity::Invoker));
528	}
529
530	#[rstest]
531	fn test_function_def_body() {
532		let func = FunctionDef::new("my_func").body("SELECT 1");
533		assert_eq!(func.body.as_ref().unwrap(), "SELECT 1");
534	}
535
536	#[rstest]
537	fn test_function_def_all_options() {
538		let func = FunctionDef::new("my_func")
539			.or_replace(true)
540			.add_parameter("a", "integer")
541			.add_parameter("b", "text")
542			.returns("integer")
543			.language(FunctionLanguage::PlPgSql)
544			.behavior(FunctionBehavior::Immutable)
545			.security(FunctionSecurity::Definer)
546			.body("BEGIN RETURN a + LENGTH(b); END;");
547
548		assert_eq!(func.name.to_string(), "my_func");
549		assert!(func.or_replace);
550		assert_eq!(func.parameters.len(), 2);
551		assert_eq!(func.returns.as_ref().unwrap(), "integer");
552		assert_eq!(func.language, Some(FunctionLanguage::PlPgSql));
553		assert_eq!(func.behavior, Some(FunctionBehavior::Immutable));
554		assert_eq!(func.security, Some(FunctionSecurity::Definer));
555		assert_eq!(
556			func.body.as_ref().unwrap(),
557			"BEGIN RETURN a + LENGTH(b); END;"
558		);
559	}
560
561	// FunctionParameter tests
562	#[rstest]
563	fn test_function_parameter_basic() {
564		let param = FunctionParameter::new();
565		assert!(param.name.is_none());
566		assert!(param.param_type.is_none());
567		assert!(param.mode.is_none());
568		assert!(param.default_value.is_none());
569	}
570
571	#[rstest]
572	fn test_function_parameter_name() {
573		let param = FunctionParameter::new().name("my_param");
574		assert_eq!(param.name.as_ref().unwrap().to_string(), "my_param");
575	}
576
577	#[rstest]
578	fn test_function_parameter_type() {
579		let param = FunctionParameter::new().param_type("integer");
580		assert_eq!(param.param_type.as_ref().unwrap(), "integer");
581	}
582
583	#[rstest]
584	fn test_function_parameter_mode_in() {
585		let param = FunctionParameter::new().mode(ParameterMode::In);
586		assert_eq!(param.mode, Some(ParameterMode::In));
587	}
588
589	#[rstest]
590	fn test_function_parameter_mode_out() {
591		let param = FunctionParameter::new().mode(ParameterMode::Out);
592		assert_eq!(param.mode, Some(ParameterMode::Out));
593	}
594
595	#[rstest]
596	fn test_function_parameter_mode_inout() {
597		let param = FunctionParameter::new().mode(ParameterMode::InOut);
598		assert_eq!(param.mode, Some(ParameterMode::InOut));
599	}
600
601	#[rstest]
602	fn test_function_parameter_mode_variadic() {
603		let param = FunctionParameter::new().mode(ParameterMode::Variadic);
604		assert_eq!(param.mode, Some(ParameterMode::Variadic));
605	}
606
607	#[rstest]
608	fn test_function_parameter_default_value() {
609		let param = FunctionParameter::new().default_value("42");
610		assert_eq!(param.default_value.as_ref().unwrap(), "42");
611	}
612
613	#[rstest]
614	fn test_function_parameter_all_options() {
615		let param = FunctionParameter::new()
616			.name("my_param")
617			.param_type("integer")
618			.mode(ParameterMode::InOut)
619			.default_value("42");
620
621		assert_eq!(param.name.as_ref().unwrap().to_string(), "my_param");
622		assert_eq!(param.param_type.as_ref().unwrap(), "integer");
623		assert_eq!(param.mode, Some(ParameterMode::InOut));
624		assert_eq!(param.default_value.as_ref().unwrap(), "42");
625	}
626
627	#[rstest]
628	fn test_function_def_add_parameter_spec() {
629		let param = FunctionParameter::new()
630			.name("my_param")
631			.param_type("integer")
632			.mode(ParameterMode::Out);
633
634		let func = FunctionDef::new("my_func").add_parameter_spec(param);
635
636		assert_eq!(func.parameters.len(), 1);
637		assert_eq!(
638			func.parameters[0].name.as_ref().unwrap().to_string(),
639			"my_param"
640		);
641		assert_eq!(func.parameters[0].param_type.as_ref().unwrap(), "integer");
642		assert_eq!(func.parameters[0].mode, Some(ParameterMode::Out));
643	}
644
645	// FunctionLanguage tests
646	#[rstest]
647	fn test_function_language_custom() {
648		let lang = FunctionLanguage::Custom("plpython3u".to_string());
649		assert_eq!(lang, FunctionLanguage::Custom("plpython3u".to_string()));
650	}
651}