Skip to main content

databend_common_ast/ast/statements/
table.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16use std::fmt::Display;
17use std::fmt::Formatter;
18use std::time::Duration;
19
20use derive_visitor::Drive;
21use derive_visitor::DriveMut;
22
23use crate::ast::CreateOption;
24use crate::ast::Expr;
25use crate::ast::Identifier;
26use crate::ast::Query;
27use crate::ast::TableIndexType;
28use crate::ast::TableReference;
29use crate::ast::TimeTravelPoint;
30use crate::ast::TypeName;
31use crate::ast::UriLocation;
32use crate::ast::quote::QuotedString;
33use crate::ast::statements::constraint::ConstraintType;
34use crate::ast::statements::show::ShowLimit;
35use crate::ast::write_comma_separated_list;
36use crate::ast::write_comma_separated_string_map;
37use crate::ast::write_dot_separated_list;
38use crate::ast::write_space_separated_string_map;
39
40#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
41pub struct ShowTablesStmt {
42    pub catalog: Option<Identifier>,
43    pub database: Option<Identifier>,
44    pub full: bool,
45    pub limit: Option<ShowLimit>,
46    pub with_history: bool,
47}
48
49impl Display for ShowTablesStmt {
50    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
51        write!(f, "SHOW")?;
52        if self.full {
53            write!(f, " FULL")?;
54        }
55        write!(f, " TABLES")?;
56        if self.with_history {
57            write!(f, " HISTORY")?;
58        }
59        if let Some(database) = &self.database {
60            write!(f, " FROM ")?;
61            if let Some(catalog) = &self.catalog {
62                write!(f, "{catalog}.",)?;
63            }
64            write!(f, "{database}")?;
65        }
66        if let Some(limit) = &self.limit {
67            write!(f, " {limit}")?;
68        }
69
70        Ok(())
71    }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
75pub struct ShowCreateTableStmt {
76    pub catalog: Option<Identifier>,
77    pub database: Option<Identifier>,
78    pub table: Identifier,
79    pub with_quoted_ident: bool,
80}
81
82impl Display for ShowCreateTableStmt {
83    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
84        write!(f, "SHOW CREATE TABLE ")?;
85        write_dot_separated_list(
86            f,
87            self.catalog
88                .iter()
89                .chain(&self.database)
90                .chain(Some(&self.table)),
91        )?;
92        if self.with_quoted_ident {
93            write!(f, " WITH QUOTED_IDENTIFIERS")?
94        }
95        Ok(())
96    }
97}
98
99#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
100pub struct ShowTablesStatusStmt {
101    pub database: Option<Identifier>,
102    pub limit: Option<ShowLimit>,
103}
104
105impl Display for ShowTablesStatusStmt {
106    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
107        write!(f, "SHOW TABLE STATUS")?;
108        if let Some(database) = &self.database {
109            write!(f, " FROM {database}")?;
110        }
111        if let Some(limit) = &self.limit {
112            write!(f, " {limit}")?;
113        }
114
115        Ok(())
116    }
117}
118
119#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
120pub struct ShowDropTablesStmt {
121    pub database: Option<Identifier>,
122    pub limit: Option<ShowLimit>,
123}
124
125impl Display for ShowDropTablesStmt {
126    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
127        write!(f, "SHOW DROP TABLES")?;
128        if let Some(database) = &self.database {
129            write!(f, " FROM {database}")?;
130        }
131        if let Some(limit) = &self.limit {
132            write!(f, " {limit}")?;
133        }
134
135        Ok(())
136    }
137}
138
139#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
140pub enum ClusterType {
141    Linear,
142    Hilbert,
143}
144
145impl Display for ClusterType {
146    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
147        match self {
148            ClusterType::Linear => write!(f, "LINEAR"),
149            ClusterType::Hilbert => write!(f, "HILBERT"),
150        }
151    }
152}
153
154impl std::str::FromStr for ClusterType {
155    type Err = ();
156    fn from_str(s: &str) -> Result<Self, Self::Err> {
157        match s.to_lowercase().as_str() {
158            "linear" => Ok(ClusterType::Linear),
159            "hilbert" => Ok(ClusterType::Hilbert),
160            _ => Err(()),
161        }
162    }
163}
164
165#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
166pub struct ClusterOption {
167    pub cluster_type: ClusterType,
168    pub cluster_exprs: Vec<Expr>,
169}
170
171impl Display for ClusterOption {
172    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
173        write!(f, "CLUSTER BY {}(", self.cluster_type)?;
174        write_comma_separated_list(f, &self.cluster_exprs)?;
175        write!(f, ")")
176    }
177}
178
179#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
180pub struct CreateTableStmt {
181    pub create_option: CreateOption,
182    pub catalog: Option<Identifier>,
183    pub database: Option<Identifier>,
184    pub table: Identifier,
185    pub source: Option<CreateTableSource>,
186    pub engine: Option<Engine>,
187    pub uri_location: Option<UriLocation>,
188    pub cluster_by: Option<ClusterOption>,
189    pub table_options: BTreeMap<String, String>,
190    pub iceberg_table_partition: Option<Vec<Identifier>>,
191    pub table_properties: Option<BTreeMap<String, String>>,
192    pub as_query: Option<Box<Query>>,
193    pub table_type: TableType,
194}
195
196#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
197pub enum TableType {
198    Normal,
199    Transient,
200    Temporary,
201}
202
203impl Display for CreateTableStmt {
204    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
205        write!(f, "CREATE")?;
206        if let CreateOption::CreateOrReplace = self.create_option {
207            write!(f, " OR REPLACE")?;
208        }
209        match self.table_type {
210            TableType::Normal => {}
211            TableType::Transient => write!(f, " TRANSIENT")?,
212            TableType::Temporary => write!(f, " TEMPORARY")?,
213        };
214        write!(f, " TABLE")?;
215        if let CreateOption::CreateIfNotExists = self.create_option {
216            write!(f, " IF NOT EXISTS")?;
217        }
218        write!(f, " ")?;
219        write_dot_separated_list(
220            f,
221            self.catalog
222                .iter()
223                .chain(&self.database)
224                .chain(Some(&self.table)),
225        )?;
226
227        if let Some(source) = &self.source {
228            write!(f, " {source}")?;
229        }
230
231        if let Some(engine) = &self.engine {
232            write!(f, " ENGINE = {engine}")?;
233        }
234
235        if let Some(uri_location) = &self.uri_location {
236            write!(f, " {uri_location}")?;
237        }
238
239        if let Some(cluster_by) = &self.cluster_by {
240            write!(f, " {cluster_by}")?;
241        }
242
243        // Format table options
244        if !self.table_options.is_empty() {
245            write!(f, " ")?;
246            write_space_separated_string_map(f, &self.table_options)?;
247        }
248
249        if let Some(iceberg_table_partition) = &self.iceberg_table_partition {
250            write!(f, " PARTITION BY(")?;
251            write_comma_separated_list(f, iceberg_table_partition)?;
252            write!(f, ")")?;
253        }
254
255        if let Some(table_properties) = &self.table_properties {
256            write!(f, " PROPERTIES(")?;
257            write_space_separated_string_map(f, table_properties)?;
258            write!(f, ")")?;
259        }
260
261        if let Some(as_query) = &self.as_query {
262            write!(f, " AS {as_query}")?;
263        }
264
265        Ok(())
266    }
267}
268
269#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
270pub struct AttachTableStmt {
271    pub catalog: Option<Identifier>,
272    pub database: Option<Identifier>,
273    pub table: Identifier,
274    pub columns_opt: Option<Vec<Identifier>>,
275    pub uri_location: UriLocation,
276}
277
278impl Display for AttachTableStmt {
279    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
280        write!(f, "ATTACH TABLE ")?;
281        write_dot_separated_list(
282            f,
283            self.catalog
284                .iter()
285                .chain(&self.database)
286                .chain(Some(&self.table)),
287        )?;
288
289        if let Some(cols) = &self.columns_opt {
290            write!(f, " (")?;
291            write_comma_separated_list(f, cols.iter())?;
292            write!(f, ")")?;
293        }
294
295        write!(f, " {}", self.uri_location)?;
296
297        Ok(())
298    }
299}
300
301#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
302pub enum CreateTableSource {
303    Columns {
304        columns: Vec<ColumnDefinition>,
305        opt_table_indexes: Option<Vec<TableIndexDefinition>>,
306        opt_column_constraints: Option<Vec<ConstraintDefinition>>,
307        opt_table_constraints: Option<Vec<ConstraintDefinition>>,
308    },
309    Like {
310        catalog: Option<Identifier>,
311        database: Option<Identifier>,
312        table: Identifier,
313    },
314}
315
316impl Display for CreateTableSource {
317    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
318        match self {
319            // `column_constraints` are not displayed, but are displayed by column
320            CreateTableSource::Columns {
321                columns,
322                opt_table_indexes,
323                opt_column_constraints: _column_constraints,
324                opt_table_constraints: table_constraints,
325            } => {
326                write!(f, "(")?;
327                write_comma_separated_list(f, columns)?;
328                if let Some(table_indexes) = opt_table_indexes {
329                    write!(f, ", ")?;
330                    write_comma_separated_list(f, table_indexes)?;
331                }
332                if let Some(constraints) = table_constraints {
333                    write!(f, ", ")?;
334                    write_comma_separated_list(f, constraints)?;
335                }
336                write!(f, ")")
337            }
338            CreateTableSource::Like {
339                catalog,
340                database,
341                table,
342            } => {
343                write!(f, "LIKE ")?;
344                write_dot_separated_list(f, catalog.iter().chain(database).chain(Some(table)))
345            }
346        }
347    }
348}
349
350#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
351pub struct DescribeTableStmt {
352    pub catalog: Option<Identifier>,
353    pub database: Option<Identifier>,
354    pub table: Identifier,
355}
356
357impl Display for DescribeTableStmt {
358    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
359        write!(f, "DESCRIBE ")?;
360        write_dot_separated_list(
361            f,
362            self.catalog
363                .iter()
364                .chain(self.database.iter().chain(Some(&self.table))),
365        )
366    }
367}
368
369#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
370pub struct DropTableStmt {
371    pub if_exists: bool,
372    pub catalog: Option<Identifier>,
373    pub database: Option<Identifier>,
374    pub table: Identifier,
375    pub all: bool,
376}
377
378impl Display for DropTableStmt {
379    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
380        write!(f, "DROP TABLE ")?;
381        if self.if_exists {
382            write!(f, "IF EXISTS ")?;
383        }
384        write_dot_separated_list(
385            f,
386            self.catalog
387                .iter()
388                .chain(&self.database)
389                .chain(Some(&self.table)),
390        )?;
391        if self.all {
392            write!(f, " ALL")?;
393        }
394
395        Ok(())
396    }
397}
398
399#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
400pub struct UndropTableStmt {
401    pub catalog: Option<Identifier>,
402    pub database: Option<Identifier>,
403    pub table: Identifier,
404}
405
406impl Display for UndropTableStmt {
407    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
408        write!(f, "UNDROP TABLE ")?;
409        write_dot_separated_list(
410            f,
411            self.catalog
412                .iter()
413                .chain(&self.database)
414                .chain(Some(&self.table)),
415        )
416    }
417}
418
419#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
420pub struct AlterTableStmt {
421    pub if_exists: bool,
422    pub table_reference: TableReference,
423    pub action: AlterTableAction,
424}
425
426impl Display for AlterTableStmt {
427    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
428        write!(f, "ALTER TABLE")?;
429        if self.if_exists {
430            write!(f, " IF EXISTS")?;
431        }
432        write!(f, " {}", self.table_reference)?;
433        write!(f, " {}", self.action)
434    }
435}
436
437#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
438pub enum SnapshotRefType {
439    Branch,
440    Tag,
441}
442
443impl Display for SnapshotRefType {
444    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
445        match self {
446            SnapshotRefType::Branch => write!(f, "BRANCH"),
447            SnapshotRefType::Tag => write!(f, "TAG"),
448        }
449    }
450}
451
452#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
453pub enum AlterTableAction {
454    RenameTable {
455        new_table: Identifier,
456    },
457    SwapWith {
458        target_table: Identifier,
459    },
460    AddColumn {
461        column: ColumnDefinition,
462        option: AddColumnOption,
463    },
464    RenameColumn {
465        old_column: Identifier,
466        new_column: Identifier,
467    },
468    ModifyTableComment {
469        new_comment: String,
470    },
471    ModifyColumn {
472        action: ModifyColumnAction,
473    },
474    AddConstraint {
475        constraint: ConstraintDefinition,
476    },
477    DropConstraint {
478        constraint_name: Identifier,
479    },
480    // (column name id, policy name)
481    AddRowAccessPolicy {
482        columns: Vec<Identifier>,
483        policy: Identifier,
484    },
485    // policy name
486    DropRowAccessPolicy {
487        policy: Identifier,
488    },
489    DropAllRowAccessPolicies,
490    DropColumn {
491        column: Identifier,
492    },
493    AlterTableClusterKey {
494        cluster_by: ClusterOption,
495    },
496    DropTableClusterKey,
497    ReclusterTable {
498        is_final: bool,
499        selection: Option<Expr>,
500        limit: Option<u64>,
501    },
502    FlashbackTo {
503        point: TimeTravelPoint,
504    },
505    SetOptions {
506        set_options: BTreeMap<String, String>,
507    },
508    UnsetOptions {
509        targets: Vec<Identifier>,
510    },
511    RefreshTableCache,
512    ModifyConnection {
513        new_connection: BTreeMap<String, String>,
514    },
515    CreateTableRef {
516        ref_type: SnapshotRefType,
517        ref_name: Identifier,
518        travel_point: Option<TimeTravelPoint>,
519        #[drive(skip)]
520        retain: Option<Duration>,
521    },
522    DropTableRef {
523        ref_type: SnapshotRefType,
524        ref_name: Identifier,
525    },
526}
527
528impl Display for AlterTableAction {
529    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
530        match self {
531            AlterTableAction::SetOptions { set_options } => {
532                write!(f, "SET OPTIONS (")?;
533                write_comma_separated_string_map(f, set_options)?;
534                write!(f, ")")?;
535            }
536            AlterTableAction::RenameTable { new_table } => {
537                write!(f, "RENAME TO {new_table}")?;
538            }
539            AlterTableAction::SwapWith { target_table } => {
540                write!(f, "SWAP WITH {target_table}")?;
541            }
542            AlterTableAction::ModifyTableComment { new_comment } => {
543                write!(f, "COMMENT={}", QuotedString(new_comment, '\''))?;
544            }
545            AlterTableAction::RenameColumn {
546                old_column,
547                new_column,
548            } => {
549                write!(f, "RENAME COLUMN {old_column} TO {new_column}")?;
550            }
551            AlterTableAction::AddColumn { column, option } => {
552                write!(f, "ADD COLUMN {column}{option}")?;
553            }
554            AlterTableAction::AddConstraint { constraint } => {
555                write!(f, "ADD {}", constraint)?;
556            }
557            AlterTableAction::DropConstraint { constraint_name } => {
558                write!(f, "DROP CONSTRAINT {}", constraint_name)?;
559            }
560            AlterTableAction::ModifyColumn { action } => {
561                write!(f, "MODIFY COLUMN {action}")?;
562            }
563            AlterTableAction::DropColumn { column } => {
564                write!(f, "DROP COLUMN {column}")?;
565            }
566            AlterTableAction::AlterTableClusterKey { cluster_by } => {
567                write!(f, "{cluster_by}")?;
568            }
569            AlterTableAction::DropTableClusterKey => {
570                write!(f, "DROP CLUSTER KEY")?;
571            }
572            AlterTableAction::ReclusterTable {
573                is_final,
574                selection,
575                limit,
576            } => {
577                write!(f, "RECLUSTER")?;
578                if *is_final {
579                    write!(f, " FINAL")?;
580                }
581                if let Some(conditions) = selection {
582                    write!(f, " WHERE {conditions}")?;
583                }
584                if let Some(limit) = limit {
585                    write!(f, " LIMIT {limit}")?;
586                }
587            }
588            AlterTableAction::FlashbackTo { point } => {
589                write!(f, "FLASHBACK TO {}", point)?;
590            }
591            AlterTableAction::UnsetOptions {
592                targets: unset_targets,
593            } => {
594                write!(f, "UNSET OPTIONS ")?;
595                if unset_targets.len() == 1 {
596                    write!(f, "{}", unset_targets[0])?;
597                } else {
598                    write!(f, "(")?;
599                    write_comma_separated_list(f, unset_targets)?;
600                    write!(f, ")")?;
601                }
602            }
603            AlterTableAction::RefreshTableCache => {
604                write!(f, "REFRESH CACHE")?;
605            }
606            AlterTableAction::ModifyConnection { new_connection } => {
607                write!(f, "CONNECTION=(")?;
608                write_space_separated_string_map(f, new_connection)?;
609                write!(f, ")")?;
610            }
611            AlterTableAction::AddRowAccessPolicy { columns, policy } => {
612                write!(f, "ADD ROW ACCESS POLICY {} ON (", policy)?;
613                write_comma_separated_list(f, columns)?;
614                write!(f, ")")?
615            }
616            AlterTableAction::DropRowAccessPolicy { policy } => {
617                write!(f, "DROP ROW ACCESS POLICY {}", policy)?
618            }
619            AlterTableAction::DropAllRowAccessPolicies => {
620                write!(f, "DROP ALL ROW ACCESS POLICIES")?
621            }
622            AlterTableAction::CreateTableRef {
623                ref_type,
624                ref_name,
625                travel_point,
626                retain,
627            } => {
628                write!(f, "CREATE {ref_type} {ref_name}")?;
629                if let Some(travel_point) = travel_point {
630                    write!(f, " AT {travel_point}")?;
631                }
632                if let Some(retain) = retain {
633                    let days = Duration::from_secs(60 * 60 * 24);
634                    if retain >= &days {
635                        let days = retain.as_secs() / (60 * 60 * 24);
636                        write!(f, " RETAIN {days} DAYS ")?;
637                    } else {
638                        let seconds = retain.as_secs();
639                        write!(f, " RETAIN {seconds} SECONDS ")?;
640                    }
641                }
642            }
643            AlterTableAction::DropTableRef { ref_type, ref_name } => {
644                write!(f, "DROP {ref_type} {ref_name}")?;
645            }
646        };
647        Ok(())
648    }
649}
650
651#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
652pub enum AddColumnOption {
653    End,
654    First,
655    After(Identifier),
656}
657
658impl Display for AddColumnOption {
659    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
660        match self {
661            AddColumnOption::First => write!(f, " FIRST"),
662            AddColumnOption::After(ident) => write!(f, " AFTER {ident}"),
663            AddColumnOption::End => Ok(()),
664        }
665    }
666}
667
668#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
669pub struct RenameTableStmt {
670    pub if_exists: bool,
671    pub catalog: Option<Identifier>,
672    pub database: Option<Identifier>,
673    pub table: Identifier,
674    pub new_catalog: Option<Identifier>,
675    pub new_database: Option<Identifier>,
676    pub new_table: Identifier,
677}
678
679impl Display for RenameTableStmt {
680    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
681        write!(f, "RENAME TABLE ")?;
682        if self.if_exists {
683            write!(f, "IF EXISTS ")?;
684        }
685        write_dot_separated_list(
686            f,
687            self.catalog
688                .iter()
689                .chain(&self.database)
690                .chain(Some(&self.table)),
691        )?;
692        write!(f, " TO ")?;
693        write_dot_separated_list(
694            f,
695            self.new_catalog
696                .iter()
697                .chain(&self.new_database)
698                .chain(Some(&self.new_table)),
699        )
700    }
701}
702
703#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
704pub struct TruncateTableStmt {
705    pub catalog: Option<Identifier>,
706    pub database: Option<Identifier>,
707    pub table: Identifier,
708}
709
710impl Display for TruncateTableStmt {
711    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
712        write!(f, "TRUNCATE TABLE ")?;
713        write_dot_separated_list(
714            f,
715            self.catalog
716                .iter()
717                .chain(&self.database)
718                .chain(Some(&self.table)),
719        )?;
720        Ok(())
721    }
722}
723
724#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
725pub struct VacuumTableStmt {
726    pub catalog: Option<Identifier>,
727    pub database: Option<Identifier>,
728    pub table: Identifier,
729    pub option: VacuumTableOption,
730}
731
732impl Display for VacuumTableStmt {
733    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
734        write!(f, "VACUUM TABLE ")?;
735        write_dot_separated_list(
736            f,
737            self.catalog
738                .iter()
739                .chain(&self.database)
740                .chain(Some(&self.table)),
741        )?;
742        write!(f, " {}", &self.option)?;
743
744        Ok(())
745    }
746}
747
748#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
749pub struct VacuumDropTableStmt {
750    pub catalog: Option<Identifier>,
751    pub database: Option<Identifier>,
752    pub option: VacuumDropTableOption,
753}
754
755impl Display for VacuumDropTableStmt {
756    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
757        write!(f, "VACUUM DROP TABLE ")?;
758        if self.catalog.is_some() || self.database.is_some() {
759            write!(f, "FROM ")?;
760            write_dot_separated_list(f, self.catalog.iter().chain(&self.database))?;
761            write!(f, " ")?;
762        }
763        write!(f, "{}", &self.option)?;
764
765        Ok(())
766    }
767}
768
769#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
770pub struct VacuumTemporaryFiles {
771    pub limit: Option<u64>,
772    #[drive(skip)]
773    pub retain: Option<Duration>,
774}
775
776impl Display for crate::ast::VacuumTemporaryFiles {
777    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
778        write!(f, "VACUUM TEMPORARY FILES")?;
779        if let Some(retain) = &self.retain {
780            let days = Duration::from_secs(60 * 60 * 24);
781            if retain >= &days {
782                let days = retain.as_secs() / (60 * 60 * 24);
783                write!(f, " RETAIN {days} DAYS")?;
784            } else {
785                let seconds = retain.as_secs();
786                write!(f, " RETAIN {seconds} SECONDS")?;
787            }
788        }
789
790        if let Some(limit) = &self.limit {
791            write!(f, " LIMIT {limit}")?;
792        }
793
794        Ok(())
795    }
796}
797
798#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
799pub struct OptimizeTableStmt {
800    pub catalog: Option<Identifier>,
801    pub database: Option<Identifier>,
802    pub table: Identifier,
803    pub action: OptimizeTableAction,
804    pub limit: Option<u64>,
805}
806
807impl Display for OptimizeTableStmt {
808    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
809        write!(f, "OPTIMIZE TABLE ")?;
810        write_dot_separated_list(
811            f,
812            self.catalog
813                .iter()
814                .chain(&self.database)
815                .chain(Some(&self.table)),
816        )?;
817        write!(f, " {}", &self.action)?;
818        if let Some(limit) = self.limit {
819            write!(f, " LIMIT {limit}")?;
820        }
821
822        Ok(())
823    }
824}
825
826#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
827pub struct AnalyzeTableStmt {
828    pub catalog: Option<Identifier>,
829    pub database: Option<Identifier>,
830    pub table: Identifier,
831    pub no_scan: bool,
832}
833
834impl Display for AnalyzeTableStmt {
835    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
836        write!(f, "ANALYZE TABLE ")?;
837        write_dot_separated_list(
838            f,
839            self.catalog
840                .iter()
841                .chain(&self.database)
842                .chain(Some(&self.table)),
843        )?;
844        if self.no_scan {
845            write!(f, " NOSCAN")?;
846        }
847
848        Ok(())
849    }
850}
851
852#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
853pub struct ExistsTableStmt {
854    pub catalog: Option<Identifier>,
855    pub database: Option<Identifier>,
856    pub table: Identifier,
857}
858
859impl Display for ExistsTableStmt {
860    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
861        write!(f, "EXISTS TABLE ")?;
862        write_dot_separated_list(
863            f,
864            self.catalog
865                .iter()
866                .chain(&self.database)
867                .chain(Some(&self.table)),
868        )
869    }
870}
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq, Drive, DriveMut)]
873pub enum Engine {
874    Null,
875    Memory,
876    Fuse,
877    View,
878    Random,
879    Iceberg,
880    Delta,
881}
882
883impl Display for Engine {
884    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
885        match self {
886            Engine::Null => write!(f, "NULL"),
887            Engine::Memory => write!(f, "MEMORY"),
888            Engine::Fuse => write!(f, "FUSE"),
889            Engine::View => write!(f, "VIEW"),
890            Engine::Random => write!(f, "RANDOM"),
891            Engine::Iceberg => write!(f, "ICEBERG"),
892            Engine::Delta => write!(f, "DELTA"),
893        }
894    }
895}
896
897impl From<&str> for Engine {
898    fn from(s: &str) -> Self {
899        match s.to_lowercase().as_str() {
900            "null" => Engine::Null,
901            "memory" => Engine::Memory,
902            "fuse" => Engine::Fuse,
903            "view" => Engine::View,
904            "random" => Engine::Random,
905            "iceberg" => Engine::Iceberg,
906            "delta" => Engine::Delta,
907            _ => unreachable!("invalid engine: {}", s),
908        }
909    }
910}
911
912#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
913pub enum CompactTarget {
914    Block,
915    Segment,
916}
917
918#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
919pub struct VacuumTableOption {
920    // Some(true) means dry run with summary option
921    pub dry_run: Option<bool>,
922}
923
924impl Display for VacuumTableOption {
925    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
926        if let Some(summary) = self.dry_run {
927            write!(f, "DRY RUN")?;
928            if summary {
929                write!(f, " SUMMARY")?;
930            }
931        }
932        Ok(())
933    }
934}
935
936#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
937pub struct VacuumDropTableOption {
938    // Some(true) means dry run with summary option
939    pub dry_run: Option<bool>,
940    pub limit: Option<usize>,
941}
942
943impl Display for VacuumDropTableOption {
944    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
945        if let Some(summary) = self.dry_run {
946            write!(f, "DRY RUN")?;
947            if summary {
948                write!(f, " SUMMARY")?;
949            }
950        }
951        if let Some(limit) = self.limit {
952            write!(f, " LIMIT {}", limit)?;
953        }
954        Ok(())
955    }
956}
957
958#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
959pub enum OptimizeTableAction {
960    All,
961    Purge { before: Option<TimeTravelPoint> },
962    Compact { target: CompactTarget },
963}
964
965impl Display for OptimizeTableAction {
966    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
967        match self {
968            OptimizeTableAction::All => write!(f, "ALL"),
969            OptimizeTableAction::Purge { before } => {
970                write!(f, "PURGE")?;
971                if let Some(point) = before {
972                    write!(f, " BEFORE {}", point)?;
973                }
974                Ok(())
975            }
976            OptimizeTableAction::Compact { target } => {
977                match target {
978                    CompactTarget::Block => {
979                        write!(f, "COMPACT")?;
980                    }
981                    CompactTarget::Segment => {
982                        write!(f, "COMPACT SEGMENT")?;
983                    }
984                }
985                Ok(())
986            }
987        }
988    }
989}
990
991#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
992pub enum ColumnExpr {
993    Default(Box<Expr>),
994    Virtual(Box<Expr>),
995    Stored(Box<Expr>),
996    AutoIncrement {
997        start: u64,
998        step: i64,
999        is_ordered: bool,
1000    },
1001}
1002
1003impl Display for ColumnExpr {
1004    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1005        match self {
1006            ColumnExpr::Default(expr) => {
1007                write!(f, " DEFAULT {expr}")?;
1008            }
1009            ColumnExpr::Virtual(expr) => {
1010                write!(f, " AS ({expr}) VIRTUAL")?;
1011            }
1012            ColumnExpr::Stored(expr) => {
1013                write!(f, " AS ({expr}) STORED")?;
1014            }
1015            ColumnExpr::AutoIncrement {
1016                start,
1017                step,
1018                is_ordered,
1019            } => {
1020                write!(f, " AUTOINCREMENT ({}, {}) ", start, step)?;
1021                if *is_ordered {
1022                    write!(f, "ORDER")?;
1023                } else {
1024                    write!(f, "NOORDER")?;
1025                }
1026            }
1027        }
1028        Ok(())
1029    }
1030}
1031
1032#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1033pub enum NullableConstraint {
1034    Null,
1035    NotNull,
1036}
1037
1038#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1039pub struct ColumnDefinition {
1040    pub name: Identifier,
1041    pub data_type: TypeName,
1042    pub expr: Option<ColumnExpr>,
1043    pub check: Option<Expr>,
1044    pub comment: Option<String>,
1045}
1046
1047impl Display for ColumnDefinition {
1048    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1049        write!(f, "{} {}", self.name, self.data_type)?;
1050        if let Some(expr) = &self.expr {
1051            write!(f, "{expr}")?;
1052        }
1053        if let Some(check_expr) = &self.check {
1054            write!(f, " CHECK ({check_expr})")?;
1055        }
1056        if let Some(comment) = &self.comment {
1057            write!(f, " COMMENT {}", QuotedString(comment, '\''))?;
1058        }
1059        Ok(())
1060    }
1061}
1062
1063#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1064pub struct TableIndexDefinition {
1065    pub index_name: Identifier,
1066    pub index_type: TableIndexType,
1067    pub columns: Vec<Identifier>,
1068    pub sync_creation: bool,
1069    pub index_options: BTreeMap<String, String>,
1070}
1071
1072impl Display for TableIndexDefinition {
1073    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1074        if !self.sync_creation {
1075            write!(f, "ASYNC ")?;
1076        }
1077        write!(f, "{} INDEX", self.index_type)?;
1078        write!(f, " {}", self.index_name)?;
1079        write!(f, " (")?;
1080        write_comma_separated_list(f, &self.columns)?;
1081        write!(f, ")")?;
1082
1083        if !self.index_options.is_empty() {
1084            write!(f, " ")?;
1085            write_space_separated_string_map(f, &self.index_options)?;
1086        }
1087
1088        Ok(())
1089    }
1090}
1091
1092#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1093pub struct ConstraintDefinition {
1094    pub name: Option<Identifier>,
1095    pub constraint_type: ConstraintType,
1096}
1097
1098impl Display for ConstraintDefinition {
1099    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1100        if let Some(constraint_name) = &self.name {
1101            write!(f, "CONSTRAINT {} ", constraint_name)?;
1102        }
1103        write!(f, "{}", self.constraint_type)
1104    }
1105}
1106
1107#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1108pub enum CreateDefinition {
1109    Column(ColumnDefinition),
1110    TableIndex(TableIndexDefinition),
1111    Constraint(ConstraintDefinition),
1112}
1113
1114impl Display for CreateDefinition {
1115    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1116        match self {
1117            CreateDefinition::Column(column_def) => {
1118                write!(f, "{}", column_def)?;
1119            }
1120            CreateDefinition::TableIndex(table_index_def) => {
1121                write!(f, "{}", table_index_def)?;
1122            }
1123            CreateDefinition::Constraint(constraint_def) => {
1124                write!(f, "{}", constraint_def)?;
1125            }
1126        }
1127        Ok(())
1128    }
1129}
1130
1131#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1132pub struct ColumnComment {
1133    pub name: Identifier,
1134    pub comment: String,
1135}
1136
1137impl Display for ColumnComment {
1138    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1139        write!(
1140            f,
1141            "{} COMMENT {}",
1142            self.name,
1143            QuotedString(&self.comment, '\'')
1144        )?;
1145        Ok(())
1146    }
1147}
1148
1149#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1150pub enum ModifyColumnAction {
1151    // Set masking policy on a column with optional USING clause
1152    // Syntax: ALTER TABLE ... MODIFY COLUMN <column> SET MASKING POLICY <policy_name> [USING (<columns>)]
1153    //
1154    // Fields:
1155    // - Identifier: The column name to apply the masking policy to
1156    // - String: The masking policy name
1157    // - Option<Vec<Identifier>>: Optional USING clause columns
1158    //
1159    // USING clause behavior (follows Snowflake semantics):
1160    // - First column: The column to mask/tokenize (must match the target column)
1161    // - Additional columns: Condition columns to evaluate for conditional masking
1162    // - If USING is omitted: Treats as normal (non-conditional) masking policy
1163    SetMaskingPolicy(Identifier, String, Option<Vec<Identifier>>),
1164    // Unset masking policy from a column
1165    UnsetMaskingPolicy(Identifier),
1166    // vec<ColumnDefinition>
1167    SetDataType(Vec<ColumnDefinition>),
1168    // column name id
1169    ConvertStoredComputedColumn(Identifier),
1170    // (column name id, new comment)
1171    Comment(Vec<ColumnComment>),
1172}
1173
1174impl Display for ModifyColumnAction {
1175    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1176        match &self {
1177            ModifyColumnAction::SetMaskingPolicy(column, name, using_columns) => {
1178                if let Some(using_columns) = using_columns {
1179                    write!(f, "{} SET MASKING POLICY {} USING (", column, name)?;
1180                    write_comma_separated_list(f, using_columns)?;
1181                    write!(f, ")")?
1182                } else {
1183                    write!(f, "{} SET MASKING POLICY {}", column, name)?
1184                }
1185            }
1186            ModifyColumnAction::UnsetMaskingPolicy(column) => {
1187                write!(f, "{} UNSET MASKING POLICY", column)?
1188            }
1189            ModifyColumnAction::SetDataType(column_defs) => {
1190                write_comma_separated_list(f, column_defs)?
1191            }
1192            ModifyColumnAction::ConvertStoredComputedColumn(column) => {
1193                write!(f, "{} DROP STORED", column)?
1194            }
1195            ModifyColumnAction::Comment(columns) => write_comma_separated_list(f, columns)?,
1196        }
1197
1198        Ok(())
1199    }
1200}
1201
1202#[derive(Debug, Clone, PartialEq, Drive, DriveMut, Default)]
1203pub struct ShowStatisticsStmt {
1204    pub catalog: Option<Identifier>,
1205    pub database: Option<Identifier>,
1206    pub target: ShowStatsTarget,
1207}
1208
1209#[derive(Debug, Clone, PartialEq, Drive, DriveMut, Default)]
1210pub enum ShowStatsTarget {
1211    #[default]
1212    Database,
1213    Table(Identifier),
1214}
1215
1216impl Display for ShowStatisticsStmt {
1217    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1218        write!(f, "SHOW STATISTICS")?;
1219        match &self.target {
1220            ShowStatsTarget::Database => {
1221                if let Some(database) = &self.database {
1222                    write!(f, " FROM DATABASE ")?;
1223                    if let Some(catalog) = &self.catalog {
1224                        write!(f, "{catalog}.",)?;
1225                    }
1226                    write!(f, "{database}")?;
1227                }
1228            }
1229            ShowStatsTarget::Table(table) => {
1230                write!(f, " FROM TABLE ")?;
1231                if let Some(database) = &self.database {
1232                    if let Some(catalog) = &self.catalog {
1233                        write!(f, "{catalog}.",)?;
1234                    }
1235                    write!(f, "{database}.")?;
1236                }
1237                write!(f, "{table}")?;
1238            }
1239        }
1240        Ok(())
1241    }
1242}