vibesql_executor/advanced_objects/
views.rs

1//! Executor for VIEW objects (SQL:1999)
2
3use vibesql_ast::*;
4use vibesql_catalog::ViewDropBehavior;
5use vibesql_storage::Database;
6
7use crate::errors::ExecutorError;
8
9/// Execute CREATE VIEW statement
10pub fn execute_create_view(stmt: &CreateViewStmt, db: &mut Database) -> Result<(), ExecutorError> {
11    use vibesql_catalog::ViewDefinition;
12
13    // Check if view already exists
14    let view_exists = db.catalog.get_view(&stmt.view_name).is_some();
15
16    // If IF NOT EXISTS and view already exists, just return success
17    if stmt.if_not_exists && view_exists {
18        return Ok(());
19    }
20
21    // If no explicit column list is provided, derive column names from the query
22    // This ensures views with SELECT * preserve original column names
23    // Use simple column names (without table prefix) for view schema compatibility
24    let columns = if stmt.columns.is_none() {
25        // Execute the query once to derive column names
26        use crate::select::SelectExecutor;
27        let executor = SelectExecutor::new(db);
28        let result = executor.execute_with_simple_columns(&stmt.query)?;
29        Some(result.columns)
30    } else {
31        stmt.columns.clone()
32    };
33
34    let view = if let Some(ref sql) = stmt.sql_definition {
35        ViewDefinition::new_with_sql(
36            stmt.view_name.clone(),
37            columns,
38            (*stmt.query).clone(),
39            stmt.with_check_option,
40            sql.clone(),
41        )
42    } else {
43        ViewDefinition::new(
44            stmt.view_name.clone(),
45            columns,
46            (*stmt.query).clone(),
47            stmt.with_check_option,
48        )
49    };
50
51    if stmt.or_replace || (stmt.if_not_exists && !view_exists) {
52        // For OR REPLACE, drop the view if it exists, then CREATE
53        if view_exists && stmt.or_replace {
54            let _ = db.catalog.drop_view(&stmt.view_name, false);
55        }
56        db.catalog.create_view(view)?;
57    } else {
58        // Regular CREATE VIEW (will fail if view already exists)
59        db.catalog.create_view(view)?;
60    }
61    Ok(())
62}
63
64/// Execute DROP VIEW statement
65pub fn execute_drop_view(stmt: &DropViewStmt, db: &mut Database) -> Result<(), ExecutorError> {
66    // Check if view exists
67    let view_exists = db.catalog.get_view(&stmt.view_name).is_some();
68
69    // If IF EXISTS is specified and view doesn't exist, succeed silently
70    if stmt.if_exists && !view_exists {
71        return Ok(());
72    }
73
74    // Determine drop behavior:
75    // - CASCADE: drop dependent views recursively
76    // - RESTRICT (explicit): fail if dependents exist
77    // - Neither: SQLite-compatible behavior (just drop, ignore dependents)
78    let drop_behavior = if stmt.cascade {
79        ViewDropBehavior::Cascade
80    } else if stmt.restrict {
81        ViewDropBehavior::Restrict
82    } else {
83        ViewDropBehavior::Silent // SQLite-compatible: allow dropping even with dependents
84    };
85
86    db.catalog.drop_view_with_behavior(&stmt.view_name, drop_behavior)?;
87    Ok(())
88}