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, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident, ObjectName, OnCommit,
12    Query, 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 partition_by: Option<Box<Expr>>,
76    pub cluster_by: Option<Vec<Ident>>,
77    pub options: Option<Vec<SqlOption>>,
78    pub strict: bool,
79}
80
81impl CreateTableBuilder {
82    pub fn new(name: ObjectName) -> Self {
83        Self {
84            or_replace: false,
85            temporary: false,
86            external: false,
87            global: None,
88            if_not_exists: false,
89            transient: false,
90            name,
91            columns: vec![],
92            constraints: vec![],
93            hive_distribution: HiveDistributionStyle::NONE,
94            hive_formats: None,
95            table_properties: vec![],
96            with_options: vec![],
97            file_format: None,
98            location: None,
99            query: None,
100            without_rowid: false,
101            like: None,
102            clone: None,
103            engine: None,
104            comment: None,
105            auto_increment_offset: None,
106            default_charset: None,
107            collation: None,
108            on_commit: None,
109            on_cluster: None,
110            order_by: None,
111            partition_by: None,
112            cluster_by: None,
113            options: None,
114            strict: false,
115        }
116    }
117    pub fn or_replace(mut self, or_replace: bool) -> Self {
118        self.or_replace = or_replace;
119        self
120    }
121
122    pub fn temporary(mut self, temporary: bool) -> Self {
123        self.temporary = temporary;
124        self
125    }
126
127    pub fn external(mut self, external: bool) -> Self {
128        self.external = external;
129        self
130    }
131
132    pub fn global(mut self, global: Option<bool>) -> Self {
133        self.global = global;
134        self
135    }
136
137    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
138        self.if_not_exists = if_not_exists;
139        self
140    }
141
142    pub fn transient(mut self, transient: bool) -> Self {
143        self.transient = transient;
144        self
145    }
146
147    pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
148        self.columns = columns;
149        self
150    }
151
152    pub fn constraints(mut self, constraints: Vec<TableConstraint>) -> Self {
153        self.constraints = constraints;
154        self
155    }
156
157    pub fn hive_distribution(mut self, hive_distribution: HiveDistributionStyle) -> Self {
158        self.hive_distribution = hive_distribution;
159        self
160    }
161
162    pub fn hive_formats(mut self, hive_formats: Option<HiveFormat>) -> Self {
163        self.hive_formats = hive_formats;
164        self
165    }
166
167    pub fn table_properties(mut self, table_properties: Vec<SqlOption>) -> Self {
168        self.table_properties = table_properties;
169        self
170    }
171
172    pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
173        self.with_options = with_options;
174        self
175    }
176    pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
177        self.file_format = file_format;
178        self
179    }
180    pub fn location(mut self, location: Option<String>) -> Self {
181        self.location = location;
182        self
183    }
184
185    pub fn query(mut self, query: Option<Box<Query>>) -> Self {
186        self.query = query;
187        self
188    }
189    pub fn without_rowid(mut self, without_rowid: bool) -> Self {
190        self.without_rowid = without_rowid;
191        self
192    }
193
194    pub fn like(mut self, like: Option<ObjectName>) -> Self {
195        self.like = like;
196        self
197    }
198
199    // Different name to allow the object to be cloned
200    pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
201        self.clone = clone;
202        self
203    }
204
205    pub fn engine(mut self, engine: Option<String>) -> Self {
206        self.engine = engine;
207        self
208    }
209
210    pub fn comment(mut self, comment: Option<String>) -> Self {
211        self.comment = comment;
212        self
213    }
214
215    pub fn auto_increment_offset(mut self, offset: Option<u32>) -> Self {
216        self.auto_increment_offset = offset;
217        self
218    }
219
220    pub fn default_charset(mut self, default_charset: Option<String>) -> Self {
221        self.default_charset = default_charset;
222        self
223    }
224
225    pub fn collation(mut self, collation: Option<String>) -> Self {
226        self.collation = collation;
227        self
228    }
229
230    pub fn on_commit(mut self, on_commit: Option<OnCommit>) -> Self {
231        self.on_commit = on_commit;
232        self
233    }
234
235    pub fn on_cluster(mut self, on_cluster: Option<String>) -> Self {
236        self.on_cluster = on_cluster;
237        self
238    }
239
240    pub fn order_by(mut self, order_by: Option<Vec<Ident>>) -> Self {
241        self.order_by = order_by;
242        self
243    }
244
245    pub fn partition_by(mut self, partition_by: Option<Box<Expr>>) -> Self {
246        self.partition_by = partition_by;
247        self
248    }
249
250    pub fn cluster_by(mut self, cluster_by: Option<Vec<Ident>>) -> Self {
251        self.cluster_by = cluster_by;
252        self
253    }
254
255    pub fn options(mut self, options: Option<Vec<SqlOption>>) -> Self {
256        self.options = options;
257        self
258    }
259
260    pub fn strict(mut self, strict: bool) -> Self {
261        self.strict = strict;
262        self
263    }
264
265    pub fn build(self) -> Statement {
266        Statement::CreateTable {
267            or_replace: self.or_replace,
268            temporary: self.temporary,
269            external: self.external,
270            global: self.global,
271            if_not_exists: self.if_not_exists,
272            transient: self.transient,
273            name: self.name,
274            columns: self.columns,
275            constraints: self.constraints,
276            hive_distribution: self.hive_distribution,
277            hive_formats: self.hive_formats,
278            table_properties: self.table_properties,
279            with_options: self.with_options,
280            file_format: self.file_format,
281            location: self.location,
282            query: self.query,
283            without_rowid: self.without_rowid,
284            like: self.like,
285            clone: self.clone,
286            engine: self.engine,
287            comment: self.comment,
288            auto_increment_offset: self.auto_increment_offset,
289            default_charset: self.default_charset,
290            collation: self.collation,
291            on_commit: self.on_commit,
292            on_cluster: self.on_cluster,
293            order_by: self.order_by,
294            partition_by: self.partition_by,
295            cluster_by: self.cluster_by,
296            options: self.options,
297            strict: self.strict,
298        }
299    }
300}
301
302impl TryFrom<Statement> for CreateTableBuilder {
303    type Error = ParserError;
304
305    // As the builder can be transformed back to a statement, it shouldn't be a problem to take the
306    // ownership.
307    fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
308        match stmt {
309            Statement::CreateTable {
310                or_replace,
311                temporary,
312                external,
313                global,
314                if_not_exists,
315                transient,
316                name,
317                columns,
318                constraints,
319                hive_distribution,
320                hive_formats,
321                table_properties,
322                with_options,
323                file_format,
324                location,
325                query,
326                without_rowid,
327                like,
328                clone,
329                engine,
330                comment,
331                auto_increment_offset,
332                default_charset,
333                collation,
334                on_commit,
335                on_cluster,
336                order_by,
337                partition_by,
338                cluster_by,
339                options,
340                strict,
341            } => Ok(Self {
342                or_replace,
343                temporary,
344                external,
345                global,
346                if_not_exists,
347                transient,
348                name,
349                columns,
350                constraints,
351                hive_distribution,
352                hive_formats,
353                table_properties,
354                with_options,
355                file_format,
356                location,
357                query,
358                without_rowid,
359                like,
360                clone,
361                engine,
362                comment,
363                auto_increment_offset,
364                default_charset,
365                collation,
366                on_commit,
367                on_cluster,
368                order_by,
369                partition_by,
370                cluster_by,
371                options,
372                strict,
373            }),
374            _ => Err(ParserError::ParserError(format!(
375                "Expected create table statement, but received: {stmt}"
376            ))),
377        }
378    }
379}
380
381/// Helper return type when parsing configuration for a BigQuery `CREATE TABLE` statement.
382#[derive(Default)]
383pub(crate) struct BigQueryTableConfiguration {
384    pub partition_by: Option<Box<Expr>>,
385    pub cluster_by: Option<Vec<Ident>>,
386    pub options: Option<Vec<SqlOption>>,
387}
388
389#[cfg(test)]
390mod tests {
391    use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
392    use crate::ast::{Ident, ObjectName, Statement};
393    use crate::parser::ParserError;
394
395    #[test]
396    pub fn test_from_valid_statement() {
397        let builder = CreateTableBuilder::new(ObjectName(vec![Ident::new("table_name")]));
398
399        let stmt = builder.clone().build();
400
401        assert_eq!(builder, CreateTableBuilder::try_from(stmt).unwrap());
402    }
403
404    #[test]
405    pub fn test_from_invalid_statement() {
406        let stmt = Statement::Commit { chain: false };
407
408        assert_eq!(
409            CreateTableBuilder::try_from(stmt).unwrap_err(),
410            ParserError::ParserError(
411                "Expected create table statement, but received: COMMIT".to_owned()
412            )
413        );
414    }
415}