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 {
69 name: param.name.clone(),
70 data_type: param.data_type.clone(),
71 })
72 .collect();
73
74 // Convert AST body to catalog body
75 let catalog_body = match &stmt.body {
76 vibesql_ast::ProcedureBody::BeginEnd(_) => {
77 // For now, store as RawSql. Full execution support comes later.
78 FunctionBody::RawSql(format!("{:?}", stmt.body))
79 }
80 vibesql_ast::ProcedureBody::RawSql(sql) => FunctionBody::RawSql(sql.clone()),
81 };
82
83 // Convert characteristics (Phase 6)
84 let deterministic = stmt.deterministic.unwrap_or(false);
85 let sql_security = stmt.sql_security.as_ref().map(|sec| match sec {
86 vibesql_ast::SqlSecurity::Definer => SqlSecurity::Definer,
87 vibesql_ast::SqlSecurity::Invoker => SqlSecurity::Invoker,
88 }).unwrap_or(SqlSecurity::Definer);
89
90 let function = if stmt.deterministic.is_some() || stmt.sql_security.is_some() || stmt.comment.is_some() || stmt.language.is_some() {
91 Function::with_characteristics(
92 stmt.function_name.clone(),
93 db.catalog.get_current_schema().to_string(),
94 catalog_params,
95 stmt.return_type.clone(),
96 catalog_body,
97 deterministic,
98 sql_security,
99 stmt.comment.clone(),
100 stmt.language.clone().unwrap_or_else(|| "SQL".to_string()),
101 )
102 } else {
103 Function::new(
104 stmt.function_name.clone(),
105 db.catalog.get_current_schema().to_string(),
106 catalog_params,
107 stmt.return_type.clone(),
108 catalog_body,
109 )
110 };
111
112 db.catalog.create_function_with_characteristics(function)?;
113 Ok(())
114}
115
116/// Execute DROP FUNCTION statement (SQL:1999 Feature P001)
117pub fn execute_drop_function(
118 stmt: &DropFunctionStmt,
119 db: &mut Database,
120) -> Result<(), ExecutorError> {
121 // Check if function exists
122 let function_exists = db.catalog.function_exists(&stmt.function_name);
123
124 // If IF EXISTS is specified and function doesn't exist, succeed silently
125 if stmt.if_exists && !function_exists {
126 return Ok(());
127 }
128
129 db.catalog.drop_function(&stmt.function_name)?;
130 Ok(())
131}