sqltk_parser/ast/
ddl.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//! AST types specific to CREATE/ALTER variants of [`Statement`](crate::ast::Statement)
19//! (commonly referred to as Data Definition Language, or DDL)
20
21#[cfg(not(feature = "std"))]
22use alloc::{boxed::Box, string::String, vec::Vec};
23use core::fmt::{self, Write};
24
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28#[cfg(feature = "visitor")]
29use sqltk_parser_derive::{Visit, VisitMut};
30
31use crate::ast::value::escape_single_quote_string;
32use crate::ast::{
33    display_comma_separated, display_separated, CreateFunctionBody, CreateFunctionUsing, DataType,
34    Expr, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel,
35    Ident, MySQLColumnPosition, ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect,
36    SequenceOptions, SqlOption, Tag, Value,
37};
38use crate::keywords::Keyword;
39use crate::tokenizer::Token;
40
41/// An `ALTER TABLE` (`Statement::AlterTable`) operation
42#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
45pub enum AlterTableOperation {
46    /// `ADD <table_constraint>`
47    AddConstraint(TableConstraint),
48    /// `ADD [COLUMN] [IF NOT EXISTS] <column_def>`
49    AddColumn {
50        /// `[COLUMN]`.
51        column_keyword: bool,
52        /// `[IF NOT EXISTS]`
53        if_not_exists: bool,
54        /// <column_def>.
55        column_def: ColumnDef,
56        /// MySQL `ALTER TABLE` only  [FIRST | AFTER column_name]
57        column_position: Option<MySQLColumnPosition>,
58    },
59    /// `ADD PROJECTION [IF NOT EXISTS] name ( SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])`
60    ///
61    /// Note: this is a ClickHouse-specific operation.
62    /// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#add-projection)
63    AddProjection {
64        if_not_exists: bool,
65        name: Ident,
66        select: ProjectionSelect,
67    },
68
69    /// `DROP PROJECTION [IF EXISTS] name`
70    ///
71    /// Note: this is a ClickHouse-specific operation.
72    /// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#drop-projection)
73    DropProjection {
74        if_exists: bool,
75        name: Ident,
76    },
77
78    /// `MATERIALIZE PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
79    ///
80    ///  Note: this is a ClickHouse-specific operation.
81    /// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#materialize-projection)
82    MaterializeProjection {
83        if_exists: bool,
84        name: Ident,
85        partition: Option<Ident>,
86    },
87
88    /// `CLEAR PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
89    ///
90    /// Note: this is a ClickHouse-specific operation.
91    /// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#clear-projection)
92    ClearProjection {
93        if_exists: bool,
94        name: Ident,
95        partition: Option<Ident>,
96    },
97
98    /// `DISABLE ROW LEVEL SECURITY`
99    ///
100    /// Note: this is a PostgreSQL-specific operation.
101    DisableRowLevelSecurity,
102    /// `DISABLE RULE rewrite_rule_name`
103    ///
104    /// Note: this is a PostgreSQL-specific operation.
105    DisableRule {
106        name: Ident,
107    },
108    /// `DISABLE TRIGGER [ trigger_name | ALL | USER ]`
109    ///
110    /// Note: this is a PostgreSQL-specific operation.
111    DisableTrigger {
112        name: Ident,
113    },
114    /// `DROP CONSTRAINT [ IF EXISTS ] <name>`
115    DropConstraint {
116        if_exists: bool,
117        name: Ident,
118        cascade: bool,
119    },
120    /// `DROP [ COLUMN ] [ IF EXISTS ] <column_name> [ CASCADE ]`
121    DropColumn {
122        column_name: Ident,
123        if_exists: bool,
124        cascade: bool,
125    },
126    /// `ATTACH PART|PARTITION <partition_expr>`
127    /// Note: this is a ClickHouse-specific operation, please refer to
128    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/pakrtition#attach-partitionpart)
129    AttachPartition {
130        // PART is not a short form of PARTITION, it's a separate keyword
131        // which represents a physical file on disk and partition is a logical entity.
132        partition: Partition,
133    },
134    /// `DETACH PART|PARTITION <partition_expr>`
135    /// Note: this is a ClickHouse-specific operation, please refer to
136    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#detach-partitionpart)
137    DetachPartition {
138        // See `AttachPartition` for more details
139        partition: Partition,
140    },
141    /// `FREEZE PARTITION <partition_expr>`
142    /// Note: this is a ClickHouse-specific operation, please refer to
143    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#freeze-partition)
144    FreezePartition {
145        partition: Partition,
146        with_name: Option<Ident>,
147    },
148    /// `UNFREEZE PARTITION <partition_expr>`
149    /// Note: this is a ClickHouse-specific operation, please refer to
150    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#unfreeze-partition)
151    UnfreezePartition {
152        partition: Partition,
153        with_name: Option<Ident>,
154    },
155    /// `DROP PRIMARY KEY`
156    ///
157    /// Note: this is a MySQL-specific operation.
158    DropPrimaryKey,
159    /// `ENABLE ALWAYS RULE rewrite_rule_name`
160    ///
161    /// Note: this is a PostgreSQL-specific operation.
162    EnableAlwaysRule {
163        name: Ident,
164    },
165    /// `ENABLE ALWAYS TRIGGER trigger_name`
166    ///
167    /// Note: this is a PostgreSQL-specific operation.
168    EnableAlwaysTrigger {
169        name: Ident,
170    },
171    /// `ENABLE REPLICA RULE rewrite_rule_name`
172    ///
173    /// Note: this is a PostgreSQL-specific operation.
174    EnableReplicaRule {
175        name: Ident,
176    },
177    /// `ENABLE REPLICA TRIGGER trigger_name`
178    ///
179    /// Note: this is a PostgreSQL-specific operation.
180    EnableReplicaTrigger {
181        name: Ident,
182    },
183    /// `ENABLE ROW LEVEL SECURITY`
184    ///
185    /// Note: this is a PostgreSQL-specific operation.
186    EnableRowLevelSecurity,
187    /// `ENABLE RULE rewrite_rule_name`
188    ///
189    /// Note: this is a PostgreSQL-specific operation.
190    EnableRule {
191        name: Ident,
192    },
193    /// `ENABLE TRIGGER [ trigger_name | ALL | USER ]`
194    ///
195    /// Note: this is a PostgreSQL-specific operation.
196    EnableTrigger {
197        name: Ident,
198    },
199    /// `RENAME TO PARTITION (partition=val)`
200    RenamePartitions {
201        old_partitions: Vec<Expr>,
202        new_partitions: Vec<Expr>,
203    },
204    /// Add Partitions
205    AddPartitions {
206        if_not_exists: bool,
207        new_partitions: Vec<Partition>,
208    },
209    DropPartitions {
210        partitions: Vec<Expr>,
211        if_exists: bool,
212    },
213    /// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
214    RenameColumn {
215        old_column_name: Ident,
216        new_column_name: Ident,
217    },
218    /// `RENAME TO <table_name>`
219    RenameTable {
220        table_name: ObjectName,
221    },
222    // CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
223    ChangeColumn {
224        old_name: Ident,
225        new_name: Ident,
226        data_type: DataType,
227        options: Vec<ColumnOption>,
228        /// MySQL `ALTER TABLE` only  [FIRST | AFTER column_name]
229        column_position: Option<MySQLColumnPosition>,
230    },
231    // CHANGE [ COLUMN ] <col_name> <data_type> [ <options> ]
232    ModifyColumn {
233        col_name: Ident,
234        data_type: DataType,
235        options: Vec<ColumnOption>,
236        /// MySQL `ALTER TABLE` only  [FIRST | AFTER column_name]
237        column_position: Option<MySQLColumnPosition>,
238    },
239    /// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
240    ///
241    /// Note: this is a PostgreSQL-specific operation.
242    RenameConstraint {
243        old_name: Ident,
244        new_name: Ident,
245    },
246    /// `ALTER [ COLUMN ]`
247    AlterColumn {
248        column_name: Ident,
249        op: AlterColumnOperation,
250    },
251    /// 'SWAP WITH <table_name>'
252    ///
253    /// Note: this is Snowflake specific <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
254    SwapWith {
255        table_name: ObjectName,
256    },
257    /// 'SET TBLPROPERTIES ( { property_key [ = ] property_val } [, ...] )'
258    SetTblProperties {
259        table_properties: Vec<SqlOption>,
260    },
261    /// `OWNER TO { <new_owner> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
262    ///
263    /// Note: this is PostgreSQL-specific <https://www.postgresql.org/docs/current/sql-altertable.html>
264    OwnerTo {
265        new_owner: Owner,
266    },
267    /// Snowflake table clustering options
268    /// <https://docs.snowflake.com/en/sql-reference/sql/alter-table#clustering-actions-clusteringaction>
269    ClusterBy {
270        exprs: Vec<Expr>,
271    },
272    DropClusteringKey,
273    SuspendRecluster,
274    ResumeRecluster,
275}
276
277/// An `ALTER Policy` (`Statement::AlterPolicy`) operation
278///
279/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-altertable.html)
280#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
281#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
282#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
283pub enum AlterPolicyOperation {
284    Rename {
285        new_name: Ident,
286    },
287    Apply {
288        to: Option<Vec<Owner>>,
289        using: Option<Expr>,
290        with_check: Option<Expr>,
291    },
292}
293
294impl fmt::Display for AlterPolicyOperation {
295    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        match self {
297            AlterPolicyOperation::Rename { new_name } => {
298                write!(f, " RENAME TO {new_name}")
299            }
300            AlterPolicyOperation::Apply {
301                to,
302                using,
303                with_check,
304            } => {
305                if let Some(to) = to {
306                    write!(f, " TO {}", display_comma_separated(to))?;
307                }
308                if let Some(using) = using {
309                    write!(f, " USING ({using})")?;
310                }
311                if let Some(with_check) = with_check {
312                    write!(f, " WITH CHECK ({with_check})")?;
313                }
314                Ok(())
315            }
316        }
317    }
318}
319
320#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
321#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
322#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
323pub enum Owner {
324    Ident(Ident),
325    CurrentRole,
326    CurrentUser,
327    SessionUser,
328}
329
330impl fmt::Display for Owner {
331    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332        match self {
333            Owner::Ident(ident) => write!(f, "{}", ident),
334            Owner::CurrentRole => write!(f, "CURRENT_ROLE"),
335            Owner::CurrentUser => write!(f, "CURRENT_USER"),
336            Owner::SessionUser => write!(f, "SESSION_USER"),
337        }
338    }
339}
340
341#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
342#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
343#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
344pub enum AlterIndexOperation {
345    RenameIndex { index_name: ObjectName },
346}
347
348impl fmt::Display for AlterTableOperation {
349    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350        match self {
351            AlterTableOperation::AddPartitions {
352                if_not_exists,
353                new_partitions,
354            } => write!(
355                f,
356                "ADD{ine} {}",
357                display_separated(new_partitions, " "),
358                ine = if *if_not_exists { " IF NOT EXISTS" } else { "" }
359            ),
360            AlterTableOperation::AddConstraint(c) => write!(f, "ADD {c}"),
361            AlterTableOperation::AddColumn {
362                column_keyword,
363                if_not_exists,
364                column_def,
365                column_position,
366            } => {
367                write!(f, "ADD")?;
368                if *column_keyword {
369                    write!(f, " COLUMN")?;
370                }
371                if *if_not_exists {
372                    write!(f, " IF NOT EXISTS")?;
373                }
374                write!(f, " {column_def}")?;
375
376                if let Some(position) = column_position {
377                    write!(f, " {position}")?;
378                }
379
380                Ok(())
381            }
382            AlterTableOperation::AddProjection {
383                if_not_exists,
384                name,
385                select: query,
386            } => {
387                write!(f, "ADD PROJECTION")?;
388                if *if_not_exists {
389                    write!(f, " IF NOT EXISTS")?;
390                }
391                write!(f, " {} ({})", name, query)
392            }
393            AlterTableOperation::DropProjection { if_exists, name } => {
394                write!(f, "DROP PROJECTION")?;
395                if *if_exists {
396                    write!(f, " IF EXISTS")?;
397                }
398                write!(f, " {}", name)
399            }
400            AlterTableOperation::MaterializeProjection {
401                if_exists,
402                name,
403                partition,
404            } => {
405                write!(f, "MATERIALIZE PROJECTION")?;
406                if *if_exists {
407                    write!(f, " IF EXISTS")?;
408                }
409                write!(f, " {}", name)?;
410                if let Some(partition) = partition {
411                    write!(f, " IN PARTITION {}", partition)?;
412                }
413                Ok(())
414            }
415            AlterTableOperation::ClearProjection {
416                if_exists,
417                name,
418                partition,
419            } => {
420                write!(f, "CLEAR PROJECTION")?;
421                if *if_exists {
422                    write!(f, " IF EXISTS")?;
423                }
424                write!(f, " {}", name)?;
425                if let Some(partition) = partition {
426                    write!(f, " IN PARTITION {}", partition)?;
427                }
428                Ok(())
429            }
430            AlterTableOperation::AlterColumn { column_name, op } => {
431                write!(f, "ALTER COLUMN {column_name} {op}")
432            }
433            AlterTableOperation::DisableRowLevelSecurity => {
434                write!(f, "DISABLE ROW LEVEL SECURITY")
435            }
436            AlterTableOperation::DisableRule { name } => {
437                write!(f, "DISABLE RULE {name}")
438            }
439            AlterTableOperation::DisableTrigger { name } => {
440                write!(f, "DISABLE TRIGGER {name}")
441            }
442            AlterTableOperation::DropPartitions {
443                partitions,
444                if_exists,
445            } => write!(
446                f,
447                "DROP{ie} PARTITION ({})",
448                display_comma_separated(partitions),
449                ie = if *if_exists { " IF EXISTS" } else { "" }
450            ),
451            AlterTableOperation::DropConstraint {
452                if_exists,
453                name,
454                cascade,
455            } => {
456                write!(
457                    f,
458                    "DROP CONSTRAINT {}{}{}",
459                    if *if_exists { "IF EXISTS " } else { "" },
460                    name,
461                    if *cascade { " CASCADE" } else { "" },
462                )
463            }
464            AlterTableOperation::DropPrimaryKey => write!(f, "DROP PRIMARY KEY"),
465            AlterTableOperation::DropColumn {
466                column_name,
467                if_exists,
468                cascade,
469            } => write!(
470                f,
471                "DROP COLUMN {}{}{}",
472                if *if_exists { "IF EXISTS " } else { "" },
473                column_name,
474                if *cascade { " CASCADE" } else { "" }
475            ),
476            AlterTableOperation::AttachPartition { partition } => {
477                write!(f, "ATTACH {partition}")
478            }
479            AlterTableOperation::DetachPartition { partition } => {
480                write!(f, "DETACH {partition}")
481            }
482            AlterTableOperation::EnableAlwaysRule { name } => {
483                write!(f, "ENABLE ALWAYS RULE {name}")
484            }
485            AlterTableOperation::EnableAlwaysTrigger { name } => {
486                write!(f, "ENABLE ALWAYS TRIGGER {name}")
487            }
488            AlterTableOperation::EnableReplicaRule { name } => {
489                write!(f, "ENABLE REPLICA RULE {name}")
490            }
491            AlterTableOperation::EnableReplicaTrigger { name } => {
492                write!(f, "ENABLE REPLICA TRIGGER {name}")
493            }
494            AlterTableOperation::EnableRowLevelSecurity => {
495                write!(f, "ENABLE ROW LEVEL SECURITY")
496            }
497            AlterTableOperation::EnableRule { name } => {
498                write!(f, "ENABLE RULE {name}")
499            }
500            AlterTableOperation::EnableTrigger { name } => {
501                write!(f, "ENABLE TRIGGER {name}")
502            }
503            AlterTableOperation::RenamePartitions {
504                old_partitions,
505                new_partitions,
506            } => write!(
507                f,
508                "PARTITION ({}) RENAME TO PARTITION ({})",
509                display_comma_separated(old_partitions),
510                display_comma_separated(new_partitions)
511            ),
512            AlterTableOperation::RenameColumn {
513                old_column_name,
514                new_column_name,
515            } => write!(f, "RENAME COLUMN {old_column_name} TO {new_column_name}"),
516            AlterTableOperation::RenameTable { table_name } => {
517                write!(f, "RENAME TO {table_name}")
518            }
519            AlterTableOperation::ChangeColumn {
520                old_name,
521                new_name,
522                data_type,
523                options,
524                column_position,
525            } => {
526                write!(f, "CHANGE COLUMN {old_name} {new_name} {data_type}")?;
527                if !options.is_empty() {
528                    write!(f, " {}", display_separated(options, " "))?;
529                }
530                if let Some(position) = column_position {
531                    write!(f, " {position}")?;
532                }
533
534                Ok(())
535            }
536            AlterTableOperation::ModifyColumn {
537                col_name,
538                data_type,
539                options,
540                column_position,
541            } => {
542                write!(f, "MODIFY COLUMN {col_name} {data_type}")?;
543                if !options.is_empty() {
544                    write!(f, " {}", display_separated(options, " "))?;
545                }
546                if let Some(position) = column_position {
547                    write!(f, " {position}")?;
548                }
549
550                Ok(())
551            }
552            AlterTableOperation::RenameConstraint { old_name, new_name } => {
553                write!(f, "RENAME CONSTRAINT {old_name} TO {new_name}")
554            }
555            AlterTableOperation::SwapWith { table_name } => {
556                write!(f, "SWAP WITH {table_name}")
557            }
558            AlterTableOperation::OwnerTo { new_owner } => {
559                write!(f, "OWNER TO {new_owner}")
560            }
561            AlterTableOperation::SetTblProperties { table_properties } => {
562                write!(
563                    f,
564                    "SET TBLPROPERTIES({})",
565                    display_comma_separated(table_properties)
566                )
567            }
568            AlterTableOperation::FreezePartition {
569                partition,
570                with_name,
571            } => {
572                write!(f, "FREEZE {partition}")?;
573                if let Some(name) = with_name {
574                    write!(f, " WITH NAME {name}")?;
575                }
576                Ok(())
577            }
578            AlterTableOperation::UnfreezePartition {
579                partition,
580                with_name,
581            } => {
582                write!(f, "UNFREEZE {partition}")?;
583                if let Some(name) = with_name {
584                    write!(f, " WITH NAME {name}")?;
585                }
586                Ok(())
587            }
588            AlterTableOperation::ClusterBy { exprs } => {
589                write!(f, "CLUSTER BY ({})", display_comma_separated(exprs))?;
590                Ok(())
591            }
592            AlterTableOperation::DropClusteringKey => {
593                write!(f, "DROP CLUSTERING KEY")?;
594                Ok(())
595            }
596            AlterTableOperation::SuspendRecluster => {
597                write!(f, "SUSPEND RECLUSTER")?;
598                Ok(())
599            }
600            AlterTableOperation::ResumeRecluster => {
601                write!(f, "RESUME RECLUSTER")?;
602                Ok(())
603            }
604        }
605    }
606}
607
608impl fmt::Display for AlterIndexOperation {
609    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610        match self {
611            AlterIndexOperation::RenameIndex { index_name } => {
612                write!(f, "RENAME TO {index_name}")
613            }
614        }
615    }
616}
617
618/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
619#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
621#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
622pub enum AlterColumnOperation {
623    /// `SET NOT NULL`
624    SetNotNull,
625    /// `DROP NOT NULL`
626    DropNotNull,
627    /// `SET DEFAULT <expr>`
628    SetDefault { value: Expr },
629    /// `DROP DEFAULT`
630    DropDefault,
631    /// `[SET DATA] TYPE <data_type> [USING <expr>]`
632    SetDataType {
633        data_type: DataType,
634        /// PostgreSQL specific
635        using: Option<Expr>,
636    },
637    /// `ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]`
638    ///
639    /// Note: this is a PostgreSQL-specific operation.
640    AddGenerated {
641        generated_as: Option<GeneratedAs>,
642        sequence_options: Option<Vec<SequenceOptions>>,
643    },
644}
645
646impl fmt::Display for AlterColumnOperation {
647    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
648        match self {
649            AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
650            AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
651            AlterColumnOperation::SetDefault { value } => {
652                write!(f, "SET DEFAULT {value}")
653            }
654            AlterColumnOperation::DropDefault {} => {
655                write!(f, "DROP DEFAULT")
656            }
657            AlterColumnOperation::SetDataType { data_type, using } => {
658                if let Some(expr) = using {
659                    write!(f, "SET DATA TYPE {data_type} USING {expr}")
660                } else {
661                    write!(f, "SET DATA TYPE {data_type}")
662                }
663            }
664            AlterColumnOperation::AddGenerated {
665                generated_as,
666                sequence_options,
667            } => {
668                let generated_as = match generated_as {
669                    Some(GeneratedAs::Always) => " ALWAYS",
670                    Some(GeneratedAs::ByDefault) => " BY DEFAULT",
671                    _ => "",
672                };
673
674                write!(f, "ADD GENERATED{generated_as} AS IDENTITY",)?;
675                if let Some(options) = sequence_options {
676                    write!(f, " (")?;
677
678                    for sequence_option in options {
679                        write!(f, "{sequence_option}")?;
680                    }
681
682                    write!(f, " )")?;
683                }
684                Ok(())
685            }
686        }
687    }
688}
689
690/// A table-level constraint, specified in a `CREATE TABLE` or an
691/// `ALTER TABLE ADD <constraint>` statement.
692#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
693#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
694#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
695pub enum TableConstraint {
696    /// MySQL [definition][1] for `UNIQUE` constraints statements:\
697    /// * `[CONSTRAINT [<name>]] UNIQUE <index_type_display> [<index_name>] [index_type] (<columns>) <index_options>`
698    ///
699    /// where:
700    /// * [index_type][2] is `USING {BTREE | HASH}`
701    /// * [index_options][3] is `{index_type | COMMENT 'string' | ... %currently unsupported stmts% } ...`
702    /// * [index_type_display][4] is `[INDEX | KEY]`
703    ///
704    /// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
705    /// [2]: IndexType
706    /// [3]: IndexOption
707    /// [4]: KeyOrIndexDisplay
708    Unique {
709        /// Constraint name.
710        ///
711        /// Can be not the same as `index_name`
712        name: Option<Ident>,
713        /// Index name
714        index_name: Option<Ident>,
715        /// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
716        index_type_display: KeyOrIndexDisplay,
717        /// Optional `USING` of [index type][1] statement before columns.
718        ///
719        /// [1]: IndexType
720        index_type: Option<IndexType>,
721        /// Identifiers of the columns that are unique.
722        columns: Vec<Ident>,
723        index_options: Vec<IndexOption>,
724        characteristics: Option<ConstraintCharacteristics>,
725        /// Optional Postgres nulls handling: `[ NULLS [ NOT ] DISTINCT ]`
726        nulls_distinct: NullsDistinctOption,
727    },
728    /// MySQL [definition][1] for `PRIMARY KEY` constraints statements:\
729    /// * `[CONSTRAINT [<name>]] PRIMARY KEY [index_name] [index_type] (<columns>) <index_options>`
730    ///
731    /// Actually the specification have no `[index_name]` but the next query will complete successfully:
732    /// ```sql
733    /// CREATE TABLE unspec_table (
734    ///   xid INT NOT NULL,
735    ///   CONSTRAINT p_name PRIMARY KEY index_name USING BTREE (xid)
736    /// );
737    /// ```
738    ///
739    /// where:
740    /// * [index_type][2] is `USING {BTREE | HASH}`
741    /// * [index_options][3] is `{index_type | COMMENT 'string' | ... %currently unsupported stmts% } ...`
742    ///
743    /// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
744    /// [2]: IndexType
745    /// [3]: IndexOption
746    PrimaryKey {
747        /// Constraint name.
748        ///
749        /// Can be not the same as `index_name`
750        name: Option<Ident>,
751        /// Index name
752        index_name: Option<Ident>,
753        /// Optional `USING` of [index type][1] statement before columns.
754        ///
755        /// [1]: IndexType
756        index_type: Option<IndexType>,
757        /// Identifiers of the columns that form the primary key.
758        columns: Vec<Ident>,
759        index_options: Vec<IndexOption>,
760        characteristics: Option<ConstraintCharacteristics>,
761    },
762    /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
763    /// REFERENCES <foreign_table> (<referred_columns>)
764    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
765    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
766    /// }`).
767    ForeignKey {
768        name: Option<Ident>,
769        columns: Vec<Ident>,
770        foreign_table: ObjectName,
771        referred_columns: Vec<Ident>,
772        on_delete: Option<ReferentialAction>,
773        on_update: Option<ReferentialAction>,
774        characteristics: Option<ConstraintCharacteristics>,
775    },
776    /// `[ CONSTRAINT <name> ] CHECK (<expr>)`
777    Check {
778        name: Option<Ident>,
779        expr: Box<Expr>,
780    },
781    /// MySQLs [index definition][1] for index creation. Not present on ANSI so, for now, the usage
782    /// is restricted to MySQL, as no other dialects that support this syntax were found.
783    ///
784    /// `{INDEX | KEY} [index_name] [index_type] (key_part,...) [index_option]...`
785    ///
786    /// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
787    Index {
788        /// Whether this index starts with KEY (true) or INDEX (false), to maintain the same syntax.
789        display_as_key: bool,
790        /// Index name.
791        name: Option<Ident>,
792        /// Optional [index type][1].
793        ///
794        /// [1]: IndexType
795        index_type: Option<IndexType>,
796        /// Referred column identifier list.
797        columns: Vec<Ident>,
798    },
799    /// MySQLs [fulltext][1] definition. Since the [`SPATIAL`][2] definition is exactly the same,
800    /// and MySQL displays both the same way, it is part of this definition as well.
801    ///
802    /// Supported syntax:
803    ///
804    /// ```markdown
805    /// {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...)
806    ///
807    /// key_part: col_name
808    /// ```
809    ///
810    /// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
811    /// [2]: https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html
812    FulltextOrSpatial {
813        /// Whether this is a `FULLTEXT` (true) or `SPATIAL` (false) definition.
814        fulltext: bool,
815        /// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
816        index_type_display: KeyOrIndexDisplay,
817        /// Optional index name.
818        opt_index_name: Option<Ident>,
819        /// Referred column identifier list.
820        columns: Vec<Ident>,
821    },
822}
823
824impl fmt::Display for TableConstraint {
825    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
826        match self {
827            TableConstraint::Unique {
828                name,
829                index_name,
830                index_type_display,
831                index_type,
832                columns,
833                index_options,
834                characteristics,
835                nulls_distinct,
836            } => {
837                write!(
838                    f,
839                    "{}UNIQUE{nulls_distinct}{index_type_display:>}{}{} ({})",
840                    display_constraint_name(name),
841                    display_option_spaced(index_name),
842                    display_option(" USING ", "", index_type),
843                    display_comma_separated(columns),
844                )?;
845
846                if !index_options.is_empty() {
847                    write!(f, " {}", display_separated(index_options, " "))?;
848                }
849
850                write!(f, "{}", display_option_spaced(characteristics))?;
851                Ok(())
852            }
853            TableConstraint::PrimaryKey {
854                name,
855                index_name,
856                index_type,
857                columns,
858                index_options,
859                characteristics,
860            } => {
861                write!(
862                    f,
863                    "{}PRIMARY KEY{}{} ({})",
864                    display_constraint_name(name),
865                    display_option_spaced(index_name),
866                    display_option(" USING ", "", index_type),
867                    display_comma_separated(columns),
868                )?;
869
870                if !index_options.is_empty() {
871                    write!(f, " {}", display_separated(index_options, " "))?;
872                }
873
874                write!(f, "{}", display_option_spaced(characteristics))?;
875                Ok(())
876            }
877            TableConstraint::ForeignKey {
878                name,
879                columns,
880                foreign_table,
881                referred_columns,
882                on_delete,
883                on_update,
884                characteristics,
885            } => {
886                write!(
887                    f,
888                    "{}FOREIGN KEY ({}) REFERENCES {}({})",
889                    display_constraint_name(name),
890                    display_comma_separated(columns),
891                    foreign_table,
892                    display_comma_separated(referred_columns),
893                )?;
894                if let Some(action) = on_delete {
895                    write!(f, " ON DELETE {action}")?;
896                }
897                if let Some(action) = on_update {
898                    write!(f, " ON UPDATE {action}")?;
899                }
900                if let Some(characteristics) = characteristics {
901                    write!(f, " {}", characteristics)?;
902                }
903                Ok(())
904            }
905            TableConstraint::Check { name, expr } => {
906                write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
907            }
908            TableConstraint::Index {
909                display_as_key,
910                name,
911                index_type,
912                columns,
913            } => {
914                write!(f, "{}", if *display_as_key { "KEY" } else { "INDEX" })?;
915                if let Some(name) = name {
916                    write!(f, " {name}")?;
917                }
918                if let Some(index_type) = index_type {
919                    write!(f, " USING {index_type}")?;
920                }
921                write!(f, " ({})", display_comma_separated(columns))?;
922
923                Ok(())
924            }
925            Self::FulltextOrSpatial {
926                fulltext,
927                index_type_display,
928                opt_index_name,
929                columns,
930            } => {
931                if *fulltext {
932                    write!(f, "FULLTEXT")?;
933                } else {
934                    write!(f, "SPATIAL")?;
935                }
936
937                write!(f, "{index_type_display:>}")?;
938
939                if let Some(name) = opt_index_name {
940                    write!(f, " {name}")?;
941                }
942
943                write!(f, " ({})", display_comma_separated(columns))?;
944
945                Ok(())
946            }
947        }
948    }
949}
950
951/// Representation whether a definition can can contains the KEY or INDEX keywords with the same
952/// meaning.
953///
954/// This enum initially is directed to `FULLTEXT`,`SPATIAL`, and `UNIQUE` indexes on create table
955/// statements of `MySQL` [(1)].
956///
957/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
958#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
959#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
960#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
961pub enum KeyOrIndexDisplay {
962    /// Nothing to display
963    None,
964    /// Display the KEY keyword
965    Key,
966    /// Display the INDEX keyword
967    Index,
968}
969
970impl KeyOrIndexDisplay {
971    pub fn is_none(self) -> bool {
972        matches!(self, Self::None)
973    }
974}
975
976impl fmt::Display for KeyOrIndexDisplay {
977    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
978        let left_space = matches!(f.align(), Some(fmt::Alignment::Right));
979
980        if left_space && !self.is_none() {
981            f.write_char(' ')?
982        }
983
984        match self {
985            KeyOrIndexDisplay::None => {
986                write!(f, "")
987            }
988            KeyOrIndexDisplay::Key => {
989                write!(f, "KEY")
990            }
991            KeyOrIndexDisplay::Index => {
992                write!(f, "INDEX")
993            }
994        }
995    }
996}
997
998/// Indexing method used by that index.
999///
1000/// This structure isn't present on ANSI, but is found at least in [`MySQL` CREATE TABLE][1],
1001/// [`MySQL` CREATE INDEX][2], and [Postgresql CREATE INDEX][3] statements.
1002///
1003/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
1004/// [2]: https://dev.mysql.com/doc/refman/8.0/en/create-index.html
1005/// [3]: https://www.postgresql.org/docs/14/sql-createindex.html
1006#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1007#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1008#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1009pub enum IndexType {
1010    BTree,
1011    Hash,
1012    // TODO add Postgresql's possible indexes
1013}
1014
1015impl fmt::Display for IndexType {
1016    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1017        match self {
1018            Self::BTree => write!(f, "BTREE"),
1019            Self::Hash => write!(f, "HASH"),
1020        }
1021    }
1022}
1023
1024/// MySQLs index option.
1025///
1026/// This structure used here [`MySQL` CREATE TABLE][1], [`MySQL` CREATE INDEX][2].
1027///
1028/// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
1029/// [2]: https://dev.mysql.com/doc/refman/8.3/en/create-index.html
1030#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1031#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1032#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1033pub enum IndexOption {
1034    Using(IndexType),
1035    Comment(String),
1036}
1037
1038impl fmt::Display for IndexOption {
1039    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1040        match self {
1041            Self::Using(index_type) => write!(f, "USING {index_type}"),
1042            Self::Comment(s) => write!(f, "COMMENT '{s}'"),
1043        }
1044    }
1045}
1046
1047/// [Postgres] unique index nulls handling option: `[ NULLS [ NOT ] DISTINCT ]`
1048///
1049/// [Postgres]: https://www.postgresql.org/docs/17/sql-altertable.html
1050#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1051#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1052#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1053pub enum NullsDistinctOption {
1054    /// Not specified
1055    None,
1056    /// NULLS DISTINCT
1057    Distinct,
1058    /// NULLS NOT DISTINCT
1059    NotDistinct,
1060}
1061
1062impl fmt::Display for NullsDistinctOption {
1063    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1064        match self {
1065            Self::None => Ok(()),
1066            Self::Distinct => write!(f, " NULLS DISTINCT"),
1067            Self::NotDistinct => write!(f, " NULLS NOT DISTINCT"),
1068        }
1069    }
1070}
1071
1072#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1073#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1074#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1075pub struct ProcedureParam {
1076    pub name: Ident,
1077    pub data_type: DataType,
1078}
1079
1080impl fmt::Display for ProcedureParam {
1081    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1082        write!(f, "{} {}", self.name, self.data_type)
1083    }
1084}
1085
1086/// SQL column definition
1087#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1088#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1089#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1090pub struct ColumnDef {
1091    pub name: Ident,
1092    pub data_type: DataType,
1093    pub collation: Option<ObjectName>,
1094    pub options: Vec<ColumnOptionDef>,
1095}
1096
1097impl fmt::Display for ColumnDef {
1098    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1099        if self.data_type == DataType::Unspecified {
1100            write!(f, "{}", self.name)?;
1101        } else {
1102            write!(f, "{} {}", self.name, self.data_type)?;
1103        }
1104        if let Some(collation) = &self.collation {
1105            write!(f, " COLLATE {collation}")?;
1106        }
1107        for option in &self.options {
1108            write!(f, " {option}")?;
1109        }
1110        Ok(())
1111    }
1112}
1113
1114/// Column definition specified in a `CREATE VIEW` statement.
1115///
1116/// Syntax
1117/// ```markdown
1118/// <name> [data_type][OPTIONS(option, ...)]
1119///
1120/// option: <name> = <value>
1121/// ```
1122///
1123/// Examples:
1124/// ```sql
1125/// name
1126/// age OPTIONS(description = "age column", tag = "prod")
1127/// amount COMMENT 'The total amount for the order line'
1128/// created_at DateTime64
1129/// ```
1130#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1131#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1132#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1133pub struct ViewColumnDef {
1134    pub name: Ident,
1135    pub data_type: Option<DataType>,
1136    pub options: Option<Vec<ColumnOption>>,
1137}
1138
1139impl fmt::Display for ViewColumnDef {
1140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1141        write!(f, "{}", self.name)?;
1142        if let Some(data_type) = self.data_type.as_ref() {
1143            write!(f, " {}", data_type)?;
1144        }
1145        if let Some(options) = self.options.as_ref() {
1146            write!(f, " {}", display_comma_separated(options.as_slice()))?;
1147        }
1148        Ok(())
1149    }
1150}
1151
1152/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
1153///
1154/// Note that implementations are substantially more permissive than the ANSI
1155/// specification on what order column options can be presented in, and whether
1156/// they are allowed to be named. The specification distinguishes between
1157/// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named
1158/// and can appear in any order, and other options (DEFAULT, GENERATED), which
1159/// cannot be named and must appear in a fixed order. `PostgreSQL`, however,
1160/// allows preceding any option with `CONSTRAINT <name>`, even those that are
1161/// not really constraints, like NULL and DEFAULT. MSSQL is less permissive,
1162/// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or
1163/// NOT NULL constraints (the last of which is in violation of the spec).
1164///
1165/// For maximum flexibility, we don't distinguish between constraint and
1166/// non-constraint options, lumping them all together under the umbrella of
1167/// "column options," and we allow any column option to be named.
1168#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1170#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1171pub struct ColumnOptionDef {
1172    pub name: Option<Ident>,
1173    pub option: ColumnOption,
1174}
1175
1176impl fmt::Display for ColumnOptionDef {
1177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1178        write!(f, "{}{}", display_constraint_name(&self.name), self.option)
1179    }
1180}
1181
1182/// Identity is a column option for defining an identity or autoincrement column in a `CREATE TABLE` statement.
1183/// Syntax
1184/// ```sql
1185/// { IDENTITY | AUTOINCREMENT } [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
1186/// ```
1187/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1188/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1189#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1190#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1191#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1192pub enum IdentityPropertyKind {
1193    /// An identity property declared via the `AUTOINCREMENT` key word
1194    /// Example:
1195    /// ```sql
1196    ///  AUTOINCREMENT(100, 1) NOORDER
1197    ///  AUTOINCREMENT START 100 INCREMENT 1 ORDER
1198    /// ```
1199    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1200    Autoincrement(IdentityProperty),
1201    /// An identity property declared via the `IDENTITY` key word
1202    /// Example, [MS SQL Server] or [Snowflake]:
1203    /// ```sql
1204    ///  IDENTITY(100, 1)
1205    /// ```
1206    /// [Snowflake]
1207    /// ```sql
1208    ///  IDENTITY(100, 1) ORDER
1209    ///  IDENTITY START 100 INCREMENT 1 NOORDER
1210    /// ```
1211    /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1212    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1213    Identity(IdentityProperty),
1214}
1215
1216impl fmt::Display for IdentityPropertyKind {
1217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1218        let (command, property) = match self {
1219            IdentityPropertyKind::Identity(property) => ("IDENTITY", property),
1220            IdentityPropertyKind::Autoincrement(property) => ("AUTOINCREMENT", property),
1221        };
1222        write!(f, "{command}")?;
1223        if let Some(parameters) = &property.parameters {
1224            write!(f, "{parameters}")?;
1225        }
1226        if let Some(order) = &property.order {
1227            write!(f, "{order}")?;
1228        }
1229        Ok(())
1230    }
1231}
1232
1233#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1234#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1235#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1236pub struct IdentityProperty {
1237    pub parameters: Option<IdentityPropertyFormatKind>,
1238    pub order: Option<IdentityPropertyOrder>,
1239}
1240
1241/// A format of parameters of identity column.
1242///
1243/// It is [Snowflake] specific.
1244/// Syntax
1245/// ```sql
1246/// (seed , increment) | START num INCREMENT num
1247/// ```
1248/// [MS SQL Server] uses one way of representing these parameters.
1249/// Syntax
1250/// ```sql
1251/// (seed , increment)
1252/// ```
1253/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1254/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1255#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1256#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1257#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1258pub enum IdentityPropertyFormatKind {
1259    /// A parameters of identity column declared like parameters of function call
1260    /// Example:
1261    /// ```sql
1262    ///  (100, 1)
1263    /// ```
1264    /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1265    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1266    FunctionCall(IdentityParameters),
1267    /// A parameters of identity column declared with keywords `START` and `INCREMENT`
1268    /// Example:
1269    /// ```sql
1270    ///  START 100 INCREMENT 1
1271    /// ```
1272    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1273    StartAndIncrement(IdentityParameters),
1274}
1275
1276impl fmt::Display for IdentityPropertyFormatKind {
1277    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1278        match self {
1279            IdentityPropertyFormatKind::FunctionCall(parameters) => {
1280                write!(f, "({}, {})", parameters.seed, parameters.increment)
1281            }
1282            IdentityPropertyFormatKind::StartAndIncrement(parameters) => {
1283                write!(
1284                    f,
1285                    " START {} INCREMENT {}",
1286                    parameters.seed, parameters.increment
1287                )
1288            }
1289        }
1290    }
1291}
1292#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1294#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1295pub struct IdentityParameters {
1296    pub seed: Expr,
1297    pub increment: Expr,
1298}
1299
1300/// The identity column option specifies how values are generated for the auto-incremented column, either in increasing or decreasing order.
1301/// Syntax
1302/// ```sql
1303/// ORDER | NOORDER
1304/// ```
1305/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1306#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1307#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1308#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1309pub enum IdentityPropertyOrder {
1310    Order,
1311    NoOrder,
1312}
1313
1314impl fmt::Display for IdentityPropertyOrder {
1315    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1316        match self {
1317            IdentityPropertyOrder::Order => write!(f, " ORDER"),
1318            IdentityPropertyOrder::NoOrder => write!(f, " NOORDER"),
1319        }
1320    }
1321}
1322
1323/// Column policy that identify a security policy of access to a column.
1324/// Syntax
1325/// ```sql
1326/// [ WITH ] MASKING POLICY <policy_name> [ USING ( <col_name> , <cond_col1> , ... ) ]
1327/// [ WITH ] PROJECTION POLICY <policy_name>
1328/// ```
1329/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1330#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1331#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1332#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1333pub enum ColumnPolicy {
1334    MaskingPolicy(ColumnPolicyProperty),
1335    ProjectionPolicy(ColumnPolicyProperty),
1336}
1337
1338impl fmt::Display for ColumnPolicy {
1339    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1340        let (command, property) = match self {
1341            ColumnPolicy::MaskingPolicy(property) => ("MASKING POLICY", property),
1342            ColumnPolicy::ProjectionPolicy(property) => ("PROJECTION POLICY", property),
1343        };
1344        if property.with {
1345            write!(f, "WITH ")?;
1346        }
1347        write!(f, "{command} {}", property.policy_name)?;
1348        if let Some(using_columns) = &property.using_columns {
1349            write!(f, " USING ({})", display_comma_separated(using_columns))?;
1350        }
1351        Ok(())
1352    }
1353}
1354
1355#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1356#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1357#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1358pub struct ColumnPolicyProperty {
1359    /// This flag indicates that the column policy option is declared using the `WITH` prefix.
1360    /// Example
1361    /// ```sql
1362    /// WITH PROJECTION POLICY sample_policy
1363    /// ```
1364    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1365    pub with: bool,
1366    pub policy_name: Ident,
1367    pub using_columns: Option<Vec<Ident>>,
1368}
1369
1370/// Tags option of column
1371/// Syntax
1372/// ```sql
1373/// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
1374/// ```
1375/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1376#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1377#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1378#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1379pub struct TagsColumnOption {
1380    /// This flag indicates that the tags option is declared using the `WITH` prefix.
1381    /// Example:
1382    /// ```sql
1383    /// WITH TAG (A = 'Tag A')
1384    /// ```
1385    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1386    pub with: bool,
1387    pub tags: Vec<Tag>,
1388}
1389
1390impl fmt::Display for TagsColumnOption {
1391    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1392        if self.with {
1393            write!(f, "WITH ")?;
1394        }
1395        write!(f, "TAG ({})", display_comma_separated(&self.tags))?;
1396        Ok(())
1397    }
1398}
1399
1400/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
1401/// TABLE` statement.
1402#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1403#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1404#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1405pub enum ColumnOption {
1406    /// `NULL`
1407    Null,
1408    /// `NOT NULL`
1409    NotNull,
1410    /// `DEFAULT <restricted-expr>`
1411    Default(Expr),
1412
1413    /// `MATERIALIZE <expr>`
1414    /// Syntax: `b INT MATERIALIZE (a + 1)`
1415    ///
1416    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/create/table#default_values)
1417    Materialized(Expr),
1418    /// `EPHEMERAL [<expr>]`
1419    ///
1420    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/create/table#default_values)
1421    Ephemeral(Option<Expr>),
1422    /// `ALIAS <expr>`
1423    ///
1424    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/create/table#default_values)
1425    Alias(Expr),
1426
1427    /// `{ PRIMARY KEY | UNIQUE } [<constraint_characteristics>]`
1428    Unique {
1429        is_primary: bool,
1430        characteristics: Option<ConstraintCharacteristics>,
1431    },
1432    /// A referential integrity constraint (`[FOREIGN KEY REFERENCES
1433    /// <foreign_table> (<referred_columns>)
1434    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
1435    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
1436    /// }
1437    /// [<constraint_characteristics>]
1438    /// `).
1439    ForeignKey {
1440        foreign_table: ObjectName,
1441        referred_columns: Vec<Ident>,
1442        on_delete: Option<ReferentialAction>,
1443        on_update: Option<ReferentialAction>,
1444        characteristics: Option<ConstraintCharacteristics>,
1445    },
1446    /// `CHECK (<expr>)`
1447    Check(Expr),
1448    /// Dialect-specific options, such as:
1449    /// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT`
1450    /// - ...
1451    DialectSpecific(Vec<Token>),
1452    CharacterSet(ObjectName),
1453    Comment(String),
1454    OnUpdate(Expr),
1455    /// `Generated`s are modifiers that follow a column definition in a `CREATE
1456    /// TABLE` statement.
1457    Generated {
1458        generated_as: GeneratedAs,
1459        sequence_options: Option<Vec<SequenceOptions>>,
1460        generation_expr: Option<Expr>,
1461        generation_expr_mode: Option<GeneratedExpressionMode>,
1462        /// false if 'GENERATED ALWAYS' is skipped (option starts with AS)
1463        generated_keyword: bool,
1464    },
1465    /// BigQuery specific: Explicit column options in a view [1] or table [2]
1466    /// Syntax
1467    /// ```sql
1468    /// OPTIONS(description="field desc")
1469    /// ```
1470    /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#view_column_option_list
1471    /// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_option_list
1472    Options(Vec<SqlOption>),
1473    /// Creates an identity or an autoincrement column in a table.
1474    /// Syntax
1475    /// ```sql
1476    /// { IDENTITY | AUTOINCREMENT } [ (seed , increment) | START num INCREMENT num ] [ ORDER | NOORDER ]
1477    /// ```
1478    /// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
1479    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1480    Identity(IdentityPropertyKind),
1481    /// SQLite specific: ON CONFLICT option on column definition
1482    /// <https://www.sqlite.org/lang_conflict.html>
1483    OnConflict(Keyword),
1484    /// Snowflake specific: an option of specifying security masking or projection policy to set on a column.
1485    /// Syntax:
1486    /// ```sql
1487    /// [ WITH ] MASKING POLICY <policy_name> [ USING ( <col_name> , <cond_col1> , ... ) ]
1488    /// [ WITH ] PROJECTION POLICY <policy_name>
1489    /// ```
1490    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1491    Policy(ColumnPolicy),
1492    /// Snowflake specific: Specifies the tag name and the tag string value.
1493    /// Syntax:
1494    /// ```sql
1495    /// [ WITH ] TAG ( <tag_name> = '<tag_value>' [ , <tag_name> = '<tag_value>' , ... ] )
1496    /// ```
1497    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/sql/create-table
1498    Tags(TagsColumnOption),
1499}
1500
1501impl fmt::Display for ColumnOption {
1502    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1503        use ColumnOption::*;
1504        match self {
1505            Null => write!(f, "NULL"),
1506            NotNull => write!(f, "NOT NULL"),
1507            Default(expr) => write!(f, "DEFAULT {expr}"),
1508            Materialized(expr) => write!(f, "MATERIALIZED {expr}"),
1509            Ephemeral(expr) => {
1510                if let Some(e) = expr {
1511                    write!(f, "EPHEMERAL {e}")
1512                } else {
1513                    write!(f, "EPHEMERAL")
1514                }
1515            }
1516            Alias(expr) => write!(f, "ALIAS {expr}"),
1517            Unique {
1518                is_primary,
1519                characteristics,
1520            } => {
1521                write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })?;
1522                if let Some(characteristics) = characteristics {
1523                    write!(f, " {}", characteristics)?;
1524                }
1525                Ok(())
1526            }
1527            ForeignKey {
1528                foreign_table,
1529                referred_columns,
1530                on_delete,
1531                on_update,
1532                characteristics,
1533            } => {
1534                write!(f, "REFERENCES {foreign_table}")?;
1535                if !referred_columns.is_empty() {
1536                    write!(f, " ({})", display_comma_separated(referred_columns))?;
1537                }
1538                if let Some(action) = on_delete {
1539                    write!(f, " ON DELETE {action}")?;
1540                }
1541                if let Some(action) = on_update {
1542                    write!(f, " ON UPDATE {action}")?;
1543                }
1544                if let Some(characteristics) = characteristics {
1545                    write!(f, " {}", characteristics)?;
1546                }
1547                Ok(())
1548            }
1549            Check(expr) => write!(f, "CHECK ({expr})"),
1550            DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
1551            CharacterSet(n) => write!(f, "CHARACTER SET {n}"),
1552            Comment(v) => write!(f, "COMMENT '{}'", escape_single_quote_string(v)),
1553            OnUpdate(expr) => write!(f, "ON UPDATE {expr}"),
1554            Generated {
1555                generated_as,
1556                sequence_options,
1557                generation_expr,
1558                generation_expr_mode,
1559                generated_keyword,
1560            } => {
1561                if let Some(expr) = generation_expr {
1562                    let modifier = match generation_expr_mode {
1563                        None => "",
1564                        Some(GeneratedExpressionMode::Virtual) => " VIRTUAL",
1565                        Some(GeneratedExpressionMode::Stored) => " STORED",
1566                    };
1567                    if *generated_keyword {
1568                        write!(f, "GENERATED ALWAYS AS ({expr}){modifier}")?;
1569                    } else {
1570                        write!(f, "AS ({expr}){modifier}")?;
1571                    }
1572                    Ok(())
1573                } else {
1574                    // Like Postgres - generated from sequence
1575                    let when = match generated_as {
1576                        GeneratedAs::Always => "ALWAYS",
1577                        GeneratedAs::ByDefault => "BY DEFAULT",
1578                        // ExpStored goes with an expression, handled above
1579                        GeneratedAs::ExpStored => unreachable!(),
1580                    };
1581                    write!(f, "GENERATED {when} AS IDENTITY")?;
1582                    if sequence_options.is_some() {
1583                        let so = sequence_options.as_ref().unwrap();
1584                        if !so.is_empty() {
1585                            write!(f, " (")?;
1586                        }
1587                        for sequence_option in so {
1588                            write!(f, "{sequence_option}")?;
1589                        }
1590                        if !so.is_empty() {
1591                            write!(f, " )")?;
1592                        }
1593                    }
1594                    Ok(())
1595                }
1596            }
1597            Options(options) => {
1598                write!(f, "OPTIONS({})", display_comma_separated(options))
1599            }
1600            Identity(parameters) => {
1601                write!(f, "{parameters}")
1602            }
1603            OnConflict(keyword) => {
1604                write!(f, "ON CONFLICT {:?}", keyword)?;
1605                Ok(())
1606            }
1607            Policy(parameters) => {
1608                write!(f, "{parameters}")
1609            }
1610            Tags(tags) => {
1611                write!(f, "{tags}")
1612            }
1613        }
1614    }
1615}
1616
1617/// `GeneratedAs`s are modifiers that follow a column option in a `generated`.
1618/// 'ExpStored' is used for a column generated from an expression and stored.
1619#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1620#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1621#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1622pub enum GeneratedAs {
1623    Always,
1624    ByDefault,
1625    ExpStored,
1626}
1627
1628/// `GeneratedExpressionMode`s are modifiers that follow an expression in a `generated`.
1629/// No modifier is typically the same as Virtual.
1630#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1631#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1632#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1633pub enum GeneratedExpressionMode {
1634    Virtual,
1635    Stored,
1636}
1637
1638#[must_use]
1639fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
1640    struct ConstraintName<'a>(&'a Option<Ident>);
1641    impl fmt::Display for ConstraintName<'_> {
1642        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1643            if let Some(name) = self.0 {
1644                write!(f, "CONSTRAINT {name} ")?;
1645            }
1646            Ok(())
1647        }
1648    }
1649    ConstraintName(name)
1650}
1651
1652/// If `option` is
1653/// * `Some(inner)` => create display struct for `"{prefix}{inner}{postfix}"`
1654/// * `_` => do nothing
1655#[must_use]
1656fn display_option<'a, T: fmt::Display>(
1657    prefix: &'a str,
1658    postfix: &'a str,
1659    option: &'a Option<T>,
1660) -> impl fmt::Display + 'a {
1661    struct OptionDisplay<'a, T>(&'a str, &'a str, &'a Option<T>);
1662    impl<T: fmt::Display> fmt::Display for OptionDisplay<'_, T> {
1663        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1664            if let Some(inner) = self.2 {
1665                let (prefix, postfix) = (self.0, self.1);
1666                write!(f, "{prefix}{inner}{postfix}")?;
1667            }
1668            Ok(())
1669        }
1670    }
1671    OptionDisplay(prefix, postfix, option)
1672}
1673
1674/// If `option` is
1675/// * `Some(inner)` => create display struct for `" {inner}"`
1676/// * `_` => do nothing
1677#[must_use]
1678fn display_option_spaced<T: fmt::Display>(option: &Option<T>) -> impl fmt::Display + '_ {
1679    display_option(" ", "", option)
1680}
1681
1682/// `<constraint_characteristics> = [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ]`
1683///
1684/// Used in UNIQUE and foreign key constraints. The individual settings may occur in any order.
1685#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Default, Eq, Ord, Hash)]
1686#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1687#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1688pub struct ConstraintCharacteristics {
1689    /// `[ DEFERRABLE | NOT DEFERRABLE ]`
1690    pub deferrable: Option<bool>,
1691    /// `[ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]`
1692    pub initially: Option<DeferrableInitial>,
1693    /// `[ ENFORCED | NOT ENFORCED ]`
1694    pub enforced: Option<bool>,
1695}
1696
1697#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1698#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1699#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1700pub enum DeferrableInitial {
1701    /// `INITIALLY IMMEDIATE`
1702    Immediate,
1703    /// `INITIALLY DEFERRED`
1704    Deferred,
1705}
1706
1707impl ConstraintCharacteristics {
1708    fn deferrable_text(&self) -> Option<&'static str> {
1709        self.deferrable.map(|deferrable| {
1710            if deferrable {
1711                "DEFERRABLE"
1712            } else {
1713                "NOT DEFERRABLE"
1714            }
1715        })
1716    }
1717
1718    fn initially_immediate_text(&self) -> Option<&'static str> {
1719        self.initially
1720            .map(|initially_immediate| match initially_immediate {
1721                DeferrableInitial::Immediate => "INITIALLY IMMEDIATE",
1722                DeferrableInitial::Deferred => "INITIALLY DEFERRED",
1723            })
1724    }
1725
1726    fn enforced_text(&self) -> Option<&'static str> {
1727        self.enforced.map(
1728            |enforced| {
1729                if enforced {
1730                    "ENFORCED"
1731                } else {
1732                    "NOT ENFORCED"
1733                }
1734            },
1735        )
1736    }
1737}
1738
1739impl fmt::Display for ConstraintCharacteristics {
1740    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1741        let deferrable = self.deferrable_text();
1742        let initially_immediate = self.initially_immediate_text();
1743        let enforced = self.enforced_text();
1744
1745        match (deferrable, initially_immediate, enforced) {
1746            (None, None, None) => Ok(()),
1747            (None, None, Some(enforced)) => write!(f, "{enforced}"),
1748            (None, Some(initial), None) => write!(f, "{initial}"),
1749            (None, Some(initial), Some(enforced)) => write!(f, "{initial} {enforced}"),
1750            (Some(deferrable), None, None) => write!(f, "{deferrable}"),
1751            (Some(deferrable), None, Some(enforced)) => write!(f, "{deferrable} {enforced}"),
1752            (Some(deferrable), Some(initial), None) => write!(f, "{deferrable} {initial}"),
1753            (Some(deferrable), Some(initial), Some(enforced)) => {
1754                write!(f, "{deferrable} {initial} {enforced}")
1755            }
1756        }
1757    }
1758}
1759
1760/// `<referential_action> =
1761/// { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }`
1762///
1763/// Used in foreign key constraints in `ON UPDATE` and `ON DELETE` options.
1764#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1765#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1766#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1767pub enum ReferentialAction {
1768    Restrict,
1769    Cascade,
1770    SetNull,
1771    NoAction,
1772    SetDefault,
1773}
1774
1775impl fmt::Display for ReferentialAction {
1776    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1777        f.write_str(match self {
1778            ReferentialAction::Restrict => "RESTRICT",
1779            ReferentialAction::Cascade => "CASCADE",
1780            ReferentialAction::SetNull => "SET NULL",
1781            ReferentialAction::NoAction => "NO ACTION",
1782            ReferentialAction::SetDefault => "SET DEFAULT",
1783        })
1784    }
1785}
1786
1787/// SQL user defined type definition
1788#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1789#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1790#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1791pub enum UserDefinedTypeRepresentation {
1792    Composite {
1793        attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
1794    },
1795    /// Note: this is PostgreSQL-specific. See <https://www.postgresql.org/docs/current/sql-createtype.html>
1796    Enum { labels: Vec<Ident> },
1797}
1798
1799impl fmt::Display for UserDefinedTypeRepresentation {
1800    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1801        match self {
1802            UserDefinedTypeRepresentation::Composite { attributes } => {
1803                write!(f, "({})", display_comma_separated(attributes))
1804            }
1805            UserDefinedTypeRepresentation::Enum { labels } => {
1806                write!(f, "ENUM ({})", display_comma_separated(labels))
1807            }
1808        }
1809    }
1810}
1811
1812/// SQL user defined type attribute definition
1813#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1814#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1815#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1816pub struct UserDefinedTypeCompositeAttributeDef {
1817    pub name: Ident,
1818    pub data_type: DataType,
1819    pub collation: Option<ObjectName>,
1820}
1821
1822impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
1823    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1824        write!(f, "{} {}", self.name, self.data_type)?;
1825        if let Some(collation) = &self.collation {
1826            write!(f, " COLLATE {collation}")?;
1827        }
1828        Ok(())
1829    }
1830}
1831
1832/// PARTITION statement used in ALTER TABLE et al. such as in Hive and ClickHouse SQL.
1833/// For example, ClickHouse's OPTIMIZE TABLE supports syntax like PARTITION ID 'partition_id' and PARTITION expr.
1834/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
1835#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1836#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1837#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1838pub enum Partition {
1839    Identifier(Ident),
1840    Expr(Expr),
1841    /// ClickHouse supports PART expr which represents physical partition in disk.
1842    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#attach-partitionpart)
1843    Part(Expr),
1844    Partitions(Vec<Expr>),
1845}
1846
1847impl fmt::Display for Partition {
1848    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1849        match self {
1850            Partition::Identifier(id) => write!(f, "PARTITION ID {id}"),
1851            Partition::Expr(expr) => write!(f, "PARTITION {expr}"),
1852            Partition::Part(expr) => write!(f, "PART {expr}"),
1853            Partition::Partitions(partitions) => {
1854                write!(f, "PARTITION ({})", display_comma_separated(partitions))
1855            }
1856        }
1857    }
1858}
1859
1860/// DEDUPLICATE statement used in OPTIMIZE TABLE et al. such as in ClickHouse SQL
1861/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
1862#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1863#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1864#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1865pub enum Deduplicate {
1866    All,
1867    ByExpression(Expr),
1868}
1869
1870impl fmt::Display for Deduplicate {
1871    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1872        match self {
1873            Deduplicate::All => write!(f, "DEDUPLICATE"),
1874            Deduplicate::ByExpression(expr) => write!(f, "DEDUPLICATE BY {expr}"),
1875        }
1876    }
1877}
1878
1879/// Hive supports `CLUSTERED BY` statement in `CREATE TABLE`.
1880/// Syntax: `CLUSTERED BY (col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS`
1881///
1882/// [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
1883#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1884#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1885#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1886pub struct ClusteredBy {
1887    pub columns: Vec<Ident>,
1888    pub sorted_by: Option<Vec<OrderByExpr>>,
1889    pub num_buckets: Value,
1890}
1891
1892impl fmt::Display for ClusteredBy {
1893    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1894        write!(
1895            f,
1896            "CLUSTERED BY ({})",
1897            display_comma_separated(&self.columns)
1898        )?;
1899        if let Some(ref sorted_by) = self.sorted_by {
1900            write!(f, " SORTED BY ({})", display_comma_separated(sorted_by))?;
1901        }
1902        write!(f, " INTO {} BUCKETS", self.num_buckets)
1903    }
1904}
1905
1906#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1907#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1908#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1909pub struct CreateFunction {
1910    pub or_replace: bool,
1911    pub temporary: bool,
1912    pub if_not_exists: bool,
1913    pub name: ObjectName,
1914    pub args: Option<Vec<OperateFunctionArg>>,
1915    pub return_type: Option<DataType>,
1916    /// The expression that defines the function.
1917    ///
1918    /// Examples:
1919    /// ```sql
1920    /// AS ((SELECT 1))
1921    /// AS "console.log();"
1922    /// ```
1923    pub function_body: Option<CreateFunctionBody>,
1924    /// Behavior attribute for the function
1925    ///
1926    /// IMMUTABLE | STABLE | VOLATILE
1927    ///
1928    /// [Postgres](https://www.postgresql.org/docs/current/sql-createfunction.html)
1929    pub behavior: Option<FunctionBehavior>,
1930    /// CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
1931    ///
1932    /// [Postgres](https://www.postgresql.org/docs/current/sql-createfunction.html)
1933    pub called_on_null: Option<FunctionCalledOnNull>,
1934    /// PARALLEL { UNSAFE | RESTRICTED | SAFE }
1935    ///
1936    /// [Postgres](https://www.postgresql.org/docs/current/sql-createfunction.html)
1937    pub parallel: Option<FunctionParallel>,
1938    /// USING ... (Hive only)
1939    pub using: Option<CreateFunctionUsing>,
1940    /// Language used in a UDF definition.
1941    ///
1942    /// Example:
1943    /// ```sql
1944    /// CREATE FUNCTION foo() LANGUAGE js AS "console.log();"
1945    /// ```
1946    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_a_javascript_udf)
1947    pub language: Option<Ident>,
1948    /// Determinism keyword used for non-sql UDF definitions.
1949    ///
1950    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11)
1951    pub determinism_specifier: Option<FunctionDeterminismSpecifier>,
1952    /// List of options for creating the function.
1953    ///
1954    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11)
1955    pub options: Option<Vec<SqlOption>>,
1956    /// Connection resource for a remote function.
1957    ///
1958    /// Example:
1959    /// ```sql
1960    /// CREATE FUNCTION foo()
1961    /// RETURNS FLOAT64
1962    /// REMOTE WITH CONNECTION us.myconnection
1963    /// ```
1964    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_a_remote_function)
1965    pub remote_connection: Option<ObjectName>,
1966}
1967
1968impl fmt::Display for CreateFunction {
1969    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1970        write!(
1971            f,
1972            "CREATE {or_replace}{temp}FUNCTION {if_not_exists}{name}",
1973            name = self.name,
1974            temp = if self.temporary { "TEMPORARY " } else { "" },
1975            or_replace = if self.or_replace { "OR REPLACE " } else { "" },
1976            if_not_exists = if self.if_not_exists {
1977                "IF NOT EXISTS "
1978            } else {
1979                ""
1980            },
1981        )?;
1982        if let Some(args) = &self.args {
1983            write!(f, "({})", display_comma_separated(args))?;
1984        }
1985        if let Some(return_type) = &self.return_type {
1986            write!(f, " RETURNS {return_type}")?;
1987        }
1988        if let Some(determinism_specifier) = &self.determinism_specifier {
1989            write!(f, " {determinism_specifier}")?;
1990        }
1991        if let Some(language) = &self.language {
1992            write!(f, " LANGUAGE {language}")?;
1993        }
1994        if let Some(behavior) = &self.behavior {
1995            write!(f, " {behavior}")?;
1996        }
1997        if let Some(called_on_null) = &self.called_on_null {
1998            write!(f, " {called_on_null}")?;
1999        }
2000        if let Some(parallel) = &self.parallel {
2001            write!(f, " {parallel}")?;
2002        }
2003        if let Some(remote_connection) = &self.remote_connection {
2004            write!(f, " REMOTE WITH CONNECTION {remote_connection}")?;
2005        }
2006        if let Some(CreateFunctionBody::AsBeforeOptions(function_body)) = &self.function_body {
2007            write!(f, " AS {function_body}")?;
2008        }
2009        if let Some(CreateFunctionBody::Return(function_body)) = &self.function_body {
2010            write!(f, " RETURN {function_body}")?;
2011        }
2012        if let Some(using) = &self.using {
2013            write!(f, " {using}")?;
2014        }
2015        if let Some(options) = &self.options {
2016            write!(
2017                f,
2018                " OPTIONS({})",
2019                display_comma_separated(options.as_slice())
2020            )?;
2021        }
2022        if let Some(CreateFunctionBody::AsAfterOptions(function_body)) = &self.function_body {
2023            write!(f, " AS {function_body}")?;
2024        }
2025        Ok(())
2026    }
2027}