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