vibesql_executor/
grant.rs

1//! GRANT statement executor
2
3use vibesql_ast::*;
4use vibesql_catalog::PrivilegeGrant;
5use vibesql_storage::Database;
6
7use crate::errors::ExecutorError;
8
9/// Executor for GRANT statements
10pub struct GrantExecutor;
11
12impl GrantExecutor {
13    /// Execute GRANT statement
14    ///
15    /// Phase 2.6: Supports validation and conformance
16    pub fn execute_grant(
17        stmt: &GrantStmt,
18        database: &mut Database,
19    ) -> Result<String, ExecutorError> {
20        // Determine actual object type (may differ from statement if SQL:1999 uses TABLE for
21        // schemas)
22        let mut actual_object_type = stmt.object_type.clone();
23
24        // Validate object exists based on object type
25        match stmt.object_type {
26            ObjectType::Table => {
27                // Special case: USAGE/EXECUTE are schema/routine privileges, not table privileges
28                // If granting USAGE/EXECUTE on a "TABLE", check if it's actually a schema first
29                // This handles SQL:1999 tests that use "ON TABLE" for schema objects
30                let is_schema_privilege = stmt
31                    .privileges
32                    .iter()
33                    .any(|p| matches!(p, PrivilegeType::Usage | PrivilegeType::Execute));
34
35                if is_schema_privilege && database.catalog.schema_exists(&stmt.object_name) {
36                    // Object is actually a schema, update the actual type
37                    actual_object_type = ObjectType::Schema;
38                } else if !database.catalog.table_exists(&stmt.object_name) {
39                    return Err(ExecutorError::TableNotFound(stmt.object_name.clone()));
40                }
41            }
42            ObjectType::Schema => {
43                if !database.catalog.schema_exists(&stmt.object_name) {
44                    return Err(ExecutorError::SchemaNotFound(stmt.object_name.clone()));
45                }
46            }
47            // SQL:1999 Feature F031-03: Domain privileges
48            // Accept without validation - full domain implementation is future work
49            ObjectType::Domain => {
50                // No validation - domains track privileges for conformance
51            }
52            // SQL:1999 Feature F031-06: Collation privileges
53            // Accept without validation - full collation implementation is future work
54            ObjectType::Collation => {
55                // No validation - collations track privileges for conformance
56            }
57            // SQL:1999 Feature F031-08: Character set privileges
58            // Accept without validation - full character set implementation is future work
59            ObjectType::CharacterSet => {
60                // No validation - character sets track privileges for conformance
61            }
62            // SQL:1999 Feature F031-09: Translation privileges
63            // Accept without validation - full translation implementation is future work
64            ObjectType::Translation => {
65                // No validation - translations track privileges for conformance
66            }
67            // SQL:1999 Feature F031-10: Type privileges
68            // Accept without validation - full UDT implementation is future work
69            ObjectType::Type => {
70                // No validation - user-defined types track privileges for conformance
71            }
72            // SQL:1999 Feature F031-11: Sequence privileges
73            // Accept without validation - full sequence implementation is future work
74            ObjectType::Sequence => {
75                // No validation - sequences track privileges for conformance
76            }
77            // Functions (SQL:1999 Feature P001)
78            ObjectType::Function | ObjectType::SpecificFunction => {
79                // Create stub if it doesn't exist (for privilege tracking)
80                if !database.catalog.function_exists(&stmt.object_name) {
81                    database
82                        .catalog
83                        .create_function_stub(
84                            stmt.object_name.clone(),
85                            database.catalog.get_current_schema().to_string(),
86                        )
87                        .map_err(|e| ExecutorError::Other(e.to_string()))?;
88                }
89            }
90            // Procedures (SQL:1999 Feature P001)
91            ObjectType::Procedure | ObjectType::SpecificProcedure => {
92                // Create stub if it doesn't exist (for privilege tracking)
93                if !database.catalog.procedure_exists(&stmt.object_name) {
94                    database
95                        .catalog
96                        .create_procedure_stub(
97                            stmt.object_name.clone(),
98                            database.catalog.get_current_schema().to_string(),
99                        )
100                        .map_err(|e| ExecutorError::Other(e.to_string()))?;
101                }
102            }
103            // Routine (generic term for function or procedure)
104            ObjectType::Routine | ObjectType::SpecificRoutine => {
105                // Try function first, create if neither exists
106                if !database.catalog.function_exists(&stmt.object_name)
107                    && !database.catalog.procedure_exists(&stmt.object_name)
108                {
109                    database
110                        .catalog
111                        .create_function_stub(
112                            stmt.object_name.clone(),
113                            database.catalog.get_current_schema().to_string(),
114                        )
115                        .map_err(|e| ExecutorError::Other(e.to_string()))?;
116                }
117            }
118            // Methods (SQL:1999 Feature S091) - OOP SQL
119            // For now, accept without validation - full UDT implementation is future work
120            ObjectType::Method
121            | ObjectType::ConstructorMethod
122            | ObjectType::StaticMethod
123            | ObjectType::InstanceMethod
124            | ObjectType::SpecificMethod
125            | ObjectType::SpecificConstructorMethod
126            | ObjectType::SpecificStaticMethod
127            | ObjectType::SpecificInstanceMethod => {
128                // No validation - methods belong to UDTs which aren't fully implemented yet
129                // Privilege tracking works by object name alone
130            }
131        }
132
133        // Validate all grantees (roles) exist
134        for grantee in &stmt.grantees {
135            if !database.catalog.role_exists(grantee) {
136                return Err(ExecutorError::RoleNotFound(grantee.clone()));
137            }
138        }
139
140        // Expand ALL PRIVILEGES based on actual object type
141        let expanded_privileges = if stmt.privileges.contains(&PrivilegeType::AllPrivileges) {
142            match actual_object_type {
143                ObjectType::Table => vec![
144                    PrivilegeType::Select(None),
145                    PrivilegeType::Insert(None),
146                    PrivilegeType::Update(None),
147                    PrivilegeType::Delete,
148                    PrivilegeType::References(None),
149                ],
150                ObjectType::Schema => vec![PrivilegeType::Usage, PrivilegeType::Create],
151                // USAGE-only objects (domains, collations, character sets, translations, types,
152                // sequences) ALL PRIVILEGES on these objects means USAGE privilege
153                ObjectType::Domain
154                | ObjectType::Collation
155                | ObjectType::CharacterSet
156                | ObjectType::Translation
157                | ObjectType::Type
158                | ObjectType::Sequence => vec![PrivilegeType::Usage],
159                // Callable objects (functions, procedures, routines, methods)
160                // ALL PRIVILEGES on callable objects means EXECUTE privilege
161                ObjectType::Function
162                | ObjectType::Procedure
163                | ObjectType::Routine
164                | ObjectType::Method
165                | ObjectType::ConstructorMethod
166                | ObjectType::StaticMethod
167                | ObjectType::InstanceMethod
168                | ObjectType::SpecificFunction
169                | ObjectType::SpecificProcedure
170                | ObjectType::SpecificRoutine
171                | ObjectType::SpecificMethod
172                | ObjectType::SpecificConstructorMethod
173                | ObjectType::SpecificStaticMethod
174                | ObjectType::SpecificInstanceMethod => vec![PrivilegeType::Execute],
175            }
176        } else {
177            stmt.privileges.clone()
178        };
179
180        // For each grantee and privilege, create a grant
181        for grantee in &stmt.grantees {
182            for privilege in &expanded_privileges {
183                let grant = PrivilegeGrant {
184                    object: stmt.object_name.clone(),
185                    object_type: actual_object_type.clone(),
186                    privilege: privilege.clone(),
187                    grantee: grantee.clone(),
188                    grantor: database.get_current_role(),
189                    with_grant_option: stmt.with_grant_option,
190                };
191                database.catalog.add_grant(grant);
192            }
193        }
194
195        let grantees_str = stmt.grantees.join(", ");
196        let privileges_str =
197            stmt.privileges.iter().map(|p| format!("{:?}", p)).collect::<Vec<_>>().join(", ");
198
199        Ok(format!("Granted {} on {} to {}", privileges_str, stmt.object_name, grantees_str))
200    }
201}