vibesql_executor/
view_ddl.rs

1//! View DDL executor
2
3use vibesql_ast::{CreateViewStmt, DropViewStmt};
4use vibesql_catalog::{ViewDefinition, ViewDropBehavior};
5use vibesql_storage::Database;
6
7use crate::errors::ExecutorError;
8
9/// Executor for view DDL statements
10pub struct ViewExecutor;
11
12impl ViewExecutor {
13    /// Execute CREATE VIEW, CREATE OR REPLACE VIEW, or CREATE VIEW IF NOT EXISTS
14    pub fn execute_create_view(
15        stmt: &CreateViewStmt,
16        database: &mut Database,
17    ) -> Result<String, ExecutorError> {
18        let view_exists = database.catalog.get_view(&stmt.view_name).is_some();
19
20        // If IF NOT EXISTS and view already exists, just return success
21        if stmt.if_not_exists && view_exists {
22            return Ok(format!("View '{}' already exists (skipped)", stmt.view_name));
23        }
24
25        // If OR REPLACE, drop the view first if it exists
26        if stmt.or_replace && view_exists {
27            // Drop the existing view (no cascade needed for OR REPLACE)
28            database.catalog.drop_view(&stmt.view_name, false).map_err(|e| {
29                ExecutorError::StorageError(format!("Failed to drop existing view: {:?}", e))
30            })?;
31        }
32
33        // Create the view definition
34        let view_def = if let Some(ref sql) = stmt.sql_definition {
35            ViewDefinition::new_with_sql(
36                stmt.view_name.clone(),
37                stmt.columns.clone(),
38                *stmt.query.clone(),
39                stmt.with_check_option,
40                sql.clone(),
41            )
42        } else {
43            ViewDefinition::new(
44                stmt.view_name.clone(),
45                stmt.columns.clone(),
46                *stmt.query.clone(),
47                stmt.with_check_option,
48            )
49        };
50
51        // Add to catalog
52        database
53            .catalog
54            .create_view(view_def)
55            .map_err(|e| ExecutorError::StorageError(format!("Failed to create view: {:?}", e)))?;
56
57        if stmt.or_replace {
58            Ok(format!("View '{}' created or replaced", stmt.view_name))
59        } else {
60            Ok(format!("View '{}' created", stmt.view_name))
61        }
62    }
63
64    /// Execute DROP VIEW
65    pub fn execute_drop_view(
66        stmt: &DropViewStmt,
67        database: &mut Database,
68    ) -> Result<String, ExecutorError> {
69        // Determine drop behavior:
70        // - CASCADE: drop dependent views recursively
71        // - RESTRICT (explicit): fail if dependents exist
72        // - Neither: SQLite-compatible behavior (just drop, ignore dependents)
73        let drop_behavior = if stmt.cascade {
74            ViewDropBehavior::Cascade
75        } else if stmt.restrict {
76            ViewDropBehavior::Restrict
77        } else {
78            ViewDropBehavior::Silent // SQLite-compatible: allow dropping even with dependents
79        };
80
81        // Drop the view
82        let result = database.catalog.drop_view_with_behavior(&stmt.view_name, drop_behavior);
83
84        match result {
85            Ok(()) => Ok(format!("View '{}' dropped", stmt.view_name)),
86            Err(e) => {
87                // If IF EXISTS and view doesn't exist, that's OK
88                if stmt.if_exists
89                    && matches!(e, vibesql_catalog::errors::CatalogError::ViewNotFound(_))
90                {
91                    Ok(format!("View '{}' does not exist (skipped)", stmt.view_name))
92                } else {
93                    Err(ExecutorError::StorageError(format!("Failed to drop view: {:?}", e)))
94                }
95            }
96        }
97    }
98}