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::quote::QuotedString;
24use crate::ast::statements::show::ShowLimit;
25use crate::ast::write_comma_separated_list;
26use crate::ast::write_comma_separated_string_map;
27use crate::ast::write_dot_separated_list;
28use crate::ast::write_space_separated_string_map;
29use crate::ast::CreateOption;
30use crate::ast::Expr;
31use crate::ast::Identifier;
32use crate::ast::Query;
33use crate::ast::TableIndexType;
34use crate::ast::TableReference;
35use crate::ast::TimeTravelPoint;
36use crate::ast::TypeName;
37use crate::ast::UriLocation;
38
39#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
40pub struct ShowTablesStmt {
41    pub catalog: Option<Identifier>,
42    pub database: Option<Identifier>,
43    pub full: bool,
44    pub limit: Option<ShowLimit>,
45    pub with_history: bool,
46}
47
48impl Display for ShowTablesStmt {
49    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
50        write!(f, "SHOW")?;
51        if self.full {
52            write!(f, " FULL")?;
53        }
54        write!(f, " TABLES")?;
55        if self.with_history {
56            write!(f, " HISTORY")?;
57        }
58        if let Some(database) = &self.database {
59            write!(f, " FROM ")?;
60            if let Some(catalog) = &self.catalog {
61                write!(f, "{catalog}.",)?;
62            }
63            write!(f, "{database}")?;
64        }
65        if let Some(limit) = &self.limit {
66            write!(f, " {limit}")?;
67        }
68
69        Ok(())
70    }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
74pub struct ShowCreateTableStmt {
75    pub catalog: Option<Identifier>,
76    pub database: Option<Identifier>,
77    pub table: Identifier,
78    pub with_quoted_ident: bool,
79}
80
81impl Display for ShowCreateTableStmt {
82    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
83        write!(f, "SHOW CREATE TABLE ")?;
84        write_dot_separated_list(
85            f,
86            self.catalog
87                .iter()
88                .chain(&self.database)
89                .chain(Some(&self.table)),
90        )?;
91        if self.with_quoted_ident {
92            write!(f, " WITH QUOTED_IDENTIFIERS")?
93        }
94        Ok(())
95    }
96}
97
98#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
99pub struct ShowTablesStatusStmt {
100    pub database: Option<Identifier>,
101    pub limit: Option<ShowLimit>,
102}
103
104impl Display for ShowTablesStatusStmt {
105    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
106        write!(f, "SHOW TABLE STATUS")?;
107        if let Some(database) = &self.database {
108            write!(f, " FROM {database}")?;
109        }
110        if let Some(limit) = &self.limit {
111            write!(f, " {limit}")?;
112        }
113
114        Ok(())
115    }
116}
117
118#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
119pub struct ShowDropTablesStmt {
120    pub database: Option<Identifier>,
121    pub limit: Option<ShowLimit>,
122}
123
124impl Display for ShowDropTablesStmt {
125    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
126        write!(f, "SHOW DROP TABLES")?;
127        if let Some(database) = &self.database {
128            write!(f, " FROM {database}")?;
129        }
130        if let Some(limit) = &self.limit {
131            write!(f, " {limit}")?;
132        }
133
134        Ok(())
135    }
136}
137
138#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
139pub enum ClusterType {
140    Linear,
141    Hilbert,
142}
143
144impl Display for ClusterType {
145    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
146        match self {
147            ClusterType::Linear => write!(f, "LINEAR"),
148            ClusterType::Hilbert => write!(f, "HILBERT"),
149        }
150    }
151}
152
153impl std::str::FromStr for ClusterType {
154    type Err = ();
155    fn from_str(s: &str) -> Result<Self, Self::Err> {
156        match s.to_lowercase().as_str() {
157            "linear" => Ok(ClusterType::Linear),
158            "hilbert" => Ok(ClusterType::Hilbert),
159            _ => Err(()),
160        }
161    }
162}
163
164#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
165pub struct ClusterOption {
166    pub cluster_type: ClusterType,
167    pub cluster_exprs: Vec<Expr>,
168}
169
170impl Display for ClusterOption {
171    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
172        write!(f, "CLUSTER BY {}(", self.cluster_type)?;
173        write_comma_separated_list(f, &self.cluster_exprs)?;
174        write!(f, ")")
175    }
176}
177
178#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
179pub struct CreateTableStmt {
180    pub create_option: CreateOption,
181    pub catalog: Option<Identifier>,
182    pub database: Option<Identifier>,
183    pub table: Identifier,
184    pub source: Option<CreateTableSource>,
185    pub engine: Option<Engine>,
186    pub uri_location: Option<UriLocation>,
187    pub cluster_by: Option<ClusterOption>,
188    pub table_options: BTreeMap<String, String>,
189    pub iceberg_table_partition: Option<Vec<Identifier>>,
190    pub table_properties: Option<BTreeMap<String, String>>,
191    pub as_query: Option<Box<Query>>,
192    pub table_type: TableType,
193}
194
195#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
196pub enum TableType {
197    Normal,
198    Transient,
199    Temporary,
200}
201
202impl Display for CreateTableStmt {
203    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
204        write!(f, "CREATE")?;
205        if let CreateOption::CreateOrReplace = self.create_option {
206            write!(f, " OR REPLACE")?;
207        }
208        match self.table_type {
209            TableType::Normal => {}
210            TableType::Transient => write!(f, " TRANSIENT")?,
211            TableType::Temporary => write!(f, " TEMPORARY")?,
212        };
213        write!(f, " TABLE")?;
214        if let CreateOption::CreateIfNotExists = self.create_option {
215            write!(f, " IF NOT EXISTS")?;
216        }
217        write!(f, " ")?;
218        write_dot_separated_list(
219            f,
220            self.catalog
221                .iter()
222                .chain(&self.database)
223                .chain(Some(&self.table)),
224        )?;
225
226        if let Some(source) = &self.source {
227            write!(f, " {source}")?;
228        }
229
230        if let Some(engine) = &self.engine {
231            write!(f, " ENGINE = {engine}")?;
232        }
233
234        if let Some(uri_location) = &self.uri_location {
235            write!(f, " {uri_location}")?;
236        }
237
238        if let Some(cluster_by) = &self.cluster_by {
239            write!(f, " {cluster_by}")?;
240        }
241
242        // Format table options
243        if !self.table_options.is_empty() {
244            write!(f, " ")?;
245            write_space_separated_string_map(f, &self.table_options)?;
246        }
247
248        if let Some(iceberg_table_partition) = &self.iceberg_table_partition {
249            write!(f, " PARTITION BY(")?;
250            write_comma_separated_list(f, iceberg_table_partition)?;
251            write!(f, ")")?;
252        }
253
254        if let Some(table_properties) = &self.table_properties {
255            write!(f, " PROPERTIES(")?;
256            write_space_separated_string_map(f, table_properties)?;
257            write!(f, ")")?;
258        }
259
260        if let Some(as_query) = &self.as_query {
261            write!(f, " AS {as_query}")?;
262        }
263
264        Ok(())
265    }
266}
267
268#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
269pub struct AttachTableStmt {
270    pub catalog: Option<Identifier>,
271    pub database: Option<Identifier>,
272    pub table: Identifier,
273    pub columns_opt: Option<Vec<Identifier>>,
274    pub uri_location: UriLocation,
275}
276
277impl Display for AttachTableStmt {
278    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
279        write!(f, "ATTACH TABLE ")?;
280        write_dot_separated_list(
281            f,
282            self.catalog
283                .iter()
284                .chain(&self.database)
285                .chain(Some(&self.table)),
286        )?;
287
288        if let Some(cols) = &self.columns_opt {
289            write!(f, " (")?;
290            write_comma_separated_list(f, cols.iter())?;
291            write!(f, ")")?;
292        }
293
294        write!(f, " {}", self.uri_location)?;
295
296        Ok(())
297    }
298}
299
300#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
301pub enum CreateTableSource {
302    Columns(Vec<ColumnDefinition>, Option<Vec<TableIndexDefinition>>),
303    Like {
304        catalog: Option<Identifier>,
305        database: Option<Identifier>,
306        table: Identifier,
307    },
308}
309
310impl Display for CreateTableSource {
311    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
312        match self {
313            CreateTableSource::Columns(columns, table_indexes) => {
314                write!(f, "(")?;
315                write_comma_separated_list(f, columns)?;
316                if let Some(table_indexes) = table_indexes {
317                    write!(f, ", ")?;
318                    write_comma_separated_list(f, table_indexes)?;
319                }
320                write!(f, ")")
321            }
322            CreateTableSource::Like {
323                catalog,
324                database,
325                table,
326            } => {
327                write!(f, "LIKE ")?;
328                write_dot_separated_list(f, catalog.iter().chain(database).chain(Some(table)))
329            }
330        }
331    }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
335pub struct DescribeTableStmt {
336    pub catalog: Option<Identifier>,
337    pub database: Option<Identifier>,
338    pub table: Identifier,
339}
340
341impl Display for DescribeTableStmt {
342    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
343        write!(f, "DESCRIBE ")?;
344        write_dot_separated_list(
345            f,
346            self.catalog
347                .iter()
348                .chain(self.database.iter().chain(Some(&self.table))),
349        )
350    }
351}
352
353#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
354pub struct DropTableStmt {
355    pub if_exists: bool,
356    pub catalog: Option<Identifier>,
357    pub database: Option<Identifier>,
358    pub table: Identifier,
359    pub all: bool,
360}
361
362impl Display for DropTableStmt {
363    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
364        write!(f, "DROP TABLE ")?;
365        if self.if_exists {
366            write!(f, "IF EXISTS ")?;
367        }
368        write_dot_separated_list(
369            f,
370            self.catalog
371                .iter()
372                .chain(&self.database)
373                .chain(Some(&self.table)),
374        )?;
375        if self.all {
376            write!(f, " ALL")?;
377        }
378
379        Ok(())
380    }
381}
382
383#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
384pub struct UndropTableStmt {
385    pub catalog: Option<Identifier>,
386    pub database: Option<Identifier>,
387    pub table: Identifier,
388}
389
390impl Display for UndropTableStmt {
391    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
392        write!(f, "UNDROP TABLE ")?;
393        write_dot_separated_list(
394            f,
395            self.catalog
396                .iter()
397                .chain(&self.database)
398                .chain(Some(&self.table)),
399        )
400    }
401}
402
403#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
404pub struct AlterTableStmt {
405    pub if_exists: bool,
406    pub table_reference: TableReference,
407    pub action: AlterTableAction,
408}
409
410impl Display for AlterTableStmt {
411    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
412        write!(f, "ALTER TABLE")?;
413        if self.if_exists {
414            write!(f, " IF EXISTS")?;
415        }
416        write!(f, " {}", self.table_reference)?;
417        write!(f, " {}", self.action)
418    }
419}
420
421#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
422pub enum AlterTableAction {
423    RenameTable {
424        new_table: Identifier,
425    },
426    AddColumn {
427        column: ColumnDefinition,
428        option: AddColumnOption,
429    },
430    RenameColumn {
431        old_column: Identifier,
432        new_column: Identifier,
433    },
434    ModifyTableComment {
435        new_comment: String,
436    },
437    ModifyColumn {
438        action: ModifyColumnAction,
439    },
440    DropColumn {
441        column: Identifier,
442    },
443    AlterTableClusterKey {
444        cluster_by: ClusterOption,
445    },
446    DropTableClusterKey,
447    ReclusterTable {
448        is_final: bool,
449        selection: Option<Expr>,
450        limit: Option<u64>,
451    },
452    FlashbackTo {
453        point: TimeTravelPoint,
454    },
455    SetOptions {
456        set_options: BTreeMap<String, String>,
457    },
458    UnsetOptions {
459        targets: Vec<Identifier>,
460    },
461    RefreshTableCache,
462    ModifyConnection {
463        new_connection: BTreeMap<String, String>,
464    },
465}
466
467impl Display for AlterTableAction {
468    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
469        match self {
470            AlterTableAction::SetOptions { set_options } => {
471                write!(f, "SET OPTIONS (")?;
472                write_comma_separated_string_map(f, set_options)?;
473                write!(f, ")")?;
474            }
475
476            AlterTableAction::RenameTable { new_table } => {
477                write!(f, "RENAME TO {new_table}")?;
478            }
479            AlterTableAction::ModifyTableComment { new_comment } => {
480                write!(f, "COMMENT={}", QuotedString(new_comment, '\''))?;
481            }
482            AlterTableAction::RenameColumn {
483                old_column,
484                new_column,
485            } => {
486                write!(f, "RENAME COLUMN {old_column} TO {new_column}")?;
487            }
488            AlterTableAction::AddColumn { column, option } => {
489                write!(f, "ADD COLUMN {column}{option}")?;
490            }
491            AlterTableAction::ModifyColumn { action } => {
492                write!(f, "MODIFY COLUMN {action}")?;
493            }
494            AlterTableAction::DropColumn { column } => {
495                write!(f, "DROP COLUMN {column}")?;
496            }
497            AlterTableAction::AlterTableClusterKey { cluster_by } => {
498                write!(f, "{cluster_by}")?;
499            }
500            AlterTableAction::DropTableClusterKey => {
501                write!(f, "DROP CLUSTER KEY")?;
502            }
503            AlterTableAction::ReclusterTable {
504                is_final,
505                selection,
506                limit,
507            } => {
508                write!(f, "RECLUSTER")?;
509                if *is_final {
510                    write!(f, " FINAL")?;
511                }
512                if let Some(conditions) = selection {
513                    write!(f, " WHERE {conditions}")?;
514                }
515                if let Some(limit) = limit {
516                    write!(f, " LIMIT {limit}")?;
517                }
518            }
519            AlterTableAction::FlashbackTo { point } => {
520                write!(f, "FLASHBACK TO {}", point)?;
521            }
522            AlterTableAction::UnsetOptions {
523                targets: unset_targets,
524            } => {
525                write!(f, "UNSET OPTIONS ")?;
526                if unset_targets.len() == 1 {
527                    write!(f, "{}", unset_targets[0])?;
528                } else {
529                    write!(f, "(")?;
530                    write_comma_separated_list(f, unset_targets)?;
531                    write!(f, ")")?;
532                }
533            }
534            AlterTableAction::RefreshTableCache => {
535                write!(f, "REFRESH CACHE")?;
536            }
537            AlterTableAction::ModifyConnection { new_connection } => {
538                write!(f, "CONNECTION=(")?;
539                write_space_separated_string_map(f, new_connection)?;
540                write!(f, ")")?;
541            }
542        };
543        Ok(())
544    }
545}
546
547#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
548pub enum AddColumnOption {
549    End,
550    First,
551    After(Identifier),
552}
553
554impl Display for AddColumnOption {
555    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
556        match self {
557            AddColumnOption::First => write!(f, " FIRST"),
558            AddColumnOption::After(ident) => write!(f, " AFTER {ident}"),
559            AddColumnOption::End => Ok(()),
560        }
561    }
562}
563
564#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
565pub struct RenameTableStmt {
566    pub if_exists: bool,
567    pub catalog: Option<Identifier>,
568    pub database: Option<Identifier>,
569    pub table: Identifier,
570    pub new_catalog: Option<Identifier>,
571    pub new_database: Option<Identifier>,
572    pub new_table: Identifier,
573}
574
575impl Display for RenameTableStmt {
576    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
577        write!(f, "RENAME TABLE ")?;
578        if self.if_exists {
579            write!(f, "IF EXISTS ")?;
580        }
581        write_dot_separated_list(
582            f,
583            self.catalog
584                .iter()
585                .chain(&self.database)
586                .chain(Some(&self.table)),
587        )?;
588        write!(f, " TO ")?;
589        write_dot_separated_list(
590            f,
591            self.new_catalog
592                .iter()
593                .chain(&self.new_database)
594                .chain(Some(&self.new_table)),
595        )
596    }
597}
598
599#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
600pub struct TruncateTableStmt {
601    pub catalog: Option<Identifier>,
602    pub database: Option<Identifier>,
603    pub table: Identifier,
604}
605
606impl Display for TruncateTableStmt {
607    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
608        write!(f, "TRUNCATE TABLE ")?;
609        write_dot_separated_list(
610            f,
611            self.catalog
612                .iter()
613                .chain(&self.database)
614                .chain(Some(&self.table)),
615        )?;
616        Ok(())
617    }
618}
619
620#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
621pub struct VacuumTableStmt {
622    pub catalog: Option<Identifier>,
623    pub database: Option<Identifier>,
624    pub table: Identifier,
625    pub option: VacuumTableOption,
626}
627
628impl Display for VacuumTableStmt {
629    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
630        write!(f, "VACUUM TABLE ")?;
631        write_dot_separated_list(
632            f,
633            self.catalog
634                .iter()
635                .chain(&self.database)
636                .chain(Some(&self.table)),
637        )?;
638        write!(f, " {}", &self.option)?;
639
640        Ok(())
641    }
642}
643
644#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
645pub struct VacuumDropTableStmt {
646    pub catalog: Option<Identifier>,
647    pub database: Option<Identifier>,
648    pub option: VacuumDropTableOption,
649}
650
651impl Display for VacuumDropTableStmt {
652    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
653        write!(f, "VACUUM DROP TABLE ")?;
654        if self.catalog.is_some() || self.database.is_some() {
655            write!(f, "FROM ")?;
656            write_dot_separated_list(f, self.catalog.iter().chain(&self.database))?;
657            write!(f, " ")?;
658        }
659        write!(f, "{}", &self.option)?;
660
661        Ok(())
662    }
663}
664
665#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
666pub struct VacuumTemporaryFiles {
667    pub limit: Option<u64>,
668    #[drive(skip)]
669    pub retain: Option<Duration>,
670}
671
672impl Display for crate::ast::VacuumTemporaryFiles {
673    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
674        write!(f, "VACUUM TEMPORARY FILES ")?;
675        if let Some(retain) = &self.retain {
676            let days = Duration::from_secs(60 * 60 * 24);
677            if retain >= &days {
678                let days = retain.as_secs() / (60 * 60 * 24);
679                write!(f, "RETAIN {days} DAYS ")?;
680            } else {
681                let seconds = retain.as_secs();
682                write!(f, "RETAIN {seconds} SECONDS ")?;
683            }
684        }
685
686        if let Some(limit) = &self.limit {
687            write!(f, " LIMIT {limit}")?;
688        }
689
690        Ok(())
691    }
692}
693
694#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
695pub struct OptimizeTableStmt {
696    pub catalog: Option<Identifier>,
697    pub database: Option<Identifier>,
698    pub table: Identifier,
699    pub action: OptimizeTableAction,
700    pub limit: Option<u64>,
701}
702
703impl Display for OptimizeTableStmt {
704    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
705        write!(f, "OPTIMIZE TABLE ")?;
706        write_dot_separated_list(
707            f,
708            self.catalog
709                .iter()
710                .chain(&self.database)
711                .chain(Some(&self.table)),
712        )?;
713        write!(f, " {}", &self.action)?;
714        if let Some(limit) = self.limit {
715            write!(f, " LIMIT {limit}")?;
716        }
717
718        Ok(())
719    }
720}
721
722#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
723pub struct AnalyzeTableStmt {
724    pub catalog: Option<Identifier>,
725    pub database: Option<Identifier>,
726    pub table: Identifier,
727    pub no_scan: bool,
728}
729
730impl Display for AnalyzeTableStmt {
731    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
732        write!(f, "ANALYZE TABLE ")?;
733        write_dot_separated_list(
734            f,
735            self.catalog
736                .iter()
737                .chain(&self.database)
738                .chain(Some(&self.table)),
739        )?;
740        if self.no_scan {
741            write!(f, " NOSCAN")?;
742        }
743
744        Ok(())
745    }
746}
747
748#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
749pub struct ExistsTableStmt {
750    pub catalog: Option<Identifier>,
751    pub database: Option<Identifier>,
752    pub table: Identifier,
753}
754
755impl Display for ExistsTableStmt {
756    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
757        write!(f, "EXISTS TABLE ")?;
758        write_dot_separated_list(
759            f,
760            self.catalog
761                .iter()
762                .chain(&self.database)
763                .chain(Some(&self.table)),
764        )
765    }
766}
767
768#[derive(Debug, Clone, Copy, PartialEq, Eq, Drive, DriveMut)]
769pub enum Engine {
770    Null,
771    Memory,
772    Fuse,
773    View,
774    Random,
775    Iceberg,
776    Delta,
777}
778
779impl Display for Engine {
780    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
781        match self {
782            Engine::Null => write!(f, "NULL"),
783            Engine::Memory => write!(f, "MEMORY"),
784            Engine::Fuse => write!(f, "FUSE"),
785            Engine::View => write!(f, "VIEW"),
786            Engine::Random => write!(f, "RANDOM"),
787            Engine::Iceberg => write!(f, "ICEBERG"),
788            Engine::Delta => write!(f, "DELTA"),
789        }
790    }
791}
792
793impl From<&str> for Engine {
794    fn from(s: &str) -> Self {
795        match s.to_lowercase().as_str() {
796            "null" => Engine::Null,
797            "memory" => Engine::Memory,
798            "fuse" => Engine::Fuse,
799            "view" => Engine::View,
800            "random" => Engine::Random,
801            "iceberg" => Engine::Iceberg,
802            "delta" => Engine::Delta,
803            _ => unreachable!("invalid engine: {}", s),
804        }
805    }
806}
807
808#[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)]
809pub enum CompactTarget {
810    Block,
811    Segment,
812}
813
814#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
815pub struct VacuumTableOption {
816    // Some(true) means dry run with summary option
817    pub dry_run: Option<bool>,
818}
819
820impl Display for VacuumTableOption {
821    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
822        if let Some(summary) = self.dry_run {
823            write!(f, "DRY RUN")?;
824            if summary {
825                write!(f, " SUMMARY")?;
826            }
827        }
828        Ok(())
829    }
830}
831
832#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
833pub struct VacuumDropTableOption {
834    // Some(true) means dry run with summary option
835    pub dry_run: Option<bool>,
836    pub limit: Option<usize>,
837}
838
839impl Display for VacuumDropTableOption {
840    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
841        if let Some(summary) = self.dry_run {
842            write!(f, "DRY RUN")?;
843            if summary {
844                write!(f, " SUMMARY")?;
845            }
846        }
847        if let Some(limit) = self.limit {
848            write!(f, " LIMIT {}", limit)?;
849        }
850        Ok(())
851    }
852}
853
854#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
855pub enum OptimizeTableAction {
856    All,
857    Purge { before: Option<TimeTravelPoint> },
858    Compact { target: CompactTarget },
859}
860
861impl Display for OptimizeTableAction {
862    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
863        match self {
864            OptimizeTableAction::All => write!(f, "ALL"),
865            OptimizeTableAction::Purge { before } => {
866                write!(f, "PURGE")?;
867                if let Some(point) = before {
868                    write!(f, " BEFORE {}", point)?;
869                }
870                Ok(())
871            }
872            OptimizeTableAction::Compact { target } => {
873                match target {
874                    CompactTarget::Block => {
875                        write!(f, "COMPACT")?;
876                    }
877                    CompactTarget::Segment => {
878                        write!(f, "COMPACT SEGMENT")?;
879                    }
880                }
881                Ok(())
882            }
883        }
884    }
885}
886
887#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
888pub enum ColumnExpr {
889    Default(Box<Expr>),
890    Virtual(Box<Expr>),
891    Stored(Box<Expr>),
892}
893
894impl Display for ColumnExpr {
895    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
896        match self {
897            ColumnExpr::Default(expr) => {
898                write!(f, " DEFAULT {expr}")?;
899            }
900            ColumnExpr::Virtual(expr) => {
901                write!(f, " AS ({expr}) VIRTUAL")?;
902            }
903            ColumnExpr::Stored(expr) => {
904                write!(f, " AS ({expr}) STORED")?;
905            }
906        }
907        Ok(())
908    }
909}
910
911#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
912pub enum NullableConstraint {
913    Null,
914    NotNull,
915}
916
917#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
918pub struct ColumnDefinition {
919    pub name: Identifier,
920    pub data_type: TypeName,
921    pub expr: Option<ColumnExpr>,
922    pub comment: Option<String>,
923}
924
925impl Display for ColumnDefinition {
926    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
927        write!(f, "{} {}", self.name, self.data_type)?;
928        if let Some(expr) = &self.expr {
929            write!(f, "{expr}")?;
930        }
931        if let Some(comment) = &self.comment {
932            write!(f, " COMMENT {}", QuotedString(comment, '\''))?;
933        }
934        Ok(())
935    }
936}
937
938#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
939pub struct TableIndexDefinition {
940    pub index_name: Identifier,
941    pub index_type: TableIndexType,
942    pub columns: Vec<Identifier>,
943    pub sync_creation: bool,
944    pub index_options: BTreeMap<String, String>,
945}
946
947impl Display for TableIndexDefinition {
948    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
949        if !self.sync_creation {
950            write!(f, "ASYNC ")?;
951        }
952        write!(f, "{} INDEX", self.index_type)?;
953        write!(f, " {}", self.index_name)?;
954        write!(f, " (")?;
955        write_comma_separated_list(f, &self.columns)?;
956        write!(f, ")")?;
957
958        if !self.index_options.is_empty() {
959            write!(f, " ")?;
960            write_space_separated_string_map(f, &self.index_options)?;
961        }
962
963        Ok(())
964    }
965}
966
967#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
968pub enum CreateDefinition {
969    Column(ColumnDefinition),
970    TableIndex(TableIndexDefinition),
971}
972
973impl Display for CreateDefinition {
974    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
975        match self {
976            CreateDefinition::Column(column_def) => {
977                write!(f, "{}", column_def)?;
978            }
979            CreateDefinition::TableIndex(table_index_def) => {
980                write!(f, "{}", table_index_def)?;
981            }
982        }
983        Ok(())
984    }
985}
986
987#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
988pub struct ColumnComment {
989    pub name: Identifier,
990    pub comment: String,
991}
992
993impl Display for ColumnComment {
994    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
995        write!(
996            f,
997            "{} COMMENT {}",
998            self.name,
999            QuotedString(&self.comment, '\'')
1000        )?;
1001        Ok(())
1002    }
1003}
1004
1005#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
1006pub enum ModifyColumnAction {
1007    // (column name id, masking policy name)
1008    SetMaskingPolicy(Identifier, String),
1009    // column name id
1010    UnsetMaskingPolicy(Identifier),
1011    // vec<ColumnDefinition>
1012    SetDataType(Vec<ColumnDefinition>),
1013    // column name id
1014    ConvertStoredComputedColumn(Identifier),
1015    // (column name id, new comment)
1016    Comment(Vec<ColumnComment>),
1017}
1018
1019impl Display for ModifyColumnAction {
1020    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
1021        match &self {
1022            ModifyColumnAction::SetMaskingPolicy(column, name) => {
1023                write!(f, "{} SET MASKING POLICY {}", column, name)?
1024            }
1025            ModifyColumnAction::UnsetMaskingPolicy(column) => {
1026                write!(f, "{} UNSET MASKING POLICY", column)?
1027            }
1028            ModifyColumnAction::SetDataType(column_defs) => {
1029                write_comma_separated_list(f, column_defs)?
1030            }
1031            ModifyColumnAction::ConvertStoredComputedColumn(column) => {
1032                write!(f, "{} DROP STORED", column)?
1033            }
1034            ModifyColumnAction::Comment(columns) => write_comma_separated_list(f, columns)?,
1035        }
1036
1037        Ok(())
1038    }
1039}