Skip to main content

qusql_parse/
delete.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    QualifiedName, SelectExpr, Span, Spanned, TableReference,
17    expression::{Expression, PRIORITY_MAX, parse_expression_unreserved},
18    keywords::{Keyword, Restrict},
19    lexer::Token,
20    parser::{ParseError, Parser},
21    qualified_name::parse_qualified_name_unreserved,
22    select::{OrderFlag, parse_select_expr, parse_table_reference},
23};
24
25/// Flags for deletion
26#[derive(Clone, Debug)]
27pub enum DeleteFlag {
28    LowPriority(Span),
29    Quick(Span),
30    Ignore(Span),
31}
32
33impl Spanned for DeleteFlag {
34    fn span(&self) -> Span {
35        match &self {
36            DeleteFlag::LowPriority(v) => v.span(),
37            DeleteFlag::Quick(v) => v.span(),
38            DeleteFlag::Ignore(v) => v.span(),
39        }
40    }
41}
42
43/// Represent a delete statement
44/// ```
45/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Delete, Statement, Issues};
46/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
47/// #
48/// let sql = "DELETE FROM t1 WHERE c1 IN (SELECT b.c1 FROM t1 b WHERE b.c2=0);";
49/// let mut issues = Issues::new(sql);
50/// let mut stmts = parse_statements(sql, &mut issues, &options);
51///
52/// # assert!(issues.is_ok());
53/// let delete: Delete = match stmts.pop() {
54///     Some(Statement::Delete(d)) => *d,
55///     _ => panic!("We should get a delete statement")
56/// };
57///
58/// assert!(delete.tables[0].identifier.as_str() == "t1");
59/// println!("{:#?}", delete.where_);
60///
61/// let sql = "DELETE `t1` FROM `t1` LEFT JOIN `t2` ON `t1`.`t2_id`=`t2`.`id` WHERE `t2`.`key`='my_key';";
62///
63/// let mut stmts = parse_statements(sql, &mut issues, &options);
64///
65/// # assert!(issues.is_ok());
66/// ```
67#[derive(Clone, Debug)]
68pub struct Delete<'a> {
69    /// Span of "DELETE"
70    pub delete_span: Span,
71    /// Flags following "DELETE"
72    pub flags: Vec<DeleteFlag>,
73    /// Span of "FROM"
74    pub from_span: Span,
75    /// Tables to do deletes on
76    pub tables: Vec<QualifiedName<'a>>,
77    /// Table to use in where clause in multi table delete
78    pub using: Vec<TableReference<'a>>,
79    /// Where expression and Span of "WHERE" if specified
80    pub where_: Option<(Expression<'a>, Span)>,
81    /// Span of "ORDER BY" and order expressions if specified
82    pub order_by: Option<(Span, Vec<(Expression<'a>, OrderFlag)>)>,
83    /// Span of "LIMIT", optional offset expression, and limit expression
84    pub limit: Option<(Span, Option<Expression<'a>>, Expression<'a>)>,
85    /// Span of "RETURNING" and select expressions after "RETURNING", if "RETURNING" is present
86    pub returning: Option<(Span, Vec<SelectExpr<'a>>)>,
87}
88
89impl<'a> Spanned for Delete<'a> {
90    fn span(&self) -> Span {
91        self.delete_span
92            .join_span(&self.flags)
93            .join_span(&self.from_span)
94            .join_span(&self.tables)
95            .join_span(&self.using)
96            .join_span(&self.where_)
97            .join_span(&self.order_by)
98            .join_span(&self.limit)
99            .join_span(&self.returning)
100    }
101}
102
103pub(crate) fn parse_delete<'a>(parser: &mut Parser<'a, '_>) -> Result<Delete<'a>, ParseError> {
104    let delete_span = parser.consume_keyword(Keyword::DELETE)?;
105    let mut flags = Vec::new();
106
107    loop {
108        match &parser.token {
109            Token::Ident(_, Keyword::LOW_PRIORITY) => flags.push(DeleteFlag::LowPriority(
110                parser.consume_keyword(Keyword::LOW_PRIORITY)?,
111            )),
112            Token::Ident(_, Keyword::QUICK) => {
113                flags.push(DeleteFlag::Quick(parser.consume_keyword(Keyword::QUICK)?))
114            }
115            Token::Ident(_, Keyword::IGNORE) => {
116                flags.push(DeleteFlag::Ignore(parser.consume_keyword(Keyword::IGNORE)?))
117            }
118            _ => break,
119        }
120    }
121
122    let mut tables = Vec::new();
123    let mut using = Vec::new();
124    let from_span = if let Some(from_span) = parser.skip_keyword(Keyword::FROM) {
125        loop {
126            tables.push(parse_qualified_name_unreserved(parser)?);
127            if parser.skip_token(Token::Comma).is_none() {
128                break;
129            }
130        }
131        from_span
132    } else {
133        loop {
134            tables.push(parse_qualified_name_unreserved(parser)?);
135            if parser.skip_token(Token::Comma).is_none() {
136                break;
137            }
138        }
139        let from_span = parser.consume_keyword(Keyword::FROM)?;
140        loop {
141            using.push(parse_table_reference(parser, Restrict::EMPTY)?);
142            if parser.skip_token(Token::Comma).is_none() {
143                break;
144            }
145        }
146        from_span
147    };
148
149    //TODO [PARTITION (partition_list)]
150    //TODO [FOR PORTION OF period FROM expr1 TO expr2]
151
152    if let Some(using_span) = parser.skip_keyword(Keyword::USING) {
153        if !using.is_empty() {
154            parser.err(
155                "Using not allowed in delete with table names before FROM",
156                &using_span,
157            );
158        }
159        loop {
160            using.push(parse_table_reference(parser, Restrict::EMPTY)?);
161            if parser.skip_token(Token::Comma).is_none() {
162                break;
163            }
164        }
165    }
166
167    let where_ = if let Some(span) = parser.skip_keyword(Keyword::WHERE) {
168        Some((parse_expression_unreserved(parser, PRIORITY_MAX)?, span))
169    } else {
170        None
171    };
172
173    let order_by = if let Some(span) = parser.skip_keyword(Keyword::ORDER) {
174        let span = parser.consume_keyword(Keyword::BY)?.join_span(&span);
175        let mut order = Vec::new();
176        loop {
177            let e = parse_expression_unreserved(parser, PRIORITY_MAX)?;
178            let f = match &parser.token {
179                Token::Ident(_, Keyword::ASC) => OrderFlag::Asc(parser.consume()),
180                Token::Ident(_, Keyword::DESC) => OrderFlag::Desc(parser.consume()),
181                _ => OrderFlag::None,
182            };
183            order.push((e, f));
184            if parser.skip_token(Token::Comma).is_none() {
185                break;
186            }
187        }
188        Some((span, order))
189    } else {
190        None
191    };
192
193    let limit = if let Some(span) = parser.skip_keyword(Keyword::LIMIT) {
194        let n = parse_expression_unreserved(parser, PRIORITY_MAX)?;
195        match parser.token {
196            Token::Comma => {
197                parser.consume();
198                Some((
199                    span,
200                    Some(n),
201                    parse_expression_unreserved(parser, PRIORITY_MAX)?,
202                ))
203            }
204            Token::Ident(_, Keyword::OFFSET) => {
205                parser.consume();
206                Some((
207                    span,
208                    Some(parse_expression_unreserved(parser, PRIORITY_MAX)?),
209                    n,
210                ))
211            }
212            _ => Some((span, None, n)),
213        }
214    } else {
215        None
216    };
217
218    let returning = if let Some(returning_span) = parser.skip_keyword(Keyword::RETURNING) {
219        let mut returning_exprs = Vec::new();
220        loop {
221            returning_exprs.push(parse_select_expr(parser)?);
222            if parser.skip_token(Token::Comma).is_none() {
223                break;
224            }
225        }
226        Some((returning_span, returning_exprs))
227    } else {
228        None
229    };
230
231    Ok(Delete {
232        flags,
233        delete_span,
234        tables,
235        using,
236        from_span,
237        where_,
238        order_by,
239        limit,
240        returning,
241    })
242}