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