Skip to main content

sqlparser/parser/
merge.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
13//! SQL Parser for a `MERGE` statement
14
15#[cfg(not(feature = "std"))]
16use alloc::{boxed::Box, format, vec, vec::Vec};
17
18use crate::{
19    ast::{
20        Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, MergeInsertKind,
21        MergeUpdateExpr, ObjectName, OutputClause, SetExpr,
22    },
23    dialect::{BigQueryDialect, GenericDialect, MySqlDialect},
24    keywords::Keyword,
25    parser::IsOptional,
26    tokenizer::TokenWithSpan,
27};
28
29use super::{Parser, ParserError};
30
31impl Parser<'_> {
32    /// Parse a `MERGE` statement, returning a `Box`ed SetExpr
33    ///
34    /// This is used to reduce the size of the stack frames in debug builds
35    pub(super) fn parse_merge_setexpr_boxed(
36        &mut self,
37        merge_token: TokenWithSpan,
38    ) -> Result<Box<SetExpr>, ParserError> {
39        Ok(Box::new(SetExpr::Merge(
40            self.parse_merge(merge_token)?.into(),
41        )))
42    }
43
44    /// Parse a `MERGE` statement
45    pub fn parse_merge(&mut self, merge_token: TokenWithSpan) -> Result<Merge, ParserError> {
46        let optimizer_hints = self.maybe_parse_optimizer_hints()?;
47        let into = self.parse_keyword(Keyword::INTO);
48
49        let table = self.parse_table_factor()?;
50
51        self.expect_keyword_is(Keyword::USING)?;
52        let source = self.parse_table_factor()?;
53        self.expect_keyword_is(Keyword::ON)?;
54        let on = self.parse_expr()?;
55        let clauses = self.parse_merge_clauses()?;
56        let output = match self.parse_one_of_keywords(&[Keyword::OUTPUT, Keyword::RETURNING]) {
57            Some(keyword) => Some(self.parse_output(keyword, self.get_current_token().clone())?),
58            None => None,
59        };
60
61        Ok(Merge {
62            merge_token: merge_token.into(),
63            optimizer_hints,
64            into,
65            table,
66            source,
67            on: Box::new(on),
68            clauses,
69            output,
70        })
71    }
72
73    fn parse_merge_clauses(&mut self) -> Result<Vec<MergeClause>, ParserError> {
74        let mut clauses = vec![];
75        loop {
76            if !(self.parse_keyword(Keyword::WHEN)) {
77                break;
78            }
79            let when_token = self.get_current_token().clone();
80
81            let mut clause_kind = MergeClauseKind::Matched;
82            if self.parse_keyword(Keyword::NOT) {
83                clause_kind = MergeClauseKind::NotMatched;
84            }
85            self.expect_keyword_is(Keyword::MATCHED)?;
86
87            if matches!(clause_kind, MergeClauseKind::NotMatched)
88                && self.parse_keywords(&[Keyword::BY, Keyword::SOURCE])
89            {
90                clause_kind = MergeClauseKind::NotMatchedBySource;
91            } else if matches!(clause_kind, MergeClauseKind::NotMatched)
92                && self.parse_keywords(&[Keyword::BY, Keyword::TARGET])
93            {
94                clause_kind = MergeClauseKind::NotMatchedByTarget;
95            }
96
97            let predicate = if self.parse_keyword(Keyword::AND) {
98                Some(self.parse_expr()?)
99            } else {
100                None
101            };
102
103            self.expect_keyword_is(Keyword::THEN)?;
104
105            let merge_clause = match self.parse_one_of_keywords(&[
106                Keyword::UPDATE,
107                Keyword::INSERT,
108                Keyword::DELETE,
109            ]) {
110                Some(Keyword::UPDATE) => {
111                    if matches!(
112                        clause_kind,
113                        MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget
114                    ) {
115                        return parser_err!(
116                            format_args!("UPDATE is not allowed in a {clause_kind} merge clause"),
117                            self.get_current_token().span.start
118                        );
119                    }
120
121                    let update_token = self.get_current_token().clone();
122                    self.expect_keyword_is(Keyword::SET)?;
123                    let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
124                    let update_predicate = if self.parse_keyword(Keyword::WHERE) {
125                        Some(self.parse_expr()?)
126                    } else {
127                        None
128                    };
129                    let delete_predicate = if self.parse_keyword(Keyword::DELETE) {
130                        let _ = self.expect_keyword(Keyword::WHERE)?;
131                        Some(self.parse_expr()?)
132                    } else {
133                        None
134                    };
135                    MergeAction::Update(MergeUpdateExpr {
136                        update_token: update_token.into(),
137                        assignments,
138                        update_predicate,
139                        delete_predicate,
140                    })
141                }
142                Some(Keyword::DELETE) => {
143                    if matches!(
144                        clause_kind,
145                        MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget
146                    ) {
147                        return parser_err!(
148                            format_args!("DELETE is not allowed in a {clause_kind} merge clause"),
149                            self.get_current_token().span.start
150                        );
151                    };
152
153                    let delete_token = self.get_current_token().clone();
154                    MergeAction::Delete {
155                        delete_token: delete_token.into(),
156                    }
157                }
158                Some(Keyword::INSERT) => {
159                    if !matches!(
160                        clause_kind,
161                        MergeClauseKind::NotMatched | MergeClauseKind::NotMatchedByTarget
162                    ) {
163                        return parser_err!(
164                            format_args!("INSERT is not allowed in a {clause_kind} merge clause"),
165                            self.get_current_token().span.start
166                        );
167                    };
168
169                    let insert_token = self.get_current_token().clone();
170                    let is_mysql = dialect_of!(self is MySqlDialect);
171
172                    let columns = self.parse_merge_clause_insert_columns(is_mysql)?;
173                    let (kind, kind_token) = if dialect_of!(self is BigQueryDialect | GenericDialect)
174                        && self.parse_keyword(Keyword::ROW)
175                    {
176                        (MergeInsertKind::Row, self.get_current_token().clone())
177                    } else {
178                        self.expect_keyword_is(Keyword::VALUES)?;
179                        let values_token = self.get_current_token().clone();
180                        let values = self.parse_values(is_mysql, false)?;
181                        (MergeInsertKind::Values(values), values_token)
182                    };
183                    let insert_predicate = if self.parse_keyword(Keyword::WHERE) {
184                        Some(self.parse_expr()?)
185                    } else {
186                        None
187                    };
188
189                    MergeAction::Insert(MergeInsertExpr {
190                        insert_token: insert_token.into(),
191                        columns,
192                        kind_token: kind_token.into(),
193                        kind,
194                        insert_predicate,
195                    })
196                }
197                _ => {
198                    return parser_err!(
199                        "expected UPDATE, DELETE or INSERT in merge clause",
200                        self.peek_token_ref().span.start
201                    );
202                }
203            };
204            clauses.push(MergeClause {
205                when_token: when_token.into(),
206                clause_kind,
207                predicate,
208                action: merge_clause,
209            });
210        }
211        Ok(clauses)
212    }
213
214    fn parse_merge_clause_insert_columns(
215        &mut self,
216        allow_empty: bool,
217    ) -> Result<Vec<ObjectName>, ParserError> {
218        self.parse_parenthesized_qualified_column_list(IsOptional::Optional, allow_empty)
219    }
220
221    /// Parses an `OUTPUT` clause if present (MSSQL).
222    pub(super) fn maybe_parse_output_clause(
223        &mut self,
224    ) -> Result<Option<OutputClause>, ParserError> {
225        if self.parse_keyword(Keyword::OUTPUT) {
226            Ok(Some(self.parse_output(
227                Keyword::OUTPUT,
228                self.get_current_token().clone(),
229            )?))
230        } else {
231            Ok(None)
232        }
233    }
234
235    pub(super) fn parse_output(
236        &mut self,
237        start_keyword: Keyword,
238        start_token: TokenWithSpan,
239    ) -> Result<OutputClause, ParserError> {
240        let select_items = self.parse_projection()?;
241        let into_table = if start_keyword == Keyword::OUTPUT && self.peek_keyword(Keyword::INTO) {
242            self.expect_keyword_is(Keyword::INTO)?;
243            Some(self.parse_select_into()?)
244        } else {
245            None
246        };
247
248        Ok(if start_keyword == Keyword::OUTPUT {
249            OutputClause::Output {
250                output_token: start_token.into(),
251                select_items,
252                into_table,
253            }
254        } else {
255            OutputClause::Returning {
256                returning_token: start_token.into(),
257                select_items,
258            }
259        })
260    }
261}