1use vibesql_ast::DropTableStmt;
4use vibesql_storage::Database;
5
6use crate::{errors::ExecutorError, privilege_checker::PrivilegeChecker};
7
8pub struct DropTableExecutor;
10
11impl DropTableExecutor {
12 pub fn execute(stmt: &DropTableStmt, database: &mut Database) -> Result<String, ExecutorError> {
54 let table_exists = database.catalog.table_exists(&stmt.table_name);
56
57 if stmt.if_exists && !table_exists {
59 return Ok(format!("Table '{}' does not exist (IF EXISTS specified)", stmt.table_name));
60 }
61
62 if !table_exists {
64 return Err(ExecutorError::TableNotFound(stmt.table_name.clone()));
65 }
66
67 PrivilegeChecker::check_drop(database, &stmt.table_name)?;
69
70 let dropped_indexes = database.catalog.drop_table_indexes(&stmt.table_name);
72 let index_count = dropped_indexes.len();
73
74 for index in &dropped_indexes {
76 let _ = database.drop_index(&index.name);
78 let _ = database.drop_spatial_index(&index.name);
80 }
81
82 database
84 .drop_table(&stmt.table_name)
85 .map_err(|e| ExecutorError::StorageError(e.to_string()))?;
86
87 if index_count > 0 {
89 Ok(format!(
90 "Table '{}' and {} associated index(es) dropped successfully",
91 stmt.table_name, index_count
92 ))
93 } else {
94 Ok(format!("Table '{}' dropped successfully", stmt.table_name))
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use vibesql_ast::{ColumnDef, CreateTableStmt};
102 use vibesql_types::DataType;
103
104 use super::*;
105 use crate::CreateTableExecutor;
106
107 #[test]
108 fn test_drop_existing_table() {
109 let mut db = Database::new();
110
111 let create_stmt = CreateTableStmt {
113 if_not_exists: false,
114 table_name: "users".to_string(),
115 columns: vec![ColumnDef {
116 name: "id".to_string(),
117 data_type: DataType::Integer,
118 nullable: false,
119 constraints: vec![],
120 default_value: None,
121 comment: None,
122 }],
123 table_constraints: vec![],
124 table_options: vec![],
125 };
126 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
127 assert!(db.catalog.table_exists("users"));
128
129 let drop_stmt = DropTableStmt { table_name: "users".to_string(), if_exists: false };
131
132 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
133 assert!(result.is_ok());
134 assert_eq!(result.unwrap(), "Table 'users' dropped successfully");
135
136 assert!(!db.catalog.table_exists("users"));
138 assert!(db.get_table("users").is_none());
139 }
140
141 #[test]
142 fn test_drop_nonexistent_table_without_if_exists() {
143 let mut db = Database::new();
144
145 let drop_stmt = DropTableStmt { table_name: "nonexistent".to_string(), if_exists: false };
146
147 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
148 assert!(result.is_err());
149 assert!(matches!(result, Err(ExecutorError::TableNotFound(_))));
150 }
151
152 #[test]
153 fn test_drop_nonexistent_table_with_if_exists() {
154 let mut db = Database::new();
155
156 let drop_stmt = DropTableStmt { table_name: "nonexistent".to_string(), if_exists: true };
157
158 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
159 assert!(result.is_ok());
160 assert_eq!(result.unwrap(), "Table 'nonexistent' does not exist (IF EXISTS specified)");
161 }
162
163 #[test]
164 fn test_drop_existing_table_with_if_exists() {
165 let mut db = Database::new();
166
167 let create_stmt = CreateTableStmt {
169 if_not_exists: false,
170 table_name: "products".to_string(),
171 columns: vec![ColumnDef {
172 name: "id".to_string(),
173 data_type: DataType::Integer,
174 nullable: false,
175 constraints: vec![],
176 default_value: None,
177 comment: None,
178 }],
179 table_constraints: vec![],
180 table_options: vec![],
181 };
182 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
183
184 let drop_stmt = DropTableStmt { table_name: "products".to_string(), if_exists: true };
186
187 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
188 assert!(result.is_ok());
189 assert_eq!(result.unwrap(), "Table 'products' dropped successfully");
190
191 assert!(!db.catalog.table_exists("products"));
193 }
194
195 #[test]
196 fn test_drop_table_with_data() {
197 let mut db = Database::new();
198
199 let create_stmt = CreateTableStmt {
201 if_not_exists: false,
202 table_name: "customers".to_string(),
203 columns: vec![
204 ColumnDef {
205 name: "id".to_string(),
206 data_type: DataType::Integer,
207 nullable: false,
208 constraints: vec![],
209 default_value: None,
210 comment: None,
211 },
212 ColumnDef {
213 name: "name".to_string(),
214 data_type: DataType::Varchar { max_length: Some(100) },
215 nullable: false,
216 constraints: vec![],
217 default_value: None,
218 comment: None,
219 },
220 ],
221 table_constraints: vec![],
222 table_options: vec![],
223 };
224 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
225
226 use vibesql_storage::Row;
228 use vibesql_types::SqlValue;
229 let row = Row::new(vec![SqlValue::Integer(1), SqlValue::Varchar(arcstr::ArcStr::from("Alice"))]);
230 db.insert_row("customers", row).unwrap();
231
232 assert_eq!(db.get_table("customers").unwrap().row_count(), 1);
234
235 let drop_stmt = DropTableStmt { table_name: "customers".to_string(), if_exists: false };
237
238 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
239 assert!(result.is_ok());
240
241 assert!(!db.catalog.table_exists("customers"));
243 assert!(db.get_table("customers").is_none());
244 }
245
246 #[test]
247 fn test_drop_and_recreate_table() {
248 let mut db = Database::new();
249
250 let create_stmt = CreateTableStmt {
252 if_not_exists: false,
253 table_name: "temp".to_string(),
254 columns: vec![ColumnDef {
255 name: "id".to_string(),
256 data_type: DataType::Integer,
257 nullable: false,
258 constraints: vec![],
259 default_value: None,
260 comment: None,
261 }],
262 table_constraints: vec![],
263 table_options: vec![],
264 };
265 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
266
267 let drop_stmt = DropTableStmt { table_name: "temp".to_string(), if_exists: false };
269 DropTableExecutor::execute(&drop_stmt, &mut db).unwrap();
270
271 let result = CreateTableExecutor::execute(&create_stmt, &mut db);
273 assert!(result.is_ok());
274 assert!(db.catalog.table_exists("temp"));
275 }
276
277 #[test]
278 fn test_drop_multiple_tables() {
279 let mut db = Database::new();
280
281 for name in &["table1", "table2", "table3"] {
283 let create_stmt = CreateTableStmt {
284 if_not_exists: false,
285 table_name: name.to_string(),
286 columns: vec![ColumnDef {
287 name: "id".to_string(),
288 data_type: DataType::Integer,
289 nullable: false,
290 constraints: vec![],
291 default_value: None,
292 comment: None,
293 }],
294 table_constraints: vec![],
295 table_options: vec![],
296 };
297 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
298 }
299
300 assert_eq!(db.list_tables().len(), 3);
301
302 for name in &["table1", "table2", "table3"] {
304 let drop_stmt = DropTableStmt { table_name: name.to_string(), if_exists: false };
305 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
306 assert!(result.is_ok());
307 }
308
309 assert_eq!(db.list_tables().len(), 0);
310 }
311
312 #[test]
313 fn test_drop_table_case_sensitivity() {
314 let mut db = Database::new();
315
316 let create_stmt = CreateTableStmt {
318 if_not_exists: false,
319 table_name: "MyTable".to_string(),
320 columns: vec![ColumnDef {
321 name: "id".to_string(),
322 data_type: DataType::Integer,
323 nullable: false,
324 constraints: vec![],
325 default_value: None,
326 comment: None,
327 }],
328 table_constraints: vec![],
329 table_options: vec![],
330 };
331 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
332
333 let drop_stmt = DropTableStmt { table_name: "MyTable".to_string(), if_exists: false };
335 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
336 assert!(result.is_ok());
337 }
338
339 #[test]
340 fn test_drop_table_cascades_to_indexes() {
341 use crate::CreateIndexExecutor;
342 use vibesql_ast::{CreateIndexStmt, IndexColumn, OrderDirection};
343
344 let mut db = Database::new();
345
346 let create_stmt = CreateTableStmt {
348 if_not_exists: false,
349 table_name: "users".to_string(),
350 columns: vec![
351 ColumnDef {
352 name: "id".to_string(),
353 data_type: DataType::Integer,
354 nullable: false,
355 constraints: vec![],
356 default_value: None,
357 comment: None,
358 },
359 ColumnDef {
360 name: "email".to_string(),
361 data_type: DataType::Varchar { max_length: Some(255) },
362 nullable: false,
363 constraints: vec![],
364 default_value: None,
365 comment: None,
366 },
367 ],
368 table_constraints: vec![],
369 table_options: vec![],
370 };
371 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
372
373 let index1_stmt = CreateIndexStmt {
375 index_name: "idx_users_email".to_string(),
376 if_not_exists: false,
377 table_name: "users".to_string(),
378 index_type: vibesql_ast::IndexType::BTree { unique: false },
379 columns: vec![IndexColumn {
380 column_name: "email".to_string(),
381 prefix_length: None,
382 direction: OrderDirection::Asc,
383 }],
384 };
385 CreateIndexExecutor::execute(&index1_stmt, &mut db).unwrap();
386
387 let index2_stmt = CreateIndexStmt {
388 index_name: "idx_users_id".to_string(),
389 if_not_exists: false,
390 table_name: "users".to_string(),
391 index_type: vibesql_ast::IndexType::BTree { unique: false },
392 columns: vec![IndexColumn {
393 column_name: "id".to_string(),
394 prefix_length: None,
395 direction: OrderDirection::Asc,
396 }],
397 };
398 CreateIndexExecutor::execute(&index2_stmt, &mut db).unwrap();
399
400 assert!(db.index_exists("idx_users_email"));
402 assert!(db.index_exists("idx_users_id"));
403
404 let drop_stmt = DropTableStmt { table_name: "users".to_string(), if_exists: false };
406 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
407 assert!(result.is_ok());
408
409 assert!(!db.catalog.table_exists("users"));
411
412 assert!(!db.index_exists("idx_users_email"));
414 assert!(!db.index_exists("idx_users_id"));
415 }
416
417 #[test]
418 fn test_drop_and_recreate_table_with_same_index_names() {
419 use crate::CreateIndexExecutor;
420 use vibesql_ast::{CreateIndexStmt, IndexColumn, OrderDirection};
421
422 let mut db = Database::new();
423
424 let create_stmt = CreateTableStmt {
426 if_not_exists: false,
427 table_name: "products".to_string(),
428 columns: vec![
429 ColumnDef {
430 name: "id".to_string(),
431 data_type: DataType::Integer,
432 nullable: false,
433 constraints: vec![],
434 default_value: None,
435 comment: None,
436 },
437 ColumnDef {
438 name: "name".to_string(),
439 data_type: DataType::Varchar { max_length: Some(100) },
440 nullable: false,
441 constraints: vec![],
442 default_value: None,
443 comment: None,
444 },
445 ],
446 table_constraints: vec![],
447 table_options: vec![],
448 };
449 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
450
451 let index_stmt = CreateIndexStmt {
453 index_name: "idx_products_name".to_string(),
454 if_not_exists: false,
455 table_name: "products".to_string(),
456 index_type: vibesql_ast::IndexType::BTree { unique: false },
457 columns: vec![IndexColumn {
458 column_name: "name".to_string(),
459 prefix_length: None,
460 direction: OrderDirection::Asc,
461 }],
462 };
463 CreateIndexExecutor::execute(&index_stmt, &mut db).unwrap();
464
465 assert!(db.index_exists("idx_products_name"));
467
468 let drop_stmt = DropTableStmt { table_name: "products".to_string(), if_exists: false };
470 DropTableExecutor::execute(&drop_stmt, &mut db).unwrap();
471
472 assert!(!db.catalog.table_exists("products"));
474 assert!(!db.index_exists("idx_products_name"));
475
476 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
478
479 let result = CreateIndexExecutor::execute(&index_stmt, &mut db);
481 assert!(result.is_ok(), "Should be able to recreate index with same name after table drop");
482 assert!(db.index_exists("idx_products_name"));
483 }
484
485 #[test]
486 fn test_drop_table_without_indexes() {
487 let mut db = Database::new();
488
489 let create_stmt = CreateTableStmt {
491 if_not_exists: false,
492 table_name: "simple_table".to_string(),
493 columns: vec![ColumnDef {
494 name: "id".to_string(),
495 data_type: DataType::Integer,
496 nullable: false,
497 constraints: vec![],
498 default_value: None,
499 comment: None,
500 }],
501 table_constraints: vec![],
502 table_options: vec![],
503 };
504 CreateTableExecutor::execute(&create_stmt, &mut db).unwrap();
505
506 let drop_stmt = DropTableStmt { table_name: "simple_table".to_string(), if_exists: false };
508 let result = DropTableExecutor::execute(&drop_stmt, &mut db);
509 assert!(result.is_ok(), "Dropping table without indexes should still work");
510 assert!(!db.catalog.table_exists("simple_table"));
511 }
512}