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