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}