sqlparser/ast/helpers/
stmt_create_table.rs

1#[cfg(not(feature = "std"))]
2use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "visitor")]
8use sqlparser_derive::{Visit, VisitMut};
9
10use crate::ast::{
11    ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit, Query,
12    SqlOption, Statement, TableConstraint,
13};
14use crate::parser::ParserError;
15
16/// Builder for create table statement variant ([1]).
17///
18/// This structure helps building and accessing a create table with more ease, without needing to:
19/// - Match the enum itself a lot of times; or
20/// - Moving a lot of variables around the code.
21///
22/// # Example
23/// ```rust
24/// use sqlparser::ast::helpers::stmt_create_table::CreateTableBuilder;
25/// use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName};
26/// let builder = CreateTableBuilder::new(ObjectName(vec![Ident::new("table_name")]))
27///    .if_not_exists(true)
28///    .columns(vec![ColumnDef {
29///        name: Ident::new("c1"),
30///        data_type: DataType::Int(None),
31///        collation: None,
32///        options: vec![],
33/// }]);
34/// // You can access internal elements with ease
35/// assert!(builder.if_not_exists);
36/// // Convert to a statement
37/// assert_eq!(
38///    builder.build().to_string(),
39///    "CREATE TABLE IF NOT EXISTS table_name (c1 INT)"
40/// )
41/// ```
42///
43/// [1]: crate::ast::Statement::CreateTable
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
47pub struct CreateTableBuilder {
48    pub or_replace: bool,
49    pub temporary: bool,
50    pub external: bool,
51    pub global: Option<bool>,
52    pub if_not_exists: bool,
53    pub transient: bool,
54    pub name: ObjectName,
55    pub columns: Vec<ColumnDef>,
56    pub constraints: Vec<TableConstraint>,
57    pub hive_distribution: HiveDistributionStyle,
58    pub hive_formats: Option<HiveFormat>,
59    pub table_properties: Vec<SqlOption>,
60    pub with_options: Vec<SqlOption>,
61    pub file_format: Option<FileFormat>,
62    pub location: Option<String>,
63    pub query: Option<Box<Query>>,
64    pub without_rowid: bool,
65    pub like: Option<ObjectName>,
66    pub clone: Option<ObjectName>,
67    pub engine: Option<String>,
68    pub comment: Option<String>,
69    pub auto_increment_offset: Option<u32>,
70    pub default_charset: Option<String>,
71    pub collation: Option<String>,
72    pub on_commit: Option<OnCommit>,
73    pub on_cluster: Option<String>,
74    pub order_by: Option<Vec<Ident>>,
75    pub strict: bool,
76}
77
78impl CreateTableBuilder {
79    pub fn new(name: ObjectName) -> Self {
80        Self {
81            or_replace: false,
82            temporary: false,
83            external: false,
84            global: None,
85            if_not_exists: false,
86            transient: false,
87            name,
88            columns: vec![],
89            constraints: vec![],
90            hive_distribution: HiveDistributionStyle::NONE,
91            hive_formats: None,
92            table_properties: vec![],
93            with_options: vec![],
94            file_format: None,
95            location: None,
96            query: None,
97            without_rowid: false,
98            like: None,
99            clone: None,
100            engine: None,
101            comment: None,
102            auto_increment_offset: None,
103            default_charset: None,
104            collation: None,
105            on_commit: None,
106            on_cluster: None,
107            order_by: None,
108            strict: false,
109        }
110    }
111    pub fn or_replace(mut self, or_replace: bool) -> Self {
112        self.or_replace = or_replace;
113        self
114    }
115
116    pub fn temporary(mut self, temporary: bool) -> Self {
117        self.temporary = temporary;
118        self
119    }
120
121    pub fn external(mut self, external: bool) -> Self {
122        self.external = external;
123        self
124    }
125
126    pub fn global(mut self, global: Option<bool>) -> Self {
127        self.global = global;
128        self
129    }
130
131    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
132        self.if_not_exists = if_not_exists;
133        self
134    }
135
136    pub fn transient(mut self, transient: bool) -> Self {
137        self.transient = transient;
138        self
139    }
140
141    pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
142        self.columns = columns;
143        self
144    }
145
146    pub fn constraints(mut self, constraints: Vec<TableConstraint>) -> Self {
147        self.constraints = constraints;
148        self
149    }
150
151    pub fn hive_distribution(mut self, hive_distribution: HiveDistributionStyle) -> Self {
152        self.hive_distribution = hive_distribution;
153        self
154    }
155
156    pub fn hive_formats(mut self, hive_formats: Option<HiveFormat>) -> Self {
157        self.hive_formats = hive_formats;
158        self
159    }
160
161    pub fn table_properties(mut self, table_properties: Vec<SqlOption>) -> Self {
162        self.table_properties = table_properties;
163        self
164    }
165
166    pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
167        self.with_options = with_options;
168        self
169    }
170    pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
171        self.file_format = file_format;
172        self
173    }
174    pub fn location(mut self, location: Option<String>) -> Self {
175        self.location = location;
176        self
177    }
178
179    pub fn query(mut self, query: Option<Box<Query>>) -> Self {
180        self.query = query;
181        self
182    }
183    pub fn without_rowid(mut self, without_rowid: bool) -> Self {
184        self.without_rowid = without_rowid;
185        self
186    }
187
188    pub fn like(mut self, like: Option<ObjectName>) -> Self {
189        self.like = like;
190        self
191    }
192
193    // Different name to allow the object to be cloned
194    pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
195        self.clone = clone;
196        self
197    }
198
199    pub fn engine(mut self, engine: Option<String>) -> Self {
200        self.engine = engine;
201        self
202    }
203
204    pub fn comment(mut self, comment: Option<String>) -> Self {
205        self.comment = comment;
206        self
207    }
208
209    pub fn auto_increment_offset(mut self, offset: Option<u32>) -> Self {
210        self.auto_increment_offset = offset;
211        self
212    }
213
214    pub fn default_charset(mut self, default_charset: Option<String>) -> Self {
215        self.default_charset = default_charset;
216        self
217    }
218
219    pub fn collation(mut self, collation: Option<String>) -> Self {
220        self.collation = collation;
221        self
222    }
223
224    pub fn on_commit(mut self, on_commit: Option<OnCommit>) -> Self {
225        self.on_commit = on_commit;
226        self
227    }
228
229    pub fn on_cluster(mut self, on_cluster: Option<String>) -> Self {
230        self.on_cluster = on_cluster;
231        self
232    }
233
234    pub fn order_by(mut self, order_by: Option<Vec<Ident>>) -> Self {
235        self.order_by = order_by;
236        self
237    }
238
239    pub fn strict(mut self, strict: bool) -> Self {
240        self.strict = strict;
241        self
242    }
243
244    pub fn build(self) -> Statement {
245        Statement::CreateTable {
246            or_replace: self.or_replace,
247            temporary: self.temporary,
248            external: self.external,
249            global: self.global,
250            if_not_exists: self.if_not_exists,
251            transient: self.transient,
252            name: self.name,
253            columns: self.columns,
254            constraints: self.constraints,
255            hive_distribution: self.hive_distribution,
256            hive_formats: self.hive_formats,
257            table_properties: self.table_properties,
258            with_options: self.with_options,
259            file_format: self.file_format,
260            location: self.location,
261            query: self.query,
262            without_rowid: self.without_rowid,
263            like: self.like,
264            clone: self.clone,
265            engine: self.engine,
266            comment: self.comment,
267            auto_increment_offset: self.auto_increment_offset,
268            default_charset: self.default_charset,
269            collation: self.collation,
270            on_commit: self.on_commit,
271            on_cluster: self.on_cluster,
272            order_by: self.order_by,
273            strict: self.strict,
274        }
275    }
276}
277
278impl TryFrom<Statement> for CreateTableBuilder {
279    type Error = ParserError;
280
281    // As the builder can be transformed back to a statement, it shouldn't be a problem to take the
282    // ownership.
283    fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
284        match stmt {
285            Statement::CreateTable {
286                or_replace,
287                temporary,
288                external,
289                global,
290                if_not_exists,
291                transient,
292                name,
293                columns,
294                constraints,
295                hive_distribution,
296                hive_formats,
297                table_properties,
298                with_options,
299                file_format,
300                location,
301                query,
302                without_rowid,
303                like,
304                clone,
305                engine,
306                comment,
307                auto_increment_offset,
308                default_charset,
309                collation,
310                on_commit,
311                on_cluster,
312                order_by,
313                strict,
314            } => Ok(Self {
315                or_replace,
316                temporary,
317                external,
318                global,
319                if_not_exists,
320                transient,
321                name,
322                columns,
323                constraints,
324                hive_distribution,
325                hive_formats,
326                table_properties,
327                with_options,
328                file_format,
329                location,
330                query,
331                without_rowid,
332                like,
333                clone,
334                engine,
335                comment,
336                auto_increment_offset,
337                default_charset,
338                collation,
339                on_commit,
340                on_cluster,
341                order_by,
342                strict,
343            }),
344            _ => Err(ParserError::ParserError(format!(
345                "Expected create table statement, but received: {stmt}"
346            ))),
347        }
348    }
349}
350
351#[cfg(test)]
352mod tests {
353    use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
354    use crate::ast::{Ident, ObjectName, Statement};
355    use crate::parser::ParserError;
356
357    #[test]
358    pub fn test_from_valid_statement() {
359        let builder = CreateTableBuilder::new(ObjectName(vec![Ident::new("table_name")]));
360
361        let stmt = builder.clone().build();
362
363        assert_eq!(builder, CreateTableBuilder::try_from(stmt).unwrap());
364    }
365
366    #[test]
367    pub fn test_from_invalid_statement() {
368        let stmt = Statement::Commit { chain: false };
369
370        assert_eq!(
371            CreateTableBuilder::try_from(stmt).unwrap_err(),
372            ParserError::ParserError(
373                "Expected create table statement, but received: COMMIT".to_owned()
374            )
375        );
376    }
377}