sqlparser/ast/
dml.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[cfg(not(feature = "std"))]
19use alloc::{
20    boxed::Box,
21    format,
22    string::{String, ToString},
23    vec::Vec,
24};
25
26use core::fmt::{self, Display};
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29#[cfg(feature = "visitor")]
30use yachtsql_sqlparser_derive::{Visit, VisitMut};
31
32use crate::display_utils::{indented_list, Indent, SpaceOrNewline};
33
34use super::{
35    display_comma_separated, query::InputFormatClause, Assignment, Expr, FromTable, Ident,
36    InsertAliases, MysqlInsertPriority, ObjectName, OnInsert, OrderByExpr, Query, SelectItem,
37    Setting, SqliteOnConflict, TableObject, TableWithJoins,
38};
39
40/// INSERT statement.
41#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
44pub struct Insert {
45    /// Only for Sqlite
46    pub or: Option<SqliteOnConflict>,
47    /// Only for mysql
48    pub ignore: bool,
49    /// INTO - optional keyword
50    pub into: bool,
51    /// TABLE
52    pub table: TableObject,
53    /// table_name as foo (for PostgreSQL)
54    pub table_alias: Option<Ident>,
55    /// COLUMNS
56    pub columns: Vec<Ident>,
57    /// Overwrite (Hive)
58    pub overwrite: bool,
59    /// A SQL query that specifies what to insert
60    pub source: Option<Box<Query>>,
61    /// MySQL `INSERT INTO ... SET`
62    /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
63    pub assignments: Vec<Assignment>,
64    /// partitioned insert (Hive)
65    pub partitioned: Option<Vec<Expr>>,
66    /// Columns defined after PARTITION
67    pub after_columns: Vec<Ident>,
68    /// whether the insert has the table keyword (Hive)
69    pub has_table_keyword: bool,
70    pub on: Option<OnInsert>,
71    /// RETURNING
72    pub returning: Option<Vec<SelectItem>>,
73    /// Only for mysql
74    pub replace_into: bool,
75    /// Only for mysql
76    pub priority: Option<MysqlInsertPriority>,
77    /// Only for mysql
78    pub insert_alias: Option<InsertAliases>,
79    /// Settings used for ClickHouse.
80    ///
81    /// ClickHouse syntax: `INSERT INTO tbl SETTINGS format_template_resultset = '/some/path/resultset.format'`
82    ///
83    /// [ClickHouse `INSERT INTO`](https://clickhouse.com/docs/en/sql-reference/statements/insert-into)
84    pub settings: Option<Vec<Setting>>,
85    /// Format for `INSERT` statement when not using standard SQL format. Can be e.g. `CSV`,
86    /// `JSON`, `JSONAsString`, `LineAsString` and more.
87    ///
88    /// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
89    ///
90    /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
91    pub format_clause: Option<InputFormatClause>,
92}
93
94impl Display for Insert {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        let table_name = if let Some(alias) = &self.table_alias {
97            format!("{0} AS {alias}", self.table)
98        } else {
99            self.table.to_string()
100        };
101
102        if let Some(on_conflict) = self.or {
103            write!(f, "INSERT {on_conflict} INTO {table_name} ")?;
104        } else {
105            write!(
106                f,
107                "{start}",
108                start = if self.replace_into {
109                    "REPLACE"
110                } else {
111                    "INSERT"
112                },
113            )?;
114            if let Some(priority) = self.priority {
115                write!(f, " {priority}",)?;
116            }
117
118            write!(
119                f,
120                "{ignore}{over}{int}{tbl} {table_name} ",
121                table_name = table_name,
122                ignore = if self.ignore { " IGNORE" } else { "" },
123                over = if self.overwrite { " OVERWRITE" } else { "" },
124                int = if self.into { " INTO" } else { "" },
125                tbl = if self.has_table_keyword { " TABLE" } else { "" },
126            )?;
127        }
128        if !self.columns.is_empty() {
129            write!(f, "({})", display_comma_separated(&self.columns))?;
130            SpaceOrNewline.fmt(f)?;
131        }
132        if let Some(ref parts) = self.partitioned {
133            if !parts.is_empty() {
134                write!(f, "PARTITION ({})", display_comma_separated(parts))?;
135                SpaceOrNewline.fmt(f)?;
136            }
137        }
138        if !self.after_columns.is_empty() {
139            write!(f, "({})", display_comma_separated(&self.after_columns))?;
140            SpaceOrNewline.fmt(f)?;
141        }
142
143        if let Some(settings) = &self.settings {
144            write!(f, "SETTINGS {}", display_comma_separated(settings))?;
145            SpaceOrNewline.fmt(f)?;
146        }
147
148        if let Some(source) = &self.source {
149            source.fmt(f)?;
150        } else if !self.assignments.is_empty() {
151            write!(f, "SET")?;
152            indented_list(f, &self.assignments)?;
153        } else if let Some(format_clause) = &self.format_clause {
154            format_clause.fmt(f)?;
155        } else if self.columns.is_empty() {
156            write!(f, "DEFAULT VALUES")?;
157        }
158
159        if let Some(insert_alias) = &self.insert_alias {
160            write!(f, " AS {0}", insert_alias.row_alias)?;
161
162            if let Some(col_aliases) = &insert_alias.col_aliases {
163                if !col_aliases.is_empty() {
164                    write!(f, " ({})", display_comma_separated(col_aliases))?;
165                }
166            }
167        }
168
169        if let Some(on) = &self.on {
170            write!(f, "{on}")?;
171        }
172
173        if let Some(returning) = &self.returning {
174            SpaceOrNewline.fmt(f)?;
175            f.write_str("RETURNING")?;
176            indented_list(f, returning)?;
177        }
178        Ok(())
179    }
180}
181
182/// DELETE statement.
183#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
186pub struct Delete {
187    /// Multi tables delete are supported in mysql
188    pub tables: Vec<ObjectName>,
189    /// FROM
190    pub from: FromTable,
191    /// USING (Snowflake, Postgres, MySQL)
192    pub using: Option<Vec<TableWithJoins>>,
193    /// WHERE
194    pub selection: Option<Expr>,
195    /// RETURNING
196    pub returning: Option<Vec<SelectItem>>,
197    /// ORDER BY (MySQL)
198    pub order_by: Vec<OrderByExpr>,
199    /// LIMIT (MySQL)
200    pub limit: Option<Expr>,
201}
202
203impl Display for Delete {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        f.write_str("DELETE")?;
206        if !self.tables.is_empty() {
207            indented_list(f, &self.tables)?;
208        }
209        match &self.from {
210            FromTable::WithFromKeyword(from) => {
211                f.write_str(" FROM")?;
212                indented_list(f, from)?;
213            }
214            FromTable::WithoutKeyword(from) => {
215                indented_list(f, from)?;
216            }
217        }
218        if let Some(using) = &self.using {
219            SpaceOrNewline.fmt(f)?;
220            f.write_str("USING")?;
221            indented_list(f, using)?;
222        }
223        if let Some(selection) = &self.selection {
224            SpaceOrNewline.fmt(f)?;
225            f.write_str("WHERE")?;
226            SpaceOrNewline.fmt(f)?;
227            Indent(selection).fmt(f)?;
228        }
229        if let Some(returning) = &self.returning {
230            SpaceOrNewline.fmt(f)?;
231            f.write_str("RETURNING")?;
232            indented_list(f, returning)?;
233        }
234        if !self.order_by.is_empty() {
235            SpaceOrNewline.fmt(f)?;
236            f.write_str("ORDER BY")?;
237            indented_list(f, &self.order_by)?;
238        }
239        if let Some(limit) = &self.limit {
240            SpaceOrNewline.fmt(f)?;
241            f.write_str("LIMIT")?;
242            SpaceOrNewline.fmt(f)?;
243            Indent(limit).fmt(f)?;
244        }
245        Ok(())
246    }
247}