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            // Optional alias: DELETE FROM table alias WHERE ...
128            if matches!(&parser.token, Token::Ident(_, k) if !k.restricted(parser.reserved() | Restrict::EMPTY))
129            {
130                parser.consume();
131            }
132            if parser.skip_token(Token::Comma).is_none() {
133                break;
134            }
135        }
136        from_span
137    } else {
138        loop {
139            tables.push(parse_qualified_name_unreserved(parser)?);
140            if parser.skip_token(Token::Comma).is_none() {
141                break;
142            }
143        }
144        let from_span = parser.consume_keyword(Keyword::FROM)?;
145        loop {
146            using.push(parse_table_reference(parser, Restrict::EMPTY)?);
147            if parser.skip_token(Token::Comma).is_none() {
148                break;
149            }
150        }
151        from_span
152    };
153
154    //TODO [PARTITION (partition_list)]
155    //TODO [FOR PORTION OF period FROM expr1 TO expr2]
156
157    if let Some(using_span) = parser.skip_keyword(Keyword::USING) {
158        if !using.is_empty() {
159            parser.err(
160                "Using not allowed in delete with table names before FROM",
161                &using_span,
162            );
163        }
164        loop {
165            using.push(parse_table_reference(parser, Restrict::EMPTY)?);
166            if parser.skip_token(Token::Comma).is_none() {
167                break;
168            }
169        }
170    }
171
172    let where_ = if let Some(span) = parser.skip_keyword(Keyword::WHERE) {
173        Some((parse_expression_unreserved(parser, PRIORITY_MAX)?, span))
174    } else {
175        None
176    };
177
178    let order_by = if let Some(span) = parser.skip_keyword(Keyword::ORDER) {
179        let span = parser.consume_keyword(Keyword::BY)?.join_span(&span);
180        let mut order = Vec::new();
181        loop {
182            let e = parse_expression_unreserved(parser, PRIORITY_MAX)?;
183            let f = match &parser.token {
184                Token::Ident(_, Keyword::ASC) => OrderFlag::Asc(parser.consume()),
185                Token::Ident(_, Keyword::DESC) => OrderFlag::Desc(parser.consume()),
186                _ => OrderFlag::None,
187            };
188            order.push((e, f));
189            if parser.skip_token(Token::Comma).is_none() {
190                break;
191            }
192        }
193        Some((span, order))
194    } else {
195        None
196    };
197
198    let limit = if let Some(span) = parser.skip_keyword(Keyword::LIMIT) {
199        let n = parse_expression_unreserved(parser, PRIORITY_MAX)?;
200        match parser.token {
201            Token::Comma => {
202                parser.consume();
203                Some((
204                    span,
205                    Some(n),
206                    parse_expression_unreserved(parser, PRIORITY_MAX)?,
207                ))
208            }
209            Token::Ident(_, Keyword::OFFSET) => {
210                parser.consume();
211                Some((
212                    span,
213                    Some(parse_expression_unreserved(parser, PRIORITY_MAX)?),
214                    n,
215                ))
216            }
217            _ => Some((span, None, n)),
218        }
219    } else {
220        None
221    };
222
223    let returning = if let Some(returning_span) = parser.skip_keyword(Keyword::RETURNING) {
224        let mut returning_exprs = Vec::new();
225        loop {
226            returning_exprs.push(parse_select_expr(parser)?);
227            if parser.skip_token(Token::Comma).is_none() {
228                break;
229            }
230        }
231        Some((returning_span, returning_exprs))
232    } else {
233        None
234    };
235
236    Ok(Delete {
237        flags,
238        delete_span,
239        tables,
240        using,
241        from_span,
242        where_,
243        order_by,
244        limit,
245        returning,
246    })
247}