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}