vibesql_executor/advanced_objects/functions.rs
1//! Executor for FUNCTION objects (SQL:1999 Feature P001)
2
3use vibesql_ast::*;
4use vibesql_storage::Database;
5
6use crate::errors::ExecutorError;
7
8/// Execute CREATE FUNCTION statement (SQL:1999 Feature P001)
9///
10/// Creates a user-defined function in the database catalog with optional characteristics.
11///
12/// Functions differ from procedures:
13/// - Functions RETURN a value and can be used in expressions
14/// - Functions are read-only (cannot modify database tables)
15/// - Functions only support IN parameters (no OUT/INOUT)
16/// - Functions have recursion depth limiting (max 100 levels)
17///
18/// # Characteristics (Phase 6)
19///
20/// - **DETERMINISTIC**: Indicates same input always produces same output
21/// - **SQL SECURITY**: DEFINER (default) or INVOKER
22/// - **COMMENT**: Documentation string
23/// - **LANGUAGE**: SQL (only supported language)
24///
25/// # Examples
26///
27/// Simple function with RETURN expression:
28/// ```sql
29/// CREATE FUNCTION add_ten(x INT) RETURNS INT
30/// DETERMINISTIC
31/// RETURN x + 10;
32/// ```
33///
34/// Complex function with BEGIN...END body:
35/// ```sql
36/// CREATE FUNCTION factorial(n INT) RETURNS INT
37/// DETERMINISTIC
38/// COMMENT 'Calculate factorial of n'
39/// BEGIN
40/// DECLARE result INT DEFAULT 1;
41/// DECLARE i INT DEFAULT 2;
42///
43/// WHILE i <= n DO
44/// SET result = result * i;
45/// SET i = i + 1;
46/// END WHILE;
47///
48/// RETURN result;
49/// END;
50/// ```
51///
52/// # Errors
53///
54/// Returns error if:
55/// - Function name already exists
56/// - Invalid parameter types or return type
57/// - Body parsing fails
58pub fn execute_create_function(
59 stmt: &CreateFunctionStmt,
60 db: &mut Database,
61) -> Result<(), ExecutorError> {
62 use vibesql_catalog::{Function, FunctionBody, FunctionParam, SqlSecurity};
63
64 // Convert AST parameters to catalog parameters
65 let catalog_params = stmt
66 .parameters
67 .iter()
68 .map(|param| FunctionParam { name: param.name.clone(), data_type: param.data_type.clone() })
69 .collect();
70
71 // Convert AST body to catalog body
72 let catalog_body = match &stmt.body {
73 vibesql_ast::ProcedureBody::BeginEnd(_) => {
74 // For now, store as RawSql. Full execution support comes later.
75 FunctionBody::RawSql(format!("{:?}", stmt.body))
76 }
77 vibesql_ast::ProcedureBody::RawSql(sql) => FunctionBody::RawSql(sql.clone()),
78 };
79
80 // Convert characteristics (Phase 6)
81 let deterministic = stmt.deterministic.unwrap_or(false);
82 let sql_security = stmt
83 .sql_security
84 .as_ref()
85 .map(|sec| match sec {
86 vibesql_ast::SqlSecurity::Definer => SqlSecurity::Definer,
87 vibesql_ast::SqlSecurity::Invoker => SqlSecurity::Invoker,
88 })
89 .unwrap_or(SqlSecurity::Definer);
90
91 let function = if stmt.deterministic.is_some()
92 || stmt.sql_security.is_some()
93 || stmt.comment.is_some()
94 || stmt.language.is_some()
95 {
96 Function::with_characteristics(
97 stmt.function_name.clone(),
98 db.catalog.get_current_schema().to_string(),
99 catalog_params,
100 stmt.return_type.clone(),
101 catalog_body,
102 deterministic,
103 sql_security,
104 stmt.comment.clone(),
105 stmt.language.clone().unwrap_or_else(|| "SQL".to_string()),
106 )
107 } else {
108 Function::new(
109 stmt.function_name.clone(),
110 db.catalog.get_current_schema().to_string(),
111 catalog_params,
112 stmt.return_type.clone(),
113 catalog_body,
114 )
115 };
116
117 db.catalog.create_function_with_characteristics(function)?;
118 Ok(())
119}
120
121/// Execute DROP FUNCTION statement (SQL:1999 Feature P001)
122pub fn execute_drop_function(
123 stmt: &DropFunctionStmt,
124 db: &mut Database,
125) -> Result<(), ExecutorError> {
126 // Check if function exists
127 let function_exists = db.catalog.function_exists(&stmt.function_name);
128
129 // If IF EXISTS is specified and function doesn't exist, succeed silently
130 if stmt.if_exists && !function_exists {
131 return Ok(());
132 }
133
134 db.catalog.drop_function(&stmt.function_name)?;
135 Ok(())
136}