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