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}