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, ForValues, HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName,
30    OnCommit, 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 partition_of: Option<ObjectName>,
98    pub for_values: Option<ForValues>,
99    pub strict: bool,
100    pub copy_grants: bool,
101    pub enable_schema_evolution: Option<bool>,
102    pub change_tracking: Option<bool>,
103    pub data_retention_time_in_days: Option<u64>,
104    pub max_data_extension_time_in_days: Option<u64>,
105    pub default_ddl_collation: Option<String>,
106    pub with_aggregation_policy: Option<ObjectName>,
107    pub with_row_access_policy: Option<RowAccessPolicy>,
108    pub with_tags: Option<Vec<Tag>>,
109    pub base_location: Option<String>,
110    pub external_volume: Option<String>,
111    pub catalog: Option<String>,
112    pub catalog_sync: Option<String>,
113    pub storage_serialization_policy: Option<StorageSerializationPolicy>,
114    pub table_options: CreateTableOptions,
115    pub target_lag: Option<String>,
116    pub warehouse: Option<Ident>,
117    pub refresh_mode: Option<RefreshModeKind>,
118    pub initialize: Option<InitializeKind>,
119    pub require_user: bool,
120}
121
122impl CreateTableBuilder {
123    pub fn new(name: ObjectName) -> Self {
124        Self {
125            or_replace: false,
126            temporary: false,
127            external: false,
128            global: None,
129            if_not_exists: false,
130            transient: false,
131            volatile: false,
132            iceberg: false,
133            dynamic: false,
134            name,
135            columns: vec![],
136            constraints: vec![],
137            hive_distribution: HiveDistributionStyle::NONE,
138            hive_formats: None,
139            file_format: None,
140            location: None,
141            query: None,
142            without_rowid: false,
143            like: None,
144            clone: None,
145            version: None,
146            comment: 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            inherits: None,
155            partition_of: None,
156            for_values: None,
157            strict: false,
158            copy_grants: false,
159            enable_schema_evolution: None,
160            change_tracking: None,
161            data_retention_time_in_days: None,
162            max_data_extension_time_in_days: None,
163            default_ddl_collation: None,
164            with_aggregation_policy: None,
165            with_row_access_policy: None,
166            with_tags: None,
167            base_location: None,
168            external_volume: None,
169            catalog: None,
170            catalog_sync: None,
171            storage_serialization_policy: None,
172            table_options: CreateTableOptions::None,
173            target_lag: None,
174            warehouse: None,
175            refresh_mode: None,
176            initialize: None,
177            require_user: false,
178        }
179    }
180    pub fn or_replace(mut self, or_replace: bool) -> Self {
181        self.or_replace = or_replace;
182        self
183    }
184
185    pub fn temporary(mut self, temporary: bool) -> Self {
186        self.temporary = temporary;
187        self
188    }
189
190    pub fn external(mut self, external: bool) -> Self {
191        self.external = external;
192        self
193    }
194
195    pub fn global(mut self, global: Option<bool>) -> Self {
196        self.global = global;
197        self
198    }
199
200    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
201        self.if_not_exists = if_not_exists;
202        self
203    }
204
205    pub fn transient(mut self, transient: bool) -> Self {
206        self.transient = transient;
207        self
208    }
209
210    pub fn volatile(mut self, volatile: bool) -> Self {
211        self.volatile = volatile;
212        self
213    }
214
215    pub fn iceberg(mut self, iceberg: bool) -> Self {
216        self.iceberg = iceberg;
217        self
218    }
219
220    pub fn dynamic(mut self, dynamic: bool) -> Self {
221        self.dynamic = dynamic;
222        self
223    }
224
225    pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
226        self.columns = columns;
227        self
228    }
229
230    pub fn constraints(mut self, constraints: Vec<TableConstraint>) -> Self {
231        self.constraints = constraints;
232        self
233    }
234
235    pub fn hive_distribution(mut self, hive_distribution: HiveDistributionStyle) -> Self {
236        self.hive_distribution = hive_distribution;
237        self
238    }
239
240    pub fn hive_formats(mut self, hive_formats: Option<HiveFormat>) -> Self {
241        self.hive_formats = hive_formats;
242        self
243    }
244
245    pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
246        self.file_format = file_format;
247        self
248    }
249    pub fn location(mut self, location: Option<String>) -> Self {
250        self.location = location;
251        self
252    }
253
254    pub fn query(mut self, query: Option<Box<Query>>) -> Self {
255        self.query = query;
256        self
257    }
258    pub fn without_rowid(mut self, without_rowid: bool) -> Self {
259        self.without_rowid = without_rowid;
260        self
261    }
262
263    pub fn like(mut self, like: Option<CreateTableLikeKind>) -> Self {
264        self.like = like;
265        self
266    }
267
268    // Different name to allow the object to be cloned
269    pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
270        self.clone = clone;
271        self
272    }
273
274    pub fn version(mut self, version: Option<TableVersion>) -> Self {
275        self.version = version;
276        self
277    }
278
279    pub fn comment_after_column_def(mut self, comment: Option<CommentDef>) -> Self {
280        self.comment = comment;
281        self
282    }
283
284    pub fn on_commit(mut self, on_commit: Option<OnCommit>) -> Self {
285        self.on_commit = on_commit;
286        self
287    }
288
289    pub fn on_cluster(mut self, on_cluster: Option<Ident>) -> Self {
290        self.on_cluster = on_cluster;
291        self
292    }
293
294    pub fn primary_key(mut self, primary_key: Option<Box<Expr>>) -> Self {
295        self.primary_key = primary_key;
296        self
297    }
298
299    pub fn order_by(mut self, order_by: Option<OneOrManyWithParens<Expr>>) -> Self {
300        self.order_by = order_by;
301        self
302    }
303
304    pub fn partition_by(mut self, partition_by: Option<Box<Expr>>) -> Self {
305        self.partition_by = partition_by;
306        self
307    }
308
309    pub fn cluster_by(mut self, cluster_by: Option<WrappedCollection<Vec<Expr>>>) -> Self {
310        self.cluster_by = cluster_by;
311        self
312    }
313
314    pub fn clustered_by(mut self, clustered_by: Option<ClusteredBy>) -> Self {
315        self.clustered_by = clustered_by;
316        self
317    }
318
319    pub fn inherits(mut self, inherits: Option<Vec<ObjectName>>) -> Self {
320        self.inherits = inherits;
321        self
322    }
323
324    pub fn partition_of(mut self, partition_of: Option<ObjectName>) -> Self {
325        self.partition_of = partition_of;
326        self
327    }
328
329    pub fn for_values(mut self, for_values: Option<ForValues>) -> Self {
330        self.for_values = for_values;
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 table_options(mut self, table_options: CreateTableOptions) -> Self {
419        self.table_options = table_options;
420        self
421    }
422
423    pub fn target_lag(mut self, target_lag: Option<String>) -> Self {
424        self.target_lag = target_lag;
425        self
426    }
427
428    pub fn warehouse(mut self, warehouse: Option<Ident>) -> Self {
429        self.warehouse = warehouse;
430        self
431    }
432
433    pub fn refresh_mode(mut self, refresh_mode: Option<RefreshModeKind>) -> Self {
434        self.refresh_mode = refresh_mode;
435        self
436    }
437
438    pub fn initialize(mut self, initialize: Option<InitializeKind>) -> Self {
439        self.initialize = initialize;
440        self
441    }
442
443    pub fn require_user(mut self, require_user: bool) -> Self {
444        self.require_user = require_user;
445        self
446    }
447
448    pub fn build(self) -> Statement {
449        CreateTable {
450            or_replace: self.or_replace,
451            temporary: self.temporary,
452            external: self.external,
453            global: self.global,
454            if_not_exists: self.if_not_exists,
455            transient: self.transient,
456            volatile: self.volatile,
457            iceberg: self.iceberg,
458            dynamic: self.dynamic,
459            name: self.name,
460            columns: self.columns,
461            constraints: self.constraints,
462            hive_distribution: self.hive_distribution,
463            hive_formats: self.hive_formats,
464            file_format: self.file_format,
465            location: self.location,
466            query: self.query,
467            without_rowid: self.without_rowid,
468            like: self.like,
469            clone: self.clone,
470            version: self.version,
471            comment: self.comment,
472            on_commit: self.on_commit,
473            on_cluster: self.on_cluster,
474            primary_key: self.primary_key,
475            order_by: self.order_by,
476            partition_by: self.partition_by,
477            cluster_by: self.cluster_by,
478            clustered_by: self.clustered_by,
479            inherits: self.inherits,
480            partition_of: self.partition_of,
481            for_values: self.for_values,
482            strict: self.strict,
483            copy_grants: self.copy_grants,
484            enable_schema_evolution: self.enable_schema_evolution,
485            change_tracking: self.change_tracking,
486            data_retention_time_in_days: self.data_retention_time_in_days,
487            max_data_extension_time_in_days: self.max_data_extension_time_in_days,
488            default_ddl_collation: self.default_ddl_collation,
489            with_aggregation_policy: self.with_aggregation_policy,
490            with_row_access_policy: self.with_row_access_policy,
491            with_tags: self.with_tags,
492            base_location: self.base_location,
493            external_volume: self.external_volume,
494            catalog: self.catalog,
495            catalog_sync: self.catalog_sync,
496            storage_serialization_policy: self.storage_serialization_policy,
497            table_options: self.table_options,
498            target_lag: self.target_lag,
499            warehouse: self.warehouse,
500            refresh_mode: self.refresh_mode,
501            initialize: self.initialize,
502            require_user: self.require_user,
503        }
504        .into()
505    }
506}
507
508impl TryFrom<Statement> for CreateTableBuilder {
509    type Error = ParserError;
510
511    // As the builder can be transformed back to a statement, it shouldn't be a problem to take the
512    // ownership.
513    fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
514        match stmt {
515            Statement::CreateTable(CreateTable {
516                or_replace,
517                temporary,
518                external,
519                global,
520                if_not_exists,
521                transient,
522                volatile,
523                iceberg,
524                dynamic,
525                name,
526                columns,
527                constraints,
528                hive_distribution,
529                hive_formats,
530                file_format,
531                location,
532                query,
533                without_rowid,
534                like,
535                clone,
536                version,
537                comment,
538                on_commit,
539                on_cluster,
540                primary_key,
541                order_by,
542                partition_by,
543                cluster_by,
544                clustered_by,
545                inherits,
546                partition_of,
547                for_values,
548                strict,
549                copy_grants,
550                enable_schema_evolution,
551                change_tracking,
552                data_retention_time_in_days,
553                max_data_extension_time_in_days,
554                default_ddl_collation,
555                with_aggregation_policy,
556                with_row_access_policy,
557                with_tags,
558                base_location,
559                external_volume,
560                catalog,
561                catalog_sync,
562                storage_serialization_policy,
563                table_options,
564                target_lag,
565                warehouse,
566                refresh_mode,
567                initialize,
568                require_user,
569            }) => Ok(Self {
570                or_replace,
571                temporary,
572                external,
573                global,
574                if_not_exists,
575                transient,
576                dynamic,
577                name,
578                columns,
579                constraints,
580                hive_distribution,
581                hive_formats,
582                file_format,
583                location,
584                query,
585                without_rowid,
586                like,
587                clone,
588                version,
589                comment,
590                on_commit,
591                on_cluster,
592                primary_key,
593                order_by,
594                partition_by,
595                cluster_by,
596                clustered_by,
597                inherits,
598                partition_of,
599                for_values,
600                strict,
601                iceberg,
602                copy_grants,
603                enable_schema_evolution,
604                change_tracking,
605                data_retention_time_in_days,
606                max_data_extension_time_in_days,
607                default_ddl_collation,
608                with_aggregation_policy,
609                with_row_access_policy,
610                with_tags,
611                volatile,
612                base_location,
613                external_volume,
614                catalog,
615                catalog_sync,
616                storage_serialization_policy,
617                table_options,
618                target_lag,
619                warehouse,
620                refresh_mode,
621                initialize,
622                require_user,
623            }),
624            _ => Err(ParserError::ParserError(format!(
625                "Expected create table statement, but received: {stmt}"
626            ))),
627        }
628    }
629}
630
631/// Helper return type when parsing configuration for a `CREATE TABLE` statement.
632#[derive(Default)]
633pub(crate) struct CreateTableConfiguration {
634    pub partition_by: Option<Box<Expr>>,
635    pub cluster_by: Option<WrappedCollection<Vec<Expr>>>,
636    pub inherits: Option<Vec<ObjectName>>,
637    pub table_options: CreateTableOptions,
638}
639
640#[cfg(test)]
641mod tests {
642    use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
643    use crate::ast::{Ident, ObjectName, Statement};
644    use crate::parser::ParserError;
645
646    #[test]
647    pub fn test_from_valid_statement() {
648        let builder = CreateTableBuilder::new(ObjectName::from(vec![Ident::new("table_name")]));
649
650        let stmt = builder.clone().build();
651
652        assert_eq!(builder, CreateTableBuilder::try_from(stmt).unwrap());
653    }
654
655    #[test]
656    pub fn test_from_invalid_statement() {
657        let stmt = Statement::Commit {
658            chain: false,
659            end: false,
660            modifier: None,
661        };
662
663        assert_eq!(
664            CreateTableBuilder::try_from(stmt).unwrap_err(),
665            ParserError::ParserError(
666                "Expected create table statement, but received: COMMIT".to_owned()
667            )
668        );
669    }
670}