Skip to main content

qusql_parse/
insert_replace.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.
12use alloc::vec::Vec;
13
14use crate::{
15    Identifier, OptSpanned, QualifiedName, Span, Spanned, Statement,
16    expression::{
17        Expression, PRIORITY_MAX, parse_expression_or_default, parse_expression_unreserved,
18    },
19    keywords::Keyword,
20    lexer::Token,
21    parser::{ParseError, Parser},
22    qualified_name::parse_qualified_name_unreserved,
23    select::{SelectExpr, parse_select_expr},
24    statement::parse_compound_query,
25};
26
27/// Flags for insert
28#[derive(Clone, Debug)]
29pub enum InsertReplaceFlag {
30    LowPriority(Span),
31    HighPriority(Span),
32    Delayed(Span),
33    Ignore(Span),
34}
35
36impl Spanned for InsertReplaceFlag {
37    fn span(&self) -> Span {
38        match &self {
39            InsertReplaceFlag::LowPriority(v) => v.span(),
40            InsertReplaceFlag::HighPriority(v) => v.span(),
41            InsertReplaceFlag::Delayed(v) => v.span(),
42            InsertReplaceFlag::Ignore(v) => v.span(),
43        }
44    }
45}
46
47#[derive(Clone, Debug)]
48pub enum InsertReplaceType {
49    Insert(Span),
50    Replace(Span),
51}
52
53impl Spanned for InsertReplaceType {
54    fn span(&self) -> Span {
55        match self {
56            InsertReplaceType::Insert(a) => a.clone(),
57            InsertReplaceType::Replace(a) => a.clone(),
58        }
59    }
60}
61
62#[derive(Clone, Debug)]
63pub enum OnConflictTarget<'a> {
64    Columns {
65        names: Vec<Identifier<'a>>,
66    },
67    OnConstraint {
68        on_constraint_span: Span,
69        name: Identifier<'a>,
70    },
71    None,
72}
73
74impl<'a> OptSpanned for OnConflictTarget<'a> {
75    fn opt_span(&self) -> Option<Span> {
76        match self {
77            OnConflictTarget::Columns { names } => names.opt_span(),
78            OnConflictTarget::OnConstraint {
79                on_constraint_span: token,
80                name,
81            } => Some(token.join_span(name)),
82            OnConflictTarget::None => None,
83        }
84    }
85}
86
87#[derive(Clone, Debug)]
88pub enum OnConflictAction<'a> {
89    DoNothing(Span),
90    DoUpdateSet {
91        do_update_set_span: Span,
92        sets: Vec<(Identifier<'a>, Expression<'a>)>,
93        where_: Option<(Span, Expression<'a>)>,
94    },
95}
96
97impl<'a> Spanned for OnConflictAction<'a> {
98    fn span(&self) -> Span {
99        match self {
100            OnConflictAction::DoNothing(span) => span.span(),
101            OnConflictAction::DoUpdateSet {
102                do_update_set_span,
103                sets,
104                where_,
105            } => do_update_set_span.join_span(sets).join_span(where_),
106        }
107    }
108}
109
110#[derive(Clone, Debug)]
111pub struct OnConflict<'a> {
112    pub on_conflict_span: Span,
113    pub target: OnConflictTarget<'a>,
114    pub action: OnConflictAction<'a>,
115}
116
117impl<'a> Spanned for OnConflict<'a> {
118    fn span(&self) -> Span {
119        self.on_conflict_span
120            .join_span(&self.target)
121            .join_span(&self.action)
122    }
123}
124
125#[derive(Clone, Debug)]
126pub struct InsertReplaceSetPair<'a> {
127    pub column: Identifier<'a>,
128    pub equal_span: Span,
129    pub value: Expression<'a>,
130}
131
132impl<'a> Spanned for InsertReplaceSetPair<'a> {
133    fn span(&self) -> Span {
134        self.column
135            .join_span(&self.equal_span)
136            .join_span(&self.value)
137    }
138}
139
140#[derive(Clone, Debug)]
141pub struct InsertReplaceSet<'a> {
142    pub set_span: Span,
143    pub pairs: Vec<InsertReplaceSetPair<'a>>,
144}
145
146impl<'a> Spanned for InsertReplaceSet<'a> {
147    fn span(&self) -> Span {
148        self.set_span.join_span(&self.pairs)
149    }
150}
151
152#[derive(Clone, Debug)]
153pub struct InsertReplaceOnDuplicateKeyUpdate<'a> {
154    pub on_duplicate_key_update_span: Span,
155    pub pairs: Vec<InsertReplaceSetPair<'a>>,
156}
157
158impl<'a> Spanned for InsertReplaceOnDuplicateKeyUpdate<'a> {
159    fn span(&self) -> Span {
160        self.on_duplicate_key_update_span.join_span(&self.pairs)
161    }
162}
163
164/// Representation of Insert or Replace Statement
165///
166/// ```
167/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statement, InsertReplace, InsertReplaceType, Statement, Issues};
168/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
169/// #
170/// let sql1 = "INSERT INTO person (first_name, last_name) VALUES ('John', 'Doe')";
171/// # let mut issues = Issues::new(sql1);
172/// let stmt1 = parse_statement(sql1, &mut issues, &options);
173/// # assert!(issues.is_ok());/// #
174/// let sql2 = "INSERT INTO contractor SELECT * FROM person WHERE status = 'c'";
175/// # let mut issues = Issues::new(sql2);
176/// let stmt2 = parse_statement(sql2, &mut issues, &options);
177/// # assert!(issues.is_ok());/// #
178/// let sql3 = "INSERT INTO account (`key`, `value`) VALUES ('foo', 42)
179///             ON DUPLICATE KEY UPDATE `value`=`value`+42";
180/// # let mut issues = Issues::new(sql3);
181/// let stmt3 = parse_statement(sql3, &mut issues, &options);
182/// # assert!(issues.is_ok());
183///
184/// let i: InsertReplace = match stmt1 {
185///     Some(Statement::InsertReplace(i)) if matches!(i.type_, InsertReplaceType::Insert(_)) => *i,
186///     _ => panic!("We should get an insert statement")
187/// };
188///
189/// assert!(i.table.identifier.as_str() == "person");
190/// println!("{:#?}", i.values.unwrap());
191///
192///
193/// let sql = "REPLACE INTO t2 VALUES (1,'Leopard'),(2,'Dog')";
194/// # let mut issues = Issues::new(sql);
195/// let stmt = parse_statement(sql, &mut issues, &options);
196/// # assert!(issues.is_ok());
197/// #
198/// let r: InsertReplace = match stmt {
199///     Some(Statement::InsertReplace(r)) if matches!(r.type_, InsertReplaceType::Replace(_)) => *r,
200///     _ => panic!("We should get an replace statement")
201/// };
202///
203/// assert!(r.table.identifier.as_str() == "t2");
204/// println!("{:#?}", r.values.unwrap());
205/// ```
206///
207/// PostgreSQL
208/// ```
209/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statement, InsertReplace, InsertReplaceType, Statement, Issues};
210/// # let options = ParseOptions::new().dialect(SQLDialect::PostgreSQL).arguments(SQLArguments::Dollar);
211/// #
212///
213/// let sql4 = "INSERT INTO contractor SELECT * FROM person WHERE status = $1 ON CONFLICT (name) DO NOTHING";
214/// # let mut issues = Issues::new(sql4);
215/// let stmt4 = parse_statement(sql4, &mut issues, &options);
216///
217/// println!("{}", issues);
218/// # assert!(issues.is_ok());
219/// ```
220#[derive(Clone, Debug)]
221pub struct InsertReplace<'a> {
222    /// Span of "INSERT" or "REPLACE"
223    pub type_: InsertReplaceType,
224    /// Flags specified after "INSERT"
225    pub flags: Vec<InsertReplaceFlag>,
226    /// Span of "INTO" if specified
227    pub into_span: Option<Span>,
228    /// Table to insert into
229    pub table: QualifiedName<'a>,
230    /// List of columns to set
231    pub columns: Vec<Identifier<'a>>,
232    /// Span of values "VALUES" and list of tuples to insert if specified
233    pub values: Option<(Span, Vec<Vec<Expression<'a>>>)>,
234    /// Select statement (possibly compound with UNION/INTERSECT/EXCEPT) to insert if specified
235    pub select: Option<Statement<'a>>,
236    /// Span of "SET" and list of key, value pairs to set if specified
237    pub set: Option<InsertReplaceSet<'a>>,
238    /// Updates to execute on duplicate key (mysql)
239    pub on_duplicate_key_update: Option<InsertReplaceOnDuplicateKeyUpdate<'a>>,
240    /// Action to take on duplicate keys (postgresql)
241    pub on_conflict: Option<OnConflict<'a>>,
242    /// AS alias with optional column list (MySQL/MariaDB): AS alias [(col1, col2, ...)]
243    pub as_alias: Option<(Span, Identifier<'a>, Option<Vec<Identifier<'a>>>)>,
244    /// Span of "RETURNING" and select expressions after "RETURNING", if "RETURNING" is present
245    pub returning: Option<(Span, Vec<SelectExpr<'a>>)>,
246}
247
248impl<'a> Spanned for InsertReplace<'a> {
249    fn span(&self) -> Span {
250        self.type_
251            .join_span(&self.flags)
252            .join_span(&self.into_span)
253            .join_span(&self.table)
254            .join_span(&self.columns)
255            .join_span(&self.values)
256            .join_span(&self.select)
257            .join_span(&self.set)
258            .join_span(&self.as_alias)
259            .join_span(&self.on_duplicate_key_update)
260            .join_span(&self.on_conflict)
261            .join_span(&self.returning)
262    }
263}
264
265pub(crate) fn parse_insert_replace<'a>(
266    parser: &mut Parser<'a, '_>,
267) -> Result<InsertReplace<'a>, ParseError> {
268    let type_ = match &parser.token {
269        Token::Ident(_, Keyword::INSERT) => InsertReplaceType::Insert(parser.consume()),
270        Token::Ident(_, Keyword::REPLACE) => InsertReplaceType::Replace(parser.consume()),
271        _ => parser.expected_failure("INSERT or REPLACE")?,
272    };
273
274    let insert = matches!(type_, InsertReplaceType::Insert(_));
275
276    let mut flags = Vec::new();
277    loop {
278        match &parser.token {
279            Token::Ident(_, Keyword::LOW_PRIORITY) => flags.push(InsertReplaceFlag::LowPriority(
280                parser.consume_keyword(Keyword::LOW_PRIORITY)?,
281            )),
282            Token::Ident(_, Keyword::HIGH_PRIORITY) => flags.push(InsertReplaceFlag::HighPriority(
283                parser.consume_keyword(Keyword::HIGH_PRIORITY)?,
284            )),
285            Token::Ident(_, Keyword::DELAYED) => flags.push(InsertReplaceFlag::Delayed(
286                parser.consume_keyword(Keyword::DELAYED)?,
287            )),
288            Token::Ident(_, Keyword::IGNORE) => flags.push(InsertReplaceFlag::Ignore(
289                parser.consume_keyword(Keyword::IGNORE)?,
290            )),
291            _ => break,
292        }
293    }
294
295    for flag in &flags {
296        match flag {
297            InsertReplaceFlag::LowPriority(_) => {}
298            InsertReplaceFlag::HighPriority(s) => {
299                if !insert {
300                    parser.err("Not supported for replace", s);
301                }
302            }
303            InsertReplaceFlag::Delayed(_) => {}
304            InsertReplaceFlag::Ignore(s) => {
305                if !insert {
306                    parser.err("Not supported for replace", s);
307                }
308            }
309        }
310    }
311
312    let into_span = parser.skip_keyword(Keyword::INTO);
313    let table = parse_qualified_name_unreserved(parser)?;
314    // [PARTITION (partition_list)]
315
316    let mut columns = Vec::new();
317    if parser.skip_token(Token::LParen).is_some() {
318        // Check for empty column list ()
319        if !matches!(parser.token, Token::RParen) {
320            parser.recovered(")", &|t| t == &Token::RParen, |parser| {
321                loop {
322                    columns.push(parser.consume_plain_identifier_unreserved()?);
323                    if parser.skip_token(Token::Comma).is_none() {
324                        break;
325                    }
326                }
327                Ok(())
328            })?;
329        }
330        parser.consume_token(Token::RParen)?;
331    }
332
333    // Parse AS alias before VALUES/SELECT/SET (PostgreSQL style)
334    let as_alias_before = if let Some(as_span) = parser.skip_keyword(Keyword::AS) {
335        let alias = parser.consume_plain_identifier_unreserved()?;
336        let columns = if parser.skip_token(Token::LParen).is_some() {
337            let mut cols = Vec::new();
338            // Check for empty column list ()
339            if !matches!(parser.token, Token::RParen) {
340                loop {
341                    cols.push(parser.consume_plain_identifier_unreserved()?);
342                    if parser.skip_token(Token::Comma).is_none() {
343                        break;
344                    }
345                }
346            }
347            parser.consume_token(Token::RParen)?;
348            Some(cols)
349        } else {
350            None
351        };
352        Some((as_span, alias, columns))
353    } else {
354        None
355    };
356
357    let mut select = None;
358    let mut values = None;
359    let mut set = None;
360    match &parser.token {
361        Token::Ident(_, Keyword::SELECT) | Token::LParen => {
362            select = Some(parse_compound_query(parser)?);
363        }
364        Token::Ident(_, Keyword::WITH) => {
365            // INSERT ... WITH [RECURSIVE] cte AS (...) SELECT ...
366            use crate::with_query::parse_with_query;
367            let wq = parse_with_query(parser)?;
368            select = Some(Statement::WithQuery(alloc::boxed::Box::new(wq)));
369        }
370        Token::Ident(_, Keyword::VALUE | Keyword::VALUES) => {
371            let values_span = parser.consume();
372            let mut values_items = Vec::new();
373            loop {
374                let mut vals = Vec::new();
375                parser.consume_token(Token::LParen)?;
376                // Check for empty VALUES ()
377                if !matches!(parser.token, Token::RParen) {
378                    parser.recovered(")", &|t| t == &Token::RParen, |parser| {
379                        loop {
380                            vals.push(parse_expression_or_default(parser, PRIORITY_MAX)?);
381                            if parser.skip_token(Token::Comma).is_none() {
382                                break;
383                            }
384                        }
385                        Ok(())
386                    })?;
387                }
388                parser.consume_token(Token::RParen)?;
389                values_items.push(vals);
390                if parser.skip_token(Token::Comma).is_none() {
391                    break;
392                }
393            }
394            values = Some((values_span, values_items));
395        }
396        Token::Ident(_, Keyword::SET) => {
397            let set_span = parser.consume_keyword(Keyword::SET)?;
398            let mut pairs = Vec::new();
399            loop {
400                let column = parser.consume_plain_identifier_unreserved()?;
401                let equal_span = parser.consume_token(Token::Eq)?;
402                let value: Expression<'_> = parse_expression_or_default(parser, PRIORITY_MAX)?;
403                pairs.push(InsertReplaceSetPair {
404                    column,
405                    equal_span,
406                    value,
407                });
408                if parser.skip_token(Token::Comma).is_none() {
409                    break;
410                }
411            }
412            if let Some(cs) = columns.opt_span() {
413                parser
414                    .err("Columns may not be used here", &cs)
415                    .frag("Together with SET", &set_span);
416            }
417            set = Some(InsertReplaceSet { set_span, pairs });
418        }
419        _ => {
420            parser.expected_error("VALUE, VALUES, SELECT or SET");
421        }
422    }
423
424    let (on_duplicate_key_update, on_conflict) =
425        if matches!(parser.token, Token::Ident(_, Keyword::ON)) {
426            let on = parser.consume_keyword(Keyword::ON)?;
427            match &parser.token {
428                Token::Ident(_, Keyword::DUPLICATE) => {
429                    let on_duplicate_key_update_span =
430                        on.join_span(&parser.consume_keywords(&[
431                            Keyword::DUPLICATE,
432                            Keyword::KEY,
433                            Keyword::UPDATE,
434                        ])?);
435                    let mut pairs = Vec::new();
436                    loop {
437                        let column = parser.consume_plain_identifier_unreserved()?;
438                        let equal_span = parser.consume_token(Token::Eq)?;
439                        let value = parse_expression_or_default(parser, PRIORITY_MAX)?;
440                        pairs.push(InsertReplaceSetPair {
441                            column,
442                            equal_span,
443                            value,
444                        });
445                        if parser.skip_token(Token::Comma).is_none() {
446                            break;
447                        }
448                    }
449                    parser.maria_only(&on_duplicate_key_update_span.join_span(&pairs));
450                    (
451                        Some(InsertReplaceOnDuplicateKeyUpdate {
452                            on_duplicate_key_update_span,
453                            pairs,
454                        }),
455                        None,
456                    )
457                }
458                Token::Ident(_, Keyword::CONFLICT) => {
459                    let on_conflict_span =
460                        on.join_span(&parser.consume_keyword(Keyword::CONFLICT)?);
461
462                    let target = match &parser.token {
463                        Token::LParen => {
464                            parser.consume_token(Token::LParen)?;
465                            let mut names = Vec::new();
466                            names.push(parser.consume_plain_identifier_unreserved()?);
467                            while parser.skip_token(Token::Comma).is_some() {
468                                names.push(parser.consume_plain_identifier_unreserved()?);
469                            }
470                            parser.consume_token(Token::RParen)?;
471                            OnConflictTarget::Columns { names }
472                        }
473                        Token::Ident(_, Keyword::ON) => {
474                            let on_constraint =
475                                parser.consume_keywords(&[Keyword::ON, Keyword::CONSTRAINT])?;
476                            let name = parser.consume_plain_identifier_unreserved()?;
477                            OnConflictTarget::OnConstraint {
478                                on_constraint_span: on_constraint,
479                                name,
480                            }
481                        }
482                        _ => OnConflictTarget::None,
483                    };
484
485                    let do_ = parser.consume_keyword(Keyword::DO)?;
486                    let action = match &parser.token {
487                        Token::Ident(_, Keyword::NOTHING) => OnConflictAction::DoNothing(
488                            do_.join_span(&parser.consume_keyword(Keyword::NOTHING)?),
489                        ),
490                        Token::Ident(_, Keyword::UPDATE) => {
491                            let do_update_set_span = do_.join_span(
492                                &parser.consume_keywords(&[Keyword::UPDATE, Keyword::SET])?,
493                            );
494                            let mut sets = Vec::new();
495                            loop {
496                                let name = parser.consume_plain_identifier_unreserved()?;
497                                parser.consume_token(Token::Eq)?;
498                                let expr = parse_expression_or_default(parser, PRIORITY_MAX)?;
499                                sets.push((name, expr));
500                                if parser.skip_token(Token::Comma).is_none() {
501                                    break;
502                                }
503                            }
504                            let where_ = if matches!(parser.token, Token::Ident(_, Keyword::WHERE))
505                            {
506                                let where_span = parser.consume_keyword(Keyword::WHERE)?;
507                                let where_expr = parse_expression_unreserved(parser, PRIORITY_MAX)?;
508                                Some((where_span, where_expr))
509                            } else {
510                                None
511                            };
512                            OnConflictAction::DoUpdateSet {
513                                do_update_set_span,
514                                sets,
515                                where_,
516                            }
517                        }
518                        _ => parser.expected_failure("'NOTHING' or 'UPDATE'")?,
519                    };
520
521                    let on_conflict = OnConflict {
522                        on_conflict_span,
523                        target,
524                        action,
525                    };
526
527                    parser.postgres_only(&on_conflict);
528
529                    (None, Some(on_conflict))
530                }
531                _ => parser.expected_failure("'DUPLICATE' OR 'CONFLICT'")?,
532            }
533        } else {
534            (None, None)
535        };
536
537    // Parse AS alias after VALUES/SELECT/SET (MySQL/MariaDB style) if not already parsed
538    let as_alias = if as_alias_before.is_none() {
539        if let Some(as_span) = parser.skip_keyword(Keyword::AS) {
540            let alias = parser.consume_plain_identifier_unreserved()?;
541            let columns = if parser.skip_token(Token::LParen).is_some() {
542                let mut cols = Vec::new();
543                // Check for empty column list ()
544                if !matches!(parser.token, Token::RParen) {
545                    loop {
546                        cols.push(parser.consume_plain_identifier_unreserved()?);
547                        if parser.skip_token(Token::Comma).is_none() {
548                            break;
549                        }
550                    }
551                }
552                parser.consume_token(Token::RParen)?;
553                Some(cols)
554            } else {
555                None
556            };
557            Some((as_span, alias, columns))
558        } else {
559            None
560        }
561    } else {
562        as_alias_before
563    };
564
565    let returning = if let Some(returning_span) = parser.skip_keyword(Keyword::RETURNING) {
566        let mut returning_exprs = Vec::new();
567        loop {
568            returning_exprs.push(parse_select_expr(parser)?);
569            if parser.skip_token(Token::Comma).is_none() {
570                break;
571            }
572        }
573        Some((returning_span, returning_exprs))
574    } else {
575        None
576    };
577
578    Ok(InsertReplace {
579        type_,
580        flags,
581        table,
582        columns,
583        into_span,
584        values,
585        select,
586        set,
587        as_alias,
588        on_duplicate_key_update,
589        on_conflict,
590        returning,
591    })
592}