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