sqlparser/ast/helpers/
stmt_create_table.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::{boxed::Box, format, string::String, vec, vec::Vec};
20
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24#[cfg(feature = "visitor")]
25use sqlparser_derive::{Visit, VisitMut};
26
27use super::super::dml::CreateTable;
28use crate::ast::{
29    ClusteredBy, ColumnDef, CommentDef, Expr, FileFormat, HiveDistributionStyle, HiveFormat, Ident,
30    ObjectName, OnCommit, OneOrManyWithParens, Query, RowAccessPolicy, SqlOption, Statement,
31    StorageSerializationPolicy, TableConstraint, TableEngine, Tag, WrappedCollection,
32};
33use crate::parser::ParserError;
34
35/// Builder for create table statement variant ([1]).
36///
37/// This structure helps building and accessing a create table with more ease, without needing to:
38/// - Match the enum itself a lot of times; or
39/// - Moving a lot of variables around the code.
40///
41/// # Example
42/// ```rust
43/// use sqlparser::ast::helpers::stmt_create_table::CreateTableBuilder;
44/// use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName};
45/// let builder = CreateTableBuilder::new(ObjectName::from(vec![Ident::new("table_name")]))
46///    .if_not_exists(true)
47///    .columns(vec![ColumnDef {
48///        name: Ident::new("c1"),
49///        data_type: DataType::Int(None),
50///        options: vec![],
51/// }]);
52/// // You can access internal elements with ease
53/// assert!(builder.if_not_exists);
54/// // Convert to a statement
55/// assert_eq!(
56///    builder.build().to_string(),
57///    "CREATE TABLE IF NOT EXISTS table_name (c1 INT)"
58/// )
59/// ```
60///
61/// [1]: crate::ast::Statement::CreateTable
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
65pub struct CreateTableBuilder {
66    pub or_replace: bool,
67    pub temporary: bool,
68    pub external: bool,
69    pub global: Option<bool>,
70    pub if_not_exists: bool,
71    pub transient: bool,
72    pub volatile: bool,
73    pub iceberg: bool,
74    pub name: ObjectName,
75    pub columns: Vec<ColumnDef>,
76    pub constraints: Vec<TableConstraint>,
77    pub hive_distribution: HiveDistributionStyle,
78    pub hive_formats: Option<HiveFormat>,
79    pub table_properties: Vec<SqlOption>,
80    pub with_options: Vec<SqlOption>,
81    pub file_format: Option<FileFormat>,
82    pub location: Option<String>,
83    pub query: Option<Box<Query>>,
84    pub without_rowid: bool,
85    pub like: Option<ObjectName>,
86    pub clone: Option<ObjectName>,
87    pub engine: Option<TableEngine>,
88    pub comment: Option<CommentDef>,
89    pub auto_increment_offset: Option<u32>,
90    pub default_charset: Option<String>,
91    pub collation: Option<String>,
92    pub on_commit: Option<OnCommit>,
93    pub on_cluster: Option<Ident>,
94    pub primary_key: Option<Box<Expr>>,
95    pub order_by: Option<OneOrManyWithParens<Expr>>,
96    pub partition_by: Option<Box<Expr>>,
97    pub cluster_by: Option<WrappedCollection<Vec<Ident>>>,
98    pub clustered_by: Option<ClusteredBy>,
99    pub options: Option<Vec<SqlOption>>,
100    pub inherits: Option<Vec<ObjectName>>,
101    pub strict: bool,
102    pub copy_grants: bool,
103    pub enable_schema_evolution: Option<bool>,
104    pub change_tracking: Option<bool>,
105    pub data_retention_time_in_days: Option<u64>,
106    pub max_data_extension_time_in_days: Option<u64>,
107    pub default_ddl_collation: Option<String>,
108    pub with_aggregation_policy: Option<ObjectName>,
109    pub with_row_access_policy: Option<RowAccessPolicy>,
110    pub with_tags: Option<Vec<Tag>>,
111    pub base_location: Option<String>,
112    pub external_volume: Option<String>,
113    pub catalog: Option<String>,
114    pub catalog_sync: Option<String>,
115    pub storage_serialization_policy: Option<StorageSerializationPolicy>,
116}
117
118impl CreateTableBuilder {
119    pub fn new(name: ObjectName) -> Self {
120        Self {
121            or_replace: false,
122            temporary: false,
123            external: false,
124            global: None,
125            if_not_exists: false,
126            transient: false,
127            volatile: false,
128            iceberg: false,
129            name,
130            columns: vec![],
131            constraints: vec![],
132            hive_distribution: HiveDistributionStyle::NONE,
133            hive_formats: None,
134            table_properties: vec![],
135            with_options: vec![],
136            file_format: None,
137            location: None,
138            query: None,
139            without_rowid: false,
140            like: None,
141            clone: None,
142            engine: None,
143            comment: None,
144            auto_increment_offset: None,
145            default_charset: None,
146            collation: None,
147            on_commit: None,
148            on_cluster: None,
149            primary_key: None,
150            order_by: None,
151            partition_by: None,
152            cluster_by: None,
153            clustered_by: None,
154            options: None,
155            inherits: None,
156            strict: false,
157            copy_grants: false,
158            enable_schema_evolution: None,
159            change_tracking: None,
160            data_retention_time_in_days: None,
161            max_data_extension_time_in_days: None,
162            default_ddl_collation: None,
163            with_aggregation_policy: None,
164            with_row_access_policy: None,
165            with_tags: None,
166            base_location: None,
167            external_volume: None,
168            catalog: None,
169            catalog_sync: None,
170            storage_serialization_policy: None,
171        }
172    }
173    pub fn or_replace(mut self, or_replace: bool) -> Self {
174        self.or_replace = or_replace;
175        self
176    }
177
178    pub fn temporary(mut self, temporary: bool) -> Self {
179        self.temporary = temporary;
180        self
181    }
182
183    pub fn external(mut self, external: bool) -> Self {
184        self.external = external;
185        self
186    }
187
188    pub fn global(mut self, global: Option<bool>) -> Self {
189        self.global = global;
190        self
191    }
192
193    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
194        self.if_not_exists = if_not_exists;
195        self
196    }
197
198    pub fn transient(mut self, transient: bool) -> Self {
199        self.transient = transient;
200        self
201    }
202
203    pub fn volatile(mut self, volatile: bool) -> Self {
204        self.volatile = volatile;
205        self
206    }
207
208    pub fn iceberg(mut self, iceberg: bool) -> Self {
209        self.iceberg = iceberg;
210        self
211    }
212
213    pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
214        self.columns = columns;
215        self
216    }
217
218    pub fn constraints(mut self, constraints: Vec<TableConstraint>) -> Self {
219        self.constraints = constraints;
220        self
221    }
222
223    pub fn hive_distribution(mut self, hive_distribution: HiveDistributionStyle) -> Self {
224        self.hive_distribution = hive_distribution;
225        self
226    }
227
228    pub fn hive_formats(mut self, hive_formats: Option<HiveFormat>) -> Self {
229        self.hive_formats = hive_formats;
230        self
231    }
232
233    pub fn table_properties(mut self, table_properties: Vec<SqlOption>) -> Self {
234        self.table_properties = table_properties;
235        self
236    }
237
238    pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
239        self.with_options = with_options;
240        self
241    }
242    pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
243        self.file_format = file_format;
244        self
245    }
246    pub fn location(mut self, location: Option<String>) -> Self {
247        self.location = location;
248        self
249    }
250
251    pub fn query(mut self, query: Option<Box<Query>>) -> Self {
252        self.query = query;
253        self
254    }
255    pub fn without_rowid(mut self, without_rowid: bool) -> Self {
256        self.without_rowid = without_rowid;
257        self
258    }
259
260    pub fn like(mut self, like: Option<ObjectName>) -> Self {
261        self.like = like;
262        self
263    }
264
265    // Different name to allow the object to be cloned
266    pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
267        self.clone = clone;
268        self
269    }
270
271    pub fn engine(mut self, engine: Option<TableEngine>) -> Self {
272        self.engine = engine;
273        self
274    }
275
276    pub fn comment(mut self, comment: Option<CommentDef>) -> Self {
277        self.comment = comment;
278        self
279    }
280
281    pub fn auto_increment_offset(mut self, offset: Option<u32>) -> Self {
282        self.auto_increment_offset = offset;
283        self
284    }
285
286    pub fn default_charset(mut self, default_charset: Option<String>) -> Self {
287        self.default_charset = default_charset;
288        self
289    }
290
291    pub fn collation(mut self, collation: Option<String>) -> Self {
292        self.collation = collation;
293        self
294    }
295
296    pub fn on_commit(mut self, on_commit: Option<OnCommit>) -> Self {
297        self.on_commit = on_commit;
298        self
299    }
300
301    pub fn on_cluster(mut self, on_cluster: Option<Ident>) -> Self {
302        self.on_cluster = on_cluster;
303        self
304    }
305
306    pub fn primary_key(mut self, primary_key: Option<Box<Expr>>) -> Self {
307        self.primary_key = primary_key;
308        self
309    }
310
311    pub fn order_by(mut self, order_by: Option<OneOrManyWithParens<Expr>>) -> Self {
312        self.order_by = order_by;
313        self
314    }
315
316    pub fn partition_by(mut self, partition_by: Option<Box<Expr>>) -> Self {
317        self.partition_by = partition_by;
318        self
319    }
320
321    pub fn cluster_by(mut self, cluster_by: Option<WrappedCollection<Vec<Ident>>>) -> Self {
322        self.cluster_by = cluster_by;
323        self
324    }
325
326    pub fn clustered_by(mut self, clustered_by: Option<ClusteredBy>) -> Self {
327        self.clustered_by = clustered_by;
328        self
329    }
330
331    pub fn options(mut self, options: Option<Vec<SqlOption>>) -> Self {
332        self.options = options;
333        self
334    }
335
336    pub fn inherits(mut self, inherits: Option<Vec<ObjectName>>) -> Self {
337        self.inherits = inherits;
338        self
339    }
340
341    pub fn strict(mut self, strict: bool) -> Self {
342        self.strict = strict;
343        self
344    }
345
346    pub fn copy_grants(mut self, copy_grants: bool) -> Self {
347        self.copy_grants = copy_grants;
348        self
349    }
350
351    pub fn enable_schema_evolution(mut self, enable_schema_evolution: Option<bool>) -> Self {
352        self.enable_schema_evolution = enable_schema_evolution;
353        self
354    }
355
356    pub fn change_tracking(mut self, change_tracking: Option<bool>) -> Self {
357        self.change_tracking = change_tracking;
358        self
359    }
360
361    pub fn data_retention_time_in_days(mut self, data_retention_time_in_days: Option<u64>) -> Self {
362        self.data_retention_time_in_days = data_retention_time_in_days;
363        self
364    }
365
366    pub fn max_data_extension_time_in_days(
367        mut self,
368        max_data_extension_time_in_days: Option<u64>,
369    ) -> Self {
370        self.max_data_extension_time_in_days = max_data_extension_time_in_days;
371        self
372    }
373
374    pub fn default_ddl_collation(mut self, default_ddl_collation: Option<String>) -> Self {
375        self.default_ddl_collation = default_ddl_collation;
376        self
377    }
378
379    pub fn with_aggregation_policy(mut self, with_aggregation_policy: Option<ObjectName>) -> Self {
380        self.with_aggregation_policy = with_aggregation_policy;
381        self
382    }
383
384    pub fn with_row_access_policy(
385        mut self,
386        with_row_access_policy: Option<RowAccessPolicy>,
387    ) -> Self {
388        self.with_row_access_policy = with_row_access_policy;
389        self
390    }
391
392    pub fn with_tags(mut self, with_tags: Option<Vec<Tag>>) -> Self {
393        self.with_tags = with_tags;
394        self
395    }
396
397    pub fn base_location(mut self, base_location: Option<String>) -> Self {
398        self.base_location = base_location;
399        self
400    }
401
402    pub fn external_volume(mut self, external_volume: Option<String>) -> Self {
403        self.external_volume = external_volume;
404        self
405    }
406
407    pub fn catalog(mut self, catalog: Option<String>) -> Self {
408        self.catalog = catalog;
409        self
410    }
411
412    pub fn catalog_sync(mut self, catalog_sync: Option<String>) -> Self {
413        self.catalog_sync = catalog_sync;
414        self
415    }
416
417    pub fn storage_serialization_policy(
418        mut self,
419        storage_serialization_policy: Option<StorageSerializationPolicy>,
420    ) -> Self {
421        self.storage_serialization_policy = storage_serialization_policy;
422        self
423    }
424
425    pub fn build(self) -> Statement {
426        Statement::CreateTable(CreateTable {
427            or_replace: self.or_replace,
428            temporary: self.temporary,
429            external: self.external,
430            global: self.global,
431            if_not_exists: self.if_not_exists,
432            transient: self.transient,
433            volatile: self.volatile,
434            iceberg: self.iceberg,
435            name: self.name,
436            columns: self.columns,
437            constraints: self.constraints,
438            hive_distribution: self.hive_distribution,
439            hive_formats: self.hive_formats,
440            table_properties: self.table_properties,
441            with_options: self.with_options,
442            file_format: self.file_format,
443            location: self.location,
444            query: self.query,
445            without_rowid: self.without_rowid,
446            like: self.like,
447            clone: self.clone,
448            engine: self.engine,
449            comment: self.comment,
450            auto_increment_offset: self.auto_increment_offset,
451            default_charset: self.default_charset,
452            collation: self.collation,
453            on_commit: self.on_commit,
454            on_cluster: self.on_cluster,
455            primary_key: self.primary_key,
456            order_by: self.order_by,
457            partition_by: self.partition_by,
458            cluster_by: self.cluster_by,
459            clustered_by: self.clustered_by,
460            options: self.options,
461            inherits: self.inherits,
462            strict: self.strict,
463            copy_grants: self.copy_grants,
464            enable_schema_evolution: self.enable_schema_evolution,
465            change_tracking: self.change_tracking,
466            data_retention_time_in_days: self.data_retention_time_in_days,
467            max_data_extension_time_in_days: self.max_data_extension_time_in_days,
468            default_ddl_collation: self.default_ddl_collation,
469            with_aggregation_policy: self.with_aggregation_policy,
470            with_row_access_policy: self.with_row_access_policy,
471            with_tags: self.with_tags,
472            base_location: self.base_location,
473            external_volume: self.external_volume,
474            catalog: self.catalog,
475            catalog_sync: self.catalog_sync,
476            storage_serialization_policy: self.storage_serialization_policy,
477        })
478    }
479}
480
481impl TryFrom<Statement> for CreateTableBuilder {
482    type Error = ParserError;
483
484    // As the builder can be transformed back to a statement, it shouldn't be a problem to take the
485    // ownership.
486    fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
487        match stmt {
488            Statement::CreateTable(CreateTable {
489                or_replace,
490                temporary,
491                external,
492                global,
493                if_not_exists,
494                transient,
495                volatile,
496                iceberg,
497                name,
498                columns,
499                constraints,
500                hive_distribution,
501                hive_formats,
502                table_properties,
503                with_options,
504                file_format,
505                location,
506                query,
507                without_rowid,
508                like,
509                clone,
510                engine,
511                comment,
512                auto_increment_offset,
513                default_charset,
514                collation,
515                on_commit,
516                on_cluster,
517                primary_key,
518                order_by,
519                partition_by,
520                cluster_by,
521                clustered_by,
522                options,
523                inherits,
524                strict,
525                copy_grants,
526                enable_schema_evolution,
527                change_tracking,
528                data_retention_time_in_days,
529                max_data_extension_time_in_days,
530                default_ddl_collation,
531                with_aggregation_policy,
532                with_row_access_policy,
533                with_tags,
534                base_location,
535                external_volume,
536                catalog,
537                catalog_sync,
538                storage_serialization_policy,
539            }) => Ok(Self {
540                or_replace,
541                temporary,
542                external,
543                global,
544                if_not_exists,
545                transient,
546                name,
547                columns,
548                constraints,
549                hive_distribution,
550                hive_formats,
551                table_properties,
552                with_options,
553                file_format,
554                location,
555                query,
556                without_rowid,
557                like,
558                clone,
559                engine,
560                comment,
561                auto_increment_offset,
562                default_charset,
563                collation,
564                on_commit,
565                on_cluster,
566                primary_key,
567                order_by,
568                partition_by,
569                cluster_by,
570                clustered_by,
571                options,
572                inherits,
573                strict,
574                iceberg,
575                copy_grants,
576                enable_schema_evolution,
577                change_tracking,
578                data_retention_time_in_days,
579                max_data_extension_time_in_days,
580                default_ddl_collation,
581                with_aggregation_policy,
582                with_row_access_policy,
583                with_tags,
584                volatile,
585                base_location,
586                external_volume,
587                catalog,
588                catalog_sync,
589                storage_serialization_policy,
590            }),
591            _ => Err(ParserError::ParserError(format!(
592                "Expected create table statement, but received: {stmt}"
593            ))),
594        }
595    }
596}
597
598/// Helper return type when parsing configuration for a `CREATE TABLE` statement.
599#[derive(Default)]
600pub(crate) struct CreateTableConfiguration {
601    pub partition_by: Option<Box<Expr>>,
602    pub cluster_by: Option<WrappedCollection<Vec<Ident>>>,
603    pub options: Option<Vec<SqlOption>>,
604    pub inherits: Option<Vec<ObjectName>>,
605}
606
607#[cfg(test)]
608mod tests {
609    use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
610    use crate::ast::{Ident, ObjectName, Statement};
611    use crate::parser::ParserError;
612
613    #[test]
614    pub fn test_from_valid_statement() {
615        let builder = CreateTableBuilder::new(ObjectName::from(vec![Ident::new("table_name")]));
616
617        let stmt = builder.clone().build();
618
619        assert_eq!(builder, CreateTableBuilder::try_from(stmt).unwrap());
620    }
621
622    #[test]
623    pub fn test_from_invalid_statement() {
624        let stmt = Statement::Commit {
625            chain: false,
626            end: false,
627            modifier: None,
628        };
629
630        assert_eq!(
631            CreateTableBuilder::try_from(stmt).unwrap_err(),
632            ParserError::ParserError(
633                "Expected create table statement, but received: COMMIT".to_owned()
634            )
635        );
636    }
637}