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