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