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