sql_parse/drop.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use alloc::vec::Vec;
14
15use crate::{
16 keywords::Keyword,
17 lexer::Token,
18 parser::{ParseError, Parser},
19 qualified_name::parse_qualified_name,
20 Identifier, QualifiedName, Span, Spanned, Statement,
21};
22
23/// Represent a drop table statement
24/// ```
25/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropTable, Statement, Issues};
26/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
27/// #
28/// let sql = "DROP TABLE `Employees`, `Customers`;";
29/// let mut issues = Issues::new(sql);
30///
31/// let mut stmts = parse_statements(sql, &mut issues, &options);
32///
33/// # assert!(issues.is_ok());
34/// let delete: DropTable = match stmts.pop() {
35/// Some(Statement::DropTable(d)) => d,
36/// _ => panic!("We should get a drop table statement")
37/// };
38///
39/// assert!(delete.tables.get(0).unwrap().identifier.as_str() == "Employees");
40/// ```
41#[derive(Debug, Clone)]
42pub struct DropTable<'a> {
43 /// Span of "DROP"
44 pub drop_span: Span,
45 /// Span of "TEMPORARY" if specified
46 pub temporary: Option<Span>,
47 /// Span of "TABLE"
48 pub table_span: Span,
49 /// Span of "IF EXISTS" if specified
50 pub if_exists: Option<Span>,
51 /// List of tables to drops
52 pub tables: Vec<QualifiedName<'a>>,
53 /// Span of "CASCADE" if specified
54 pub cascade: Option<Span>,
55}
56
57impl<'a> Spanned for DropTable<'a> {
58 fn span(&self) -> Span {
59 self.drop_span
60 .join_span(&self.temporary)
61 .join_span(&self.table_span)
62 .join_span(&self.if_exists)
63 .join_span(&self.tables)
64 }
65}
66
67/// Represent a drop view statement
68/// ```
69/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropView, Statement, Issues};
70/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
71/// #
72/// let sql = "DROP VIEW `Employees`, `Customers`;";
73/// let mut issues = Issues::new(sql);
74/// let mut stmts = parse_statements(sql, &mut issues, &options);
75///
76/// # assert!(issues.is_ok());
77/// let delete: DropView = match stmts.pop() {
78/// Some(Statement::DropView(d)) => d,
79/// _ => panic!("We should get a drop table statement")
80/// };
81///
82/// assert!(delete.views.get(0).unwrap().identifier.as_str() == "Employees");
83/// ```
84#[derive(Debug, Clone)]
85pub struct DropView<'a> {
86 /// Span of "DROP"
87 pub drop_span: Span,
88 /// Span of "TEMPORARY" if specified
89 pub temporary: Option<Span>,
90 /// Span of "VIEW"
91 pub view_span: Span,
92 /// Span of "IF EXISTS"
93 pub if_exists: Option<Span>,
94 /// List of views to drop
95 pub views: Vec<QualifiedName<'a>>,
96}
97
98impl<'a> Spanned for DropView<'a> {
99 fn span(&self) -> Span {
100 self.drop_span
101 .join_span(&self.temporary)
102 .join_span(&self.view_span)
103 .join_span(&self.if_exists)
104 .join_span(&self.views)
105 }
106}
107
108/// Represent a drop database statement
109/// ```
110/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropDatabase, Statement, Issues};
111/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
112/// #
113/// let sql = "DROP DATABASE mydb;";
114/// let mut issues = Issues::new(sql);
115/// let mut stmts = parse_statements(sql, &mut issues, &options);
116///
117/// # assert!(issues.is_ok());
118/// #
119/// let s: DropDatabase = match stmts.pop() {
120/// Some(Statement::DropDatabase(s)) => s,
121/// _ => panic!("We should get a drop database statement")
122/// };
123///
124/// assert!(s.database.as_str() == "mydb");
125/// ```
126#[derive(Debug, Clone)]
127pub struct DropDatabase<'a> {
128 /// Span of "DROP"
129 pub drop_span: Span,
130 /// Span of "DATABASE"
131 pub database_span: Span,
132 /// Span of "IF EXISTS" if specified
133 pub if_exists: Option<Span>,
134 /// Name of database to drop
135 pub database: Identifier<'a>,
136}
137
138impl<'a> Spanned for DropDatabase<'a> {
139 fn span(&self) -> Span {
140 self.drop_span
141 .join_span(&self.database_span)
142 .join_span(&self.if_exists)
143 .join_span(&self.database)
144 }
145}
146
147/// Represent a drop event statement
148/// ```
149/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropEvent, Statement, Issues};
150/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
151/// #
152/// let sql = "DROP EVENT myevent;";
153/// let mut issues = Issues::new(sql);
154/// let mut stmts = parse_statements(sql, &mut issues, &options);
155///
156/// # assert!(issues.is_ok());
157/// let s: DropEvent = match stmts.pop() {
158/// Some(Statement::DropEvent(s)) => s,
159/// _ => panic!("We should get a drop event statement")
160/// };
161///
162/// assert!(s.event.identifier.as_str() == "myevent");
163/// ```
164#[derive(Debug, Clone)]
165pub struct DropEvent<'a> {
166 /// Span of "DROP"
167 pub drop_span: Span,
168 /// Span of "EVENT"
169 pub event_span: Span,
170 /// Span of "IF EXISTS" if specified
171 pub if_exists: Option<Span>,
172 /// Event to drop
173 pub event: QualifiedName<'a>,
174}
175
176impl<'a> Spanned for DropEvent<'a> {
177 fn span(&self) -> Span {
178 self.drop_span
179 .join_span(&self.event_span)
180 .join_span(&self.if_exists)
181 .join_span(&self.event)
182 }
183}
184
185/// Represent a drop function statement
186/// ```
187/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropFunction, Statement, Issues};
188/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
189/// #
190/// let sql = "DROP FUNCTION myfunc;";
191/// let mut issues = Issues::new(sql);
192/// let mut stmts = parse_statements(sql, &mut issues, &options);
193///
194/// # assert!(issues.is_ok());
195/// let s: DropFunction = match stmts.pop() {
196/// Some(Statement::DropFunction(s)) => s,
197/// _ => panic!("We should get a drop function statement")
198/// };
199///
200/// assert!(s.function.identifier.as_str() == "myfunc");
201/// ```
202#[derive(Debug, Clone)]
203pub struct DropFunction<'a> {
204 /// Span of "DROP"
205 pub drop_span: Span,
206 /// Span of "FUNCTION"
207 pub function_span: Span,
208 /// Span of "IF EXISTS" if specified
209 pub if_exists: Option<Span>,
210 /// Function to drop
211 pub function: QualifiedName<'a>,
212}
213
214impl<'a> Spanned for DropFunction<'a> {
215 fn span(&self) -> Span {
216 self.drop_span
217 .join_span(&self.function_span)
218 .join_span(&self.if_exists)
219 .join_span(&self.function)
220 }
221}
222
223/// Represent a drop procedure statement
224/// ```
225/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropProcedure, Statement, Issues};
226/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
227/// #
228/// let sql = "DROP PROCEDURE myproc;";
229/// let mut issues = Issues::new(sql);
230/// let mut stmts = parse_statements(sql, &mut issues, &options);
231///
232/// # assert!(issues.is_ok());
233/// let s: DropProcedure = match stmts.pop() {
234/// Some(Statement::DropProcedure(s)) => s,
235/// _ => panic!("We should get a drop procedure statement")
236/// };
237///
238/// assert!(s.procedure.identifier.as_str() == "myproc");
239/// ```
240#[derive(Debug, Clone)]
241pub struct DropProcedure<'a> {
242 /// Span of "DROP"
243 pub drop_span: Span,
244 /// Span of "PROCEDURE"
245 pub procedure_span: Span,
246 /// Span of "IF EXISTS" if specified
247 pub if_exists: Option<Span>,
248 /// Procedure to drop
249 pub procedure: QualifiedName<'a>,
250}
251
252impl<'a> Spanned for DropProcedure<'a> {
253 fn span(&self) -> Span {
254 self.drop_span
255 .join_span(&self.procedure_span)
256 .join_span(&self.if_exists)
257 .join_span(&self.procedure)
258 }
259}
260
261/// Represent a drop server statement
262/// ```
263/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropServer, Statement, Issues};
264/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
265/// #
266/// let sql = "DROP SERVER myserver;";
267/// let mut issues = Issues::new(sql);
268/// let mut stmts = parse_statements(sql, &mut issues, &options);
269///
270/// # assert!(issues.is_ok());
271/// #
272/// let s: DropServer = match stmts.pop() {
273/// Some(Statement::DropServer(s)) => s,
274/// _ => panic!("We should get a drop server statement")
275/// };
276///
277/// assert!(s.server.as_str() == "myserver");
278/// ```
279#[derive(Debug, Clone)]
280pub struct DropServer<'a> {
281 /// Span of "DROP"
282 pub drop_span: Span,
283 /// Span of "SERVER"
284 pub server_span: Span,
285 /// Span of "IF EXISTS" if specified
286 pub if_exists: Option<Span>,
287 /// Server to drop
288 pub server: Identifier<'a>,
289}
290
291impl<'a> Spanned for DropServer<'a> {
292 fn span(&self) -> Span {
293 self.drop_span
294 .join_span(&self.server_span)
295 .join_span(&self.if_exists)
296 .join_span(&self.server)
297 }
298}
299
300/// Represent a drop trigger statement
301/// ```
302/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropTrigger, Statement, Issues};
303/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
304/// #
305/// let sql = "DROP TRIGGER IF EXISTS `foo`.`mytrigger`;";
306/// let mut issues = Issues::new(sql);
307/// let mut stmts = parse_statements(sql, &mut issues, &options);
308///
309/// # assert!(issues.is_ok());
310/// let s: DropTrigger = match stmts.pop() {
311/// Some(Statement::DropTrigger(s)) => s,
312/// _ => panic!("We should get a drop trigger statement")
313/// };
314///
315/// assert!(s.identifier.identifier.as_str() == "mytrigger");
316/// ```
317#[derive(Debug, Clone)]
318pub struct DropTrigger<'a> {
319 /// Span of "DROP"
320 pub drop_span: Span,
321 /// Span of "TRIGGER"
322 pub trigger_span: Span,
323 /// Span of "IF EXISTS" if specified
324 pub if_exists: Option<Span>,
325 /// Trigger to drop
326 pub identifier: QualifiedName<'a>,
327}
328
329impl<'a> Spanned for DropTrigger<'a> {
330 fn span(&self) -> Span {
331 self.drop_span
332 .join_span(&self.trigger_span)
333 .join_span(&self.if_exists)
334 .join_span(&self.identifier)
335 }
336}
337
338pub(crate) fn parse_drop<'a>(parser: &mut Parser<'a, '_>) -> Result<Statement<'a>, ParseError> {
339 let drop_span = parser.consume_keyword(Keyword::DROP)?;
340 let temporary = parser.skip_keyword(Keyword::TEMPORARY);
341 match &parser.token {
342 Token::Ident(_, Keyword::TABLE) => {
343 let table_span = parser.consume_keyword(Keyword::TABLE)?;
344 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
345 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
346 } else {
347 None
348 };
349 let mut tables = Vec::new();
350 loop {
351 tables.push(parse_qualified_name(parser)?);
352 if parser.skip_token(Token::Comma).is_none() {
353 break;
354 }
355 }
356 let cascade = if parser.options.dialect.is_postgresql() {
357 parser.skip_keyword(Keyword::CASCADE)
358 } else {
359 None
360 };
361 Ok(Statement::DropTable(DropTable {
362 drop_span,
363 temporary,
364 table_span,
365 if_exists,
366 tables,
367 cascade,
368 }))
369 }
370 Token::Ident(_, kw @ Keyword::DATABASE | kw @ Keyword::SCHEMA) => {
371 // TODO complain about temporary
372 let kw = *kw;
373 let database_span = parser.consume_keyword(kw)?;
374 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
375 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
376 } else {
377 None
378 };
379 let database = parser.consume_plain_identifier()?;
380 Ok(Statement::DropDatabase(DropDatabase {
381 drop_span,
382 database_span,
383 if_exists,
384 database,
385 }))
386 }
387 Token::Ident(_, Keyword::EVENT) => {
388 // TODO complain about temporary
389 let event_span = parser.consume_keyword(Keyword::EVENT)?;
390 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
391 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
392 } else {
393 None
394 };
395 let event = parse_qualified_name(parser)?;
396 Ok(Statement::DropEvent(DropEvent {
397 drop_span,
398 event_span,
399 if_exists,
400 event,
401 }))
402 }
403 Token::Ident(_, Keyword::FUNCTION) => {
404 // TODO complain about temporary
405 let function_span = parser.consume_keyword(Keyword::FUNCTION)?;
406 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
407 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
408 } else {
409 None
410 };
411 let function = parse_qualified_name(parser)?;
412 Ok(Statement::DropFunction(DropFunction {
413 drop_span,
414 function_span,
415 if_exists,
416 function,
417 }))
418 }
419 Token::Ident(_, Keyword::INDEX) => {
420 // DROP INDEX [IF EXISTS] index_name ON tbl_name
421 let index_span = parser.consume_keyword(Keyword::INDEX)?;
422 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
423 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
424 } else {
425 None
426 };
427 let index_name = parser.consume_plain_identifier()?;
428 let on = if let Some(span) = parser.skip_keyword(Keyword::ON) {
429 let table_name = parse_qualified_name(parser)?;
430 Some((span, table_name))
431 } else {
432 None
433 };
434
435 let v = DropIndex {
436 drop_span,
437 index_span,
438 if_exists,
439 index_name,
440 on,
441 };
442
443 if v.on.is_none() && parser.options.dialect.is_maria() {
444 parser.err("On required for index drops in MariaDb", &v);
445 }
446 if v.on.is_some() && parser.options.dialect.is_postgresql() {
447 parser.err("On not supported for index drops in PostgreSQL", &v);
448 }
449 Ok(Statement::DropIndex(v))
450 }
451 Token::Ident(_, Keyword::PROCEDURE) => {
452 // TODO complain about temporary
453 let procedure_span = parser.consume_keyword(Keyword::PROCEDURE)?;
454 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
455 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
456 } else {
457 None
458 };
459 let procedure = parse_qualified_name(parser)?;
460 Ok(Statement::DropProcedure(DropProcedure {
461 drop_span,
462 procedure_span,
463 if_exists,
464 procedure,
465 }))
466 }
467 Token::Ident(_, Keyword::SEQUENCE) => {
468 // DROP [TEMPORARY] SEQUENCE [IF EXISTS] [/*COMMENT TO SAVE*/] sequence_name [, sequence_name] ...
469 parser.todo(file!(), line!())
470 }
471 Token::Ident(_, Keyword::SERVER) => {
472 // TODO complain about temporary
473 let server_span = parser.consume_keyword(Keyword::SERVER)?;
474 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
475 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
476 } else {
477 None
478 };
479 let server = parser.consume_plain_identifier()?;
480 Ok(Statement::DropServer(DropServer {
481 drop_span,
482 server_span,
483 if_exists,
484 server,
485 }))
486 }
487 Token::Ident(_, Keyword::TRIGGER) => {
488 let trigger_span = parser.consume_keyword(Keyword::TRIGGER)?;
489 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
490 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
491 } else {
492 None
493 };
494 let identifier = parse_qualified_name(parser)?;
495 Ok(Statement::DropTrigger(DropTrigger {
496 drop_span,
497 trigger_span,
498 if_exists,
499 identifier,
500 }))
501 }
502 Token::Ident(_, Keyword::VIEW) => {
503 let view_span = parser.consume_keyword(Keyword::VIEW)?;
504 let if_exists = if let Some(span) = parser.skip_keyword(Keyword::IF) {
505 Some(parser.consume_keyword(Keyword::EXISTS)?.join_span(&span))
506 } else {
507 None
508 };
509 let mut views = Vec::new();
510 loop {
511 views.push(parse_qualified_name(parser)?);
512 if parser.skip_token(Token::Comma).is_none() {
513 break;
514 }
515 }
516 // TODO [RESTRICT | CASCADE]
517 Ok(Statement::DropView(DropView {
518 drop_span,
519 temporary,
520 view_span,
521 if_exists,
522 views,
523 }))
524 }
525 Token::Ident(_, Keyword::USER) => {
526 // DROP USER [IF EXISTS] user_name [, user_name] ..
527 parser.todo(file!(), line!())
528 }
529 _ => parser.expected_failure("droppable"),
530 }
531}
532
533/// Represent a drop index statement.
534///
535/// MariaDB example
536/// ```
537/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropIndex, Statement, Issues};
538/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
539/// #
540/// let sql = "DROP INDEX IF EXISTS `myindex` ON `bar`;";
541/// let mut issues = Issues::new(sql);
542/// let mut stmts = parse_statements(sql, &mut issues, &options);
543///
544/// # assert!(issues.is_ok());
545/// let s: DropIndex = match stmts.pop() {
546/// Some(Statement::DropIndex(s)) => s,
547/// _ => panic!("We should get a drop trigger statement")
548/// };
549///
550/// assert!(s.index_name.as_str() == "myindex");
551/// ```
552///
553/// PostgreSQL example
554/// ```
555/// # use sql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, DropIndex, Statement, Issues};
556/// # let options = ParseOptions::new().dialect(SQLDialect::PostgreSQL);
557/// #
558/// let sql = "DROP INDEX IF EXISTS \"myindex\";";
559/// let mut issues = Issues::new(sql);
560/// let mut stmts = parse_statements(sql, &mut issues, &options);
561///
562/// # assert!(issues.is_ok(), "{}", issues);
563/// #
564/// let s: DropIndex = match stmts.pop() {
565/// Some(Statement::DropIndex(s)) => s,
566/// _ => panic!("We should get a drop trigger statement")
567/// };
568///
569/// assert!(s.index_name.as_str() == "myindex");
570/// ```
571#[derive(Debug, Clone)]
572pub struct DropIndex<'a> {
573 /// Span of "DROP"
574 pub drop_span: Span,
575 /// Span of "INDEX"
576 pub index_span: Span,
577 /// Span of "IF EXISTS" if specified
578 pub if_exists: Option<Span>,
579 pub index_name: Identifier<'a>,
580 pub on: Option<(Span, QualifiedName<'a>)>,
581}
582
583impl<'a> Spanned for DropIndex<'a> {
584 fn span(&self) -> Span {
585 self.drop_span
586 .join_span(&self.index_span)
587 .join_span(&self.if_exists)
588 .join_span(&self.index_name)
589 .join_span(&self.on)
590 }
591}