// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! SQL Abstract Syntax Tree (AST) types
#[cfg(not(feature = "std"))]
use alloc::{
    boxed::Box,
    format,
    string::{String, ToString},
    vec,
    vec::Vec,
};
use helpers::{
    attached_token::AttachedToken,
    stmt_data_loading::{FileStagingCommand, StageLoadSelectItemKind},
};

use core::cmp::Ordering;
use core::ops::{Deref, DerefMut};
use core::{
    fmt::{self, Display},
    hash,
};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

use crate::{
    display_utils::SpaceOrNewline,
    tokenizer::{Span, Token},
};
use crate::{
    display_utils::{Indent, NewLine},
    keywords::Keyword,
};

pub use self::data_type::{
    ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember,
    ExactNumberInfo, IntervalFields, StructBracketKind, TimezoneInfo,
};
pub use self::dcl::{
    AlterRoleOperation, CreateRole, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
    SetConfigValue, Use,
};
pub use self::ddl::{
    Alignment, AlterCollation, AlterCollationOperation, AlterColumnOperation, AlterConnectorOwner,
    AlterFunction, AlterFunctionAction, AlterFunctionKind, AlterFunctionOperation,
    AlterIndexOperation, AlterOperator, AlterOperatorClass, AlterOperatorClassOperation,
    AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicy,
    AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
    AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
    AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
    ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
    ColumnPolicyProperty, ConstraintCharacteristics, CreateCollation, CreateCollationDefinition,
    CreateConnector, CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
    CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
    CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial, DistStyle,
    DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
    DropOperatorSignature, DropPolicy, DropTrigger, ForValues, FunctionReturnType, GeneratedAs,
    GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
    IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
    KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, OperatorClassItem,
    OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition,
    PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
    TagsColumnOption, TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
    UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
    UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
};
pub use self::dml::{
    Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
    MergeInsertKind, MergeUpdateExpr, MultiTableInsertIntoClause, MultiTableInsertType,
    MultiTableInsertValue, MultiTableInsertValues, MultiTableInsertWhenClause, OutputClause,
    Update,
};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
    AfterMatchSkip, ConnectByKind, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
    ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, ExprWithAliasAndOrderBy, Fetch, ForClause,
    ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias,
    IlikeSelectItem, InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint,
    JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn,
    JsonTableNestedColumn, LateralView, LimitClause, LockClause, LockType, MatchRecognizePattern,
    MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
    OffsetRows, OpenJsonTableColumn, OrderBy, OrderByExpr, OrderByKind, OrderByOptions,
    PipeOperator, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
    RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
    SelectFlavor, SelectInto, SelectItem, SelectItemQualifiedWildcardKind, SelectModifiers,
    SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table, TableAlias,
    TableAliasColumnDef, TableFactor, TableFunctionArgs, TableIndexHintForClause,
    TableIndexHintType, TableIndexHints, TableIndexType, TableSample, TableSampleBucket,
    TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed,
    TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity,
    UpdateTableFromKind, ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
    XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn,
    XmlTableColumnOption,
};

pub use self::trigger::{
    TriggerEvent, TriggerExecBody, TriggerExecBodyType, TriggerObject, TriggerPeriod,
    TriggerReferencing, TriggerReferencingType,
};

pub use self::value::{
    escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,
    NormalizationForm, QuoteDelimitedString, TrimWhereField, Value, ValueWithSpan,
};

use crate::ast::helpers::key_value_options::KeyValueOptions;
use crate::ast::helpers::stmt_data_loading::StageParamsObject;

#[cfg(feature = "visitor")]
pub use visitor::*;

pub use self::data_type::GeometricTypeKind;

mod data_type;
mod dcl;
mod ddl;
mod dml;
/// Helper modules for building and manipulating AST nodes.
pub mod helpers;
pub mod table_constraints;
pub use table_constraints::{
    CheckConstraint, ConstraintUsingIndex, ForeignKeyConstraint, FullTextOrSpatialConstraint,
    IndexConstraint, PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
};
mod operator;
mod query;
mod spans;
pub use spans::Spanned;

pub mod comments;
mod trigger;
mod value;

#[cfg(feature = "visitor")]
mod visitor;

/// Helper used to format a slice using a separator string (e.g., `", "`).
pub struct DisplaySeparated<'a, T>
where
    T: fmt::Display,
{
    slice: &'a [T],
    sep: &'static str,
}

impl<T> fmt::Display for DisplaySeparated<'_, T>
where
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut delim = "";
        for t in self.slice {
            f.write_str(delim)?;
            delim = self.sep;
            t.fmt(f)?;
        }
        Ok(())
    }
}

pub(crate) fn display_separated<'a, T>(slice: &'a [T], sep: &'static str) -> DisplaySeparated<'a, T>
where
    T: fmt::Display,
{
    DisplaySeparated { slice, sep }
}

pub(crate) fn display_comma_separated<T>(slice: &[T]) -> DisplaySeparated<'_, T>
where
    T: fmt::Display,
{
    DisplaySeparated { slice, sep: ", " }
}

/// Writes the given statements to the formatter, each ending with
/// a semicolon and space separated.
fn format_statement_list(f: &mut fmt::Formatter, statements: &[Statement]) -> fmt::Result {
    write!(f, "{}", display_separated(statements, "; "))?;
    // We manually insert semicolon for the last statement,
    // since display_separated doesn't handle that case.
    write!(f, ";")
}

/// A item `T` enclosed in a pair of parentheses
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Parens<T> {
    /// the opening parenthesis token, i.e. `(`
    pub opening_token: AttachedToken,
    /// content enclosed in parentheses
    pub content: T,
    /// the closing parenthesis token, i.e. `)`
    pub closing_token: AttachedToken,
}

impl<T> Parens<T> {
    /// Constructor wrapping `content` into `Parens` with an empty span;
    /// useful for testing purposes.
    pub fn with_empty_span(content: T) -> Self {
        Self {
            opening_token: AttachedToken::empty(),
            content,
            closing_token: AttachedToken::empty(),
        }
    }
}

impl<T> Deref for Parens<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.content
    }
}

impl<T> DerefMut for Parens<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.content
    }
}

/// An identifier, decomposed into its value or character data and the quote style.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Ident {
    /// The value of the identifier without quotes.
    pub value: String,
    /// The starting quote if any. Valid quote characters are the single quote,
    /// double quote, backtick, and opening square bracket.
    pub quote_style: Option<char>,
    /// The span of the identifier in the original SQL string.
    pub span: Span,
}

impl PartialEq for Ident {
    fn eq(&self, other: &Self) -> bool {
        let Ident {
            value,
            quote_style,
            // exhaustiveness check; we ignore spans in comparisons
            span: _,
        } = self;

        value == &other.value && quote_style == &other.quote_style
    }
}

impl core::hash::Hash for Ident {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        let Ident {
            value,
            quote_style,
            // exhaustiveness check; we ignore spans in hashes
            span: _,
        } = self;

        value.hash(state);
        quote_style.hash(state);
    }
}

impl Eq for Ident {}

impl PartialOrd for Ident {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Ident {
    fn cmp(&self, other: &Self) -> Ordering {
        let Ident {
            value,
            quote_style,
            // exhaustiveness check; we ignore spans in ordering
            span: _,
        } = self;

        let Ident {
            value: other_value,
            quote_style: other_quote_style,
            // exhaustiveness check; we ignore spans in ordering
            span: _,
        } = other;

        // First compare by value, then by quote_style
        value
            .cmp(other_value)
            .then_with(|| quote_style.cmp(other_quote_style))
    }
}

impl Ident {
    /// Create a new identifier with the given value and no quotes and an empty span.
    pub fn new<S>(value: S) -> Self
    where
        S: Into<String>,
    {
        Ident {
            value: value.into(),
            quote_style: None,
            span: Span::empty(),
        }
    }

    /// Create a new quoted identifier with the given quote and value. This function
    /// panics if the given quote is not a valid quote character.
    pub fn with_quote<S>(quote: char, value: S) -> Self
    where
        S: Into<String>,
    {
        assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '[');
        Ident {
            value: value.into(),
            quote_style: Some(quote),
            span: Span::empty(),
        }
    }

    /// Create an `Ident` with the given `span` and `value` (unquoted).
    pub fn with_span<S>(span: Span, value: S) -> Self
    where
        S: Into<String>,
    {
        Ident {
            value: value.into(),
            quote_style: None,
            span,
        }
    }

    /// Create a quoted `Ident` with the given `quote` and `span`.
    pub fn with_quote_and_span<S>(quote: char, span: Span, value: S) -> Self
    where
        S: Into<String>,
    {
        assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '[');
        Ident {
            value: value.into(),
            quote_style: Some(quote),
            span,
        }
    }
}

impl From<&str> for Ident {
    fn from(value: &str) -> Self {
        Ident {
            value: value.to_string(),
            quote_style: None,
            span: Span::empty(),
        }
    }
}

impl fmt::Display for Ident {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.quote_style {
            Some(q) if q == '"' || q == '\'' || q == '`' => {
                let escaped = value::escape_quoted_string(&self.value, q);
                write!(f, "{q}{escaped}{q}")
            }
            Some('[') => write!(f, "[{}]", self.value),
            None => f.write_str(&self.value),
            _ => panic!("unexpected quote style"),
        }
    }
}

/// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ObjectName(pub Vec<ObjectNamePart>);

impl From<Vec<Ident>> for ObjectName {
    fn from(idents: Vec<Ident>) -> Self {
        ObjectName(idents.into_iter().map(ObjectNamePart::Identifier).collect())
    }
}

impl From<Ident> for ObjectName {
    fn from(ident: Ident) -> Self {
        ObjectName(vec![ObjectNamePart::Identifier(ident)])
    }
}

impl fmt::Display for ObjectName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", display_separated(&self.0, "."))
    }
}

/// A single part of an ObjectName
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ObjectNamePart {
    /// A single identifier part, e.g. `schema` or `table`.
    Identifier(Ident),
    /// A function that returns an identifier (dialect-specific).
    Function(ObjectNamePartFunction),
}

impl ObjectNamePart {
    /// Return the identifier if this is an `Identifier` variant.
    pub fn as_ident(&self) -> Option<&Ident> {
        match self {
            ObjectNamePart::Identifier(ident) => Some(ident),
            ObjectNamePart::Function(_) => None,
        }
    }
}

impl fmt::Display for ObjectNamePart {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ObjectNamePart::Identifier(ident) => write!(f, "{ident}"),
            ObjectNamePart::Function(func) => write!(f, "{func}"),
        }
    }
}

/// An object name part that consists of a function that dynamically
/// constructs identifiers.
///
/// - [Snowflake](https://docs.snowflake.com/en/sql-reference/identifier-literal)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ObjectNamePartFunction {
    /// The function name that produces the object name part.
    pub name: Ident,
    /// Function arguments used to compute the identifier.
    pub args: Vec<FunctionArg>,
}

impl fmt::Display for ObjectNamePartFunction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}(", self.name)?;
        write!(f, "{})", display_comma_separated(&self.args))
    }
}

/// Represents an Array Expression, either
/// `ARRAY[..]`, or `[..]`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Array {
    /// The list of expressions between brackets
    pub elem: Vec<Expr>,

    /// `true` for  `ARRAY[..]`, `false` for `[..]`
    pub named: bool,
}

impl fmt::Display for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}[{}]",
            if self.named { "ARRAY" } else { "" },
            display_comma_separated(&self.elem)
        )
    }
}

/// Represents an INTERVAL expression, roughly in the following format:
/// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
/// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
///
/// The parser does not validate the `<value>`, nor does it ensure
/// that the `<leading_field>` units >= the units in `<last_field>`,
/// so the user will have to reject intervals like `HOUR TO YEAR`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Interval {
    /// The interval value expression (commonly a string literal).
    pub value: Box<Expr>,
    /// Optional leading time unit (e.g., `HOUR`, `MINUTE`).
    pub leading_field: Option<DateTimeField>,
    /// Optional leading precision for the leading field.
    pub leading_precision: Option<u64>,
    /// Optional trailing time unit for a range (e.g., `SECOND`).
    pub last_field: Option<DateTimeField>,
    /// The fractional seconds precision, when specified.
    ///
    /// See SQL `SECOND(n)` or `SECOND(m, n)` forms.
    pub fractional_seconds_precision: Option<u64>,
}

impl fmt::Display for Interval {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = self.value.as_ref();
        match (
            &self.leading_field,
            self.leading_precision,
            self.fractional_seconds_precision,
        ) {
            (
                Some(DateTimeField::Second),
                Some(leading_precision),
                Some(fractional_seconds_precision),
            ) => {
                // When the leading field is SECOND, the parser guarantees that
                // the last field is None.
                assert!(self.last_field.is_none());
                write!(
                    f,
                    "INTERVAL {value} SECOND ({leading_precision}, {fractional_seconds_precision})"
                )
            }
            _ => {
                write!(f, "INTERVAL {value}")?;
                if let Some(leading_field) = &self.leading_field {
                    write!(f, " {leading_field}")?;
                }
                if let Some(leading_precision) = self.leading_precision {
                    write!(f, " ({leading_precision})")?;
                }
                if let Some(last_field) = &self.last_field {
                    write!(f, " TO {last_field}")?;
                }
                if let Some(fractional_seconds_precision) = self.fractional_seconds_precision {
                    write!(f, " ({fractional_seconds_precision})")?;
                }
                Ok(())
            }
        }
    }
}

/// A field definition within a struct
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct StructField {
    /// Optional name of the struct field.
    pub field_name: Option<Ident>,
    /// The field data type.
    pub field_type: DataType,
    /// Struct field options (e.g., `OPTIONS(...)` on BigQuery).
    /// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_name_and_column_schema)
    pub options: Option<Vec<SqlOption>>,
}

impl fmt::Display for StructField {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(name) = &self.field_name {
            write!(f, "{name} {}", self.field_type)?;
        } else {
            write!(f, "{}", self.field_type)?;
        }
        if let Some(options) = &self.options {
            write!(f, " OPTIONS({})", display_separated(options, ", "))
        } else {
            Ok(())
        }
    }
}

/// A field definition within a union
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/union.html
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UnionField {
    /// Name of the union field.
    pub field_name: Ident,
    /// Type of the union field.
    pub field_type: DataType,
}

impl fmt::Display for UnionField {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}", self.field_name, self.field_type)
    }
}

/// A dictionary field within a dictionary.
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DictionaryField {
    /// Dictionary key identifier.
    pub key: Ident,
    /// Value expression for the dictionary entry.
    pub value: Box<Expr>,
}

impl fmt::Display for DictionaryField {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}: {}", self.key, self.value)
    }
}

/// Represents a Map expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Map {
    /// Entries of the map as key/value pairs.
    pub entries: Vec<MapEntry>,
}

impl Display for Map {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MAP {{{}}}", display_comma_separated(&self.entries))
    }
}

/// A map field within a map.
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MapEntry {
    /// Key expression of the map entry.
    pub key: Box<Expr>,
    /// Value expression of the map entry.
    pub value: Box<Expr>,
}

impl fmt::Display for MapEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}: {}", self.key, self.value)
    }
}

/// Options for `CAST` / `TRY_CAST`
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CastFormat {
    /// A simple cast format specified by a `Value`.
    Value(ValueWithSpan),
    /// A cast format with an explicit time zone: `(format, timezone)`.
    ValueAtTimeZone(ValueWithSpan, ValueWithSpan),
}

/// An element of a JSON path.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonPathElem {
    /// Accesses an object field using dot notation, e.g. `obj:foo.bar.baz`.
    ///
    /// See <https://docs.snowflake.com/en/user-guide/querying-semistructured#dot-notation>.
    Dot {
        /// The object key text (without quotes).
        key: String,
        /// `true` when the key was quoted in the source.
        quoted: bool,
    },
    /// Accesses an object field or array element using bracket notation,
    /// e.g. `obj['foo']`.
    ///
    /// See <https://docs.snowflake.com/en/user-guide/querying-semistructured#bracket-notation>.
    Bracket {
        /// The expression used as the bracket key (string or numeric expression).
        key: Expr,
    },
    /// Access an object field using colon bracket notation
    /// e.g. `obj:['foo']`
    ///
    /// See <https://docs.databricks.com/en/sql/language-manual/functions/colonsign.html>
    ColonBracket {
        /// The expression used as the bracket key (string or numeric expression).
        key: Expr,
    },
}

/// A JSON path.
///
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured>.
/// See <https://docs.databricks.com/en/sql/language-manual/sql-ref-json-path-expression.html>.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct JsonPath {
    /// Sequence of path elements that form the JSON path.
    pub path: Vec<JsonPathElem>,
}

impl fmt::Display for JsonPath {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (i, elem) in self.path.iter().enumerate() {
            match elem {
                JsonPathElem::Dot { key, quoted } => {
                    if i == 0 {
                        write!(f, ":")?;
                    } else {
                        write!(f, ".")?;
                    }

                    if *quoted {
                        write!(f, "\"{}\"", escape_double_quote_string(key))?;
                    } else {
                        write!(f, "{key}")?;
                    }
                }
                JsonPathElem::Bracket { key } => {
                    write!(f, "[{key}]")?;
                }
                JsonPathElem::ColonBracket { key } => {
                    write!(f, ":[{key}]")?;
                }
            }
        }
        Ok(())
    }
}

/// The syntax used for in a cast expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CastKind {
    /// The standard SQL cast syntax, e.g. `CAST(<expr> as <datatype>)`
    Cast,
    /// A cast that returns `NULL` on failure, e.g. `TRY_CAST(<expr> as <datatype>)`.
    ///
    /// See <https://docs.snowflake.com/en/sql-reference/functions/try_cast>.
    /// See <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql>.
    TryCast,
    /// A cast that returns `NULL` on failure, bigQuery-specific ,  e.g. `SAFE_CAST(<expr> as <datatype>)`.
    ///
    /// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting>.
    SafeCast,
    /// `<expr> :: <datatype>`
    DoubleColon,
}

/// `MATCH` type for constraint references
///
/// See: <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ConstraintReferenceMatchKind {
    /// `MATCH FULL`
    Full,
    /// `MATCH PARTIAL`
    Partial,
    /// `MATCH SIMPLE`
    Simple,
}

impl fmt::Display for ConstraintReferenceMatchKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Full => write!(f, "MATCH FULL"),
            Self::Partial => write!(f, "MATCH PARTIAL"),
            Self::Simple => write!(f, "MATCH SIMPLE"),
        }
    }
}

/// `EXTRACT` syntax variants.
///
/// In Snowflake dialect, the `EXTRACT` expression can support either the `from` syntax
/// or the comma syntax.
///
/// See <https://docs.snowflake.com/en/sql-reference/functions/extract>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ExtractSyntax {
    /// `EXTRACT( <date_or_time_part> FROM <date_or_time_expr> )`
    From,
    /// `EXTRACT( <date_or_time_part> , <date_or_timestamp_expr> )`
    Comma,
}

/// The syntax used in a CEIL or FLOOR expression.
///
/// The `CEIL/FLOOR(<datetime value expression> TO <time unit>)` is an Amazon Kinesis Data Analytics extension.
/// See <https://docs.aws.amazon.com/kinesisanalytics/latest/sqlref/sql-reference-ceil.html> for
/// details.
///
/// Other dialects either support `CEIL/FLOOR( <expr> [, <scale>])` format or just
/// `CEIL/FLOOR(<expr>)`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CeilFloorKind {
    /// `CEIL( <expr> TO <DateTimeField>)`
    DateTimeField(DateTimeField),
    /// `CEIL( <expr> [, <scale>])`
    Scale(ValueWithSpan),
}

/// A WHEN clause in a CASE expression containing both
/// the condition and its corresponding result
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CaseWhen {
    /// The `WHEN` condition expression.
    pub condition: Expr,
    /// The expression returned when `condition` matches.
    pub result: Expr,
}

impl fmt::Display for CaseWhen {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("WHEN ")?;
        self.condition.fmt(f)?;
        f.write_str(" THEN")?;
        SpaceOrNewline.fmt(f)?;
        Indent(&self.result).fmt(f)?;
        Ok(())
    }
}

/// An SQL expression of any type.
///
/// # Semantics / Type Checking
///
/// The parser does not distinguish between expressions of different types
/// (e.g. boolean vs string). The caller is responsible for detecting and
/// validating types as necessary (for example  `WHERE 1` vs `SELECT 1=1`)
/// See the [README.md] for more details.
///
/// [README.md]: https://github.com/apache/datafusion-sqlparser-rs/blob/main/README.md#syntax-vs-semantics
///
/// # Equality and Hashing Does not Include Source Locations
///
/// The `Expr` type implements `PartialEq` and `Eq` based on the semantic value
/// of the expression (not bitwise comparison). This means that `Expr` instances
/// that are semantically equivalent but have different spans (locations in the
/// source tree) will compare as equal.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "visitor",
    derive(Visit, VisitMut),
    visit(with = "visit_expr")
)]
pub enum Expr {
    /// Identifier e.g. table name or column name
    Identifier(Ident),
    /// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col`
    CompoundIdentifier(Vec<Ident>),
    /// Multi-part expression access.
    ///
    /// This structure represents an access chain in structured / nested types
    /// such as maps, arrays, and lists:
    /// - Array
    ///     - A 1-dim array `a[1]` will be represented like:
    ///       `CompoundFieldAccess(Ident('a'), vec![Subscript(1)]`
    ///     - A 2-dim array `a[1][2]` will be represented like:
    ///       `CompoundFieldAccess(Ident('a'), vec![Subscript(1), Subscript(2)]`
    /// - Map or Struct (Bracket-style)
    ///     - A map `a['field1']` will be represented like:
    ///       `CompoundFieldAccess(Ident('a'), vec![Subscript('field')]`
    ///     - A 2-dim map `a['field1']['field2']` will be represented like:
    ///       `CompoundFieldAccess(Ident('a'), vec![Subscript('field1'), Subscript('field2')]`
    /// - Struct (Dot-style) (only effect when the chain contains both subscript and expr)
    ///     - A struct access `a[field1].field2` will be represented like:
    ///       `CompoundFieldAccess(Ident('a'), vec![Subscript('field1'), Ident('field2')]`
    /// - If a struct access likes `a.field1.field2`, it will be represented by CompoundIdentifier([a, field1, field2])
    CompoundFieldAccess {
        /// The base expression being accessed.
        root: Box<Expr>,
        /// Sequence of access operations (subscript or identifier accesses).
        access_chain: Vec<AccessExpr>,
    },
    /// Access data nested in a value containing semi-structured data, such as
    /// the `VARIANT` type on Snowflake. for example `src:customer[0].name`.
    ///
    /// See <https://docs.snowflake.com/en/user-guide/querying-semistructured>.
    /// See <https://docs.databricks.com/en/sql/language-manual/functions/colonsign.html>.
    JsonAccess {
        /// The value being queried.
        value: Box<Expr>,
        /// The path to the data to extract.
        path: JsonPath,
    },
    /// `IS FALSE` operator
    IsFalse(Box<Expr>),
    /// `IS NOT FALSE` operator
    IsNotFalse(Box<Expr>),
    /// `IS TRUE` operator
    IsTrue(Box<Expr>),
    /// `IS NOT TRUE` operator
    IsNotTrue(Box<Expr>),
    /// `IS NULL` operator
    IsNull(Box<Expr>),
    /// `IS NOT NULL` operator
    IsNotNull(Box<Expr>),
    /// `IS UNKNOWN` operator
    IsUnknown(Box<Expr>),
    /// `IS NOT UNKNOWN` operator
    IsNotUnknown(Box<Expr>),
    /// `IS DISTINCT FROM` operator
    IsDistinctFrom(Box<Expr>, Box<Expr>),
    /// `IS NOT DISTINCT FROM` operator
    IsNotDistinctFrom(Box<Expr>, Box<Expr>),
    /// `<expr> IS [ NOT ] [ form ] NORMALIZED`
    IsNormalized {
        /// Expression being tested.
        expr: Box<Expr>,
        /// Optional normalization `form` (e.g., NFC, NFD).
        form: Option<NormalizationForm>,
        /// `true` when `NOT` is present.
        negated: bool,
    },
    /// `[ NOT ] IN (val1, val2, ...)`
    InList {
        /// Left-hand expression to test for membership.
        expr: Box<Expr>,
        /// Literal list of expressions to check against.
        list: Vec<Expr>,
        /// `true` when the `NOT` modifier is present.
        negated: bool,
    },
    /// `[ NOT ] IN (SELECT ...)`
    InSubquery {
        /// Left-hand expression to test for membership.
        expr: Box<Expr>,
        /// The subquery providing the candidate values.
        subquery: Box<Query>,
        /// `true` when the `NOT` modifier is present.
        negated: bool,
    },
    /// `[ NOT ] IN UNNEST(array_expression)`
    InUnnest {
        /// Left-hand expression to test for membership.
        expr: Box<Expr>,
        /// Array expression being unnested.
        array_expr: Box<Expr>,
        /// `true` when the `NOT` modifier is present.
        negated: bool,
    },
    /// `<expr> [ NOT ] BETWEEN <low> AND <high>`
    Between {
        /// Expression being compared.
        expr: Box<Expr>,
        /// `true` when the `NOT` modifier is present.
        negated: bool,
        /// Lower bound.
        low: Box<Expr>,
        /// Upper bound.
        high: Box<Expr>,
    },
    /// Binary operation e.g. `1 + 1` or `foo > bar`
    BinaryOp {
        /// Left operand.
        left: Box<Expr>,
        /// Operator between operands.
        op: BinaryOperator,
        /// Right operand.
        right: Box<Expr>,
    },
    /// `[NOT] LIKE <pattern> [ESCAPE <escape_character>]`
    Like {
        /// `true` when `NOT` is present.
        negated: bool,
        /// Snowflake supports the ANY keyword to match against a list of patterns
        /// <https://docs.snowflake.com/en/sql-reference/functions/like_any>
        any: bool,
        /// Expression to match.
        expr: Box<Expr>,
        /// Pattern expression.
        pattern: Box<Expr>,
        /// Optional escape character.
        escape_char: Option<ValueWithSpan>,
    },
    /// `ILIKE` (case-insensitive `LIKE`)
    ILike {
        /// `true` when `NOT` is present.
        negated: bool,
        /// Snowflake supports the ANY keyword to match against a list of patterns
        /// <https://docs.snowflake.com/en/sql-reference/functions/like_any>
        any: bool,
        /// Expression to match.
        expr: Box<Expr>,
        /// Pattern expression.
        pattern: Box<Expr>,
        /// Optional escape character.
        escape_char: Option<ValueWithSpan>,
    },
    /// `SIMILAR TO` regex
    SimilarTo {
        /// `true` when `NOT` is present.
        negated: bool,
        /// Expression to test.
        expr: Box<Expr>,
        /// Pattern expression.
        pattern: Box<Expr>,
        /// Optional escape character.
        escape_char: Option<ValueWithSpan>,
    },
    /// MySQL: `RLIKE` regex or `REGEXP` regex
    RLike {
        /// `true` when `NOT` is present.
        negated: bool,
        /// Expression to test.
        expr: Box<Expr>,
        /// Pattern expression.
        pattern: Box<Expr>,
        /// true for REGEXP, false for RLIKE (no difference in semantics)
        regexp: bool,
    },
    /// `ANY` operation e.g. `foo > ANY(bar)`, comparison operator is one of `[=, >, <, =>, =<, !=]`
    /// <https://docs.snowflake.com/en/sql-reference/operators-subquery#all-any>
    AnyOp {
        /// Left operand.
        left: Box<Expr>,
        /// Comparison operator.
        compare_op: BinaryOperator,
        /// Right-hand subquery expression.
        right: Box<Expr>,
        /// ANY and SOME are synonymous: <https://docs.cloudera.com/cdw-runtime/cloud/using-hiveql/topics/hive_comparison_predicates.html>
        is_some: bool,
    },
    /// `ALL` operation e.g. `foo > ALL(bar)`, comparison operator is one of `[=, >, <, =>, =<, !=]`
    /// <https://docs.snowflake.com/en/sql-reference/operators-subquery#all-any>
    AllOp {
        /// Left operand.
        left: Box<Expr>,
        /// Comparison operator.
        compare_op: BinaryOperator,
        /// Right-hand subquery expression.
        right: Box<Expr>,
    },

    /// Unary operation e.g. `NOT foo`
    UnaryOp {
        /// The unary operator (e.g., `NOT`, `-`).
        op: UnaryOperator,
        /// Operand expression.
        expr: Box<Expr>,
    },
    /// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)`
    Convert {
        /// CONVERT (false) or TRY_CONVERT (true)
        /// <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql?view=sql-server-ver16>
        is_try: bool,
        /// The expression to convert.
        expr: Box<Expr>,
        /// The target data type, if provided.
        data_type: Option<DataType>,
        /// Optional target character encoding (e.g., `utf8mb4`).
        charset: Option<ObjectName>,
        /// `true` when target precedes the value (MSSQL syntax).
        target_before_value: bool,
        /// How to translate the expression.
        ///
        /// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style
        styles: Vec<Expr>,
    },
    /// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
    Cast {
        /// The cast kind (e.g., `CAST`, `TRY_CAST`).
        kind: CastKind,
        /// Expression being cast.
        expr: Box<Expr>,
        /// Target data type.
        data_type: DataType,
        /// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB
        /// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key
        /// specifications, so it's a flag here.
        ///
        /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast
        array: bool,
        /// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
        ///
        /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
        format: Option<CastFormat>,
    },
    /// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
    AtTimeZone {
        /// Timestamp expression to shift.
        timestamp: Box<Expr>,
        /// Time zone expression to apply.
        time_zone: Box<Expr>,
    },
    /// Extract a field from a timestamp e.g. `EXTRACT(MONTH FROM foo)`
    /// Or `EXTRACT(MONTH, foo)`
    ///
    /// Syntax:
    /// ```sql
    /// EXTRACT(DateTimeField FROM <expr>) | EXTRACT(DateTimeField, <expr>)
    /// ```
    Extract {
        /// Which datetime field is being extracted.
        field: DateTimeField,
        /// Syntax variant used (`From` or `Comma`).
        syntax: ExtractSyntax,
        /// Expression to extract from.
        expr: Box<Expr>,
    },
    /// ```sql
    /// CEIL(<expr> [TO DateTimeField])
    /// ```
    /// ```sql
    /// CEIL( <input_expr> [, <scale_expr> ] )
    /// ```
    Ceil {
        /// Expression to ceil.
        expr: Box<Expr>,
        /// The CEIL/FLOOR kind (datetime field or scale).
        field: CeilFloorKind,
    },
    /// ```sql
    /// FLOOR(<expr> [TO DateTimeField])
    /// ```
    /// ```sql
    /// FLOOR( <input_expr> [, <scale_expr> ] )
    ///
    Floor {
        /// Expression to floor.
        expr: Box<Expr>,
        /// The CEIL/FLOOR kind (datetime field or scale).
        field: CeilFloorKind,
    },
    /// ```sql
    /// POSITION(<expr> in <expr>)
    /// ```
    Position {
        /// Expression to search for.
        expr: Box<Expr>,
        /// Expression to search in.
        r#in: Box<Expr>,
    },
    /// ```sql
    /// SUBSTRING(<expr> [FROM <expr>] [FOR <expr>])
    /// ```
    /// or
    /// ```sql
    /// SUBSTRING(<expr>, <expr>, <expr>)
    /// ```
    Substring {
        /// Source expression.
        expr: Box<Expr>,
        /// Optional `FROM` expression.
        substring_from: Option<Box<Expr>>,
        /// Optional `FOR` expression.
        substring_for: Option<Box<Expr>>,

        /// false if the expression is represented using the `SUBSTRING(expr [FROM start] [FOR len])` syntax
        /// true if the expression is represented using the `SUBSTRING(expr, start, len)` syntax
        /// This flag is used for formatting.
        special: bool,

        /// true if the expression is represented using the `SUBSTR` shorthand
        /// This flag is used for formatting.
        shorthand: bool,
    },
    /// ```sql
    /// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)
    /// TRIM(<expr>)
    /// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic
    /// ```
    Trim {
        /// Which side to trim: `BOTH`, `LEADING`, or `TRAILING`.
        trim_where: Option<TrimWhereField>,
        /// Optional expression specifying what to trim from the value `expr`.
        trim_what: Option<Box<Expr>>,
        /// The expression to trim from.
        expr: Box<Expr>,
        /// Optional list of characters to trim (dialect-specific).
        trim_characters: Option<Vec<Expr>>,
    },
    /// ```sql
    /// OVERLAY(<expr> PLACING <expr> FROM <expr>[ FOR <expr> ]
    /// ```
    Overlay {
        /// The target expression being overlayed.
        expr: Box<Expr>,
        /// The expression to place into the target.
        overlay_what: Box<Expr>,
        /// The `FROM` position expression indicating where to start overlay.
        overlay_from: Box<Expr>,
        /// Optional `FOR` length expression limiting the overlay span.
        overlay_for: Option<Box<Expr>>,
    },
    /// `expr COLLATE collation`
    Collate {
        /// The expression being collated.
        expr: Box<Expr>,
        /// The collation name to apply to the expression.
        collation: ObjectName,
    },
    /// Nested expression e.g. `(foo > bar)` or `(1)`
    Nested(Box<Expr>),
    /// A literal value, such as string, number, date or NULL
    Value(ValueWithSpan),
    /// Prefixed expression, e.g. introducer strings, projection prefix
    /// <https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html>
    /// <https://docs.snowflake.com/en/sql-reference/constructs/connect-by>
    Prefixed {
        /// The prefix identifier (introducer or projection prefix).
        prefix: Ident,
        /// The value expression being prefixed.
        /// Hint: you can unwrap the string value using `value.into_string()`.
        value: Box<Expr>,
    },
    /// A constant of form `<data_type> 'value'`.
    /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
    /// as well as constants of other types (a non-standard PostgreSQL extension).
    TypedString(TypedString),
    /// Scalar function call e.g. `LEFT(foo, 5)`
    Function(Function),
    /// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
    ///
    /// Note we only recognize a complete single expression as `<condition>`,
    /// not `< 0` nor `1, 2, 3` as allowed in a `<simple when clause>` per
    /// <https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#simple-when-clause>
    Case {
        /// The attached `CASE` token (keeps original spacing/comments).
        case_token: AttachedToken,
        /// The attached `END` token (keeps original spacing/comments).
        end_token: AttachedToken,
        /// Optional operand expression after `CASE` (for simple CASE).
        operand: Option<Box<Expr>>,
        /// The `WHEN ... THEN` conditions and results.
        conditions: Vec<CaseWhen>,
        /// Optional `ELSE` result expression.
        else_result: Option<Box<Expr>>,
    },
    /// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like
    /// `WHERE [ NOT ] EXISTS (SELECT ...)`.
    Exists {
        /// The subquery checked by `EXISTS`.
        subquery: Box<Query>,
        /// Whether the `EXISTS` is negated (`NOT EXISTS`).
        negated: bool,
    },
    /// A parenthesized subquery `(SELECT ...)`, used in expression like
    /// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
    Subquery(Box<Query>),
    /// The `GROUPING SETS` expr.
    GroupingSets(Vec<Vec<Expr>>),
    /// The `CUBE` expr.
    Cube(Vec<Vec<Expr>>),
    /// The `ROLLUP` expr.
    Rollup(Vec<Vec<Expr>>),
    /// ROW / TUPLE a single value, such as `SELECT (1, 2)`
    Tuple(Vec<Expr>),
    /// `Struct` literal expression
    /// Syntax:
    /// ```sql
    /// STRUCT<[field_name] field_type, ...>( expr1 [, ... ])
    ///
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type)
    /// [Databricks](https://docs.databricks.com/en/sql/language-manual/functions/struct.html)
    /// ```
    Struct {
        /// Struct values.
        values: Vec<Expr>,
        /// Struct field definitions.
        fields: Vec<StructField>,
    },
    /// `BigQuery` specific: An named expression in a typeless struct [1]
    ///
    /// Syntax
    /// ```sql
    /// 1 AS A
    /// ```
    /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
    Named {
        /// The expression being named.
        expr: Box<Expr>,
        /// The assigned identifier name for the expression.
        name: Ident,
    },
    /// `DuckDB` specific `Struct` literal expression [1]
    ///
    /// Syntax:
    /// ```sql
    /// syntax: {'field_name': expr1[, ... ]}
    /// ```
    /// [1]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
    Dictionary(Vec<DictionaryField>),
    /// `DuckDB` specific `Map` literal expression [1]
    ///
    /// Syntax:
    /// ```sql
    /// syntax: Map {key1: value1[, ... ]}
    /// ```
    /// [1]: https://duckdb.org/docs/sql/data_types/map#creating-maps
    Map(Map),
    /// An array expression e.g. `ARRAY[1, 2]`
    Array(Array),
    /// An interval expression e.g. `INTERVAL '1' YEAR`
    Interval(Interval),
    /// `MySQL` specific text search function [(1)].
    ///
    /// Syntax:
    /// ```sql
    /// MATCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
    ///
    /// <col> = CompoundIdentifier
    /// <expr> = String literal
    /// ```
    /// [(1)]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html#function_match
    MatchAgainst {
        /// `(<col>, <col>, ...)`.
        columns: Vec<ObjectName>,
        /// `<expr>`.
        match_value: ValueWithSpan,
        /// `<search modifier>`
        opt_search_modifier: Option<SearchModifier>,
    },
    /// An unqualified `*` wildcard token (e.g. `*`).
    Wildcard(AttachedToken),
    /// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
    /// (Same caveats apply to `QualifiedWildcard` as to `Wildcard`.)
    QualifiedWildcard(ObjectName, AttachedToken),
    /// Some dialects support an older syntax for outer joins where columns are
    /// marked with the `(+)` operator in the WHERE clause, for example:
    ///
    /// ```sql
    /// SELECT t1.c1, t2.c2 FROM t1, t2 WHERE t1.c1 = t2.c2 (+)
    /// ```
    ///
    /// which is equivalent to
    ///
    /// ```sql
    /// SELECT t1.c1, t2.c2 FROM t1 LEFT OUTER JOIN t2 ON t1.c1 = t2.c2
    /// ```
    ///
    /// See <https://docs.snowflake.com/en/sql-reference/constructs/where#joins-in-the-where-clause>.
    OuterJoin(Box<Expr>),
    /// A reference to the prior level in a CONNECT BY clause.
    Prior(Box<Expr>),
    /// A lambda function.
    ///
    /// Syntax:
    /// ```plaintext
    /// param -> expr | (param1, ...) -> expr
    /// ```
    ///
    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function)
    /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
    /// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda)
    Lambda(LambdaFunction),
    /// Checks membership of a value in a JSON array
    MemberOf(MemberOf),
}

impl Expr {
    /// Creates a new [`Expr::Value`]
    pub fn value(value: impl Into<ValueWithSpan>) -> Self {
        Expr::Value(value.into())
    }
}

/// The contents inside the `[` and `]` in a subscript expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Subscript {
    /// Accesses the element of the array at the given index.
    Index {
        /// The index expression used to access the array element.
        index: Expr,
    },

    /// Accesses a slice of an array on PostgreSQL, e.g.
    ///
    /// ```plaintext
    /// => select (array[1,2,3,4,5,6])[2:5];
    /// -----------
    /// {2,3,4,5}
    /// ```
    ///
    /// The lower and/or upper bound can be omitted to slice from the start or
    /// end of the array respectively.
    ///
    /// See <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-ACCESSING>.
    ///
    /// Also supports an optional "stride" as the last element (this is not
    /// supported by postgres), e.g.
    ///
    /// ```plaintext
    /// => select (array[1,2,3,4,5,6])[1:6:2];
    /// -----------
    /// {1,3,5}
    /// ```
    Slice {
        /// Optional lower bound for the slice (inclusive).
        lower_bound: Option<Expr>,
        /// Optional upper bound for the slice (inclusive).
        upper_bound: Option<Expr>,
        /// Optional stride for the slice (step size).
        stride: Option<Expr>,
    },
}

impl fmt::Display for Subscript {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Subscript::Index { index } => write!(f, "{index}"),
            Subscript::Slice {
                lower_bound,
                upper_bound,
                stride,
            } => {
                if let Some(lower) = lower_bound {
                    write!(f, "{lower}")?;
                }
                write!(f, ":")?;
                if let Some(upper) = upper_bound {
                    write!(f, "{upper}")?;
                }
                if let Some(stride) = stride {
                    write!(f, ":")?;
                    write!(f, "{stride}")?;
                }
                Ok(())
            }
        }
    }
}

/// An element of a [`Expr::CompoundFieldAccess`].
/// It can be an expression or a subscript.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AccessExpr {
    /// Accesses a field using dot notation, e.g. `foo.bar.baz`.
    Dot(Expr),
    /// Accesses a field or array element using bracket notation, e.g. `foo['bar']`.
    Subscript(Subscript),
}

impl fmt::Display for AccessExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AccessExpr::Dot(expr) => write!(f, ".{expr}"),
            AccessExpr::Subscript(subscript) => write!(f, "[{subscript}]"),
        }
    }
}

/// A lambda function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LambdaFunction {
    /// The parameters to the lambda function.
    pub params: OneOrManyWithParens<LambdaFunctionParameter>,
    /// The body of the lambda function.
    pub body: Box<Expr>,
    /// The syntax style used to write the lambda function.
    pub syntax: LambdaSyntax,
}

impl fmt::Display for LambdaFunction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.syntax {
            LambdaSyntax::Arrow => write!(f, "{} -> {}", self.params, self.body),
            LambdaSyntax::LambdaKeyword => {
                // For lambda keyword syntax, display params without parentheses
                // e.g., `lambda x, y : expr` not `lambda (x, y) : expr`
                write!(f, "lambda ")?;
                match &self.params {
                    OneOrManyWithParens::One(p) => write!(f, "{p}")?,
                    OneOrManyWithParens::Many(ps) => write!(f, "{}", display_comma_separated(ps))?,
                };
                write!(f, " : {}", self.body)
            }
        }
    }
}

/// A parameter to a lambda function, optionally with a data type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LambdaFunctionParameter {
    /// The name of the parameter
    pub name: Ident,
    /// The optional data type of the parameter
    /// [Snowflake Syntax](https://docs.snowflake.com/en/sql-reference/functions/filter#arguments)
    pub data_type: Option<DataType>,
}

impl fmt::Display for LambdaFunctionParameter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.data_type {
            Some(dt) => write!(f, "{} {}", self.name, dt),
            None => write!(f, "{}", self.name),
        }
    }
}

/// The syntax style for a lambda function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum LambdaSyntax {
    /// Arrow syntax: `param -> expr` or `(param1, param2) -> expr`
    ///
    /// <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-lambda-functions>
    ///
    /// Supported, but deprecated in DuckDB:
    /// <https://duckdb.org/docs/stable/sql/functions/lambda>
    Arrow,
    /// Lambda keyword syntax: `lambda param : expr` or `lambda param1, param2 : expr`
    ///
    /// Recommended in DuckDB:
    /// <https://duckdb.org/docs/stable/sql/functions/lambda>
    LambdaKeyword,
}

/// Encapsulates the common pattern in SQL where either one unparenthesized item
/// such as an identifier or expression is permitted, or multiple of the same
/// item in a parenthesized list. For accessing items regardless of the form,
/// `OneOrManyWithParens` implements `Deref<Target = [T]>` and `IntoIterator`,
/// so you can call slice methods on it and iterate over items
/// # Examples
/// Accessing as a slice:
/// ```
/// # use sqlparser::ast::OneOrManyWithParens;
/// let one = OneOrManyWithParens::One("a");
///
/// assert_eq!(one[0], "a");
/// assert_eq!(one.len(), 1);
/// ```
/// Iterating:
/// ```
/// # use sqlparser::ast::OneOrManyWithParens;
/// let one = OneOrManyWithParens::One("a");
/// let many = OneOrManyWithParens::Many(vec!["a", "b"]);
///
/// assert_eq!(one.into_iter().chain(many).collect::<Vec<_>>(), vec!["a", "a", "b"] );
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OneOrManyWithParens<T> {
    /// A single `T`, unparenthesized.
    One(T),
    /// One or more `T`s, parenthesized.
    Many(Vec<T>),
}

impl<T> Deref for OneOrManyWithParens<T> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        match self {
            OneOrManyWithParens::One(one) => core::slice::from_ref(one),
            OneOrManyWithParens::Many(many) => many,
        }
    }
}

impl<T> AsRef<[T]> for OneOrManyWithParens<T> {
    fn as_ref(&self) -> &[T] {
        self
    }
}

impl<'a, T> IntoIterator for &'a OneOrManyWithParens<T> {
    type Item = &'a T;
    type IntoIter = core::slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

/// Owned iterator implementation of `OneOrManyWithParens`
#[derive(Debug, Clone)]
pub struct OneOrManyWithParensIntoIter<T> {
    inner: OneOrManyWithParensIntoIterInner<T>,
}

#[derive(Debug, Clone)]
enum OneOrManyWithParensIntoIterInner<T> {
    One(core::iter::Once<T>),
    Many(<Vec<T> as IntoIterator>::IntoIter),
}

impl<T> core::iter::FusedIterator for OneOrManyWithParensIntoIter<T>
where
    core::iter::Once<T>: core::iter::FusedIterator,
    <Vec<T> as IntoIterator>::IntoIter: core::iter::FusedIterator,
{
}

impl<T> core::iter::ExactSizeIterator for OneOrManyWithParensIntoIter<T>
where
    core::iter::Once<T>: core::iter::ExactSizeIterator,
    <Vec<T> as IntoIterator>::IntoIter: core::iter::ExactSizeIterator,
{
}

impl<T> core::iter::Iterator for OneOrManyWithParensIntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        match &mut self.inner {
            OneOrManyWithParensIntoIterInner::One(one) => one.next(),
            OneOrManyWithParensIntoIterInner::Many(many) => many.next(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        match &self.inner {
            OneOrManyWithParensIntoIterInner::One(one) => one.size_hint(),
            OneOrManyWithParensIntoIterInner::Many(many) => many.size_hint(),
        }
    }

    fn count(self) -> usize
    where
        Self: Sized,
    {
        match self.inner {
            OneOrManyWithParensIntoIterInner::One(one) => one.count(),
            OneOrManyWithParensIntoIterInner::Many(many) => many.count(),
        }
    }

    fn fold<B, F>(mut self, init: B, f: F) -> B
    where
        Self: Sized,
        F: FnMut(B, Self::Item) -> B,
    {
        match &mut self.inner {
            OneOrManyWithParensIntoIterInner::One(one) => one.fold(init, f),
            OneOrManyWithParensIntoIterInner::Many(many) => many.fold(init, f),
        }
    }
}

impl<T> core::iter::DoubleEndedIterator for OneOrManyWithParensIntoIter<T> {
    fn next_back(&mut self) -> Option<Self::Item> {
        match &mut self.inner {
            OneOrManyWithParensIntoIterInner::One(one) => one.next_back(),
            OneOrManyWithParensIntoIterInner::Many(many) => many.next_back(),
        }
    }
}

impl<T> IntoIterator for OneOrManyWithParens<T> {
    type Item = T;

    type IntoIter = OneOrManyWithParensIntoIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        let inner = match self {
            OneOrManyWithParens::One(one) => {
                OneOrManyWithParensIntoIterInner::One(core::iter::once(one))
            }
            OneOrManyWithParens::Many(many) => {
                OneOrManyWithParensIntoIterInner::Many(many.into_iter())
            }
        };

        OneOrManyWithParensIntoIter { inner }
    }
}

impl<T> fmt::Display for OneOrManyWithParens<T>
where
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OneOrManyWithParens::One(value) => write!(f, "{value}"),
            OneOrManyWithParens::Many(values) => {
                write!(f, "({})", display_comma_separated(values))
            }
        }
    }
}

impl fmt::Display for CastFormat {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CastFormat::Value(v) => write!(f, "{v}"),
            CastFormat::ValueAtTimeZone(v, tz) => write!(f, "{v} AT TIME ZONE {tz}"),
        }
    }
}

impl fmt::Display for Expr {
    #[cfg_attr(feature = "recursive-protection", recursive::recursive)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Expr::Identifier(s) => write!(f, "{s}"),
            Expr::Wildcard(_) => f.write_str("*"),
            Expr::QualifiedWildcard(prefix, _) => write!(f, "{prefix}.*"),
            Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
            Expr::CompoundFieldAccess { root, access_chain } => {
                write!(f, "{root}")?;
                for field in access_chain {
                    write!(f, "{field}")?;
                }
                Ok(())
            }
            Expr::IsTrue(ast) => write!(f, "{ast} IS TRUE"),
            Expr::IsNotTrue(ast) => write!(f, "{ast} IS NOT TRUE"),
            Expr::IsFalse(ast) => write!(f, "{ast} IS FALSE"),
            Expr::IsNotFalse(ast) => write!(f, "{ast} IS NOT FALSE"),
            Expr::IsNull(ast) => write!(f, "{ast} IS NULL"),
            Expr::IsNotNull(ast) => write!(f, "{ast} IS NOT NULL"),
            Expr::IsUnknown(ast) => write!(f, "{ast} IS UNKNOWN"),
            Expr::IsNotUnknown(ast) => write!(f, "{ast} IS NOT UNKNOWN"),
            Expr::InList {
                expr,
                list,
                negated,
            } => write!(
                f,
                "{} {}IN ({})",
                expr,
                if *negated { "NOT " } else { "" },
                display_comma_separated(list)
            ),
            Expr::InSubquery {
                expr,
                subquery,
                negated,
            } => write!(
                f,
                "{} {}IN ({})",
                expr,
                if *negated { "NOT " } else { "" },
                subquery
            ),
            Expr::InUnnest {
                expr,
                array_expr,
                negated,
            } => write!(
                f,
                "{} {}IN UNNEST({})",
                expr,
                if *negated { "NOT " } else { "" },
                array_expr
            ),
            Expr::Between {
                expr,
                negated,
                low,
                high,
            } => write!(
                f,
                "{} {}BETWEEN {} AND {}",
                expr,
                if *negated { "NOT " } else { "" },
                low,
                high
            ),
            Expr::BinaryOp { left, op, right } => write!(f, "{left} {op} {right}"),
            Expr::Like {
                negated,
                expr,
                pattern,
                escape_char,
                any,
            } => match escape_char {
                Some(ch) => write!(
                    f,
                    "{} {}LIKE {}{} ESCAPE {}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    if *any { "ANY " } else { "" },
                    pattern,
                    ch
                ),
                _ => write!(
                    f,
                    "{} {}LIKE {}{}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    if *any { "ANY " } else { "" },
                    pattern
                ),
            },
            Expr::ILike {
                negated,
                expr,
                pattern,
                escape_char,
                any,
            } => match escape_char {
                Some(ch) => write!(
                    f,
                    "{} {}ILIKE {}{} ESCAPE {}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    if *any { "ANY" } else { "" },
                    pattern,
                    ch
                ),
                _ => write!(
                    f,
                    "{} {}ILIKE {}{}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    if *any { "ANY " } else { "" },
                    pattern
                ),
            },
            Expr::RLike {
                negated,
                expr,
                pattern,
                regexp,
            } => write!(
                f,
                "{} {}{} {}",
                expr,
                if *negated { "NOT " } else { "" },
                if *regexp { "REGEXP" } else { "RLIKE" },
                pattern
            ),
            Expr::IsNormalized {
                expr,
                form,
                negated,
            } => {
                let not_ = if *negated { "NOT " } else { "" };
                if form.is_none() {
                    write!(f, "{expr} IS {not_}NORMALIZED")
                } else {
                    write!(
                        f,
                        "{} IS {}{} NORMALIZED",
                        expr,
                        not_,
                        form.as_ref().unwrap()
                    )
                }
            }
            Expr::SimilarTo {
                negated,
                expr,
                pattern,
                escape_char,
            } => match escape_char {
                Some(ch) => write!(
                    f,
                    "{} {}SIMILAR TO {} ESCAPE {}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    pattern,
                    ch
                ),
                _ => write!(
                    f,
                    "{} {}SIMILAR TO {}",
                    expr,
                    if *negated { "NOT " } else { "" },
                    pattern
                ),
            },
            Expr::AnyOp {
                left,
                compare_op,
                right,
                is_some,
            } => {
                let add_parens = !matches!(right.as_ref(), Expr::Subquery(_));
                write!(
                    f,
                    "{left} {compare_op} {}{}{right}{}",
                    if *is_some { "SOME" } else { "ANY" },
                    if add_parens { "(" } else { "" },
                    if add_parens { ")" } else { "" },
                )
            }
            Expr::AllOp {
                left,
                compare_op,
                right,
            } => {
                let add_parens = !matches!(right.as_ref(), Expr::Subquery(_));
                write!(
                    f,
                    "{left} {compare_op} ALL{}{right}{}",
                    if add_parens { "(" } else { "" },
                    if add_parens { ")" } else { "" },
                )
            }
            Expr::UnaryOp { op, expr } => {
                if op == &UnaryOperator::PGPostfixFactorial {
                    write!(f, "{expr}{op}")
                } else if matches!(
                    op,
                    UnaryOperator::Not
                        | UnaryOperator::Hash
                        | UnaryOperator::AtDashAt
                        | UnaryOperator::DoubleAt
                        | UnaryOperator::QuestionDash
                        | UnaryOperator::QuestionPipe
                ) {
                    write!(f, "{op} {expr}")
                } else {
                    write!(f, "{op}{expr}")
                }
            }
            Expr::Convert {
                is_try,
                expr,
                target_before_value,
                data_type,
                charset,
                styles,
            } => {
                write!(f, "{}CONVERT(", if *is_try { "TRY_" } else { "" })?;
                if let Some(data_type) = data_type {
                    if let Some(charset) = charset {
                        write!(f, "{expr}, {data_type} CHARACTER SET {charset}")
                    } else if *target_before_value {
                        write!(f, "{data_type}, {expr}")
                    } else {
                        write!(f, "{expr}, {data_type}")
                    }
                } else if let Some(charset) = charset {
                    write!(f, "{expr} USING {charset}")
                } else {
                    write!(f, "{expr}") // This should never happen
                }?;
                if !styles.is_empty() {
                    write!(f, ", {}", display_comma_separated(styles))?;
                }
                write!(f, ")")
            }
            Expr::Cast {
                kind,
                expr,
                data_type,
                array,
                format,
            } => match kind {
                CastKind::Cast => {
                    write!(f, "CAST({expr} AS {data_type}")?;
                    if *array {
                        write!(f, " ARRAY")?;
                    }
                    if let Some(format) = format {
                        write!(f, " FORMAT {format}")?;
                    }
                    write!(f, ")")
                }
                CastKind::TryCast => {
                    if let Some(format) = format {
                        write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
                    } else {
                        write!(f, "TRY_CAST({expr} AS {data_type})")
                    }
                }
                CastKind::SafeCast => {
                    if let Some(format) = format {
                        write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
                    } else {
                        write!(f, "SAFE_CAST({expr} AS {data_type})")
                    }
                }
                CastKind::DoubleColon => {
                    write!(f, "{expr}::{data_type}")
                }
            },
            Expr::Extract {
                field,
                syntax,
                expr,
            } => match syntax {
                ExtractSyntax::From => write!(f, "EXTRACT({field} FROM {expr})"),
                ExtractSyntax::Comma => write!(f, "EXTRACT({field}, {expr})"),
            },
            Expr::Ceil { expr, field } => match field {
                CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => {
                    write!(f, "CEIL({expr})")
                }
                CeilFloorKind::DateTimeField(dt_field) => write!(f, "CEIL({expr} TO {dt_field})"),
                CeilFloorKind::Scale(s) => write!(f, "CEIL({expr}, {s})"),
            },
            Expr::Floor { expr, field } => match field {
                CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => {
                    write!(f, "FLOOR({expr})")
                }
                CeilFloorKind::DateTimeField(dt_field) => write!(f, "FLOOR({expr} TO {dt_field})"),
                CeilFloorKind::Scale(s) => write!(f, "FLOOR({expr}, {s})"),
            },
            Expr::Position { expr, r#in } => write!(f, "POSITION({expr} IN {in})"),
            Expr::Collate { expr, collation } => write!(f, "{expr} COLLATE {collation}"),
            Expr::Nested(ast) => write!(f, "({ast})"),
            Expr::Value(v) => write!(f, "{v}"),
            Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"),
            Expr::TypedString(ts) => ts.fmt(f),
            Expr::Function(fun) => fun.fmt(f),
            Expr::Case {
                case_token: _,
                end_token: _,
                operand,
                conditions,
                else_result,
            } => {
                f.write_str("CASE")?;
                if let Some(operand) = operand {
                    f.write_str(" ")?;
                    operand.fmt(f)?;
                }
                for when in conditions {
                    SpaceOrNewline.fmt(f)?;
                    Indent(when).fmt(f)?;
                }
                if let Some(else_result) = else_result {
                    SpaceOrNewline.fmt(f)?;
                    Indent("ELSE").fmt(f)?;
                    SpaceOrNewline.fmt(f)?;
                    Indent(Indent(else_result)).fmt(f)?;
                }
                SpaceOrNewline.fmt(f)?;
                f.write_str("END")
            }
            Expr::Exists { subquery, negated } => write!(
                f,
                "{}EXISTS ({})",
                if *negated { "NOT " } else { "" },
                subquery
            ),
            Expr::Subquery(s) => write!(f, "({s})"),
            Expr::GroupingSets(sets) => {
                write!(f, "GROUPING SETS (")?;
                let mut sep = "";
                for set in sets {
                    write!(f, "{sep}")?;
                    sep = ", ";
                    write!(f, "({})", display_comma_separated(set))?;
                }
                write!(f, ")")
            }
            Expr::Cube(sets) => {
                write!(f, "CUBE (")?;
                let mut sep = "";
                for set in sets {
                    write!(f, "{sep}")?;
                    sep = ", ";
                    if set.len() == 1 {
                        write!(f, "{}", set[0])?;
                    } else {
                        write!(f, "({})", display_comma_separated(set))?;
                    }
                }
                write!(f, ")")
            }
            Expr::Rollup(sets) => {
                write!(f, "ROLLUP (")?;
                let mut sep = "";
                for set in sets {
                    write!(f, "{sep}")?;
                    sep = ", ";
                    if set.len() == 1 {
                        write!(f, "{}", set[0])?;
                    } else {
                        write!(f, "({})", display_comma_separated(set))?;
                    }
                }
                write!(f, ")")
            }
            Expr::Substring {
                expr,
                substring_from,
                substring_for,
                special,
                shorthand,
            } => {
                f.write_str("SUBSTR")?;
                if !*shorthand {
                    f.write_str("ING")?;
                }
                write!(f, "({expr}")?;
                if let Some(from_part) = substring_from {
                    if *special {
                        write!(f, ", {from_part}")?;
                    } else {
                        write!(f, " FROM {from_part}")?;
                    }
                }
                if let Some(for_part) = substring_for {
                    if *special {
                        write!(f, ", {for_part}")?;
                    } else {
                        write!(f, " FOR {for_part}")?;
                    }
                }

                write!(f, ")")
            }
            Expr::Overlay {
                expr,
                overlay_what,
                overlay_from,
                overlay_for,
            } => {
                write!(
                    f,
                    "OVERLAY({expr} PLACING {overlay_what} FROM {overlay_from}"
                )?;
                if let Some(for_part) = overlay_for {
                    write!(f, " FOR {for_part}")?;
                }

                write!(f, ")")
            }
            Expr::IsDistinctFrom(a, b) => write!(f, "{a} IS DISTINCT FROM {b}"),
            Expr::IsNotDistinctFrom(a, b) => write!(f, "{a} IS NOT DISTINCT FROM {b}"),
            Expr::Trim {
                expr,
                trim_where,
                trim_what,
                trim_characters,
            } => {
                write!(f, "TRIM(")?;
                if let Some(ident) = trim_where {
                    write!(f, "{ident} ")?;
                }
                if let Some(trim_char) = trim_what {
                    write!(f, "{trim_char} FROM {expr}")?;
                } else {
                    write!(f, "{expr}")?;
                }
                if let Some(characters) = trim_characters {
                    write!(f, ", {}", display_comma_separated(characters))?;
                }

                write!(f, ")")
            }
            Expr::Tuple(exprs) => {
                write!(f, "({})", display_comma_separated(exprs))
            }
            Expr::Struct { values, fields } => {
                if !fields.is_empty() {
                    write!(
                        f,
                        "STRUCT<{}>({})",
                        display_comma_separated(fields),
                        display_comma_separated(values)
                    )
                } else {
                    write!(f, "STRUCT({})", display_comma_separated(values))
                }
            }
            Expr::Named { expr, name } => {
                write!(f, "{expr} AS {name}")
            }
            Expr::Dictionary(fields) => {
                write!(f, "{{{}}}", display_comma_separated(fields))
            }
            Expr::Map(map) => {
                write!(f, "{map}")
            }
            Expr::Array(set) => {
                write!(f, "{set}")
            }
            Expr::JsonAccess { value, path } => {
                write!(f, "{value}{path}")
            }
            Expr::AtTimeZone {
                timestamp,
                time_zone,
            } => {
                write!(f, "{timestamp} AT TIME ZONE {time_zone}")
            }
            Expr::Interval(interval) => {
                write!(f, "{interval}")
            }
            Expr::MatchAgainst {
                columns,
                match_value: match_expr,
                opt_search_modifier,
            } => {
                write!(f, "MATCH ({}) AGAINST ", display_comma_separated(columns),)?;

                if let Some(search_modifier) = opt_search_modifier {
                    write!(f, "({match_expr} {search_modifier})")?;
                } else {
                    write!(f, "({match_expr})")?;
                }

                Ok(())
            }
            Expr::OuterJoin(expr) => {
                write!(f, "{expr} (+)")
            }
            Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
            Expr::Lambda(lambda) => write!(f, "{lambda}"),
            Expr::MemberOf(member_of) => write!(f, "{member_of}"),
        }
    }
}

/// The type of a window used in `OVER` clauses.
///
/// A window can be either an inline specification (`WindowSpec`) or a
/// reference to a previously defined named window.
///
/// - `WindowSpec(WindowSpec)`: An inline window specification, e.g.
///   `OVER (PARTITION BY ... ORDER BY ...)`.
/// - `NamedWindow(Ident)`: A reference to a named window declared elsewhere.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WindowType {
    /// An inline window specification.
    WindowSpec(WindowSpec),
    /// A reference to a previously defined named window.
    NamedWindow(Ident),
}

impl Display for WindowType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            WindowType::WindowSpec(spec) => {
                f.write_str("(")?;
                NewLine.fmt(f)?;
                Indent(spec).fmt(f)?;
                NewLine.fmt(f)?;
                f.write_str(")")
            }
            WindowType::NamedWindow(name) => name.fmt(f),
        }
    }
}

/// A window specification (i.e. `OVER ([window_name] PARTITION BY .. ORDER BY .. etc.)`)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WindowSpec {
    /// Optional window name.
    ///
    /// You can find it at least in [MySQL][1], [BigQuery][2], [PostgreSQL][3]
    ///
    /// [1]: https://dev.mysql.com/doc/refman/8.0/en/window-functions-named-windows.html
    /// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls
    /// [3]: https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS
    pub window_name: Option<Ident>,
    /// `OVER (PARTITION BY ...)`
    pub partition_by: Vec<Expr>,
    /// `OVER (ORDER BY ...)`
    pub order_by: Vec<OrderByExpr>,
    /// `OVER (window frame)`
    pub window_frame: Option<WindowFrame>,
}

impl fmt::Display for WindowSpec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut is_first = true;
        if let Some(window_name) = &self.window_name {
            if !is_first {
                SpaceOrNewline.fmt(f)?;
            }
            is_first = false;
            write!(f, "{window_name}")?;
        }
        if !self.partition_by.is_empty() {
            if !is_first {
                SpaceOrNewline.fmt(f)?;
            }
            is_first = false;
            write!(
                f,
                "PARTITION BY {}",
                display_comma_separated(&self.partition_by)
            )?;
        }
        if !self.order_by.is_empty() {
            if !is_first {
                SpaceOrNewline.fmt(f)?;
            }
            is_first = false;
            write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?;
        }
        if let Some(window_frame) = &self.window_frame {
            if !is_first {
                SpaceOrNewline.fmt(f)?;
            }
            if let Some(end_bound) = &window_frame.end_bound {
                write!(
                    f,
                    "{} BETWEEN {} AND {}",
                    window_frame.units, window_frame.start_bound, end_bound
                )?;
            } else {
                write!(f, "{} {}", window_frame.units, window_frame.start_bound)?;
            }
        }
        Ok(())
    }
}

/// Specifies the data processed by a window function, e.g.
/// `RANGE UNBOUNDED PRECEDING` or `ROWS BETWEEN 5 PRECEDING AND CURRENT ROW`.
///
/// Note: The parser does not validate the specified bounds; the caller should
/// reject invalid bounds like `ROWS UNBOUNDED FOLLOWING` before execution.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WindowFrame {
    /// Units for the frame (e.g. `ROWS`, `RANGE`, `GROUPS`).
    pub units: WindowFrameUnits,
    /// The start bound of the window frame.
    pub start_bound: WindowFrameBound,
    /// The right bound of the `BETWEEN .. AND` clause. The end bound of `None`
    /// indicates the shorthand form (e.g. `ROWS 1 PRECEDING`), which must
    /// behave the same as `end_bound = WindowFrameBound::CurrentRow`.
    pub end_bound: Option<WindowFrameBound>,
    // TBD: EXCLUDE
}

impl Default for WindowFrame {
    /// Returns default value for window frame
    ///
    /// See [this page](https://www.sqlite.org/windowfunctions.html#frame_specifications) for more details.
    fn default() -> Self {
        Self {
            units: WindowFrameUnits::Range,
            start_bound: WindowFrameBound::Preceding(None),
            end_bound: None,
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Units used to describe the window frame scope.
pub enum WindowFrameUnits {
    /// `ROWS` unit.
    Rows,
    /// `RANGE` unit.
    Range,
    /// `GROUPS` unit.
    Groups,
}

impl fmt::Display for WindowFrameUnits {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            WindowFrameUnits::Rows => "ROWS",
            WindowFrameUnits::Range => "RANGE",
            WindowFrameUnits::Groups => "GROUPS",
        })
    }
}

/// Specifies Ignore / Respect NULL within window functions.
/// For example
/// `FIRST_VALUE(column2) IGNORE NULLS OVER (PARTITION BY column1)`
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How NULL values are treated in certain window functions.
pub enum NullTreatment {
    /// Ignore NULL values (e.g. `IGNORE NULLS`).
    IgnoreNulls,
    /// Respect NULL values (e.g. `RESPECT NULLS`).
    RespectNulls,
}

impl fmt::Display for NullTreatment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            NullTreatment::IgnoreNulls => "IGNORE NULLS",
            NullTreatment::RespectNulls => "RESPECT NULLS",
        })
    }
}

/// Specifies [WindowFrame]'s `start_bound` and `end_bound`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WindowFrameBound {
    /// `CURRENT ROW`
    CurrentRow,
    /// `<N> PRECEDING` or `UNBOUNDED PRECEDING`
    Preceding(Option<Box<Expr>>),
    /// `<N> FOLLOWING` or `UNBOUNDED FOLLOWING`.
    Following(Option<Box<Expr>>),
}

impl fmt::Display for WindowFrameBound {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
            WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
            WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
            WindowFrameBound::Preceding(Some(n)) => write!(f, "{n} PRECEDING"),
            WindowFrameBound::Following(Some(n)) => write!(f, "{n} FOLLOWING"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Indicates partition operation type for partition management statements.
pub enum AddDropSync {
    /// Add partitions.
    ADD,
    /// Drop partitions.
    DROP,
    /// Sync partitions.
    SYNC,
}

impl fmt::Display for AddDropSync {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AddDropSync::SYNC => f.write_str("SYNC PARTITIONS"),
            AddDropSync::DROP => f.write_str("DROP PARTITIONS"),
            AddDropSync::ADD => f.write_str("ADD PARTITIONS"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Object kinds supported by `SHOW CREATE` statements.
pub enum ShowCreateObject {
    /// An event object for `SHOW CREATE EVENT`.
    Event,
    /// A function object for `SHOW CREATE FUNCTION`.
    Function,
    /// A procedure object for `SHOW CREATE PROCEDURE`.
    Procedure,
    /// A table object for `SHOW CREATE TABLE`.
    Table,
    /// A trigger object for `SHOW CREATE TRIGGER`.
    Trigger,
    /// A view object for `SHOW CREATE VIEW`.
    View,
}

impl fmt::Display for ShowCreateObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ShowCreateObject::Event => f.write_str("EVENT"),
            ShowCreateObject::Function => f.write_str("FUNCTION"),
            ShowCreateObject::Procedure => f.write_str("PROCEDURE"),
            ShowCreateObject::Table => f.write_str("TABLE"),
            ShowCreateObject::Trigger => f.write_str("TRIGGER"),
            ShowCreateObject::View => f.write_str("VIEW"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Objects that can be targeted by a `COMMENT` statement.
pub enum CommentObject {
    /// A collation.
    Collation,
    /// A table column.
    Column,
    /// A database.
    Database,
    /// A domain.
    Domain,
    /// An extension.
    Extension,
    /// A function.
    Function,
    /// An index.
    Index,
    /// A materialized view.
    MaterializedView,
    /// A procedure.
    Procedure,
    /// A role.
    Role,
    /// A schema.
    Schema,
    /// A sequence.
    Sequence,
    /// A table.
    Table,
    /// A type.
    Type,
    /// A user.
    User,
    /// A view.
    View,
}

impl fmt::Display for CommentObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CommentObject::Collation => f.write_str("COLLATION"),
            CommentObject::Column => f.write_str("COLUMN"),
            CommentObject::Database => f.write_str("DATABASE"),
            CommentObject::Domain => f.write_str("DOMAIN"),
            CommentObject::Extension => f.write_str("EXTENSION"),
            CommentObject::Function => f.write_str("FUNCTION"),
            CommentObject::Index => f.write_str("INDEX"),
            CommentObject::MaterializedView => f.write_str("MATERIALIZED VIEW"),
            CommentObject::Procedure => f.write_str("PROCEDURE"),
            CommentObject::Role => f.write_str("ROLE"),
            CommentObject::Schema => f.write_str("SCHEMA"),
            CommentObject::Sequence => f.write_str("SEQUENCE"),
            CommentObject::Table => f.write_str("TABLE"),
            CommentObject::Type => f.write_str("TYPE"),
            CommentObject::User => f.write_str("USER"),
            CommentObject::View => f.write_str("VIEW"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Password specification variants used in user-related statements.
pub enum Password {
    /// A concrete password expression.
    Password(Expr),
    /// Represents a `NULL` password.
    NullPassword,
}

/// A `CASE` statement.
///
/// Examples:
/// ```sql
/// CASE
///     WHEN EXISTS(SELECT 1)
///         THEN SELECT 1 FROM T;
///     WHEN EXISTS(SELECT 2)
///         THEN SELECT 1 FROM U;
///     ELSE
///         SELECT 1 FROM V;
/// END CASE;
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#case_search_expression)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/case)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CaseStatement {
    /// The `CASE` token that starts the statement.
    pub case_token: AttachedToken,
    /// Optional expression to match against in `CASE ... WHEN`.
    pub match_expr: Option<Expr>,
    /// The `WHEN ... THEN` blocks of the `CASE` statement.
    pub when_blocks: Vec<ConditionalStatementBlock>,
    /// Optional `ELSE` block for the `CASE` statement.
    pub else_block: Option<ConditionalStatementBlock>,
    /// The last token of the statement (`END` or `CASE`).
    pub end_case_token: AttachedToken,
}

impl fmt::Display for CaseStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let CaseStatement {
            case_token: _,
            match_expr,
            when_blocks,
            else_block,
            end_case_token: AttachedToken(end),
        } = self;

        write!(f, "CASE")?;

        if let Some(expr) = match_expr {
            write!(f, " {expr}")?;
        }

        if !when_blocks.is_empty() {
            write!(f, " {}", display_separated(when_blocks, " "))?;
        }

        if let Some(else_block) = else_block {
            write!(f, " {else_block}")?;
        }

        write!(f, " END")?;

        if let Token::Word(w) = &end.token {
            if w.keyword == Keyword::CASE {
                write!(f, " CASE")?;
            }
        }

        Ok(())
    }
}

/// An `IF` statement.
///
/// Example (BigQuery or Snowflake):
/// ```sql
/// IF TRUE THEN
///     SELECT 1;
///     SELECT 2;
/// ELSEIF TRUE THEN
///     SELECT 3;
/// ELSE
///     SELECT 4;
/// END IF
/// ```
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#if)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/if)
///
/// Example (MSSQL):
/// ```sql
/// IF 1=1 SELECT 1 ELSE SELECT 2
/// ```
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql?view=sql-server-ver16)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct IfStatement {
    /// The initial `IF` block containing the condition and statements.
    pub if_block: ConditionalStatementBlock,
    /// Additional `ELSEIF` blocks.
    pub elseif_blocks: Vec<ConditionalStatementBlock>,
    /// Optional `ELSE` block.
    pub else_block: Option<ConditionalStatementBlock>,
    /// Optional trailing `END` token for the `IF` statement.
    pub end_token: Option<AttachedToken>,
}

impl fmt::Display for IfStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let IfStatement {
            if_block,
            elseif_blocks,
            else_block,
            end_token,
        } = self;

        write!(f, "{if_block}")?;

        for elseif_block in elseif_blocks {
            write!(f, " {elseif_block}")?;
        }

        if let Some(else_block) = else_block {
            write!(f, " {else_block}")?;
        }

        if let Some(AttachedToken(end_token)) = end_token {
            write!(f, " END {end_token}")?;
        }

        Ok(())
    }
}

/// A `WHILE` statement.
///
/// Example:
/// ```sql
/// WHILE @@FETCH_STATUS = 0
/// BEGIN
///    FETCH NEXT FROM c1 INTO @var1, @var2;
/// END
/// ```
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/while-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WhileStatement {
    /// Block executed while the condition holds.
    pub while_block: ConditionalStatementBlock,
}

impl fmt::Display for WhileStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let WhileStatement { while_block } = self;
        write!(f, "{while_block}")?;
        Ok(())
    }
}

/// A block within a [Statement::Case] or [Statement::If] or [Statement::While]-like statement
///
/// Example 1:
/// ```sql
/// WHEN EXISTS(SELECT 1) THEN SELECT 1;
/// ```
///
/// Example 2:
/// ```sql
/// IF TRUE THEN SELECT 1; SELECT 2;
/// ```
///
/// Example 3:
/// ```sql
/// ELSE SELECT 1; SELECT 2;
/// ```
///
/// Example 4:
/// ```sql
/// WHILE @@FETCH_STATUS = 0
/// BEGIN
///    FETCH NEXT FROM c1 INTO @var1, @var2;
/// END
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ConditionalStatementBlock {
    /// Token representing the start of the block (e.g., WHEN/IF/WHILE).
    pub start_token: AttachedToken,
    /// Optional condition expression for the block.
    pub condition: Option<Expr>,
    /// Optional token for the `THEN` keyword.
    pub then_token: Option<AttachedToken>,
    /// The statements contained in this conditional block.
    pub conditional_statements: ConditionalStatements,
}

impl ConditionalStatementBlock {
    /// Get the statements in this conditional block.
    pub fn statements(&self) -> &Vec<Statement> {
        self.conditional_statements.statements()
    }
}

impl fmt::Display for ConditionalStatementBlock {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ConditionalStatementBlock {
            start_token: AttachedToken(start_token),
            condition,
            then_token,
            conditional_statements,
        } = self;

        write!(f, "{start_token}")?;

        if let Some(condition) = condition {
            write!(f, " {condition}")?;
        }

        if then_token.is_some() {
            write!(f, " THEN")?;
        }

        if !conditional_statements.statements().is_empty() {
            write!(f, " {conditional_statements}")?;
        }

        Ok(())
    }
}

/// A list of statements in a [ConditionalStatementBlock].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Statements used inside conditional blocks (`IF`, `WHEN`, `WHILE`).
pub enum ConditionalStatements {
    /// Simple sequence of statements (no `BEGIN`/`END`).
    Sequence {
        /// The statements in the sequence.
        statements: Vec<Statement>,
    },
    /// Block enclosed by `BEGIN` and `END`.
    BeginEnd(BeginEndStatements),
}

impl ConditionalStatements {
    /// Get the statements in this conditional statements block.
    pub fn statements(&self) -> &Vec<Statement> {
        match self {
            ConditionalStatements::Sequence { statements } => statements,
            ConditionalStatements::BeginEnd(bes) => &bes.statements,
        }
    }
}

impl fmt::Display for ConditionalStatements {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConditionalStatements::Sequence { statements } => {
                if !statements.is_empty() {
                    format_statement_list(f, statements)?;
                }
                Ok(())
            }
            ConditionalStatements::BeginEnd(bes) => write!(f, "{bes}"),
        }
    }
}

/// Represents a list of statements enclosed within `BEGIN` and `END` keywords.
/// Example:
/// ```sql
/// BEGIN
///     SELECT 1;
///     SELECT 2;
/// END
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct BeginEndStatements {
    /// Token representing the `BEGIN` keyword (may include span info).
    pub begin_token: AttachedToken,
    /// Statements contained within the block.
    pub statements: Vec<Statement>,
    /// Token representing the `END` keyword (may include span info).
    pub end_token: AttachedToken,
}

impl fmt::Display for BeginEndStatements {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let BeginEndStatements {
            begin_token: AttachedToken(begin_token),
            statements,
            end_token: AttachedToken(end_token),
        } = self;

        if begin_token.token != Token::EOF {
            write!(f, "{begin_token} ")?;
        }
        if !statements.is_empty() {
            format_statement_list(f, statements)?;
        }
        if end_token.token != Token::EOF {
            write!(f, " {end_token}")?;
        }
        Ok(())
    }
}

/// A `RAISE` statement.
///
/// Examples:
/// ```sql
/// RAISE USING MESSAGE = 'error';
///
/// RAISE myerror;
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#raise)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/raise)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RaiseStatement {
    /// Optional value provided to the RAISE statement.
    pub value: Option<RaiseStatementValue>,
}

impl fmt::Display for RaiseStatement {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let RaiseStatement { value } = self;

        write!(f, "RAISE")?;
        if let Some(value) = value {
            write!(f, " {value}")?;
        }

        Ok(())
    }
}

/// Represents the error value of a [RaiseStatement].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum RaiseStatementValue {
    /// `RAISE USING MESSAGE = 'error'`
    UsingMessage(Expr),
    /// `RAISE myerror`
    Expr(Expr),
}

impl fmt::Display for RaiseStatementValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RaiseStatementValue::Expr(expr) => write!(f, "{expr}"),
            RaiseStatementValue::UsingMessage(expr) => write!(f, "USING MESSAGE = {expr}"),
        }
    }
}

/// A MSSQL `THROW` statement.
///
/// ```sql
/// THROW [ error_number, message, state ]
/// ```
///
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ThrowStatement {
    /// Error number expression.
    pub error_number: Option<Box<Expr>>,
    /// Error message expression.
    pub message: Option<Box<Expr>>,
    /// State expression.
    pub state: Option<Box<Expr>>,
}

impl fmt::Display for ThrowStatement {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ThrowStatement {
            error_number,
            message,
            state,
        } = self;

        write!(f, "THROW")?;
        if let (Some(error_number), Some(message), Some(state)) = (error_number, message, state) {
            write!(f, " {error_number}, {message}, {state}")?;
        }
        Ok(())
    }
}

/// Represents an expression assignment within a variable `DECLARE` statement.
///
/// Examples:
/// ```sql
/// DECLARE variable_name := 42
/// DECLARE variable_name DEFAULT 42
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DeclareAssignment {
    /// Plain expression specified.
    Expr(Box<Expr>),

    /// Expression assigned via the `DEFAULT` keyword
    Default(Box<Expr>),

    /// Expression assigned via the `:=` syntax
    ///
    /// Example:
    /// ```sql
    /// DECLARE variable_name := 42;
    /// ```
    DuckAssignment(Box<Expr>),

    /// Expression via the `FOR` keyword
    ///
    /// Example:
    /// ```sql
    /// DECLARE c1 CURSOR FOR res
    /// ```
    For(Box<Expr>),

    /// Expression via the `=` syntax.
    ///
    /// Example:
    /// ```sql
    /// DECLARE @variable AS INT = 100
    /// ```
    MsSqlAssignment(Box<Expr>),
}

impl fmt::Display for DeclareAssignment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeclareAssignment::Expr(expr) => {
                write!(f, "{expr}")
            }
            DeclareAssignment::Default(expr) => {
                write!(f, "DEFAULT {expr}")
            }
            DeclareAssignment::DuckAssignment(expr) => {
                write!(f, ":= {expr}")
            }
            DeclareAssignment::MsSqlAssignment(expr) => {
                write!(f, "= {expr}")
            }
            DeclareAssignment::For(expr) => {
                write!(f, "FOR {expr}")
            }
        }
    }
}

/// Represents the type of a `DECLARE` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DeclareType {
    /// Cursor variable type. e.g. [Snowflake] [PostgreSQL] [MsSql]
    ///
    /// [Snowflake]: https://docs.snowflake.com/en/developer-guide/snowflake-scripting/cursors#declaring-a-cursor
    /// [PostgreSQL]: https://www.postgresql.org/docs/current/plpgsql-cursors.html
    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-cursor-transact-sql
    Cursor,

    /// Result set variable type. [Snowflake]
    ///
    /// Syntax:
    /// ```text
    /// <resultset_name> RESULTSET [ { DEFAULT | := } ( <query> ) ] ;
    /// ```
    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare#resultset-declaration-syntax
    ResultSet,

    /// Exception declaration syntax. [Snowflake]
    ///
    /// Syntax:
    /// ```text
    /// <exception_name> EXCEPTION [ ( <exception_number> , '<exception_message>' ) ] ;
    /// ```
    /// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare#exception-declaration-syntax
    Exception,
}

impl fmt::Display for DeclareType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeclareType::Cursor => {
                write!(f, "CURSOR")
            }
            DeclareType::ResultSet => {
                write!(f, "RESULTSET")
            }
            DeclareType::Exception => {
                write!(f, "EXCEPTION")
            }
        }
    }
}

/// A `DECLARE` statement.
/// [PostgreSQL] [Snowflake] [BigQuery]
///
/// Examples:
/// ```sql
/// DECLARE variable_name := 42
/// DECLARE liahona CURSOR FOR SELECT * FROM films;
/// ```
///
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-declare.html
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#declare
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Declare {
    /// The name(s) being declared.
    /// Example: `DECLARE a, b, c DEFAULT 42;
    pub names: Vec<Ident>,
    /// Data-type assigned to the declared variable.
    /// Example: `DECLARE x INT64 DEFAULT 42;
    pub data_type: Option<DataType>,
    /// Expression being assigned to the declared variable.
    pub assignment: Option<DeclareAssignment>,
    /// Represents the type of the declared variable.
    pub declare_type: Option<DeclareType>,
    /// Causes the cursor to return data in binary rather than in text format.
    pub binary: Option<bool>,
    /// None = Not specified
    /// Some(true) = INSENSITIVE
    /// Some(false) = ASENSITIVE
    pub sensitive: Option<bool>,
    /// None = Not specified
    /// Some(true) = SCROLL
    /// Some(false) = NO SCROLL
    pub scroll: Option<bool>,
    /// None = Not specified
    /// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
    /// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
    pub hold: Option<bool>,
    /// `FOR <query>` clause in a CURSOR declaration.
    pub for_query: Option<Box<Query>>,
}

impl fmt::Display for Declare {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Declare {
            names,
            data_type,
            assignment,
            declare_type,
            binary,
            sensitive,
            scroll,
            hold,
            for_query,
        } = self;
        write!(f, "{}", display_comma_separated(names))?;

        if let Some(true) = binary {
            write!(f, " BINARY")?;
        }

        if let Some(sensitive) = sensitive {
            if *sensitive {
                write!(f, " INSENSITIVE")?;
            } else {
                write!(f, " ASENSITIVE")?;
            }
        }

        if let Some(scroll) = scroll {
            if *scroll {
                write!(f, " SCROLL")?;
            } else {
                write!(f, " NO SCROLL")?;
            }
        }

        if let Some(declare_type) = declare_type {
            write!(f, " {declare_type}")?;
        }

        if let Some(hold) = hold {
            if *hold {
                write!(f, " WITH HOLD")?;
            } else {
                write!(f, " WITHOUT HOLD")?;
            }
        }

        if let Some(query) = for_query {
            write!(f, " FOR {query}")?;
        }

        if let Some(data_type) = data_type {
            write!(f, " {data_type}")?;
        }

        if let Some(expr) = assignment {
            write!(f, " {expr}")?;
        }
        Ok(())
    }
}

/// Sql options of a `CREATE TABLE` statement.
#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options allowed within a `CREATE TABLE` statement.
pub enum CreateTableOptions {
    /// No options specified.
    #[default]
    None,
    /// Options specified using the `WITH` keyword, e.g. `WITH (k = v)`.
    With(Vec<SqlOption>),
    /// Options specified using the `OPTIONS(...)` clause.
    Options(Vec<SqlOption>),
    /// Plain space-separated options.
    Plain(Vec<SqlOption>),
    /// Table properties (e.g., TBLPROPERTIES / storage properties).
    TableProperties(Vec<SqlOption>),
}

impl fmt::Display for CreateTableOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateTableOptions::With(with_options) => {
                write!(f, "WITH ({})", display_comma_separated(with_options))
            }
            CreateTableOptions::Options(options) => {
                write!(f, "OPTIONS({})", display_comma_separated(options))
            }
            CreateTableOptions::TableProperties(options) => {
                write!(f, "TBLPROPERTIES ({})", display_comma_separated(options))
            }
            CreateTableOptions::Plain(options) => {
                write!(f, "{}", display_separated(options, " "))
            }
            CreateTableOptions::None => Ok(()),
        }
    }
}

/// A `FROM` clause within a `DELETE` statement.
///
/// Syntax
/// ```sql
/// [FROM] table
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FromTable {
    /// An explicit `FROM` keyword was specified.
    WithFromKeyword(Vec<TableWithJoins>),
    /// BigQuery: `FROM` keyword was omitted.
    /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement>
    WithoutKeyword(Vec<TableWithJoins>),
}
impl Display for FromTable {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FromTable::WithFromKeyword(tables) => {
                write!(f, "FROM {}", display_comma_separated(tables))
            }
            FromTable::WithoutKeyword(tables) => {
                write!(f, "{}", display_comma_separated(tables))
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Variants for the `SET` family of statements.
pub enum Set {
    /// SQL Standard-style
    /// SET a = 1;
    /// `SET var = value` (standard SQL-style assignment).
    SingleAssignment {
        /// Optional scope modifier (`SESSION` / `LOCAL`).
        scope: Option<ContextModifier>,
        /// Whether this is a Hive-style `HIVEVAR:` assignment.
        hivevar: bool,
        /// Variable name to assign.
        variable: ObjectName,
        /// Values assigned to the variable.
        values: Vec<Expr>,
    },
    /// Snowflake-style
    /// SET (a, b, ..) = (1, 2, ..);
    /// `SET (a, b) = (1, 2)` (tuple assignment syntax).
    ParenthesizedAssignments {
        /// Variables being assigned in tuple form.
        variables: Vec<ObjectName>,
        /// Corresponding values for the variables.
        values: Vec<Expr>,
    },
    /// MySQL-style
    /// SET a = 1, b = 2, ..;
    /// `SET a = 1, b = 2` (MySQL-style comma-separated assignments).
    MultipleAssignments {
        /// List of `SET` assignments (MySQL-style comma-separated).
        assignments: Vec<SetAssignment>,
    },
    /// Session authorization for Postgres/Redshift
    ///
    /// ```sql
    /// SET SESSION AUTHORIZATION { user_name | DEFAULT }
    /// ```
    ///
    /// See <https://www.postgresql.org/docs/current/sql-set-session-authorization.html>
    /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_SET_SESSION_AUTHORIZATION.html>
    SetSessionAuthorization(SetSessionAuthorizationParam),
    /// MS-SQL session
    ///
    /// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
    SetSessionParam(SetSessionParamKind),
    /// ```sql
    /// SET [ SESSION | LOCAL ] ROLE role_name
    /// ```
    ///
    /// Sets session state. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4]
    ///
    /// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
    /// [2]: https://www.postgresql.org/docs/14/sql-set-role.html
    /// [3]: https://dev.mysql.com/doc/refman/8.0/en/set-role.html
    /// [4]: https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10004.htm
    SetRole {
        /// Non-ANSI optional identifier to inform if the role is defined inside the current session (`SESSION`) or transaction (`LOCAL`).
        context_modifier: Option<ContextModifier>,
        /// Role name. If NONE is specified, then the current role name is removed.
        role_name: Option<Ident>,
    },
    /// ```sql
    /// SET TIME ZONE <value>
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statements
    /// `SET TIME ZONE <value>` is an alias for `SET timezone TO <value>` in PostgreSQL
    /// However, we allow it for all dialects.
    /// `SET TIME ZONE` statement. `local` indicates the `LOCAL` keyword.
    /// `SET TIME ZONE <value>` statement.
    SetTimeZone {
        /// Whether the `LOCAL` keyword was specified.
        local: bool,
        /// Time zone expression value.
        value: Expr,
    },
    /// ```sql
    /// SET NAMES 'charset_name' [COLLATE 'collation_name']
    /// ```
    SetNames {
        /// Character set name to set.
        charset_name: Ident,
        /// Optional collation name.
        collation_name: Option<String>,
    },
    /// ```sql
    /// SET NAMES DEFAULT
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    SetNamesDefault {},
    /// ```sql
    /// SET TRANSACTION ...
    /// ```
    SetTransaction {
        /// Transaction modes (e.g., ISOLATION LEVEL, READ ONLY).
        modes: Vec<TransactionMode>,
        /// Optional snapshot value for transaction snapshot control.
        snapshot: Option<ValueWithSpan>,
        /// `true` when the `SESSION` keyword was used.
        session: bool,
    },
}

impl Display for Set {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::ParenthesizedAssignments { variables, values } => write!(
                f,
                "SET ({}) = ({})",
                display_comma_separated(variables),
                display_comma_separated(values)
            ),
            Self::MultipleAssignments { assignments } => {
                write!(f, "SET {}", display_comma_separated(assignments))
            }
            Self::SetRole {
                context_modifier,
                role_name,
            } => {
                let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE"));
                write!(
                    f,
                    "SET {modifier}ROLE {role_name}",
                    modifier = context_modifier.map(|m| format!("{m}")).unwrap_or_default()
                )
            }
            Self::SetSessionAuthorization(kind) => write!(f, "SET SESSION AUTHORIZATION {kind}"),
            Self::SetSessionParam(kind) => write!(f, "SET {kind}"),
            Self::SetTransaction {
                modes,
                snapshot,
                session,
            } => {
                if *session {
                    write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?;
                } else {
                    write!(f, "SET TRANSACTION")?;
                }
                if !modes.is_empty() {
                    write!(f, " {}", display_comma_separated(modes))?;
                }
                if let Some(snapshot_id) = snapshot {
                    write!(f, " SNAPSHOT {snapshot_id}")?;
                }
                Ok(())
            }
            Self::SetTimeZone { local, value } => {
                f.write_str("SET ")?;
                if *local {
                    f.write_str("LOCAL ")?;
                }
                write!(f, "TIME ZONE {value}")
            }
            Self::SetNames {
                charset_name,
                collation_name,
            } => {
                write!(f, "SET NAMES {charset_name}")?;

                if let Some(collation) = collation_name {
                    f.write_str(" COLLATE ")?;
                    f.write_str(collation)?;
                };

                Ok(())
            }
            Self::SetNamesDefault {} => {
                f.write_str("SET NAMES DEFAULT")?;

                Ok(())
            }
            Set::SingleAssignment {
                scope,
                hivevar,
                variable,
                values,
            } => {
                write!(
                    f,
                    "SET {}{}{} = {}",
                    scope.map(|s| format!("{s}")).unwrap_or_default(),
                    if *hivevar { "HIVEVAR:" } else { "" },
                    variable,
                    display_comma_separated(values)
                )
            }
        }
    }
}

/// A representation of a `WHEN` arm with all the identifiers catched and the statements to execute
/// for the arm.
///
/// Snowflake: <https://docs.snowflake.com/en/sql-reference/snowflake-scripting/exception>
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#beginexceptionend>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ExceptionWhen {
    /// Identifiers that trigger this branch (error conditions).
    pub idents: Vec<Ident>,
    /// Statements to execute when the condition matches.
    pub statements: Vec<Statement>,
}

impl Display for ExceptionWhen {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "WHEN {idents} THEN",
            idents = display_separated(&self.idents, " OR ")
        )?;

        if !self.statements.is_empty() {
            write!(f, " ")?;
            format_statement_list(f, &self.statements)?;
        }

        Ok(())
    }
}

/// ANALYZE statement
///
/// Supported syntax varies by dialect:
/// - Hive: `ANALYZE TABLE t [PARTITION (...)] COMPUTE STATISTICS [NOSCAN] [FOR COLUMNS [col1, ...]] [CACHE METADATA]`
/// - PostgreSQL: `ANALYZE [VERBOSE] [t [(col1, ...)]]` See <https://www.postgresql.org/docs/current/sql-analyze.html>
/// - General: `ANALYZE [TABLE] t`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Analyze {
    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
    /// Name of the table to analyze. `None` for bare `ANALYZE`.
    pub table_name: Option<ObjectName>,
    /// Optional partition expressions to restrict the analysis.
    pub partitions: Option<Vec<Expr>>,
    /// `true` when analyzing specific columns (Hive `FOR COLUMNS` syntax).
    pub for_columns: bool,
    /// Columns to analyze.
    pub columns: Vec<Ident>,
    /// Whether to cache metadata before analyzing.
    pub cache_metadata: bool,
    /// Whether to skip scanning the table.
    pub noscan: bool,
    /// Whether to compute statistics during analysis.
    pub compute_statistics: bool,
    /// Whether the `TABLE` keyword was present.
    pub has_table_keyword: bool,
}

impl fmt::Display for Analyze {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ANALYZE")?;
        if let Some(ref table_name) = self.table_name {
            if self.has_table_keyword {
                write!(f, " TABLE")?;
            }
            write!(f, " {table_name}")?;
        }
        if !self.for_columns && !self.columns.is_empty() {
            write!(f, " ({})", display_comma_separated(&self.columns))?;
        }
        if let Some(ref parts) = self.partitions {
            if !parts.is_empty() {
                write!(f, " PARTITION ({})", display_comma_separated(parts))?;
            }
        }
        if self.compute_statistics {
            write!(f, " COMPUTE STATISTICS")?;
        }
        if self.noscan {
            write!(f, " NOSCAN")?;
        }
        if self.cache_metadata {
            write!(f, " CACHE METADATA")?;
        }
        if self.for_columns {
            write!(f, " FOR COLUMNS")?;
            if !self.columns.is_empty() {
                write!(f, " {}", display_comma_separated(&self.columns))?;
            }
        }
        Ok(())
    }
}

/// A top-level statement (SELECT, INSERT, CREATE, etc.)
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "visitor",
    derive(Visit, VisitMut),
    visit(with = "visit_statement")
)]
pub enum Statement {
    /// ```sql
    /// ANALYZE
    /// ```
    /// Analyze (Hive)
    Analyze(Analyze),
    /// `SET` statements (session, transaction, timezone, etc.).
    Set(Set),
    /// ```sql
    /// TRUNCATE
    /// ```
    /// Truncate (Hive)
    Truncate(Truncate),
    /// ```sql
    /// MSCK
    /// ```
    /// Msck (Hive)
    Msck(Msck),
    /// ```sql
    /// SELECT
    /// ```
    Query(Box<Query>),
    /// ```sql
    /// INSERT
    /// ```
    Insert(Insert),
    /// ```sql
    /// INSTALL
    /// ```
    Install {
        /// Only for DuckDB
        extension_name: Ident,
    },
    /// ```sql
    /// LOAD
    /// ```
    Load {
        /// Only for DuckDB
        extension_name: Ident,
    },
    // TODO: Support ROW FORMAT
    /// LOAD DATA from a directory or query source.
    Directory {
        /// Whether to overwrite existing files.
        overwrite: bool,
        /// Whether the directory is local to the server.
        local: bool,
        /// Path to the directory or files.
        path: String,
        /// Optional file format for the data.
        file_format: Option<FileFormat>,
        /// Source query providing data to load.
        source: Box<Query>,
    },
    /// A `CASE` statement.
    Case(CaseStatement),
    /// An `IF` statement.
    If(IfStatement),
    /// A `WHILE` statement.
    While(WhileStatement),
    /// A `RAISE` statement.
    Raise(RaiseStatement),
    /// ```sql
    /// CALL <function>
    /// ```
    Call(Function),
    /// ```sql
    /// COPY [TO | FROM] ...
    /// ```
    Copy {
        /// The source of 'COPY TO', or the target of 'COPY FROM'
        source: CopySource,
        /// If true, is a 'COPY TO' statement. If false is a 'COPY FROM'
        to: bool,
        /// The target of 'COPY TO', or the source of 'COPY FROM'
        target: CopyTarget,
        /// WITH options (from PostgreSQL version 9.0)
        options: Vec<CopyOption>,
        /// WITH options (before PostgreSQL version 9.0)
        legacy_options: Vec<CopyLegacyOption>,
        /// VALUES a vector of values to be copied
        values: Vec<Option<String>>,
    },
    /// ```sql
    /// COPY INTO <table> | <location>
    /// ```
    /// See:
    /// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
    /// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
    ///
    /// Copy Into syntax available for Snowflake is different than the one implemented in
    /// Postgres. Although they share common prefix, it is reasonable to implement them
    /// in different enums. This can be refactored later once custom dialects
    /// are allowed to have custom Statements.
    CopyIntoSnowflake {
        /// Kind of COPY INTO operation (table or location).
        kind: CopyIntoSnowflakeKind,
        /// Target object for the COPY INTO operation.
        into: ObjectName,
        /// Optional list of target columns.
        into_columns: Option<Vec<Ident>>,
        /// Optional source object name (staged data).
        from_obj: Option<ObjectName>,
        /// Optional alias for the source object.
        from_obj_alias: Option<Ident>,
        /// Stage-specific parameters (e.g., credentials, path).
        stage_params: StageParamsObject,
        /// Optional list of transformations applied when loading.
        from_transformations: Option<Vec<StageLoadSelectItemKind>>,
        /// Optional source query instead of a staged object.
        from_query: Option<Box<Query>>,
        /// Optional list of specific file names to load.
        files: Option<Vec<String>>,
        /// Optional filename matching pattern.
        pattern: Option<String>,
        /// File format options.
        file_format: KeyValueOptions,
        /// Additional copy options.
        copy_options: KeyValueOptions,
        /// Optional validation mode string.
        validation_mode: Option<String>,
        /// Optional partition expression for loading.
        partition: Option<Box<Expr>>,
    },
    /// ```sql
    /// OPEN cursor_name
    /// ```
    /// Opens a cursor.
    Open(OpenStatement),
    /// ```sql
    /// CLOSE
    /// ```
    /// Closes the portal underlying an open cursor.
    Close {
        /// Cursor name
        cursor: CloseCursor,
    },
    /// ```sql
    /// UPDATE
    /// ```
    Update(Update),
    /// ```sql
    /// DELETE
    /// ```
    Delete(Delete),
    /// ```sql
    /// CREATE VIEW
    /// ```
    CreateView(CreateView),
    /// ```sql
    /// CREATE TABLE
    /// ```
    CreateTable(CreateTable),
    /// ```sql
    /// CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
    /// ```
    /// Sqlite specific statement
    CreateVirtualTable {
        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
        /// Name of the virtual table module instance.
        name: ObjectName,
        /// `true` when `IF NOT EXISTS` was specified.
        if_not_exists: bool,
        /// Module name used by the virtual table.
        module_name: Ident,
        /// Arguments passed to the module.
        module_args: Vec<Ident>,
    },
    /// ```sql
    /// `CREATE INDEX`
    /// ```
    CreateIndex(CreateIndex),
    /// ```sql
    /// CREATE ROLE
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.html)
    CreateRole(CreateRole),
    /// ```sql
    /// CREATE SECRET
    /// ```
    /// See [DuckDB](https://duckdb.org/docs/sql/statements/create_secret.html)
    CreateSecret {
        /// `true` when `OR REPLACE` was specified.
        or_replace: bool,
        /// Optional `TEMPORARY` flag.
        temporary: Option<bool>,
        /// `true` when `IF NOT EXISTS` was present.
        if_not_exists: bool,
        /// Optional secret name.
        name: Option<Ident>,
        /// Optional storage specifier identifier.
        storage_specifier: Option<Ident>,
        /// The secret type identifier.
        secret_type: Ident,
        /// Additional secret options.
        options: Vec<SecretOption>,
    },
    /// A `CREATE SERVER` statement.
    CreateServer(CreateServerStatement),
    /// ```sql
    /// CREATE POLICY
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
    CreatePolicy(CreatePolicy),
    /// ```sql
    /// CREATE CONNECTOR
    /// ```
    /// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector)
    CreateConnector(CreateConnector),
    /// ```sql
    /// CREATE OPERATOR
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperator.html)
    CreateOperator(CreateOperator),
    /// ```sql
    /// CREATE OPERATOR FAMILY
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopfamily.html)
    CreateOperatorFamily(CreateOperatorFamily),
    /// ```sql
    /// CREATE OPERATOR CLASS
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopclass.html)
    CreateOperatorClass(CreateOperatorClass),
    /// ```sql
    /// ALTER TABLE
    /// ```
    AlterTable(AlterTable),
    /// ```sql
    /// ALTER SCHEMA
    /// ```
    /// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#alter_schema_collate_statement)
    AlterSchema(AlterSchema),
    /// ```sql
    /// ALTER INDEX
    /// ```
    AlterIndex {
        /// Name of the index to alter.
        name: ObjectName,
        /// The operation to perform on the index.
        operation: AlterIndexOperation,
    },
    /// ```sql
    /// ALTER VIEW
    /// ```
    AlterView {
        /// View name being altered.
        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
        name: ObjectName,
        /// Optional new column list for the view.
        columns: Vec<Ident>,
        /// Replacement query for the view definition.
        query: Box<Query>,
        /// Additional WITH options for the view.
        with_options: Vec<SqlOption>,
    },
    /// ```sql
    /// ALTER FUNCTION
    /// ALTER AGGREGATE
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alterfunction.html)
    /// and [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteraggregate.html)
    AlterFunction(AlterFunction),
    /// ```sql
    /// ALTER TYPE
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertype.html)
    /// ```
    AlterType(AlterType),
    /// ```sql
    /// ALTER COLLATION
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altercollation.html)
    AlterCollation(AlterCollation),
    /// ```sql
    /// ALTER OPERATOR
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteroperator.html)
    AlterOperator(AlterOperator),
    /// ```sql
    /// ALTER OPERATOR FAMILY
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropfamily.html)
    AlterOperatorFamily(AlterOperatorFamily),
    /// ```sql
    /// ALTER OPERATOR CLASS
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropclass.html)
    AlterOperatorClass(AlterOperatorClass),
    /// ```sql
    /// ALTER ROLE
    /// ```
    AlterRole {
        /// Role name being altered.
        name: Ident,
        /// Operation to perform on the role.
        operation: AlterRoleOperation,
    },
    /// ```sql
    /// ALTER POLICY <NAME> ON <TABLE NAME> [<OPERATION>]
    /// ```
    /// (Postgresql-specific)
    AlterPolicy(AlterPolicy),
    /// ```sql
    /// ALTER CONNECTOR connector_name SET DCPROPERTIES(property_name=property_value, ...);
    /// or
    /// ALTER CONNECTOR connector_name SET URL new_url;
    /// or
    /// ALTER CONNECTOR connector_name SET OWNER [USER|ROLE] user_or_role;
    /// ```
    /// (Hive-specific)
    AlterConnector {
        /// Name of the connector to alter.
        name: Ident,
        /// Optional connector properties to set.
        properties: Option<Vec<SqlOption>>,
        /// Optional new URL for the connector.
        url: Option<String>,
        /// Optional new owner specification.
        owner: Option<ddl::AlterConnectorOwner>,
    },
    /// ```sql
    /// ALTER SESSION SET sessionParam
    /// ALTER SESSION UNSET <param_name> [ , <param_name> , ... ]
    /// ```
    /// See <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
    AlterSession {
        /// true is to set for the session parameters, false is to unset
        set: bool,
        /// The session parameters to set or unset
        session_params: KeyValueOptions,
    },
    /// ```sql
    /// ATTACH DATABASE 'path/to/file' AS alias
    /// ```
    /// (SQLite-specific)
    AttachDatabase {
        /// The name to bind to the newly attached database
        schema_name: Ident,
        /// An expression that indicates the path to the database file
        database_file_name: Expr,
        /// true if the syntax is 'ATTACH DATABASE', false if it's just 'ATTACH'
        database: bool,
    },
    /// (DuckDB-specific)
    /// ```sql
    /// ATTACH 'sqlite_file.db' AS sqlite_db (READ_ONLY, TYPE SQLITE);
    /// ```
    /// See <https://duckdb.org/docs/sql/statements/attach.html>
    AttachDuckDBDatabase {
        /// `true` when `IF NOT EXISTS` was present.
        if_not_exists: bool,
        /// `true` if the syntax used `ATTACH DATABASE` rather than `ATTACH`.
        database: bool,
        /// The path identifier to the database file being attached.
        database_path: Ident,
        /// Optional alias assigned to the attached database.
        database_alias: Option<Ident>,
        /// Dialect-specific attach options (e.g., `READ_ONLY`).
        attach_options: Vec<AttachDuckDBDatabaseOption>,
    },
    /// (DuckDB-specific)
    /// ```sql
    /// DETACH db_alias;
    /// ```
    /// See <https://duckdb.org/docs/sql/statements/attach.html>
    DetachDuckDBDatabase {
        /// `true` when `IF EXISTS` was present.
        if_exists: bool,
        /// `true` if the syntax used `DETACH DATABASE` rather than `DETACH`.
        database: bool,
        /// Alias of the database to detach.
        database_alias: Ident,
    },
    /// ```sql
    /// DROP [TABLE, VIEW, ...]
    /// ```
    Drop {
        /// The type of the object to drop: TABLE, VIEW, etc.
        object_type: ObjectType,
        /// An optional `IF EXISTS` clause. (Non-standard.)
        if_exists: bool,
        /// One or more objects to drop. (ANSI SQL requires exactly one.)
        names: Vec<ObjectName>,
        /// Whether `CASCADE` was specified. This will be `false` when
        /// `RESTRICT` or no drop behavior at all was specified.
        cascade: bool,
        /// Whether `RESTRICT` was specified. This will be `false` when
        /// `CASCADE` or no drop behavior at all was specified.
        restrict: bool,
        /// Hive allows you specify whether the table's stored data will be
        /// deleted along with the dropped table
        purge: bool,
        /// MySQL-specific "TEMPORARY" keyword
        temporary: bool,
        /// MySQL-specific drop index syntax, which requires table specification
        /// See <https://dev.mysql.com/doc/refman/8.4/en/drop-index.html>
        table: Option<ObjectName>,
    },
    /// ```sql
    /// DROP FUNCTION
    /// ```
    DropFunction(DropFunction),
    /// ```sql
    /// DROP DOMAIN
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-dropdomain.html)
    ///
    /// DROP DOMAIN [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
    ///
    DropDomain(DropDomain),
    /// ```sql
    /// DROP PROCEDURE
    /// ```
    DropProcedure {
        /// `true` when `IF EXISTS` was present.
        if_exists: bool,
        /// One or more functions/procedures to drop.
        proc_desc: Vec<FunctionDesc>,
        /// Optional drop behavior (`CASCADE` or `RESTRICT`).
        drop_behavior: Option<DropBehavior>,
    },
    /// ```sql
    /// DROP SECRET
    /// ```
    DropSecret {
        /// `true` when `IF EXISTS` was present.
        if_exists: bool,
        /// Optional `TEMPORARY` marker.
        temporary: Option<bool>,
        /// Name of the secret to drop.
        name: Ident,
        /// Optional storage specifier identifier.
        storage_specifier: Option<Ident>,
    },
    ///```sql
    /// DROP POLICY
    /// ```
    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droppolicy.html)
    DropPolicy(DropPolicy),
    /// ```sql
    /// DROP CONNECTOR
    /// ```
    /// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-DropConnector)
    DropConnector {
        /// `true` when `IF EXISTS` was present.
        if_exists: bool,
        /// Name of the connector to drop.
        name: Ident,
    },
    /// ```sql
    /// DECLARE
    /// ```
    /// Declare Cursor Variables
    ///
    /// Note: this is a PostgreSQL-specific statement,
    /// but may also compatible with other SQL.
    Declare {
        /// Cursor declaration statements collected by `DECLARE`.
        stmts: Vec<Declare>,
    },
    /// ```sql
    /// CREATE EXTENSION [ IF NOT EXISTS ] extension_name
    ///     [ WITH ] [ SCHEMA schema_name ]
    ///              [ VERSION version ]
    ///              [ CASCADE ]
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement,
    CreateExtension(CreateExtension),
    /// ```sql
    /// CREATE COLLATION
    /// ```
    /// Note: this is a PostgreSQL-specific statement.
    /// <https://www.postgresql.org/docs/current/sql-createcollation.html>
    CreateCollation(CreateCollation),
    /// ```sql
    /// DROP EXTENSION [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
    /// ```
    /// Note: this is a PostgreSQL-specific statement.
    /// <https://www.postgresql.org/docs/current/sql-dropextension.html>
    DropExtension(DropExtension),
    /// ```sql
    /// DROP OPERATOR [ IF EXISTS ] name ( { left_type | NONE } , right_type ) [, ...] [ CASCADE | RESTRICT ]
    /// ```
    /// Note: this is a PostgreSQL-specific statement.
    /// <https://www.postgresql.org/docs/current/sql-dropoperator.html>
    DropOperator(DropOperator),
    /// ```sql
    /// DROP OPERATOR FAMILY [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ]
    /// ```
    /// Note: this is a PostgreSQL-specific statement.
    /// <https://www.postgresql.org/docs/current/sql-dropopfamily.html>
    DropOperatorFamily(DropOperatorFamily),
    /// ```sql
    /// DROP OPERATOR CLASS [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ]
    /// ```
    /// Note: this is a PostgreSQL-specific statement.
    /// <https://www.postgresql.org/docs/current/sql-dropopclass.html>
    DropOperatorClass(DropOperatorClass),
    /// ```sql
    /// FETCH
    /// ```
    /// Retrieve rows from a query using a cursor
    ///
    /// Note: this is a PostgreSQL-specific statement,
    /// but may also compatible with other SQL.
    Fetch {
        /// Cursor name
        name: Ident,
        /// The fetch direction (e.g., `FORWARD`, `BACKWARD`).
        direction: FetchDirection,
        /// The fetch position (e.g., `ALL`, `NEXT`, `ABSOLUTE`).
        position: FetchPosition,
        /// Optional target table to fetch rows into.
        into: Option<ObjectName>,
    },
    /// ```sql
    /// FLUSH [NO_WRITE_TO_BINLOG | LOCAL] flush_option [, flush_option] ... | tables_option
    /// ```
    ///
    /// Note: this is a Mysql-specific statement,
    /// but may also compatible with other SQL.
    Flush {
        /// The specific flush option or object to flush.
        object_type: FlushType,
        /// Optional flush location (dialect-specific).
        location: Option<FlushLocation>,
        /// Optional channel name used for flush operations.
        channel: Option<String>,
        /// Whether a read lock was requested.
        read_lock: bool,
        /// Whether this is an export flush operation.
        export: bool,
        /// Optional list of tables involved in the flush.
        tables: Vec<ObjectName>,
    },
    /// ```sql
    /// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement,
    /// but may also compatible with other SQL.
    Discard {
        /// The kind of object(s) to discard (ALL, PLANS, etc.).
        object_type: DiscardObject,
    },
    /// `SHOW FUNCTIONS`
    ///
    /// Note: this is a Presto-specific statement.
    ShowFunctions {
        /// Optional filter for which functions to display.
        filter: Option<ShowStatementFilter>,
    },
    /// ```sql
    /// SHOW <variable>
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement.
    ShowVariable {
        /// Variable name as one or more identifiers.
        variable: Vec<Ident>,
    },
    /// ```sql
    /// SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern' | WHERE expr]
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    ShowStatus {
        /// Optional filter for which status entries to display.
        filter: Option<ShowStatementFilter>,
        /// `true` when `GLOBAL` scope was requested.
        global: bool,
        /// `true` when `SESSION` scope was requested.
        session: bool,
    },
    /// ```sql
    /// SHOW VARIABLES
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    ShowVariables {
        /// Optional filter for which variables to display.
        filter: Option<ShowStatementFilter>,
        /// `true` when `GLOBAL` scope was requested.
        global: bool,
        /// `true` when `SESSION` scope was requested.
        session: bool,
    },
    /// ```sql
    /// SHOW CREATE TABLE
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    ShowCreate {
        /// The kind of object being shown (TABLE, VIEW, etc.).
        obj_type: ShowCreateObject,
        /// The name of the object to show create statement for.
        obj_name: ObjectName,
    },
    /// ```sql
    /// SHOW COLUMNS
    /// ```
    ShowColumns {
        /// `true` when extended column information was requested.
        extended: bool,
        /// `true` when full column details were requested.
        full: bool,
        /// Additional options for `SHOW COLUMNS`.
        show_options: ShowStatementOptions,
    },
    /// ```sql
    /// SHOW CATALOGS
    /// ```
    ShowCatalogs {
        /// `true` when terse output format was requested.
        terse: bool,
        /// `true` when history information was requested.
        history: bool,
        /// Additional options for `SHOW CATALOGS`.
        show_options: ShowStatementOptions,
    },
    /// ```sql
    /// SHOW DATABASES
    /// ```
    ShowDatabases {
        /// `true` when terse output format was requested.
        terse: bool,
        /// `true` when history information was requested.
        history: bool,
        /// Additional options for `SHOW DATABASES`.
        show_options: ShowStatementOptions,
    },
    /// ```sql
    /// SHOW [FULL] PROCESSLIST
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    ShowProcessList {
        /// `true` when full process information was requested.
        full: bool,
    },
    /// ```sql
    /// SHOW SCHEMAS
    /// ```
    ShowSchemas {
        /// `true` when terse (compact) output was requested.
        terse: bool,
        /// `true` when history information was requested.
        history: bool,
        /// Additional options for `SHOW SCHEMAS`.
        show_options: ShowStatementOptions,
    },
    // ```sql
    // SHOW {CHARACTER SET | CHARSET}
    // ```
    // [MySQL]:
    // <https://dev.mysql.com/doc/refman/8.4/en/show.html#:~:text=SHOW%20%7BCHARACTER%20SET%20%7C%20CHARSET%7D%20%5Blike_or_where%5D>
    /// Show the available character sets (alias `CHARSET`).
    ShowCharset(ShowCharset),
    /// ```sql
    /// SHOW OBJECTS LIKE 'line%' IN mydb.public
    /// ```
    /// Snowflake-specific statement
    /// <https://docs.snowflake.com/en/sql-reference/sql/show-objects>
    ShowObjects(ShowObjects),
    /// ```sql
    /// SHOW TABLES
    /// ```
    ShowTables {
        /// `true` when terse output format was requested (compact listing).
        terse: bool,
        /// `true` when history rows are requested.
        history: bool,
        /// `true` when extended information should be shown.
        extended: bool,
        /// `true` when a full listing was requested.
        full: bool,
        /// `true` when external tables should be included.
        external: bool,
        /// Additional options for `SHOW` statements.
        show_options: ShowStatementOptions,
    },
    /// ```sql
    /// SHOW VIEWS
    /// ```
    ShowViews {
        /// `true` when terse output format was requested.
        terse: bool,
        /// `true` when materialized views should be included.
        materialized: bool,
        /// Additional options for `SHOW` statements.
        show_options: ShowStatementOptions,
    },
    /// ```sql
    /// SHOW COLLATION
    /// ```
    ///
    /// Note: this is a MySQL-specific statement.
    ShowCollation {
        /// Optional filter for which collations to display.
        filter: Option<ShowStatementFilter>,
    },
    /// ```sql
    /// `USE ...`
    /// ```
    Use(Use),
    /// ```sql
    /// START  [ TRANSACTION | WORK ] | START TRANSACTION } ...
    /// ```
    /// If `begin` is false.
    ///
    /// ```sql
    /// `BEGIN  [ TRANSACTION | WORK ] | START TRANSACTION } ...`
    /// ```
    /// If `begin` is true
    StartTransaction {
        /// Transaction modes such as `ISOLATION LEVEL` or `READ WRITE`.
        modes: Vec<TransactionMode>,
        /// `true` when this was parsed as `BEGIN` instead of `START`.
        begin: bool,
        /// Optional specific keyword used: `TRANSACTION` or `WORK`.
        transaction: Option<BeginTransactionKind>,
        /// Optional transaction modifier (e.g., `AND NO CHAIN`).
        modifier: Option<TransactionModifier>,
        /// List of statements belonging to the `BEGIN` block.
        /// Example:
        /// ```sql
        /// BEGIN
        ///     SELECT 1;
        ///     SELECT 2;
        /// END;
        /// ```
        statements: Vec<Statement>,
        /// Exception handling with exception clauses.
        /// Example:
        /// ```sql
        /// EXCEPTION
        ///     WHEN EXCEPTION_1 THEN
        ///         SELECT 2;
        ///     WHEN EXCEPTION_2 OR EXCEPTION_3 THEN
        ///         SELECT 3;
        ///     WHEN OTHER THEN
        ///         SELECT 4;
        /// ```
        /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#beginexceptionend>
        /// <https://docs.snowflake.com/en/sql-reference/snowflake-scripting/exception>
        exception: Option<Vec<ExceptionWhen>>,
        /// TRUE if the statement has an `END` keyword.
        has_end_keyword: bool,
    },
    /// ```sql
    /// COMMENT ON ...
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement.
    Comment {
        /// Type of object being commented (table, column, etc.).
        object_type: CommentObject,
        /// Name of the object the comment applies to.
        object_name: ObjectName,
        /// Optional comment text (None to remove comment).
        comment: Option<String>,
        /// An optional `IF EXISTS` clause. (Non-standard.)
        /// See <https://docs.snowflake.com/en/sql-reference/sql/comment>
        if_exists: bool,
    },
    /// ```sql
    /// COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]
    /// ```
    /// If `end` is false
    ///
    /// ```sql
    /// END [ TRY | CATCH ]
    /// ```
    /// If `end` is true
    Commit {
        /// `true` when `AND [ NO ] CHAIN` was present.
        chain: bool,
        /// `true` when this `COMMIT` was parsed as an `END` block terminator.
        end: bool,
        /// Optional transaction modifier for commit semantics.
        modifier: Option<TransactionModifier>,
    },
    /// ```sql
    /// ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ] [ TO [ SAVEPOINT ] savepoint_name ]
    /// ```
    Rollback {
        /// `true` when `AND [ NO ] CHAIN` was present.
        chain: bool,
        /// Optional savepoint name to roll back to.
        savepoint: Option<Ident>,
    },
    /// ```sql
    /// CREATE SCHEMA
    /// ```
    CreateSchema {
        /// `<schema name> | AUTHORIZATION <schema authorization identifier>  | <schema name>  AUTHORIZATION <schema authorization identifier>`
        schema_name: SchemaName,
        /// `true` when `IF NOT EXISTS` was present.
        if_not_exists: bool,
        /// Schema properties.
        ///
        /// ```sql
        /// CREATE SCHEMA myschema WITH (key1='value1');
        /// ```
        ///
        /// [Trino](https://trino.io/docs/current/sql/create-schema.html)
        with: Option<Vec<SqlOption>>,
        /// Schema options.
        ///
        /// ```sql
        /// CREATE SCHEMA myschema OPTIONS(key1='value1');
        /// ```
        ///
        /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_schema_statement)
        options: Option<Vec<SqlOption>>,
        /// Default collation specification for the schema.
        ///
        /// ```sql
        /// CREATE SCHEMA myschema DEFAULT COLLATE 'und:ci';
        /// ```
        ///
        /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_schema_statement)
        default_collate_spec: Option<Expr>,
        /// Clones a schema
        ///
        /// ```sql
        /// CREATE SCHEMA myschema CLONE otherschema
        /// ```
        ///
        /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-clone#databases-schemas)
        clone: Option<ObjectName>,
    },
    /// ```sql
    /// CREATE DATABASE
    /// ```
    /// See:
    /// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
    CreateDatabase {
        /// Database name.
        db_name: ObjectName,
        /// `IF NOT EXISTS` flag.
        if_not_exists: bool,
        /// Optional location URI.
        location: Option<String>,
        /// Optional managed location.
        managed_location: Option<String>,
        /// `OR REPLACE` flag.
        or_replace: bool,
        /// `TRANSIENT` flag.
        transient: bool,
        /// Optional clone source.
        clone: Option<ObjectName>,
        /// Optional data retention time in days.
        data_retention_time_in_days: Option<u64>,
        /// Optional maximum data extension time in days.
        max_data_extension_time_in_days: Option<u64>,
        /// Optional external volume identifier.
        external_volume: Option<String>,
        /// Optional catalog name.
        catalog: Option<String>,
        /// Whether to replace invalid characters.
        replace_invalid_characters: Option<bool>,
        /// Default DDL collation string.
        default_ddl_collation: Option<String>,
        /// Storage serialization policy.
        storage_serialization_policy: Option<StorageSerializationPolicy>,
        /// Optional comment.
        comment: Option<String>,
        /// Optional default character set (MySQL).
        default_charset: Option<String>,
        /// Optional default collation (MySQL).
        default_collation: Option<String>,
        /// Optional catalog sync identifier.
        catalog_sync: Option<String>,
        /// Catalog sync namespace mode.
        catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
        /// Optional flatten delimiter for namespace sync.
        catalog_sync_namespace_flatten_delimiter: Option<String>,
        /// Optional tags for the database.
        with_tags: Option<Vec<Tag>>,
        /// Optional contact entries for the database.
        with_contacts: Option<Vec<ContactEntry>>,
    },
    /// ```sql
    /// CREATE FUNCTION
    /// ```
    ///
    /// Supported variants:
    /// 1. [Hive](https://cwiki.apache.org/confluence/display/hive/languagemanual+ddl#LanguageManualDDL-Create/Drop/ReloadFunction)
    /// 2. [PostgreSQL](https://www.postgresql.org/docs/15/sql-createfunction.html)
    /// 3. [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement)
    /// 4. [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql)
    CreateFunction(CreateFunction),
    /// CREATE TRIGGER statement. See struct [CreateTrigger] for details.
    CreateTrigger(CreateTrigger),
    /// DROP TRIGGER statement. See struct [DropTrigger] for details.
    DropTrigger(DropTrigger),
    /// ```sql
    /// CREATE PROCEDURE
    /// ```
    CreateProcedure {
        /// `OR ALTER` flag.
        or_alter: bool,
        /// Procedure name.
        name: ObjectName,
        /// Optional procedure parameters.
        params: Option<Vec<ProcedureParam>>,
        /// Optional language identifier.
        language: Option<Ident>,
        /// Procedure body statements.
        body: ConditionalStatements,
    },
    /// ```sql
    /// CREATE MACRO
    /// ```
    ///
    /// Supported variants:
    /// 1. [DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
    CreateMacro {
        /// `OR REPLACE` flag.
        or_replace: bool,
        /// Whether macro is temporary.
        temporary: bool,
        /// Macro name.
        name: ObjectName,
        /// Optional macro arguments.
        args: Option<Vec<MacroArg>>,
        /// Macro definition body.
        definition: MacroDefinition,
    },
    /// ```sql
    /// CREATE STAGE
    /// ```
    /// See <https://docs.snowflake.com/en/sql-reference/sql/create-stage>
    CreateStage {
        /// `OR REPLACE` flag for stage.
        or_replace: bool,
        /// Whether stage is temporary.
        temporary: bool,
        /// `IF NOT EXISTS` flag.
        if_not_exists: bool,
        /// Stage name.
        name: ObjectName,
        /// Stage parameters.
        stage_params: StageParamsObject,
        /// Directory table parameters.
        directory_table_params: KeyValueOptions,
        /// File format options.
        file_format: KeyValueOptions,
        /// Copy options for stage.
        copy_options: KeyValueOptions,
        /// Optional comment.
        comment: Option<String>,
    },
    /// ```sql
    /// ASSERT <condition> [AS <message>]
    /// ```
    Assert {
        /// Assertion condition expression.
        condition: Expr,
        /// Optional message expression.
        message: Option<Expr>,
    },
    /// ```sql
    /// GRANT privileges ON objects TO grantees
    /// ```
    Grant(Grant),
    /// ```sql
    /// DENY privileges ON object TO grantees
    /// ```
    Deny(DenyStatement),
    /// ```sql
    /// REVOKE privileges ON objects FROM grantees
    /// ```
    Revoke(Revoke),
    /// ```sql
    /// DEALLOCATE [ PREPARE ] { name | ALL }
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement.
    Deallocate {
        /// Name to deallocate (or `ALL`).
        name: Ident,
        /// Whether `PREPARE` keyword was present.
        prepare: bool,
    },
    /// ```sql
    /// An `EXECUTE` statement
    /// ```
    ///
    /// Postgres: <https://www.postgresql.org/docs/current/sql-execute.html>
    /// MSSQL: <https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure>
    /// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#execute_immediate>
    /// Snowflake: <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
    Execute {
        /// Optional function/procedure name.
        name: Option<ObjectName>,
        /// Parameter expressions passed to execute.
        parameters: Vec<Expr>,
        /// Whether parentheses were present around `parameters`.
        has_parentheses: bool,
        /// Is this an `EXECUTE IMMEDIATE`.
        immediate: bool,
        /// Identifiers to capture results into.
        into: Vec<Ident>,
        /// `USING` expressions with optional aliases.
        using: Vec<ExprWithAlias>,
        /// Whether the last parameter is the return value of the procedure
        /// MSSQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql?view=sql-server-ver17#output>
        output: bool,
        /// Whether to invoke the procedure with the default parameter values
        /// MSSQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql?view=sql-server-ver17#default>
        default: bool,
    },
    /// ```sql
    /// PREPARE name [ ( data_type [, ...] ) ] AS statement
    /// ```
    ///
    /// Note: this is a PostgreSQL-specific statement.
    Prepare {
        /// Name of the prepared statement.
        name: Ident,
        /// Optional data types for parameters.
        data_types: Vec<DataType>,
        /// Statement being prepared.
        statement: Box<Statement>,
    },
    /// ```sql
    /// KILL [CONNECTION | QUERY | MUTATION]
    /// ```
    ///
    /// See <https://clickhouse.com/docs/en/sql-reference/statements/kill/>
    /// See <https://dev.mysql.com/doc/refman/8.0/en/kill.html>
    Kill {
        /// Optional kill modifier (CONNECTION, QUERY, MUTATION).
        modifier: Option<KillType>,
        // processlist_id
        /// The id of the process to kill.
        id: u64,
    },
    /// ```sql
    /// [EXPLAIN | DESC | DESCRIBE] TABLE
    /// ```
    /// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/explain.html>
    ExplainTable {
        /// `EXPLAIN | DESC | DESCRIBE`
        describe_alias: DescribeAlias,
        /// Hive style `FORMATTED | EXTENDED`
        hive_format: Option<HiveDescribeFormat>,
        /// Snowflake and ClickHouse support `DESC|DESCRIBE TABLE <table_name>` syntax
        ///
        /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/desc-table.html)
        /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/describe-table)
        has_table_keyword: bool,
        /// Table name
        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
        table_name: ObjectName,
    },
    /// ```sql
    /// [EXPLAIN | DESC | DESCRIBE]  <statement>
    /// ```
    Explain {
        /// `EXPLAIN | DESC | DESCRIBE`
        describe_alias: DescribeAlias,
        /// Carry out the command and show actual run times and other statistics.
        analyze: bool,
        /// Display additional information regarding the plan.
        verbose: bool,
        /// `EXPLAIN QUERY PLAN`
        /// Display the query plan without running the query.
        ///
        /// [SQLite](https://sqlite.org/lang_explain.html)
        query_plan: bool,
        /// `EXPLAIN ESTIMATE`
        /// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/statements/explain#explain-estimate)
        estimate: bool,
        /// A SQL query that specifies what to explain
        statement: Box<Statement>,
        /// Optional output format of explain
        format: Option<AnalyzeFormatKind>,
        /// Postgres style utility options, `(analyze, verbose true)`
        options: Option<Vec<UtilityOption>>,
    },
    /// ```sql
    /// SAVEPOINT
    /// ```
    /// Define a new savepoint within the current transaction
    Savepoint {
        /// Name of the savepoint being defined.
        name: Ident,
    },
    /// ```sql
    /// RELEASE [ SAVEPOINT ] savepoint_name
    /// ```
    ReleaseSavepoint {
        /// Name of the savepoint to release.
        name: Ident,
    },
    /// A `MERGE` statement.
    ///
    /// ```sql
    /// MERGE INTO <target_table> USING <source> ON <join_expr> { matchedClause | notMatchedClause } [ ... ]
    /// ```
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
    /// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver16)
    Merge(Merge),
    /// ```sql
    /// CACHE [ FLAG ] TABLE <table_name> [ OPTIONS('K1' = 'V1', 'K2' = V2) ] [ AS ] [ <query> ]
    /// ```
    ///
    /// See [Spark SQL docs] for more details.
    ///
    /// [Spark SQL docs]: https://docs.databricks.com/spark/latest/spark-sql/language-manual/sql-ref-syntax-aux-cache-cache-table.html
    Cache {
        /// Table flag
        table_flag: Option<ObjectName>,
        /// Table name
        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
        table_name: ObjectName,
        /// `true` if `AS` keyword was present before the query.
        has_as: bool,
        /// Table confs
        options: Vec<SqlOption>,
        /// Cache table as a Query
        query: Option<Box<Query>>,
    },
    /// ```sql
    /// UNCACHE TABLE [ IF EXISTS ]  <table_name>
    /// ```
    UNCache {
        /// Table name
        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
        table_name: ObjectName,
        /// `true` when `IF EXISTS` was present.
        if_exists: bool,
    },
    /// ```sql
    /// CREATE [ { TEMPORARY | TEMP } ] SEQUENCE [ IF NOT EXISTS ] <sequence_name>
    /// ```
    /// Define a new sequence:
    CreateSequence {
        /// Whether the sequence is temporary.
        temporary: bool,
        /// `IF NOT EXISTS` flag.
        if_not_exists: bool,
        /// Sequence name.
        name: ObjectName,
        /// Optional data type for the sequence.
        data_type: Option<DataType>,
        /// Sequence options (INCREMENT, MINVALUE, etc.).
        sequence_options: Vec<SequenceOptions>,
        /// Optional `OWNED BY` target.
        owned_by: Option<ObjectName>,
    },
    /// A `CREATE DOMAIN` statement.
    CreateDomain(CreateDomain),
    /// ```sql
    /// CREATE TYPE <name>
    /// ```
    CreateType {
        /// Type name to create.
        name: ObjectName,
        /// Optional type representation details.
        representation: Option<UserDefinedTypeRepresentation>,
    },
    /// ```sql
    /// PRAGMA <schema-name>.<pragma-name> = <pragma-value>
    /// ```
    Pragma {
        /// Pragma name (possibly qualified).
        name: ObjectName,
        /// Optional pragma value.
        value: Option<ValueWithSpan>,
        /// Whether the pragma used `=`.
        is_eq: bool,
    },
    /// ```sql
    /// LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]
    /// ```
    ///
    /// See <https://www.postgresql.org/docs/current/sql-lock.html>
    Lock(Lock),
    /// ```sql
    /// LOCK TABLES <table_name> [READ [LOCAL] | [LOW_PRIORITY] WRITE]
    /// ```
    /// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
    LockTables {
        /// List of tables to lock with modes.
        tables: Vec<LockTable>,
    },
    /// ```sql
    /// UNLOCK TABLES
    /// ```
    /// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
    UnlockTables,
    /// Unloads the result of a query to file
    ///
    /// [Athena](https://docs.aws.amazon.com/athena/latest/ug/unload.html):
    /// ```sql
    /// UNLOAD(statement) TO <destination> [ WITH options ]
    /// ```
    ///
    /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html):
    /// ```sql
    /// UNLOAD('statement') TO <destination> [ OPTIONS ]
    /// ```
    Unload {
        /// Optional query AST to unload.
        query: Option<Box<Query>>,
        /// Optional original query text.
        query_text: Option<String>,
        /// Destination identifier.
        to: Ident,
        /// Optional IAM role/auth information.
        auth: Option<IamRoleKind>,
        /// Additional `WITH` options.
        with: Vec<SqlOption>,
        /// Legacy copy-style options.
        options: Vec<CopyLegacyOption>,
    },
    /// ClickHouse:
    /// ```sql
    /// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
    /// ```
    /// See ClickHouse <https://clickhouse.com/docs/en/sql-reference/statements/optimize>
    ///
    /// Databricks:
    /// ```sql
    /// OPTIMIZE table_name [WHERE predicate] [ZORDER BY (col_name1 [, ...])]
    /// ```
    /// See Databricks <https://docs.databricks.com/en/sql/language-manual/delta-optimize.html>
    OptimizeTable {
        /// Table name to optimize.
        name: ObjectName,
        /// Whether the `TABLE` keyword was present (ClickHouse uses `OPTIMIZE TABLE`, Databricks uses `OPTIMIZE`).
        has_table_keyword: bool,
        /// Optional cluster identifier.
        /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
        on_cluster: Option<Ident>,
        /// Optional partition spec.
        /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
        partition: Option<Partition>,
        /// Whether `FINAL` was specified.
        /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
        include_final: bool,
        /// Optional deduplication settings.
        /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
        deduplicate: Option<Deduplicate>,
        /// Optional WHERE predicate.
        /// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-optimize.html)
        predicate: Option<Expr>,
        /// Optional ZORDER BY columns.
        /// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-optimize.html)
        zorder: Option<Vec<Expr>>,
    },
    /// ```sql
    /// LISTEN
    /// ```
    /// listen for a notification channel
    ///
    /// See Postgres <https://www.postgresql.org/docs/current/sql-listen.html>
    LISTEN {
        /// Notification channel identifier.
        channel: Ident,
    },
    /// ```sql
    /// UNLISTEN
    /// ```
    /// stop listening for a notification
    ///
    /// See Postgres <https://www.postgresql.org/docs/current/sql-unlisten.html>
    UNLISTEN {
        /// Notification channel identifier.
        channel: Ident,
    },
    /// ```sql
    /// NOTIFY channel [ , payload ]
    /// ```
    /// send a notification event together with an optional "payload" string to channel
    ///
    /// See Postgres <https://www.postgresql.org/docs/current/sql-notify.html>
    NOTIFY {
        /// Notification channel identifier.
        channel: Ident,
        /// Optional payload string.
        payload: Option<String>,
    },
    /// ```sql
    /// LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename
    /// [PARTITION (partcol1=val1, partcol2=val2 ...)]
    /// [INPUTFORMAT 'inputformat' SERDE 'serde']
    /// ```
    /// Loading files into tables
    ///
    /// See Hive <https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362036#LanguageManualDML-Loadingfilesintotables>
    LoadData {
        /// Whether `LOCAL` is present.
        local: bool,
        /// Input path for files to load.
        inpath: String,
        /// Whether `OVERWRITE` was specified.
        overwrite: bool,
        /// Target table name to load into.
        table_name: ObjectName,
        /// Optional partition specification.
        partitioned: Option<Vec<Expr>>,
        /// Optional table format information.
        table_format: Option<HiveLoadDataFormat>,
    },
    /// ```sql
    /// Rename TABLE tbl_name TO new_tbl_name[, tbl_name2 TO new_tbl_name2] ...
    /// ```
    /// Renames one or more tables
    ///
    /// See Mysql <https://dev.mysql.com/doc/refman/9.1/en/rename-table.html>
    RenameTable(Vec<RenameTable>),
    /// Snowflake `LIST`
    /// See: <https://docs.snowflake.com/en/sql-reference/sql/list>
    List(FileStagingCommand),
    /// Snowflake `REMOVE`
    /// See: <https://docs.snowflake.com/en/sql-reference/sql/remove>
    Remove(FileStagingCommand),
    /// RaiseError (MSSQL)
    /// RAISERROR ( { msg_id | msg_str | @local_variable }
    /// { , severity , state }
    /// [ , argument [ , ...n ] ] )
    /// [ WITH option [ , ...n ] ]
    /// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16>
    RaisError {
        /// Error message expression or identifier.
        message: Box<Expr>,
        /// Severity expression.
        severity: Box<Expr>,
        /// State expression.
        state: Box<Expr>,
        /// Substitution arguments for the message.
        arguments: Vec<Expr>,
        /// Additional `WITH` options for RAISERROR.
        options: Vec<RaisErrorOption>,
    },
    /// A MSSQL `THROW` statement.
    Throw(ThrowStatement),
    /// ```sql
    /// PRINT msg_str | @local_variable | string_expr
    /// ```
    ///
    /// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/print-transact-sql>
    Print(PrintStatement),
    /// MSSQL `WAITFOR` statement.
    ///
    /// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
    WaitFor(WaitForStatement),
    /// ```sql
    /// RETURN [ expression ]
    /// ```
    ///
    /// See [ReturnStatement]
    Return(ReturnStatement),
    /// Export data statement
    ///
    /// Example:
    /// ```sql
    /// EXPORT DATA OPTIONS(uri='gs://bucket/folder/*', format='PARQUET', overwrite=true) AS
    /// SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10
    /// ```
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/export-statements)
    ExportData(ExportData),
    /// ```sql
    /// CREATE [OR REPLACE] USER <user> [IF NOT EXISTS]
    /// ```
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
    CreateUser(CreateUser),
    /// ```sql
    /// ALTER USER \[ IF EXISTS \] \[ <name> \]
    /// ```
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
    AlterUser(AlterUser),
    /// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
    ///
    /// ```sql
    /// VACUUM tbl
    /// ```
    /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
    Vacuum(VacuumStatement),
    /// Restore the value of a run-time parameter to the default value.
    ///
    /// ```sql
    /// RESET configuration_parameter;
    /// RESET ALL;
    /// ```
    /// [PostgreSQL](https://www.postgresql.org/docs/current/sql-reset.html)
    Reset(ResetStatement),
}

impl From<Analyze> for Statement {
    fn from(analyze: Analyze) -> Self {
        Statement::Analyze(analyze)
    }
}

impl From<ddl::Truncate> for Statement {
    fn from(truncate: ddl::Truncate) -> Self {
        Statement::Truncate(truncate)
    }
}

impl From<Lock> for Statement {
    fn from(lock: Lock) -> Self {
        Statement::Lock(lock)
    }
}

impl From<ddl::Msck> for Statement {
    fn from(msck: ddl::Msck) -> Self {
        Statement::Msck(msck)
    }
}

/// ```sql
/// {COPY | REVOKE} CURRENT GRANTS
/// ```
///
/// - [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#optional-parameters)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CurrentGrantsKind {
    /// `COPY CURRENT GRANTS` (copy current grants to target).
    CopyCurrentGrants,
    /// `REVOKE CURRENT GRANTS` (revoke current grants from target).
    RevokeCurrentGrants,
}

impl fmt::Display for CurrentGrantsKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CurrentGrantsKind::CopyCurrentGrants => write!(f, "COPY CURRENT GRANTS"),
            CurrentGrantsKind::RevokeCurrentGrants => write!(f, "REVOKE CURRENT GRANTS"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `RAISERROR` options
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16#options>
pub enum RaisErrorOption {
    /// Log the error.
    Log,
    /// Do not wait for completion.
    NoWait,
    /// Set the error state.
    SetError,
}

impl fmt::Display for RaisErrorOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RaisErrorOption::Log => write!(f, "LOG"),
            RaisErrorOption::NoWait => write!(f, "NOWAIT"),
            RaisErrorOption::SetError => write!(f, "SETERROR"),
        }
    }
}

impl fmt::Display for Statement {
    /// Formats a SQL statement with support for pretty printing.
    ///
    /// When using the alternate flag (`{:#}`), the statement will be formatted with proper
    /// indentation and line breaks. For example:
    ///
    /// ```
    /// # use sqlparser::dialect::GenericDialect;
    /// # use sqlparser::parser::Parser;
    /// let sql = "SELECT a, b FROM table_1";
    /// let ast = Parser::parse_sql(&GenericDialect, sql).unwrap();
    ///
    /// // Regular formatting
    /// assert_eq!(format!("{}", ast[0]), "SELECT a, b FROM table_1");
    ///
    /// // Pretty printing
    /// assert_eq!(format!("{:#}", ast[0]),
    /// r#"SELECT
    ///   a,
    ///   b
    /// FROM
    ///   table_1"#);
    /// ```
    // Clippy thinks this function is too complicated, but it is painful to
    // split up without extracting structs for each `Statement` variant.
    #[allow(clippy::cognitive_complexity)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Statement::Flush {
                object_type,
                location,
                channel,
                read_lock,
                export,
                tables,
            } => {
                write!(f, "FLUSH")?;
                if let Some(location) = location {
                    f.write_str(" ")?;
                    location.fmt(f)?;
                }
                write!(f, " {object_type}")?;

                if let Some(channel) = channel {
                    write!(f, " FOR CHANNEL {channel}")?;
                }

                write!(
                    f,
                    "{tables}{read}{export}",
                    tables = if !tables.is_empty() {
                        format!(" {}", display_comma_separated(tables))
                    } else {
                        String::new()
                    },
                    export = if *export { " FOR EXPORT" } else { "" },
                    read = if *read_lock { " WITH READ LOCK" } else { "" }
                )
            }
            Statement::Kill { modifier, id } => {
                write!(f, "KILL ")?;

                if let Some(m) = modifier {
                    write!(f, "{m} ")?;
                }

                write!(f, "{id}")
            }
            Statement::ExplainTable {
                describe_alias,
                hive_format,
                has_table_keyword,
                table_name,
            } => {
                write!(f, "{describe_alias} ")?;

                if let Some(format) = hive_format {
                    write!(f, "{format} ")?;
                }
                if *has_table_keyword {
                    write!(f, "TABLE ")?;
                }

                write!(f, "{table_name}")
            }
            Statement::Explain {
                describe_alias,
                verbose,
                analyze,
                query_plan,
                estimate,
                statement,
                format,
                options,
            } => {
                write!(f, "{describe_alias} ")?;

                if *query_plan {
                    write!(f, "QUERY PLAN ")?;
                }
                if *analyze {
                    write!(f, "ANALYZE ")?;
                }
                if *estimate {
                    write!(f, "ESTIMATE ")?;
                }

                if *verbose {
                    write!(f, "VERBOSE ")?;
                }

                if let Some(format) = format {
                    write!(f, "{format} ")?;
                }

                if let Some(options) = options {
                    write!(f, "({}) ", display_comma_separated(options))?;
                }

                write!(f, "{statement}")
            }
            Statement::Query(s) => s.fmt(f),
            Statement::Declare { stmts } => {
                write!(f, "DECLARE ")?;
                write!(f, "{}", display_separated(stmts, "; "))
            }
            Statement::Fetch {
                name,
                direction,
                position,
                into,
            } => {
                write!(f, "FETCH {direction} {position} {name}")?;

                if let Some(into) = into {
                    write!(f, " INTO {into}")?;
                }

                Ok(())
            }
            Statement::Directory {
                overwrite,
                local,
                path,
                file_format,
                source,
            } => {
                write!(
                    f,
                    "INSERT{overwrite}{local} DIRECTORY '{path}'",
                    overwrite = if *overwrite { " OVERWRITE" } else { "" },
                    local = if *local { " LOCAL" } else { "" },
                    path = path
                )?;
                if let Some(ref ff) = file_format {
                    write!(f, " STORED AS {ff}")?
                }
                write!(f, " {source}")
            }
            Statement::Msck(msck) => msck.fmt(f),
            Statement::Truncate(truncate) => truncate.fmt(f),
            Statement::Case(stmt) => {
                write!(f, "{stmt}")
            }
            Statement::If(stmt) => {
                write!(f, "{stmt}")
            }
            Statement::While(stmt) => {
                write!(f, "{stmt}")
            }
            Statement::Raise(stmt) => {
                write!(f, "{stmt}")
            }
            Statement::AttachDatabase {
                schema_name,
                database_file_name,
                database,
            } => {
                let keyword = if *database { "DATABASE " } else { "" };
                write!(f, "ATTACH {keyword}{database_file_name} AS {schema_name}")
            }
            Statement::AttachDuckDBDatabase {
                if_not_exists,
                database,
                database_path,
                database_alias,
                attach_options,
            } => {
                write!(
                    f,
                    "ATTACH{database}{if_not_exists} {database_path}",
                    database = if *database { " DATABASE" } else { "" },
                    if_not_exists = if *if_not_exists { " IF NOT EXISTS" } else { "" },
                )?;
                if let Some(alias) = database_alias {
                    write!(f, " AS {alias}")?;
                }
                if !attach_options.is_empty() {
                    write!(f, " ({})", display_comma_separated(attach_options))?;
                }
                Ok(())
            }
            Statement::DetachDuckDBDatabase {
                if_exists,
                database,
                database_alias,
            } => {
                write!(
                    f,
                    "DETACH{database}{if_exists} {database_alias}",
                    database = if *database { " DATABASE" } else { "" },
                    if_exists = if *if_exists { " IF EXISTS" } else { "" },
                )?;
                Ok(())
            }
            Statement::Analyze(analyze) => analyze.fmt(f),
            Statement::Insert(insert) => insert.fmt(f),
            Statement::Install {
                extension_name: name,
            } => write!(f, "INSTALL {name}"),

            Statement::Load {
                extension_name: name,
            } => write!(f, "LOAD {name}"),

            Statement::Call(function) => write!(f, "CALL {function}"),

            Statement::Copy {
                source,
                to,
                target,
                options,
                legacy_options,
                values,
            } => {
                write!(f, "COPY")?;
                match source {
                    CopySource::Query(query) => write!(f, " ({query})")?,
                    CopySource::Table {
                        table_name,
                        columns,
                    } => {
                        write!(f, " {table_name}")?;
                        if !columns.is_empty() {
                            write!(f, " ({})", display_comma_separated(columns))?;
                        }
                    }
                }
                write!(f, " {} {}", if *to { "TO" } else { "FROM" }, target)?;
                if !options.is_empty() {
                    write!(f, " ({})", display_comma_separated(options))?;
                }
                if !legacy_options.is_empty() {
                    write!(f, " {}", display_separated(legacy_options, " "))?;
                }
                if !values.is_empty() {
                    writeln!(f, ";")?;
                    let mut delim = "";
                    for v in values {
                        write!(f, "{delim}")?;
                        delim = "\t";
                        if let Some(v) = v {
                            write!(f, "{v}")?;
                        } else {
                            write!(f, "\\N")?;
                        }
                    }
                    write!(f, "\n\\.")?;
                }
                Ok(())
            }
            Statement::Update(update) => update.fmt(f),
            Statement::Delete(delete) => delete.fmt(f),
            Statement::Open(open) => open.fmt(f),
            Statement::Close { cursor } => {
                write!(f, "CLOSE {cursor}")?;

                Ok(())
            }
            Statement::CreateDatabase {
                db_name,
                if_not_exists,
                location,
                managed_location,
                or_replace,
                transient,
                clone,
                data_retention_time_in_days,
                max_data_extension_time_in_days,
                external_volume,
                catalog,
                replace_invalid_characters,
                default_ddl_collation,
                storage_serialization_policy,
                comment,
                default_charset,
                default_collation,
                catalog_sync,
                catalog_sync_namespace_mode,
                catalog_sync_namespace_flatten_delimiter,
                with_tags,
                with_contacts,
            } => {
                write!(
                    f,
                    "CREATE {or_replace}{transient}DATABASE {if_not_exists}{name}",
                    or_replace = if *or_replace { "OR REPLACE " } else { "" },
                    transient = if *transient { "TRANSIENT " } else { "" },
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                    name = db_name,
                )?;

                if let Some(l) = location {
                    write!(f, " LOCATION '{l}'")?;
                }
                if let Some(ml) = managed_location {
                    write!(f, " MANAGEDLOCATION '{ml}'")?;
                }
                if let Some(clone) = clone {
                    write!(f, " CLONE {clone}")?;
                }

                if let Some(value) = data_retention_time_in_days {
                    write!(f, " DATA_RETENTION_TIME_IN_DAYS = {value}")?;
                }

                if let Some(value) = max_data_extension_time_in_days {
                    write!(f, " MAX_DATA_EXTENSION_TIME_IN_DAYS = {value}")?;
                }

                if let Some(vol) = external_volume {
                    write!(f, " EXTERNAL_VOLUME = '{vol}'")?;
                }

                if let Some(cat) = catalog {
                    write!(f, " CATALOG = '{cat}'")?;
                }

                if let Some(true) = replace_invalid_characters {
                    write!(f, " REPLACE_INVALID_CHARACTERS = TRUE")?;
                } else if let Some(false) = replace_invalid_characters {
                    write!(f, " REPLACE_INVALID_CHARACTERS = FALSE")?;
                }

                if let Some(collation) = default_ddl_collation {
                    write!(f, " DEFAULT_DDL_COLLATION = '{collation}'")?;
                }

                if let Some(policy) = storage_serialization_policy {
                    write!(f, " STORAGE_SERIALIZATION_POLICY = {policy}")?;
                }

                if let Some(comment) = comment {
                    write!(f, " COMMENT = '{comment}'")?;
                }

                if let Some(charset) = default_charset {
                    write!(f, " DEFAULT CHARACTER SET {charset}")?;
                }

                if let Some(collation) = default_collation {
                    write!(f, " DEFAULT COLLATE {collation}")?;
                }

                if let Some(sync) = catalog_sync {
                    write!(f, " CATALOG_SYNC = '{sync}'")?;
                }

                if let Some(mode) = catalog_sync_namespace_mode {
                    write!(f, " CATALOG_SYNC_NAMESPACE_MODE = {mode}")?;
                }

                if let Some(delim) = catalog_sync_namespace_flatten_delimiter {
                    write!(f, " CATALOG_SYNC_NAMESPACE_FLATTEN_DELIMITER = '{delim}'")?;
                }

                if let Some(tags) = with_tags {
                    write!(f, " WITH TAG ({})", display_comma_separated(tags))?;
                }

                if let Some(contacts) = with_contacts {
                    write!(f, " WITH CONTACT ({})", display_comma_separated(contacts))?;
                }
                Ok(())
            }
            Statement::CreateFunction(create_function) => create_function.fmt(f),
            Statement::CreateDomain(create_domain) => create_domain.fmt(f),
            Statement::CreateTrigger(create_trigger) => create_trigger.fmt(f),
            Statement::DropTrigger(drop_trigger) => drop_trigger.fmt(f),
            Statement::CreateProcedure {
                name,
                or_alter,
                params,
                language,
                body,
            } => {
                write!(
                    f,
                    "CREATE {or_alter}PROCEDURE {name}",
                    or_alter = if *or_alter { "OR ALTER " } else { "" },
                    name = name
                )?;

                if let Some(p) = params {
                    if !p.is_empty() {
                        write!(f, " ({})", display_comma_separated(p))?;
                    }
                }

                if let Some(language) = language {
                    write!(f, " LANGUAGE {language}")?;
                }

                write!(f, " AS {body}")
            }
            Statement::CreateMacro {
                or_replace,
                temporary,
                name,
                args,
                definition,
            } => {
                write!(
                    f,
                    "CREATE {or_replace}{temp}MACRO {name}",
                    temp = if *temporary { "TEMPORARY " } else { "" },
                    or_replace = if *or_replace { "OR REPLACE " } else { "" },
                )?;
                if let Some(args) = args {
                    write!(f, "({})", display_comma_separated(args))?;
                }
                match definition {
                    MacroDefinition::Expr(expr) => write!(f, " AS {expr}")?,
                    MacroDefinition::Table(query) => write!(f, " AS TABLE {query}")?,
                }
                Ok(())
            }
            Statement::CreateView(create_view) => create_view.fmt(f),
            Statement::CreateTable(create_table) => create_table.fmt(f),
            Statement::LoadData {
                local,
                inpath,
                overwrite,
                table_name,
                partitioned,
                table_format,
            } => {
                write!(
                    f,
                    "LOAD DATA {local}INPATH '{inpath}' {overwrite}INTO TABLE {table_name}",
                    local = if *local { "LOCAL " } else { "" },
                    inpath = inpath,
                    overwrite = if *overwrite { "OVERWRITE " } else { "" },
                    table_name = table_name,
                )?;
                if let Some(ref parts) = &partitioned {
                    if !parts.is_empty() {
                        write!(f, " PARTITION ({})", display_comma_separated(parts))?;
                    }
                }
                if let Some(HiveLoadDataFormat {
                    serde,
                    input_format,
                }) = &table_format
                {
                    write!(f, " INPUTFORMAT {input_format} SERDE {serde}")?;
                }
                Ok(())
            }
            Statement::CreateVirtualTable {
                name,
                if_not_exists,
                module_name,
                module_args,
            } => {
                write!(
                    f,
                    "CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}",
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                    name = name,
                    module_name = module_name
                )?;
                if !module_args.is_empty() {
                    write!(f, " ({})", display_comma_separated(module_args))?;
                }
                Ok(())
            }
            Statement::CreateIndex(create_index) => create_index.fmt(f),
            Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
            Statement::CreateCollation(create_collation) => write!(f, "{create_collation}"),
            Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
            Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
            Statement::DropOperatorFamily(drop_operator_family) => {
                write!(f, "{drop_operator_family}")
            }
            Statement::DropOperatorClass(drop_operator_class) => {
                write!(f, "{drop_operator_class}")
            }
            Statement::CreateRole(create_role) => write!(f, "{create_role}"),
            Statement::CreateSecret {
                or_replace,
                temporary,
                if_not_exists,
                name,
                storage_specifier,
                secret_type,
                options,
            } => {
                write!(
                    f,
                    "CREATE {or_replace}",
                    or_replace = if *or_replace { "OR REPLACE " } else { "" },
                )?;
                if let Some(t) = temporary {
                    write!(f, "{}", if *t { "TEMPORARY " } else { "PERSISTENT " })?;
                }
                write!(
                    f,
                    "SECRET {if_not_exists}",
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                )?;
                if let Some(n) = name {
                    write!(f, "{n} ")?;
                };
                if let Some(s) = storage_specifier {
                    write!(f, "IN {s} ")?;
                }
                write!(f, "( TYPE {secret_type}",)?;
                if !options.is_empty() {
                    write!(f, ", {o}", o = display_comma_separated(options))?;
                }
                write!(f, " )")?;
                Ok(())
            }
            Statement::CreateServer(stmt) => {
                write!(f, "{stmt}")
            }
            Statement::CreatePolicy(policy) => write!(f, "{policy}"),
            Statement::CreateConnector(create_connector) => create_connector.fmt(f),
            Statement::CreateOperator(create_operator) => create_operator.fmt(f),
            Statement::CreateOperatorFamily(create_operator_family) => {
                create_operator_family.fmt(f)
            }
            Statement::CreateOperatorClass(create_operator_class) => create_operator_class.fmt(f),
            Statement::AlterTable(alter_table) => write!(f, "{alter_table}"),
            Statement::AlterIndex { name, operation } => {
                write!(f, "ALTER INDEX {name} {operation}")
            }
            Statement::AlterView {
                name,
                columns,
                query,
                with_options,
            } => {
                write!(f, "ALTER VIEW {name}")?;
                if !with_options.is_empty() {
                    write!(f, " WITH ({})", display_comma_separated(with_options))?;
                }
                if !columns.is_empty() {
                    write!(f, " ({})", display_comma_separated(columns))?;
                }
                write!(f, " AS {query}")
            }
            Statement::AlterFunction(alter_function) => write!(f, "{alter_function}"),
            Statement::AlterType(AlterType { name, operation }) => {
                write!(f, "ALTER TYPE {name} {operation}")
            }
            Statement::AlterCollation(alter_collation) => write!(f, "{alter_collation}"),
            Statement::AlterOperator(alter_operator) => write!(f, "{alter_operator}"),
            Statement::AlterOperatorFamily(alter_operator_family) => {
                write!(f, "{alter_operator_family}")
            }
            Statement::AlterOperatorClass(alter_operator_class) => {
                write!(f, "{alter_operator_class}")
            }
            Statement::AlterRole { name, operation } => {
                write!(f, "ALTER ROLE {name} {operation}")
            }
            Statement::AlterPolicy(alter_policy) => write!(f, "{alter_policy}"),
            Statement::AlterConnector {
                name,
                properties,
                url,
                owner,
            } => {
                write!(f, "ALTER CONNECTOR {name}")?;
                if let Some(properties) = properties {
                    write!(
                        f,
                        " SET DCPROPERTIES({})",
                        display_comma_separated(properties)
                    )?;
                }
                if let Some(url) = url {
                    write!(f, " SET URL '{url}'")?;
                }
                if let Some(owner) = owner {
                    write!(f, " SET OWNER {owner}")?;
                }
                Ok(())
            }
            Statement::AlterSession {
                set,
                session_params,
            } => {
                write!(
                    f,
                    "ALTER SESSION {set}",
                    set = if *set { "SET" } else { "UNSET" }
                )?;
                if !session_params.options.is_empty() {
                    if *set {
                        write!(f, " {session_params}")?;
                    } else {
                        let options = session_params
                            .options
                            .iter()
                            .map(|p| p.option_name.clone())
                            .collect::<Vec<_>>();
                        write!(f, " {}", display_separated(&options, ", "))?;
                    }
                }
                Ok(())
            }
            Statement::Drop {
                object_type,
                if_exists,
                names,
                cascade,
                restrict,
                purge,
                temporary,
                table,
            } => {
                write!(
                    f,
                    "DROP {}{}{} {}{}{}{}",
                    if *temporary { "TEMPORARY " } else { "" },
                    object_type,
                    if *if_exists { " IF EXISTS" } else { "" },
                    display_comma_separated(names),
                    if *cascade { " CASCADE" } else { "" },
                    if *restrict { " RESTRICT" } else { "" },
                    if *purge { " PURGE" } else { "" },
                )?;
                if let Some(table_name) = table.as_ref() {
                    write!(f, " ON {table_name}")?;
                };
                Ok(())
            }
            Statement::DropFunction(drop_function) => write!(f, "{drop_function}"),
            Statement::DropDomain(DropDomain {
                if_exists,
                name,
                drop_behavior,
            }) => {
                write!(
                    f,
                    "DROP DOMAIN{} {name}",
                    if *if_exists { " IF EXISTS" } else { "" },
                )?;
                if let Some(op) = drop_behavior {
                    write!(f, " {op}")?;
                }
                Ok(())
            }
            Statement::DropProcedure {
                if_exists,
                proc_desc,
                drop_behavior,
            } => {
                write!(
                    f,
                    "DROP PROCEDURE{} {}",
                    if *if_exists { " IF EXISTS" } else { "" },
                    display_comma_separated(proc_desc),
                )?;
                if let Some(op) = drop_behavior {
                    write!(f, " {op}")?;
                }
                Ok(())
            }
            Statement::DropSecret {
                if_exists,
                temporary,
                name,
                storage_specifier,
            } => {
                write!(f, "DROP ")?;
                if let Some(t) = temporary {
                    write!(f, "{}", if *t { "TEMPORARY " } else { "PERSISTENT " })?;
                }
                write!(
                    f,
                    "SECRET {if_exists}{name}",
                    if_exists = if *if_exists { "IF EXISTS " } else { "" },
                )?;
                if let Some(s) = storage_specifier {
                    write!(f, " FROM {s}")?;
                }
                Ok(())
            }
            Statement::DropPolicy(policy) => write!(f, "{policy}"),
            Statement::DropConnector { if_exists, name } => {
                write!(
                    f,
                    "DROP CONNECTOR {if_exists}{name}",
                    if_exists = if *if_exists { "IF EXISTS " } else { "" }
                )?;
                Ok(())
            }
            Statement::Discard { object_type } => {
                write!(f, "DISCARD {object_type}")?;
                Ok(())
            }
            Self::Set(set) => write!(f, "{set}"),
            Statement::ShowVariable { variable } => {
                write!(f, "SHOW")?;
                if !variable.is_empty() {
                    write!(f, " {}", display_separated(variable, " "))?;
                }
                Ok(())
            }
            Statement::ShowStatus {
                filter,
                global,
                session,
            } => {
                write!(f, "SHOW")?;
                if *global {
                    write!(f, " GLOBAL")?;
                }
                if *session {
                    write!(f, " SESSION")?;
                }
                write!(f, " STATUS")?;
                if filter.is_some() {
                    write!(f, " {}", filter.as_ref().unwrap())?;
                }
                Ok(())
            }
            Statement::ShowVariables {
                filter,
                global,
                session,
            } => {
                write!(f, "SHOW")?;
                if *global {
                    write!(f, " GLOBAL")?;
                }
                if *session {
                    write!(f, " SESSION")?;
                }
                write!(f, " VARIABLES")?;
                if filter.is_some() {
                    write!(f, " {}", filter.as_ref().unwrap())?;
                }
                Ok(())
            }
            Statement::ShowCreate { obj_type, obj_name } => {
                write!(f, "SHOW CREATE {obj_type} {obj_name}",)?;
                Ok(())
            }
            Statement::ShowColumns {
                extended,
                full,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {extended}{full}COLUMNS{show_options}",
                    extended = if *extended { "EXTENDED " } else { "" },
                    full = if *full { "FULL " } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowDatabases {
                terse,
                history,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {terse}DATABASES{history}{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                    history = if *history { " HISTORY" } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowCatalogs {
                terse,
                history,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {terse}CATALOGS{history}{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                    history = if *history { " HISTORY" } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowProcessList { full } => {
                write!(
                    f,
                    "SHOW {full}PROCESSLIST",
                    full = if *full { "FULL " } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowSchemas {
                terse,
                history,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {terse}SCHEMAS{history}{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                    history = if *history { " HISTORY" } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowObjects(ShowObjects {
                terse,
                show_options,
            }) => {
                write!(
                    f,
                    "SHOW {terse}OBJECTS{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowTables {
                terse,
                history,
                extended,
                full,
                external,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {terse}{extended}{full}{external}TABLES{history}{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                    extended = if *extended { "EXTENDED " } else { "" },
                    full = if *full { "FULL " } else { "" },
                    external = if *external { "EXTERNAL " } else { "" },
                    history = if *history { " HISTORY" } else { "" },
                )?;
                Ok(())
            }
            Statement::ShowViews {
                terse,
                materialized,
                show_options,
            } => {
                write!(
                    f,
                    "SHOW {terse}{materialized}VIEWS{show_options}",
                    terse = if *terse { "TERSE " } else { "" },
                    materialized = if *materialized { "MATERIALIZED " } else { "" }
                )?;
                Ok(())
            }
            Statement::ShowFunctions { filter } => {
                write!(f, "SHOW FUNCTIONS")?;
                if let Some(filter) = filter {
                    write!(f, " {filter}")?;
                }
                Ok(())
            }
            Statement::Use(use_expr) => use_expr.fmt(f),
            Statement::ShowCollation { filter } => {
                write!(f, "SHOW COLLATION")?;
                if let Some(filter) = filter {
                    write!(f, " {filter}")?;
                }
                Ok(())
            }
            Statement::ShowCharset(show_stm) => show_stm.fmt(f),
            Statement::StartTransaction {
                modes,
                begin: syntax_begin,
                transaction,
                modifier,
                statements,
                exception,
                has_end_keyword,
            } => {
                if *syntax_begin {
                    if let Some(modifier) = *modifier {
                        write!(f, "BEGIN {modifier}")?;
                    } else {
                        write!(f, "BEGIN")?;
                    }
                } else {
                    write!(f, "START")?;
                }
                if let Some(transaction) = transaction {
                    write!(f, " {transaction}")?;
                }
                if !modes.is_empty() {
                    write!(f, " {}", display_comma_separated(modes))?;
                }
                if !statements.is_empty() {
                    write!(f, " ")?;
                    format_statement_list(f, statements)?;
                }
                if let Some(exception_when) = exception {
                    write!(f, " EXCEPTION")?;
                    for when in exception_when {
                        write!(f, " {when}")?;
                    }
                }
                if *has_end_keyword {
                    write!(f, " END")?;
                }
                Ok(())
            }
            Statement::Commit {
                chain,
                end: end_syntax,
                modifier,
            } => {
                if *end_syntax {
                    write!(f, "END")?;
                    if let Some(modifier) = *modifier {
                        write!(f, " {modifier}")?;
                    }
                    if *chain {
                        write!(f, " AND CHAIN")?;
                    }
                } else {
                    write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" })?;
                }
                Ok(())
            }
            Statement::Rollback { chain, savepoint } => {
                write!(f, "ROLLBACK")?;

                if *chain {
                    write!(f, " AND CHAIN")?;
                }

                if let Some(savepoint) = savepoint {
                    write!(f, " TO SAVEPOINT {savepoint}")?;
                }

                Ok(())
            }
            Statement::CreateSchema {
                schema_name,
                if_not_exists,
                with,
                options,
                default_collate_spec,
                clone,
            } => {
                write!(
                    f,
                    "CREATE SCHEMA {if_not_exists}{name}",
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                    name = schema_name
                )?;

                if let Some(collate) = default_collate_spec {
                    write!(f, " DEFAULT COLLATE {collate}")?;
                }

                if let Some(with) = with {
                    write!(f, " WITH ({})", display_comma_separated(with))?;
                }

                if let Some(options) = options {
                    write!(f, " OPTIONS({})", display_comma_separated(options))?;
                }

                if let Some(clone) = clone {
                    write!(f, " CLONE {clone}")?;
                }
                Ok(())
            }
            Statement::Assert { condition, message } => {
                write!(f, "ASSERT {condition}")?;
                if let Some(m) = message {
                    write!(f, " AS {m}")?;
                }
                Ok(())
            }
            Statement::Grant(grant) => write!(f, "{grant}"),
            Statement::Deny(s) => write!(f, "{s}"),
            Statement::Revoke(revoke) => write!(f, "{revoke}"),
            Statement::Deallocate { name, prepare } => write!(
                f,
                "DEALLOCATE {prepare}{name}",
                prepare = if *prepare { "PREPARE " } else { "" },
                name = name,
            ),
            Statement::Execute {
                name,
                parameters,
                has_parentheses,
                immediate,
                into,
                using,
                output,
                default,
            } => {
                let (open, close) = if *has_parentheses {
                    // Space before `(` only when there is no name directly preceding it.
                    (if name.is_some() { "(" } else { " (" }, ")")
                } else {
                    (if parameters.is_empty() { "" } else { " " }, "")
                };
                write!(f, "EXECUTE")?;
                if *immediate {
                    write!(f, " IMMEDIATE")?;
                }
                if let Some(name) = name {
                    write!(f, " {name}")?;
                }
                write!(f, "{open}{}{close}", display_comma_separated(parameters),)?;
                if !into.is_empty() {
                    write!(f, " INTO {}", display_comma_separated(into))?;
                }
                if !using.is_empty() {
                    write!(f, " USING {}", display_comma_separated(using))?;
                };
                if *output {
                    write!(f, " OUTPUT")?;
                }
                if *default {
                    write!(f, " DEFAULT")?;
                }
                Ok(())
            }
            Statement::Prepare {
                name,
                data_types,
                statement,
            } => {
                write!(f, "PREPARE {name} ")?;
                if !data_types.is_empty() {
                    write!(f, "({}) ", display_comma_separated(data_types))?;
                }
                write!(f, "AS {statement}")
            }
            Statement::Comment {
                object_type,
                object_name,
                comment,
                if_exists,
            } => {
                write!(f, "COMMENT ")?;
                if *if_exists {
                    write!(f, "IF EXISTS ")?
                };
                write!(f, "ON {object_type} {object_name} IS ")?;
                if let Some(c) = comment {
                    write!(f, "'{c}'")
                } else {
                    write!(f, "NULL")
                }
            }
            Statement::Savepoint { name } => {
                write!(f, "SAVEPOINT ")?;
                write!(f, "{name}")
            }
            Statement::ReleaseSavepoint { name } => {
                write!(f, "RELEASE SAVEPOINT {name}")
            }
            Statement::Merge(merge) => merge.fmt(f),
            Statement::Cache {
                table_name,
                table_flag,
                has_as,
                options,
                query,
            } => {
                if let Some(table_flag) = table_flag {
                    write!(f, "CACHE {table_flag} TABLE {table_name}")?;
                } else {
                    write!(f, "CACHE TABLE {table_name}")?;
                }

                if !options.is_empty() {
                    write!(f, " OPTIONS({})", display_comma_separated(options))?;
                }

                match (*has_as, query) {
                    (true, Some(query)) => write!(f, " AS {query}"),
                    (true, None) => f.write_str(" AS"),
                    (false, Some(query)) => write!(f, " {query}"),
                    (false, None) => Ok(()),
                }
            }
            Statement::UNCache {
                table_name,
                if_exists,
            } => {
                if *if_exists {
                    write!(f, "UNCACHE TABLE IF EXISTS {table_name}")
                } else {
                    write!(f, "UNCACHE TABLE {table_name}")
                }
            }
            Statement::CreateSequence {
                temporary,
                if_not_exists,
                name,
                data_type,
                sequence_options,
                owned_by,
            } => {
                let as_type: String = if let Some(dt) = data_type.as_ref() {
                    //Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
                    // " AS ".to_owned() + &dt.to_string()
                    [" AS ", &dt.to_string()].concat()
                } else {
                    "".to_string()
                };
                write!(
                    f,
                    "CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                    temporary = if *temporary { "TEMPORARY " } else { "" },
                    name = name,
                    as_type = as_type
                )?;
                for sequence_option in sequence_options {
                    write!(f, "{sequence_option}")?;
                }
                if let Some(ob) = owned_by.as_ref() {
                    write!(f, " OWNED BY {ob}")?;
                }
                write!(f, "")
            }
            Statement::CreateStage {
                or_replace,
                temporary,
                if_not_exists,
                name,
                stage_params,
                directory_table_params,
                file_format,
                copy_options,
                comment,
                ..
            } => {
                write!(
                    f,
                    "CREATE {or_replace}{temp}STAGE {if_not_exists}{name}{stage_params}",
                    temp = if *temporary { "TEMPORARY " } else { "" },
                    or_replace = if *or_replace { "OR REPLACE " } else { "" },
                    if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
                )?;
                if !directory_table_params.options.is_empty() {
                    write!(f, " DIRECTORY=({directory_table_params})")?;
                }
                if !file_format.options.is_empty() {
                    write!(f, " FILE_FORMAT=({file_format})")?;
                }
                if !copy_options.options.is_empty() {
                    write!(f, " COPY_OPTIONS=({copy_options})")?;
                }
                if comment.is_some() {
                    write!(f, " COMMENT='{}'", comment.as_ref().unwrap())?;
                }
                Ok(())
            }
            Statement::CopyIntoSnowflake {
                kind,
                into,
                into_columns,
                from_obj,
                from_obj_alias,
                stage_params,
                from_transformations,
                from_query,
                files,
                pattern,
                file_format,
                copy_options,
                validation_mode,
                partition,
            } => {
                write!(f, "COPY INTO {into}")?;
                if let Some(into_columns) = into_columns {
                    write!(f, " ({})", display_comma_separated(into_columns))?;
                }
                if let Some(from_transformations) = from_transformations {
                    // Data load with transformation
                    if let Some(from_stage) = from_obj {
                        write!(
                            f,
                            " FROM (SELECT {} FROM {}{}",
                            display_separated(from_transformations, ", "),
                            from_stage,
                            stage_params
                        )?;
                    }
                    if let Some(from_obj_alias) = from_obj_alias {
                        write!(f, " AS {from_obj_alias}")?;
                    }
                    write!(f, ")")?;
                } else if let Some(from_obj) = from_obj {
                    // Standard data load
                    write!(f, " FROM {from_obj}{stage_params}")?;
                    if let Some(from_obj_alias) = from_obj_alias {
                        write!(f, " AS {from_obj_alias}")?;
                    }
                } else if let Some(from_query) = from_query {
                    // Data unload from query
                    write!(f, " FROM ({from_query})")?;
                }

                if let Some(files) = files {
                    write!(f, " FILES = ('{}')", display_separated(files, "', '"))?;
                }
                if let Some(pattern) = pattern {
                    write!(f, " PATTERN = '{pattern}'")?;
                }
                if let Some(partition) = partition {
                    write!(f, " PARTITION BY {partition}")?;
                }
                if !file_format.options.is_empty() {
                    write!(f, " FILE_FORMAT=({file_format})")?;
                }
                if !copy_options.options.is_empty() {
                    match kind {
                        CopyIntoSnowflakeKind::Table => {
                            write!(f, " COPY_OPTIONS=({copy_options})")?
                        }
                        CopyIntoSnowflakeKind::Location => write!(f, " {copy_options}")?,
                    }
                }
                if let Some(validation_mode) = validation_mode {
                    write!(f, " VALIDATION_MODE = {validation_mode}")?;
                }
                Ok(())
            }
            Statement::CreateType {
                name,
                representation,
            } => {
                write!(f, "CREATE TYPE {name}")?;
                if let Some(repr) = representation {
                    write!(f, " {repr}")?;
                }
                Ok(())
            }
            Statement::Pragma { name, value, is_eq } => {
                write!(f, "PRAGMA {name}")?;
                if value.is_some() {
                    let val = value.as_ref().unwrap();
                    if *is_eq {
                        write!(f, " = {val}")?;
                    } else {
                        write!(f, "({val})")?;
                    }
                }
                Ok(())
            }
            Statement::Lock(lock) => lock.fmt(f),
            Statement::LockTables { tables } => {
                write!(f, "LOCK TABLES {}", display_comma_separated(tables))
            }
            Statement::UnlockTables => {
                write!(f, "UNLOCK TABLES")
            }
            Statement::Unload {
                query,
                query_text,
                to,
                auth,
                with,
                options,
            } => {
                write!(f, "UNLOAD(")?;
                if let Some(query) = query {
                    write!(f, "{query}")?;
                }
                if let Some(query_text) = query_text {
                    write!(f, "'{query_text}'")?;
                }
                write!(f, ") TO {to}")?;
                if let Some(auth) = auth {
                    write!(f, " IAM_ROLE {auth}")?;
                }
                if !with.is_empty() {
                    write!(f, " WITH ({})", display_comma_separated(with))?;
                }
                if !options.is_empty() {
                    write!(f, " {}", display_separated(options, " "))?;
                }
                Ok(())
            }
            Statement::OptimizeTable {
                name,
                has_table_keyword,
                on_cluster,
                partition,
                include_final,
                deduplicate,
                predicate,
                zorder,
            } => {
                write!(f, "OPTIMIZE")?;
                if *has_table_keyword {
                    write!(f, " TABLE")?;
                }
                write!(f, " {name}")?;
                if let Some(on_cluster) = on_cluster {
                    write!(f, " ON CLUSTER {on_cluster}")?;
                }
                if let Some(partition) = partition {
                    write!(f, " {partition}")?;
                }
                if *include_final {
                    write!(f, " FINAL")?;
                }
                if let Some(deduplicate) = deduplicate {
                    write!(f, " {deduplicate}")?;
                }
                if let Some(predicate) = predicate {
                    write!(f, " WHERE {predicate}")?;
                }
                if let Some(zorder) = zorder {
                    write!(f, " ZORDER BY ({})", display_comma_separated(zorder))?;
                }
                Ok(())
            }
            Statement::LISTEN { channel } => {
                write!(f, "LISTEN {channel}")?;
                Ok(())
            }
            Statement::UNLISTEN { channel } => {
                write!(f, "UNLISTEN {channel}")?;
                Ok(())
            }
            Statement::NOTIFY { channel, payload } => {
                write!(f, "NOTIFY {channel}")?;
                if let Some(payload) = payload {
                    write!(f, ", '{payload}'")?;
                }
                Ok(())
            }
            Statement::RenameTable(rename_tables) => {
                write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
            }
            Statement::RaisError {
                message,
                severity,
                state,
                arguments,
                options,
            } => {
                write!(f, "RAISERROR({message}, {severity}, {state}")?;
                if !arguments.is_empty() {
                    write!(f, ", {}", display_comma_separated(arguments))?;
                }
                write!(f, ")")?;
                if !options.is_empty() {
                    write!(f, " WITH {}", display_comma_separated(options))?;
                }
                Ok(())
            }
            Statement::Throw(s) => write!(f, "{s}"),
            Statement::Print(s) => write!(f, "{s}"),
            Statement::WaitFor(s) => write!(f, "{s}"),
            Statement::Return(r) => write!(f, "{r}"),
            Statement::List(command) => write!(f, "LIST {command}"),
            Statement::Remove(command) => write!(f, "REMOVE {command}"),
            Statement::ExportData(e) => write!(f, "{e}"),
            Statement::CreateUser(s) => write!(f, "{s}"),
            Statement::AlterSchema(s) => write!(f, "{s}"),
            Statement::Vacuum(s) => write!(f, "{s}"),
            Statement::AlterUser(s) => write!(f, "{s}"),
            Statement::Reset(s) => write!(f, "{s}"),
        }
    }
}

/// Can use to describe options in create sequence or table column type identity
/// ```sql
/// [ INCREMENT [ BY ] increment ]
///     [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
///     [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SequenceOptions {
    /// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
    IncrementBy(Expr, bool),
    /// `MINVALUE <expr>` or `NO MINVALUE`.
    MinValue(Option<Expr>),
    /// `MAXVALUE <expr>` or `NO MAXVALUE`.
    MaxValue(Option<Expr>),
    /// `START [WITH] <expr>`; second value indicates presence of `WITH`.
    StartWith(Expr, bool),
    /// `CACHE <expr>` option.
    Cache(Expr),
    /// `CYCLE` or `NO CYCLE` option.
    Cycle(bool),
}

impl fmt::Display for SequenceOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SequenceOptions::IncrementBy(increment, by) => {
                write!(
                    f,
                    " INCREMENT{by} {increment}",
                    by = if *by { " BY" } else { "" },
                    increment = increment
                )
            }
            SequenceOptions::MinValue(Some(expr)) => {
                write!(f, " MINVALUE {expr}")
            }
            SequenceOptions::MinValue(None) => {
                write!(f, " NO MINVALUE")
            }
            SequenceOptions::MaxValue(Some(expr)) => {
                write!(f, " MAXVALUE {expr}")
            }
            SequenceOptions::MaxValue(None) => {
                write!(f, " NO MAXVALUE")
            }
            SequenceOptions::StartWith(start, with) => {
                write!(
                    f,
                    " START{with} {start}",
                    with = if *with { " WITH" } else { "" },
                    start = start
                )
            }
            SequenceOptions::Cache(cache) => {
                write!(f, " CACHE {}", *cache)
            }
            SequenceOptions::Cycle(no) => {
                write!(f, " {}CYCLE", if *no { "NO " } else { "" })
            }
        }
    }
}

/// Assignment for a `SET` statement (name [=|TO] value)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct SetAssignment {
    /// Optional context scope (e.g., SESSION or LOCAL).
    pub scope: Option<ContextModifier>,
    /// Assignment target name.
    pub name: ObjectName,
    /// Assigned expression value.
    pub value: Expr,
}

impl fmt::Display for SetAssignment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}{} = {}",
            self.scope.map(|s| format!("{s}")).unwrap_or_default(),
            self.name,
            self.value
        )
    }
}

/// Target of a `TRUNCATE TABLE` command
///
/// Note this is its own struct because `visit_relation` requires an `ObjectName` (not a `Vec<ObjectName>`)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TruncateTableTarget {
    /// name of the table being truncated
    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
    pub name: ObjectName,
    /// Postgres-specific option: explicitly exclude descendants (also default without ONLY)
    /// ```sql
    /// TRUNCATE TABLE ONLY name
    /// ```
    /// <https://www.postgresql.org/docs/current/sql-truncate.html>
    pub only: bool,
    /// Postgres-specific option: asterisk after table name to explicitly indicate descendants
    /// ```sql
    /// TRUNCATE TABLE name [ * ]
    /// ```
    /// <https://www.postgresql.org/docs/current/sql-truncate.html>
    pub has_asterisk: bool,
}

impl fmt::Display for TruncateTableTarget {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.only {
            write!(f, "ONLY ")?;
        };
        write!(f, "{}", self.name)?;
        if self.has_asterisk {
            write!(f, " *")?;
        };
        Ok(())
    }
}

/// A `LOCK` statement.
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Lock {
    /// List of tables to lock.
    pub tables: Vec<LockTableTarget>,
    /// Lock mode.
    pub lock_mode: Option<LockTableMode>,
    /// Whether `NOWAIT` was specified.
    pub nowait: bool,
}

impl fmt::Display for Lock {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "LOCK TABLE {}", display_comma_separated(&self.tables))?;
        if let Some(lock_mode) = &self.lock_mode {
            write!(f, " IN {lock_mode} MODE")?;
        }
        if self.nowait {
            write!(f, " NOWAIT")?;
        }
        Ok(())
    }
}

/// Target of a `LOCK TABLE` command
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LockTableTarget {
    /// Name of the table being locked.
    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
    pub name: ObjectName,
    /// Whether `ONLY` was specified to exclude descendant tables.
    pub only: bool,
    /// Whether `*` was specified to explicitly include descendant tables.
    pub has_asterisk: bool,
}

impl fmt::Display for LockTableTarget {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.only {
            write!(f, "ONLY ")?;
        }
        write!(f, "{}", self.name)?;
        if self.has_asterisk {
            write!(f, " *")?;
        }
        Ok(())
    }
}

/// PostgreSQL lock modes for `LOCK TABLE`.
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum LockTableMode {
    /// `ACCESS SHARE`
    AccessShare,
    /// `ROW SHARE`
    RowShare,
    /// `ROW EXCLUSIVE`
    RowExclusive,
    /// `SHARE UPDATE EXCLUSIVE`
    ShareUpdateExclusive,
    /// `SHARE`
    Share,
    /// `SHARE ROW EXCLUSIVE`
    ShareRowExclusive,
    /// `EXCLUSIVE`
    Exclusive,
    /// `ACCESS EXCLUSIVE`
    AccessExclusive,
}

impl fmt::Display for LockTableMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            Self::AccessShare => "ACCESS SHARE",
            Self::RowShare => "ROW SHARE",
            Self::RowExclusive => "ROW EXCLUSIVE",
            Self::ShareUpdateExclusive => "SHARE UPDATE EXCLUSIVE",
            Self::Share => "SHARE",
            Self::ShareRowExclusive => "SHARE ROW EXCLUSIVE",
            Self::Exclusive => "EXCLUSIVE",
            Self::AccessExclusive => "ACCESS EXCLUSIVE",
        };
        write!(f, "{text}")
    }
}

/// PostgreSQL identity option for TRUNCATE table
/// [ RESTART IDENTITY | CONTINUE IDENTITY ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TruncateIdentityOption {
    /// Restart identity values (RESTART IDENTITY).
    Restart,
    /// Continue identity values (CONTINUE IDENTITY).
    Continue,
}

/// Cascade/restrict option for Postgres TRUNCATE table, MySQL GRANT/REVOKE, etc.
/// [ CASCADE | RESTRICT ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CascadeOption {
    /// Apply cascading action (e.g., CASCADE).
    Cascade,
    /// Restrict the action (e.g., RESTRICT).
    Restrict,
}

impl Display for CascadeOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CascadeOption::Cascade => write!(f, "CASCADE"),
            CascadeOption::Restrict => write!(f, "RESTRICT"),
        }
    }
}

/// Transaction started with [ TRANSACTION | WORK | TRAN ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum BeginTransactionKind {
    /// Standard `TRANSACTION` keyword.
    Transaction,
    /// Alternate `WORK` keyword.
    Work,
    /// MSSQL shorthand `TRAN` keyword.
    /// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/begin-transaction-transact-sql>
    Tran,
}

impl Display for BeginTransactionKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            BeginTransactionKind::Transaction => write!(f, "TRANSACTION"),
            BeginTransactionKind::Work => write!(f, "WORK"),
            BeginTransactionKind::Tran => write!(f, "TRAN"),
        }
    }
}

/// Can use to describe options in  create sequence or table column type identity
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MinMaxValue {
    /// Clause is not specified.
    Empty,
    /// NO MINVALUE / NO MAXVALUE.
    None,
    /// `MINVALUE <expr>` / `MAXVALUE <expr>`.
    Some(Expr),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[non_exhaustive]
/// Behavior to apply for `INSERT` when a conflict occurs.
pub enum OnInsert {
    /// ON DUPLICATE KEY UPDATE (MySQL when the key already exists, then execute an update instead)
    DuplicateKeyUpdate(Vec<Assignment>),
    /// ON CONFLICT is a PostgreSQL and Sqlite extension
    OnConflict(OnConflict),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Optional aliases for `INSERT` targets: row alias and optional column aliases.
pub struct InsertAliases {
    /// Row alias (table-style alias) for the inserted values.
    pub row_alias: ObjectName,
    /// Optional list of column aliases for the inserted values.
    pub col_aliases: Option<Vec<Ident>>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
pub struct TableAliasWithoutColumns {
    /// `true` if the aliases was explicitly introduced with the "AS" keyword
    pub explicit: bool,
    /// the alias name itself
    pub alias: Ident,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `ON CONFLICT` clause representation.
pub struct OnConflict {
    /// Optional conflict target specifying columns or constraint.
    pub conflict_target: Option<ConflictTarget>,
    /// Action to take when a conflict occurs.
    pub action: OnConflictAction,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Target specification for an `ON CONFLICT` clause.
pub enum ConflictTarget {
    /// Target specified as a list of columns.
    Columns(Vec<Ident>),
    /// Target specified as a named constraint.
    OnConstraint(ObjectName),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Action to perform when an `ON CONFLICT` target is matched.
pub enum OnConflictAction {
    /// Do nothing on conflict.
    DoNothing,
    /// Perform an update on conflict.
    DoUpdate(DoUpdate),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Details for `DO UPDATE` action of an `ON CONFLICT` clause.
pub struct DoUpdate {
    /// Column assignments to perform on update.
    pub assignments: Vec<Assignment>,
    /// Optional WHERE clause limiting the update.
    pub selection: Option<Expr>,
}

impl fmt::Display for OnInsert {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::DuplicateKeyUpdate(expr) => write!(
                f,
                " ON DUPLICATE KEY UPDATE {}",
                display_comma_separated(expr)
            ),
            Self::OnConflict(o) => write!(f, "{o}"),
        }
    }
}
impl fmt::Display for OnConflict {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, " ON CONFLICT")?;
        if let Some(target) = &self.conflict_target {
            write!(f, "{target}")?;
        }
        write!(f, " {}", self.action)
    }
}
impl fmt::Display for ConflictTarget {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConflictTarget::Columns(cols) => write!(f, "({})", display_comma_separated(cols)),
            ConflictTarget::OnConstraint(name) => write!(f, " ON CONSTRAINT {name}"),
        }
    }
}
impl fmt::Display for OnConflictAction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::DoNothing => write!(f, "DO NOTHING"),
            Self::DoUpdate(do_update) => {
                write!(f, "DO UPDATE")?;
                if !do_update.assignments.is_empty() {
                    write!(
                        f,
                        " SET {}",
                        display_comma_separated(&do_update.assignments)
                    )?;
                }
                if let Some(selection) = &do_update.selection {
                    write!(f, " WHERE {selection}")?;
                }
                Ok(())
            }
        }
    }
}

/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Privileges {
    /// All privileges applicable to the object type
    All {
        /// Optional keyword from the spec, ignored in practice
        with_privileges_keyword: bool,
    },
    /// Specific privileges (e.g. `SELECT`, `INSERT`)
    Actions(Vec<Action>),
}

impl fmt::Display for Privileges {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Privileges::All {
                with_privileges_keyword,
            } => {
                write!(
                    f,
                    "ALL{}",
                    if *with_privileges_keyword {
                        " PRIVILEGES"
                    } else {
                        ""
                    }
                )
            }
            Privileges::Actions(actions) => {
                write!(f, "{}", display_comma_separated(actions))
            }
        }
    }
}

/// Specific direction for FETCH statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FetchDirection {
    /// Fetch a specific count of rows.
    Count {
        /// The limit value for the count.
        limit: ValueWithSpan,
    },
    /// Fetch the next row.
    Next,
    /// Fetch the prior row.
    Prior,
    /// Fetch the first row.
    First,
    /// Fetch the last row.
    Last,
    /// Fetch an absolute row by index.
    Absolute {
        /// The absolute index value.
        limit: ValueWithSpan,
    },
    /// Fetch a row relative to the current position.
    Relative {
        /// The relative offset value.
        limit: ValueWithSpan,
    },
    /// Fetch all rows.
    All,
    // FORWARD
    // FORWARD count
    /// Fetch forward by an optional limit.
    Forward {
        /// Optional forward limit.
        limit: Option<ValueWithSpan>,
    },
    /// Fetch all forward rows.
    ForwardAll,
    // BACKWARD
    // BACKWARD count
    /// Fetch backward by an optional limit.
    Backward {
        /// Optional backward limit.
        limit: Option<ValueWithSpan>,
    },
    /// Fetch all backward rows.
    BackwardAll,
}

impl fmt::Display for FetchDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
            FetchDirection::Next => f.write_str("NEXT")?,
            FetchDirection::Prior => f.write_str("PRIOR")?,
            FetchDirection::First => f.write_str("FIRST")?,
            FetchDirection::Last => f.write_str("LAST")?,
            FetchDirection::Absolute { limit } => {
                f.write_str("ABSOLUTE ")?;
                f.write_str(&limit.to_string())?;
            }
            FetchDirection::Relative { limit } => {
                f.write_str("RELATIVE ")?;
                f.write_str(&limit.to_string())?;
            }
            FetchDirection::All => f.write_str("ALL")?,
            FetchDirection::Forward { limit } => {
                f.write_str("FORWARD")?;

                if let Some(l) = limit {
                    f.write_str(" ")?;
                    f.write_str(&l.to_string())?;
                }
            }
            FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
            FetchDirection::Backward { limit } => {
                f.write_str("BACKWARD")?;

                if let Some(l) = limit {
                    f.write_str(" ")?;
                    f.write_str(&l.to_string())?;
                }
            }
            FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
        };

        Ok(())
    }
}

/// The "position" for a FETCH statement.
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/fetch-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FetchPosition {
    /// Use `FROM <pos>` position specifier.
    From,
    /// Use `IN <pos>` position specifier.
    In,
}

impl fmt::Display for FetchPosition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FetchPosition::From => f.write_str("FROM")?,
            FetchPosition::In => f.write_str("IN")?,
        };

        Ok(())
    }
}

/// A privilege on a database object (table, sequence, etc.).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Action {
    /// Add a search optimization.
    AddSearchOptimization,
    /// Apply an `APPLY` operation with a specific type.
    Apply {
        /// The type of apply operation.
        apply_type: ActionApplyType,
    },
    /// Apply a budget operation.
    ApplyBudget,
    /// Attach a listing.
    AttachListing,
    /// Attach a policy.
    AttachPolicy,
    /// Audit operation.
    Audit,
    /// Bind a service endpoint.
    BindServiceEndpoint,
    /// Connect permission.
    Connect,
    /// Create action, optionally specifying an object type.
    Create {
        /// Optional object type to create.
        obj_type: Option<ActionCreateObjectType>,
    },
    /// Actions related to database roles.
    DatabaseRole {
        /// The role name.
        role: ObjectName,
    },
    /// Delete permission.
    Delete,
    /// Drop permission.
    Drop,
    /// Evolve schema permission.
    EvolveSchema,
    /// Exec action (execute) with optional object type.
    Exec {
        /// Optional execute object type.
        obj_type: Option<ActionExecuteObjectType>,
    },
    /// Execute action with optional object type.
    Execute {
        /// Optional execute object type.
        obj_type: Option<ActionExecuteObjectType>,
    },
    /// Failover operation.
    Failover,
    /// Use imported privileges.
    ImportedPrivileges,
    /// Import a share.
    ImportShare,
    /// Insert rows with optional column list.
    Insert {
        /// Optional list of target columns for insert.
        columns: Option<Vec<Ident>>,
    },
    /// Manage operation with a specific manage type.
    Manage {
        /// The specific manage sub-type.
        manage_type: ActionManageType,
    },
    /// Manage releases.
    ManageReleases,
    /// Manage versions.
    ManageVersions,
    /// Modify operation with an optional modify type.
    Modify {
        /// The optional modify sub-type.
        modify_type: Option<ActionModifyType>,
    },
    /// Monitor operation with an optional monitor type.
    Monitor {
        /// The optional monitor sub-type.
        monitor_type: Option<ActionMonitorType>,
    },
    /// Operate permission.
    Operate,
    /// Override share restrictions.
    OverrideShareRestrictions,
    /// Ownership permission.
    Ownership,
    /// Purchase a data exchange listing.
    PurchaseDataExchangeListing,

    /// Read access.
    Read,
    /// Read session-level access.
    ReadSession,
    /// References with optional column list.
    References {
        /// Optional list of referenced column identifiers.
        columns: Option<Vec<Ident>>,
    },
    /// Replication permission.
    Replicate,
    /// Resolve all references.
    ResolveAll,
    /// Role-related permission with target role name.
    Role {
        /// The target role name.
        role: ObjectName,
    },
    /// Select permission with optional column list.
    Select {
        /// Optional list of selected columns.
        columns: Option<Vec<Ident>>,
    },
    /// Temporary object permission.
    Temporary,
    /// Trigger-related permission.
    Trigger,
    /// Truncate permission.
    Truncate,
    /// Update permission with optional affected columns.
    Update {
        /// Optional list of columns affected by update.
        columns: Option<Vec<Ident>>,
    },
    /// Usage permission.
    Usage,
}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Action::AddSearchOptimization => f.write_str("ADD SEARCH OPTIMIZATION")?,
            Action::Apply { apply_type } => write!(f, "APPLY {apply_type}")?,
            Action::ApplyBudget => f.write_str("APPLYBUDGET")?,
            Action::AttachListing => f.write_str("ATTACH LISTING")?,
            Action::AttachPolicy => f.write_str("ATTACH POLICY")?,
            Action::Audit => f.write_str("AUDIT")?,
            Action::BindServiceEndpoint => f.write_str("BIND SERVICE ENDPOINT")?,
            Action::Connect => f.write_str("CONNECT")?,
            Action::Create { obj_type } => {
                f.write_str("CREATE")?;
                if let Some(obj_type) = obj_type {
                    write!(f, " {obj_type}")?
                }
            }
            Action::DatabaseRole { role } => write!(f, "DATABASE ROLE {role}")?,
            Action::Delete => f.write_str("DELETE")?,
            Action::Drop => f.write_str("DROP")?,
            Action::EvolveSchema => f.write_str("EVOLVE SCHEMA")?,
            Action::Exec { obj_type } => {
                f.write_str("EXEC")?;
                if let Some(obj_type) = obj_type {
                    write!(f, " {obj_type}")?
                }
            }
            Action::Execute { obj_type } => {
                f.write_str("EXECUTE")?;
                if let Some(obj_type) = obj_type {
                    write!(f, " {obj_type}")?
                }
            }
            Action::Failover => f.write_str("FAILOVER")?,
            Action::ImportedPrivileges => f.write_str("IMPORTED PRIVILEGES")?,
            Action::ImportShare => f.write_str("IMPORT SHARE")?,
            Action::Insert { .. } => f.write_str("INSERT")?,
            Action::Manage { manage_type } => write!(f, "MANAGE {manage_type}")?,
            Action::ManageReleases => f.write_str("MANAGE RELEASES")?,
            Action::ManageVersions => f.write_str("MANAGE VERSIONS")?,
            Action::Modify { modify_type } => {
                write!(f, "MODIFY")?;
                if let Some(modify_type) = modify_type {
                    write!(f, " {modify_type}")?;
                }
            }
            Action::Monitor { monitor_type } => {
                write!(f, "MONITOR")?;
                if let Some(monitor_type) = monitor_type {
                    write!(f, " {monitor_type}")?
                }
            }
            Action::Operate => f.write_str("OPERATE")?,
            Action::OverrideShareRestrictions => f.write_str("OVERRIDE SHARE RESTRICTIONS")?,
            Action::Ownership => f.write_str("OWNERSHIP")?,
            Action::PurchaseDataExchangeListing => f.write_str("PURCHASE DATA EXCHANGE LISTING")?,
            Action::Read => f.write_str("READ")?,
            Action::ReadSession => f.write_str("READ SESSION")?,
            Action::References { .. } => f.write_str("REFERENCES")?,
            Action::Replicate => f.write_str("REPLICATE")?,
            Action::ResolveAll => f.write_str("RESOLVE ALL")?,
            Action::Role { role } => write!(f, "ROLE {role}")?,
            Action::Select { .. } => f.write_str("SELECT")?,
            Action::Temporary => f.write_str("TEMPORARY")?,
            Action::Trigger => f.write_str("TRIGGER")?,
            Action::Truncate => f.write_str("TRUNCATE")?,
            Action::Update { .. } => f.write_str("UPDATE")?,
            Action::Usage => f.write_str("USAGE")?,
        };
        match self {
            Action::Insert { columns }
            | Action::References { columns }
            | Action::Select { columns }
            | Action::Update { columns } => {
                if let Some(columns) = columns {
                    write!(f, " ({})", display_comma_separated(columns))?;
                }
            }
            _ => (),
        };
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `CREATE` privilege.
pub enum ActionCreateObjectType {
    /// An account-level object.
    Account,
    /// An application object.
    Application,
    /// An application package object.
    ApplicationPackage,
    /// A compute pool object.
    ComputePool,
    /// A data exchange listing.
    DataExchangeListing,
    /// A database object.
    Database,
    /// An external volume object.
    ExternalVolume,
    /// A failover group object.
    FailoverGroup,
    /// An integration object.
    Integration,
    /// A network policy object.
    NetworkPolicy,
    /// An organization listing.
    OrganiationListing,
    /// A replication group object.
    ReplicationGroup,
    /// A role object.
    Role,
    /// A schema object.
    Schema,
    /// A share object.
    Share,
    /// A user object.
    User,
    /// A warehouse object.
    Warehouse,
}

impl fmt::Display for ActionCreateObjectType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionCreateObjectType::Account => write!(f, "ACCOUNT"),
            ActionCreateObjectType::Application => write!(f, "APPLICATION"),
            ActionCreateObjectType::ApplicationPackage => write!(f, "APPLICATION PACKAGE"),
            ActionCreateObjectType::ComputePool => write!(f, "COMPUTE POOL"),
            ActionCreateObjectType::DataExchangeListing => write!(f, "DATA EXCHANGE LISTING"),
            ActionCreateObjectType::Database => write!(f, "DATABASE"),
            ActionCreateObjectType::ExternalVolume => write!(f, "EXTERNAL VOLUME"),
            ActionCreateObjectType::FailoverGroup => write!(f, "FAILOVER GROUP"),
            ActionCreateObjectType::Integration => write!(f, "INTEGRATION"),
            ActionCreateObjectType::NetworkPolicy => write!(f, "NETWORK POLICY"),
            ActionCreateObjectType::OrganiationListing => write!(f, "ORGANIZATION LISTING"),
            ActionCreateObjectType::ReplicationGroup => write!(f, "REPLICATION GROUP"),
            ActionCreateObjectType::Role => write!(f, "ROLE"),
            ActionCreateObjectType::Schema => write!(f, "SCHEMA"),
            ActionCreateObjectType::Share => write!(f, "SHARE"),
            ActionCreateObjectType::User => write!(f, "USER"),
            ActionCreateObjectType::Warehouse => write!(f, "WAREHOUSE"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `APPLY` privilege.
pub enum ActionApplyType {
    /// Apply an aggregation policy.
    AggregationPolicy,
    /// Apply an authentication policy.
    AuthenticationPolicy,
    /// Apply a join policy.
    JoinPolicy,
    /// Apply a masking policy.
    MaskingPolicy,
    /// Apply a packages policy.
    PackagesPolicy,
    /// Apply a password policy.
    PasswordPolicy,
    /// Apply a projection policy.
    ProjectionPolicy,
    /// Apply a row access policy.
    RowAccessPolicy,
    /// Apply a session policy.
    SessionPolicy,
    /// Apply a tag.
    Tag,
}

impl fmt::Display for ActionApplyType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionApplyType::AggregationPolicy => write!(f, "AGGREGATION POLICY"),
            ActionApplyType::AuthenticationPolicy => write!(f, "AUTHENTICATION POLICY"),
            ActionApplyType::JoinPolicy => write!(f, "JOIN POLICY"),
            ActionApplyType::MaskingPolicy => write!(f, "MASKING POLICY"),
            ActionApplyType::PackagesPolicy => write!(f, "PACKAGES POLICY"),
            ActionApplyType::PasswordPolicy => write!(f, "PASSWORD POLICY"),
            ActionApplyType::ProjectionPolicy => write!(f, "PROJECTION POLICY"),
            ActionApplyType::RowAccessPolicy => write!(f, "ROW ACCESS POLICY"),
            ActionApplyType::SessionPolicy => write!(f, "SESSION POLICY"),
            ActionApplyType::Tag => write!(f, "TAG"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `EXECUTE` privilege.
pub enum ActionExecuteObjectType {
    /// Alert object.
    Alert,
    /// Data metric function object.
    DataMetricFunction,
    /// Managed alert object.
    ManagedAlert,
    /// Managed task object.
    ManagedTask,
    /// Task object.
    Task,
}

impl fmt::Display for ActionExecuteObjectType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionExecuteObjectType::Alert => write!(f, "ALERT"),
            ActionExecuteObjectType::DataMetricFunction => write!(f, "DATA METRIC FUNCTION"),
            ActionExecuteObjectType::ManagedAlert => write!(f, "MANAGED ALERT"),
            ActionExecuteObjectType::ManagedTask => write!(f, "MANAGED TASK"),
            ActionExecuteObjectType::Task => write!(f, "TASK"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MANAGE` privilege.
pub enum ActionManageType {
    /// Account support cases management.
    AccountSupportCases,
    /// Event sharing management.
    EventSharing,
    /// Grants management.
    Grants,
    /// Listing auto-fulfillment management.
    ListingAutoFulfillment,
    /// Organization support cases management.
    OrganizationSupportCases,
    /// User support cases management.
    UserSupportCases,
    /// Warehouses management.
    Warehouses,
}

impl fmt::Display for ActionManageType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionManageType::AccountSupportCases => write!(f, "ACCOUNT SUPPORT CASES"),
            ActionManageType::EventSharing => write!(f, "EVENT SHARING"),
            ActionManageType::Grants => write!(f, "GRANTS"),
            ActionManageType::ListingAutoFulfillment => write!(f, "LISTING AUTO FULFILLMENT"),
            ActionManageType::OrganizationSupportCases => write!(f, "ORGANIZATION SUPPORT CASES"),
            ActionManageType::UserSupportCases => write!(f, "USER SUPPORT CASES"),
            ActionManageType::Warehouses => write!(f, "WAREHOUSES"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MODIFY` privilege.
pub enum ActionModifyType {
    /// Modify log level.
    LogLevel,
    /// Modify trace level.
    TraceLevel,
    /// Modify session log level.
    SessionLogLevel,
    /// Modify session trace level.
    SessionTraceLevel,
}

impl fmt::Display for ActionModifyType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionModifyType::LogLevel => write!(f, "LOG LEVEL"),
            ActionModifyType::TraceLevel => write!(f, "TRACE LEVEL"),
            ActionModifyType::SessionLogLevel => write!(f, "SESSION LOG LEVEL"),
            ActionModifyType::SessionTraceLevel => write!(f, "SESSION TRACE LEVEL"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MONITOR` privilege.
pub enum ActionMonitorType {
    /// Monitor execution.
    Execution,
    /// Monitor security.
    Security,
    /// Monitor usage.
    Usage,
}

impl fmt::Display for ActionMonitorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ActionMonitorType::Execution => write!(f, "EXECUTION"),
            ActionMonitorType::Security => write!(f, "SECURITY"),
            ActionMonitorType::Usage => write!(f, "USAGE"),
        }
    }
}

/// The principal that receives the privileges
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Grantee {
    /// The category/type of grantee (role, user, share, etc.).
    pub grantee_type: GranteesType,
    /// Optional name of the grantee (identifier or user@host).
    pub name: Option<GranteeName>,
}

impl fmt::Display for Grantee {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.grantee_type {
            GranteesType::Role => {
                write!(f, "ROLE ")?;
            }
            GranteesType::Share => {
                write!(f, "SHARE ")?;
            }
            GranteesType::User => {
                write!(f, "USER ")?;
            }
            GranteesType::Group => {
                write!(f, "GROUP ")?;
            }
            GranteesType::Public => {
                write!(f, "PUBLIC ")?;
            }
            GranteesType::DatabaseRole => {
                write!(f, "DATABASE ROLE ")?;
            }
            GranteesType::Application => {
                write!(f, "APPLICATION ")?;
            }
            GranteesType::ApplicationRole => {
                write!(f, "APPLICATION ROLE ")?;
            }
            GranteesType::None => (),
        }
        if let Some(ref name) = self.name {
            name.fmt(f)?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// The kind of principal receiving privileges.
pub enum GranteesType {
    /// A role principal.
    Role,
    /// A share principal.
    Share,
    /// A user principal.
    User,
    /// A group principal.
    Group,
    /// The public principal.
    Public,
    /// A database role principal.
    DatabaseRole,
    /// An application principal.
    Application,
    /// An application role principal.
    ApplicationRole,
    /// No specific principal (e.g. `NONE`).
    None,
}

/// Users/roles designated in a GRANT/REVOKE
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum GranteeName {
    /// A bare identifier
    ObjectName(ObjectName),
    /// A MySQL user/host pair such as 'root'@'%'
    UserHost {
        /// The user identifier portion.
        user: Ident,
        /// The host identifier portion.
        host: Ident,
    },
}

impl fmt::Display for GranteeName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            GranteeName::ObjectName(name) => name.fmt(f),
            GranteeName::UserHost { user, host } => {
                write!(f, "{user}@{host}")
            }
        }
    }
}

/// Objects on which privileges are granted in a GRANT statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum GrantObjects {
    /// Grant privileges on `ALL SEQUENCES IN SCHEMA <schema_name> [, ...]`
    AllSequencesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `ALL TABLES IN SCHEMA <schema_name> [, ...]`
    AllTablesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `ALL VIEWS IN SCHEMA <schema_name> [, ...]`
    AllViewsInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `ALL MATERIALIZED VIEWS IN SCHEMA <schema_name> [, ...]`
    AllMaterializedViewsInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `ALL EXTERNAL TABLES IN SCHEMA <schema_name> [, ...]`
    AllExternalTablesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `ALL FUNCTIONS IN SCHEMA <schema_name> [, ...]`
    AllFunctionsInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE SCHEMAS IN DATABASE <database_name> [, ...]`
    FutureSchemasInDatabase {
        /// The target database names.
        databases: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE TABLES IN SCHEMA <schema_name> [, ...]`
    FutureTablesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE VIEWS IN SCHEMA <schema_name> [, ...]`
    FutureViewsInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE EXTERNAL TABLES IN SCHEMA <schema_name> [, ...]`
    FutureExternalTablesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE MATERIALIZED VIEWS IN SCHEMA <schema_name> [, ...]`
    FutureMaterializedViewsInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on `FUTURE SEQUENCES IN SCHEMA <schema_name> [, ...]`
    FutureSequencesInSchema {
        /// The target schema names.
        schemas: Vec<ObjectName>,
    },
    /// Grant privileges on specific databases
    Databases(Vec<ObjectName>),
    /// Grant privileges on specific schemas
    Schemas(Vec<ObjectName>),
    /// Grant privileges on specific sequences
    Sequences(Vec<ObjectName>),
    /// Grant privileges on specific tables
    Tables(Vec<ObjectName>),
    /// Grant privileges on specific views
    Views(Vec<ObjectName>),
    /// Grant privileges on specific warehouses
    Warehouses(Vec<ObjectName>),
    /// Grant privileges on specific integrations
    Integrations(Vec<ObjectName>),
    /// Grant privileges on resource monitors
    ResourceMonitors(Vec<ObjectName>),
    /// Grant privileges on users
    Users(Vec<ObjectName>),
    /// Grant privileges on compute pools
    ComputePools(Vec<ObjectName>),
    /// Grant privileges on connections
    Connections(Vec<ObjectName>),
    /// Grant privileges on failover groups
    FailoverGroup(Vec<ObjectName>),
    /// Grant privileges on replication group
    ReplicationGroup(Vec<ObjectName>),
    /// Grant privileges on external volumes
    ExternalVolumes(Vec<ObjectName>),
    /// Grant privileges on a procedure. In dialects that
    /// support overloading, the argument types must be specified.
    ///
    /// For example:
    /// `GRANT USAGE ON PROCEDURE foo(varchar) TO ROLE role1`
    Procedure {
        /// The procedure name.
        name: ObjectName,
        /// Optional argument types for overloaded procedures.
        arg_types: Vec<DataType>,
    },

    /// Grant privileges on a function. In dialects that
    /// support overloading, the argument types must be specified.
    ///
    /// For example:
    /// `GRANT USAGE ON FUNCTION foo(varchar) TO ROLE role1`
    Function {
        /// The function name.
        name: ObjectName,
        /// Optional argument types for overloaded functions.
        arg_types: Vec<DataType>,
    },
}

impl fmt::Display for GrantObjects {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            GrantObjects::Sequences(sequences) => {
                write!(f, "SEQUENCE {}", display_comma_separated(sequences))
            }
            GrantObjects::Databases(databases) => {
                write!(f, "DATABASE {}", display_comma_separated(databases))
            }
            GrantObjects::Schemas(schemas) => {
                write!(f, "SCHEMA {}", display_comma_separated(schemas))
            }
            GrantObjects::Tables(tables) => {
                write!(f, "{}", display_comma_separated(tables))
            }
            GrantObjects::Views(views) => {
                write!(f, "VIEW {}", display_comma_separated(views))
            }
            GrantObjects::Warehouses(warehouses) => {
                write!(f, "WAREHOUSE {}", display_comma_separated(warehouses))
            }
            GrantObjects::Integrations(integrations) => {
                write!(f, "INTEGRATION {}", display_comma_separated(integrations))
            }
            GrantObjects::AllSequencesInSchema { schemas } => {
                write!(
                    f,
                    "ALL SEQUENCES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::AllTablesInSchema { schemas } => {
                write!(
                    f,
                    "ALL TABLES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::AllExternalTablesInSchema { schemas } => {
                write!(
                    f,
                    "ALL EXTERNAL TABLES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::AllViewsInSchema { schemas } => {
                write!(
                    f,
                    "ALL VIEWS IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::AllMaterializedViewsInSchema { schemas } => {
                write!(
                    f,
                    "ALL MATERIALIZED VIEWS IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::AllFunctionsInSchema { schemas } => {
                write!(
                    f,
                    "ALL FUNCTIONS IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::FutureSchemasInDatabase { databases } => {
                write!(
                    f,
                    "FUTURE SCHEMAS IN DATABASE {}",
                    display_comma_separated(databases)
                )
            }
            GrantObjects::FutureTablesInSchema { schemas } => {
                write!(
                    f,
                    "FUTURE TABLES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::FutureExternalTablesInSchema { schemas } => {
                write!(
                    f,
                    "FUTURE EXTERNAL TABLES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::FutureViewsInSchema { schemas } => {
                write!(
                    f,
                    "FUTURE VIEWS IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::FutureMaterializedViewsInSchema { schemas } => {
                write!(
                    f,
                    "FUTURE MATERIALIZED VIEWS IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::FutureSequencesInSchema { schemas } => {
                write!(
                    f,
                    "FUTURE SEQUENCES IN SCHEMA {}",
                    display_comma_separated(schemas)
                )
            }
            GrantObjects::ResourceMonitors(objects) => {
                write!(f, "RESOURCE MONITOR {}", display_comma_separated(objects))
            }
            GrantObjects::Users(objects) => {
                write!(f, "USER {}", display_comma_separated(objects))
            }
            GrantObjects::ComputePools(objects) => {
                write!(f, "COMPUTE POOL {}", display_comma_separated(objects))
            }
            GrantObjects::Connections(objects) => {
                write!(f, "CONNECTION {}", display_comma_separated(objects))
            }
            GrantObjects::FailoverGroup(objects) => {
                write!(f, "FAILOVER GROUP {}", display_comma_separated(objects))
            }
            GrantObjects::ReplicationGroup(objects) => {
                write!(f, "REPLICATION GROUP {}", display_comma_separated(objects))
            }
            GrantObjects::ExternalVolumes(objects) => {
                write!(f, "EXTERNAL VOLUME {}", display_comma_separated(objects))
            }
            GrantObjects::Procedure { name, arg_types } => {
                write!(f, "PROCEDURE {name}")?;
                if !arg_types.is_empty() {
                    write!(f, "({})", display_comma_separated(arg_types))?;
                }
                Ok(())
            }
            GrantObjects::Function { name, arg_types } => {
                write!(f, "FUNCTION {name}")?;
                if !arg_types.is_empty() {
                    write!(f, "({})", display_comma_separated(arg_types))?;
                }
                Ok(())
            }
        }
    }
}

/// A `DENY` statement
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/deny-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DenyStatement {
    /// The privileges to deny.
    pub privileges: Privileges,
    /// The objects the privileges apply to.
    pub objects: GrantObjects,
    /// The grantees (users/roles) to whom the denial applies.
    pub grantees: Vec<Grantee>,
    /// Optional identifier of the principal that performed the grant.
    pub granted_by: Option<Ident>,
    /// Optional cascade option controlling dependent objects.
    pub cascade: Option<CascadeOption>,
}

impl fmt::Display for DenyStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "DENY {}", self.privileges)?;
        write!(f, " ON {}", self.objects)?;
        if !self.grantees.is_empty() {
            write!(f, " TO {}", display_comma_separated(&self.grantees))?;
        }
        if let Some(cascade) = &self.cascade {
            write!(f, " {cascade}")?;
        }
        if let Some(granted_by) = &self.granted_by {
            write!(f, " AS {granted_by}")?;
        }
        Ok(())
    }
}

/// SQL assignment `foo = expr` as used in SQLUpdate
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Assignment {
    /// The left-hand side of the assignment.
    pub target: AssignmentTarget,
    /// The expression assigned to the target.
    pub value: Expr,
}

impl fmt::Display for Assignment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} = {}", self.target, self.value)
    }
}

/// Left-hand side of an assignment in an UPDATE statement,
/// e.g. `foo` in `foo = 5` (ColumnName assignment) or
/// `(a, b)` in `(a, b) = (1, 2)` (Tuple assignment).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AssignmentTarget {
    /// A single column
    ColumnName(ObjectName),
    /// A tuple of columns
    Tuple(Vec<ObjectName>),
}

impl fmt::Display for AssignmentTarget {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AssignmentTarget::ColumnName(column) => write!(f, "{column}"),
            AssignmentTarget::Tuple(columns) => write!(f, "({})", display_comma_separated(columns)),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Expression forms allowed as a function argument.
pub enum FunctionArgExpr {
    /// A normal expression argument.
    Expr(Expr),
    /// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
    QualifiedWildcard(ObjectName),
    /// An unqualified `*` wildcard.
    Wildcard,
    /// An unqualified `*` wildcard with additional options, e.g. `* EXCLUDE(col)`.
    ///
    /// Used in Snowflake to support expressions like `HASH(* EXCLUDE(col))`.
    WildcardWithOptions(WildcardAdditionalOptions),
}

impl From<Expr> for FunctionArgExpr {
    fn from(wildcard_expr: Expr) -> Self {
        match wildcard_expr {
            Expr::QualifiedWildcard(prefix, _) => Self::QualifiedWildcard(prefix),
            Expr::Wildcard(_) => Self::Wildcard,
            expr => Self::Expr(expr),
        }
    }
}

impl fmt::Display for FunctionArgExpr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionArgExpr::Expr(expr) => write!(f, "{expr}"),
            FunctionArgExpr::QualifiedWildcard(prefix) => write!(f, "{prefix}.*"),
            FunctionArgExpr::Wildcard => f.write_str("*"),
            FunctionArgExpr::WildcardWithOptions(opts) => write!(f, "*{opts}"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Operator used to separate function arguments
pub enum FunctionArgOperator {
    /// function(arg1 = value1)
    Equals,
    /// function(arg1 => value1)
    RightArrow,
    /// function(arg1 := value1)
    Assignment,
    /// function(arg1 : value1)
    Colon,
    /// function(arg1 VALUE value1)
    Value,
}

impl fmt::Display for FunctionArgOperator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionArgOperator::Equals => f.write_str("="),
            FunctionArgOperator::RightArrow => f.write_str("=>"),
            FunctionArgOperator::Assignment => f.write_str(":="),
            FunctionArgOperator::Colon => f.write_str(":"),
            FunctionArgOperator::Value => f.write_str("VALUE"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Forms of function arguments (named, expression-named, or positional).
pub enum FunctionArg {
    /// `name` is identifier
    ///
    /// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'false'
    Named {
        /// The identifier name of the argument.
        name: Ident,
        /// The argument expression or wildcard form.
        arg: FunctionArgExpr,
        /// The operator separating name and value.
        operator: FunctionArgOperator,
    },
    /// `name` is arbitrary expression
    ///
    /// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'true'
    ExprNamed {
        /// The expression used as the argument name.
        name: Expr,
        /// The argument expression or wildcard form.
        arg: FunctionArgExpr,
        /// The operator separating name and value.
        operator: FunctionArgOperator,
    },
    /// An unnamed argument (positional), given by expression or wildcard.
    Unnamed(FunctionArgExpr),
}

impl fmt::Display for FunctionArg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionArg::Named {
                name,
                arg,
                operator,
            } => write!(f, "{name} {operator} {arg}"),
            FunctionArg::ExprNamed {
                name,
                arg,
                operator,
            } => write!(f, "{name} {operator} {arg}"),
            FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_arg}"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Which cursor(s) to close.
pub enum CloseCursor {
    /// Close all cursors.
    All,
    /// Close a specific cursor by name.
    Specific {
        /// The name of the cursor to close.
        name: Ident,
    },
}

impl fmt::Display for CloseCursor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CloseCursor::All => write!(f, "ALL"),
            CloseCursor::Specific { name } => write!(f, "{name}"),
        }
    }
}

/// A Drop Domain statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DropDomain {
    /// Whether to drop the domain if it exists
    pub if_exists: bool,
    /// The name of the domain to drop
    pub name: ObjectName,
    /// The behavior to apply when dropping the domain
    pub drop_behavior: Option<DropBehavior>,
}

/// A constant of form `<data_type> 'value'`.
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
/// as well as constants of other types (a non-standard PostgreSQL extension).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct TypedString {
    /// The data type of the typed string (e.g. DATE, TIME, TIMESTAMP).
    pub data_type: DataType,
    /// The value of the constant.
    /// Hint: you can unwrap the string value using `value.into_string()`.
    pub value: ValueWithSpan,
    /// Flags whether this TypedString uses the [ODBC syntax].
    ///
    /// Example:
    /// ```sql
    /// -- An ODBC date literal:
    /// SELECT {d '2025-07-16'}
    /// -- This is equivalent to the standard ANSI SQL literal:
    /// SELECT DATE '2025-07-16'
    ///
    /// [ODBC syntax]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals?view=sql-server-2017
    pub uses_odbc_syntax: bool,
}

impl fmt::Display for TypedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let data_type = &self.data_type;
        let value = &self.value;
        match self.uses_odbc_syntax {
            false => {
                write!(f, "{data_type}")?;
                write!(f, " {value}")
            }
            true => {
                let prefix = match data_type {
                    DataType::Date => "d",
                    DataType::Time(..) => "t",
                    DataType::Timestamp(..) => "ts",
                    _ => "?",
                };
                write!(f, "{{{prefix} {value}}}")
            }
        }
    }
}

/// A function call
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Function {
    /// The function name (may be qualified).
    pub name: ObjectName,
    /// Flags whether this function call uses the [ODBC syntax].
    ///
    /// Example:
    /// ```sql
    /// SELECT {fn CONCAT('foo', 'bar')}
    /// ```
    ///
    /// [ODBC syntax]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/scalar-function-calls?view=sql-server-2017
    pub uses_odbc_syntax: bool,
    /// The parameters to the function, including any options specified within the
    /// delimiting parentheses.
    ///
    /// Example:
    /// ```plaintext
    /// HISTOGRAM(0.5, 0.6)(x, y)
    /// ```
    ///
    /// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions)
    pub parameters: FunctionArguments,
    /// The arguments to the function, including any options specified within the
    /// delimiting parentheses.
    pub args: FunctionArguments,
    /// e.g. `x > 5` in `COUNT(x) FILTER (WHERE x > 5)`
    pub filter: Option<Box<Expr>>,
    /// Indicates how `NULL`s should be handled in the calculation.
    ///
    /// Example:
    /// ```plaintext
    /// FIRST_VALUE( <expr> ) [ { IGNORE | RESPECT } NULLS ] OVER ...
    /// ```
    ///
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/first_value)
    pub null_treatment: Option<NullTreatment>,
    /// The `OVER` clause, indicating a window function call.
    pub over: Option<WindowType>,
    /// A clause used with certain aggregate functions to control the ordering
    /// within grouped sets before the function is applied.
    ///
    /// Syntax:
    /// ```plaintext
    /// <aggregate_function>(expression) WITHIN GROUP (ORDER BY key [ASC | DESC], ...)
    /// ```
    pub within_group: Vec<OrderByExpr>,
}

impl fmt::Display for Function {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.uses_odbc_syntax {
            write!(f, "{{fn ")?;
        }

        write!(f, "{}{}{}", self.name, self.parameters, self.args)?;

        if !self.within_group.is_empty() {
            write!(
                f,
                " WITHIN GROUP (ORDER BY {})",
                display_comma_separated(&self.within_group)
            )?;
        }

        if let Some(filter_cond) = &self.filter {
            write!(f, " FILTER (WHERE {filter_cond})")?;
        }

        if let Some(null_treatment) = &self.null_treatment {
            write!(f, " {null_treatment}")?;
        }

        if let Some(o) = &self.over {
            f.write_str(" OVER ")?;
            o.fmt(f)?;
        }

        if self.uses_odbc_syntax {
            write!(f, "}}")?;
        }

        Ok(())
    }
}

/// The arguments passed to a function call.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArguments {
    /// Used for special functions like `CURRENT_TIMESTAMP` that are invoked
    /// without parentheses.
    None,
    /// On some dialects, a subquery can be passed without surrounding
    /// parentheses if it's the sole argument to the function.
    Subquery(Box<Query>),
    /// A normal function argument list, including any clauses within it such as
    /// `DISTINCT` or `ORDER BY`.
    List(FunctionArgumentList),
}

impl fmt::Display for FunctionArguments {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FunctionArguments::None => Ok(()),
            FunctionArguments::Subquery(query) => write!(f, "({query})"),
            FunctionArguments::List(args) => write!(f, "({args})"),
        }
    }
}

/// This represents everything inside the parentheses when calling a function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionArgumentList {
    /// `[ ALL | DISTINCT ]`
    pub duplicate_treatment: Option<DuplicateTreatment>,
    /// The function arguments.
    pub args: Vec<FunctionArg>,
    /// Additional clauses specified within the argument list.
    pub clauses: Vec<FunctionArgumentClause>,
}

impl fmt::Display for FunctionArgumentList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(duplicate_treatment) = self.duplicate_treatment {
            write!(f, "{duplicate_treatment} ")?;
        }
        write!(f, "{}", display_comma_separated(&self.args))?;
        if !self.clauses.is_empty() {
            if !self.args.is_empty() {
                write!(f, " ")?;
            }
            write!(f, "{}", display_separated(&self.clauses, " "))?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clauses that can appear inside a function argument list.
pub enum FunctionArgumentClause {
    /// Indicates how `NULL`s should be handled in the calculation, e.g. in `FIRST_VALUE` on [BigQuery].
    ///
    /// Syntax:
    /// ```plaintext
    /// { IGNORE | RESPECT } NULLS ]
    /// ```
    ///
    /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value
    IgnoreOrRespectNulls(NullTreatment),
    /// Specifies the the ordering for some ordered set aggregates, e.g. `ARRAY_AGG` on [BigQuery].
    ///
    /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#array_agg
    OrderBy(Vec<OrderByExpr>),
    /// Specifies a limit for the `ARRAY_AGG` and `ARRAY_CONCAT_AGG` functions on BigQuery.
    Limit(Expr),
    /// Specifies the behavior on overflow of the `LISTAGG` function.
    ///
    /// See <https://trino.io/docs/current/functions/aggregate.html>.
    OnOverflow(ListAggOnOverflow),
    /// Specifies a minimum or maximum bound on the input to [`ANY_VALUE`] on BigQuery.
    ///
    /// Syntax:
    /// ```plaintext
    /// HAVING { MAX | MIN } expression
    /// ```
    ///
    /// [`ANY_VALUE`]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#any_value
    Having(HavingBound),
    /// The `SEPARATOR` clause to the [`GROUP_CONCAT`] function in MySQL.
    ///
    /// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
    Separator(ValueWithSpan),
    /// The `ON NULL` clause for some JSON functions.
    ///
    /// [MSSQL `JSON_ARRAY`](https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16)
    /// [MSSQL `JSON_OBJECT`](https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16>)
    /// [PostgreSQL JSON functions](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING)
    JsonNullClause(JsonNullClause),
    /// The `RETURNING` clause for some JSON functions in PostgreSQL
    ///
    /// [`JSON_OBJECT`](https://www.postgresql.org/docs/current/functions-json.html#:~:text=json_object)
    JsonReturningClause(JsonReturningClause),
}

impl fmt::Display for FunctionArgumentClause {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => {
                write!(f, "{null_treatment}")
            }
            FunctionArgumentClause::OrderBy(order_by) => {
                write!(f, "ORDER BY {}", display_comma_separated(order_by))
            }
            FunctionArgumentClause::Limit(limit) => write!(f, "LIMIT {limit}"),
            FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
            FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
            FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
            FunctionArgumentClause::JsonNullClause(null_clause) => write!(f, "{null_clause}"),
            FunctionArgumentClause::JsonReturningClause(returning_clause) => {
                write!(f, "{returning_clause}")
            }
        }
    }
}

/// A method call
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Method {
    /// The expression on which the method is invoked.
    pub expr: Box<Expr>,
    // always non-empty
    /// The sequence of chained method calls.
    pub method_chain: Vec<Function>,
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}.{}",
            self.expr,
            display_separated(&self.method_chain, ".")
        )
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How duplicate values are treated inside function argument lists.
pub enum DuplicateTreatment {
    /// Consider only unique values.
    Distinct,
    /// Retain all duplicate values (the default).
    All,
}

impl fmt::Display for DuplicateTreatment {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DuplicateTreatment::Distinct => write!(f, "DISTINCT"),
            DuplicateTreatment::All => write!(f, "ALL"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How the `ANALYZE`/`EXPLAIN ANALYZE` format is specified.
pub enum AnalyzeFormatKind {
    /// Format provided as a keyword, e.g. `FORMAT JSON`.
    Keyword(AnalyzeFormat),
    /// Format provided as an assignment, e.g. `FORMAT=JSON`.
    Assignment(AnalyzeFormat),
}

impl fmt::Display for AnalyzeFormatKind {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            AnalyzeFormatKind::Keyword(format) => write!(f, "FORMAT {format}"),
            AnalyzeFormatKind::Assignment(format) => write!(f, "FORMAT={format}"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Output formats supported for `ANALYZE`/`EXPLAIN ANALYZE`.
pub enum AnalyzeFormat {
    /// Plain text format.
    TEXT,
    /// Graphviz DOT format.
    GRAPHVIZ,
    /// JSON format.
    JSON,
    /// Traditional explain output.
    TRADITIONAL,
    /// Tree-style explain output.
    TREE,
}

impl fmt::Display for AnalyzeFormat {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(match self {
            AnalyzeFormat::TEXT => "TEXT",
            AnalyzeFormat::GRAPHVIZ => "GRAPHVIZ",
            AnalyzeFormat::JSON => "JSON",
            AnalyzeFormat::TRADITIONAL => "TRADITIONAL",
            AnalyzeFormat::TREE => "TREE",
        })
    }
}

/// External table's available file format
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FileFormat {
    /// Text file format.
    TEXTFILE,
    /// Sequence file format.
    SEQUENCEFILE,
    /// ORC file format.
    ORC,
    /// Parquet file format.
    PARQUET,
    /// Avro file format.
    AVRO,
    /// RCFile format.
    RCFILE,
    /// JSON file format.
    JSONFILE,
}

impl fmt::Display for FileFormat {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::FileFormat::*;
        f.write_str(match self {
            TEXTFILE => "TEXTFILE",
            SEQUENCEFILE => "SEQUENCEFILE",
            ORC => "ORC",
            PARQUET => "PARQUET",
            AVRO => "AVRO",
            RCFILE => "RCFILE",
            JSONFILE => "JSONFILE",
        })
    }
}

/// The `ON OVERFLOW` clause of a LISTAGG invocation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ListAggOnOverflow {
    /// `ON OVERFLOW ERROR`
    Error,

    /// `ON OVERFLOW TRUNCATE [ <filler> ] WITH[OUT] COUNT`
    Truncate {
        /// Optional filler expression used when truncating.
        filler: Option<Box<Expr>>,
        /// Whether to include a count when truncating.
        with_count: bool,
    },
}

impl fmt::Display for ListAggOnOverflow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ON OVERFLOW")?;
        match self {
            ListAggOnOverflow::Error => write!(f, " ERROR"),
            ListAggOnOverflow::Truncate { filler, with_count } => {
                write!(f, " TRUNCATE")?;
                if let Some(filler) = filler {
                    write!(f, " {filler}")?;
                }
                if *with_count {
                    write!(f, " WITH")?;
                } else {
                    write!(f, " WITHOUT")?;
                }
                write!(f, " COUNT")
            }
        }
    }
}

/// The `HAVING` clause in a call to `ANY_VALUE` on BigQuery.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct HavingBound(pub HavingBoundKind, pub Expr);

impl fmt::Display for HavingBound {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "HAVING {} {}", self.0, self.1)
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Which bound is used in a HAVING clause for ANY_VALUE on BigQuery.
pub enum HavingBoundKind {
    /// The minimum bound.
    Min,
    /// The maximum bound.
    Max,
}

impl fmt::Display for HavingBoundKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            HavingBoundKind::Min => write!(f, "MIN"),
            HavingBoundKind::Max => write!(f, "MAX"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Types of database objects referenced by DDL statements.
pub enum ObjectType {
    /// A collation.
    Collation,
    /// A table.
    Table,
    /// A view.
    View,
    /// A materialized view.
    MaterializedView,
    /// An index.
    Index,
    /// A schema.
    Schema,
    /// A database.
    Database,
    /// A role.
    Role,
    /// A sequence.
    Sequence,
    /// A stage.
    Stage,
    /// A type definition.
    Type,
    /// A user.
    User,
    /// A stream.
    Stream,
}

impl fmt::Display for ObjectType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            ObjectType::Collation => "COLLATION",
            ObjectType::Table => "TABLE",
            ObjectType::View => "VIEW",
            ObjectType::MaterializedView => "MATERIALIZED VIEW",
            ObjectType::Index => "INDEX",
            ObjectType::Schema => "SCHEMA",
            ObjectType::Database => "DATABASE",
            ObjectType::Role => "ROLE",
            ObjectType::Sequence => "SEQUENCE",
            ObjectType::Stage => "STAGE",
            ObjectType::Type => "TYPE",
            ObjectType::User => "USER",
            ObjectType::Stream => "STREAM",
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Types supported by `KILL` statements.
pub enum KillType {
    /// Kill a connection.
    Connection,
    /// Kill a running query.
    Query,
    /// Kill a mutation (ClickHouse).
    Mutation,
}

impl fmt::Display for KillType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            // MySQL
            KillType::Connection => "CONNECTION",
            KillType::Query => "QUERY",
            // Clickhouse supports Mutation
            KillType::Mutation => "MUTATION",
        })
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Distribution style options for Hive tables.
pub enum HiveDistributionStyle {
    /// Partitioned distribution with the given columns.
    PARTITIONED {
        /// Columns used for partitioning.
        columns: Vec<ColumnDef>,
    },
    /// Skewed distribution definition.
    SKEWED {
        /// Columns participating in the skew definition.
        columns: Vec<ColumnDef>,
        /// Columns listed in the `ON` clause for skewing.
        on: Vec<ColumnDef>,
        /// Whether skewed data is stored as directories.
        stored_as_directories: bool,
    },
    /// No distribution style specified.
    NONE,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Row format specification for Hive tables (SERDE or DELIMITED).
pub enum HiveRowFormat {
    /// SerDe class specification with the implementing class name.
    SERDE {
        /// The SerDe implementation class name.
        class: String,
    },
    /// Delimited row format with one or more delimiter specifications.
    DELIMITED {
        /// The list of delimiters used for delimiting fields/lines.
        delimiters: Vec<HiveRowDelimiter>,
    },
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Format specification for `LOAD DATA` Hive operations.
pub struct HiveLoadDataFormat {
    /// SerDe expression used for the table.
    pub serde: Expr,
    /// Input format expression.
    pub input_format: Expr,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A single row delimiter specification for Hive `ROW FORMAT`.
pub struct HiveRowDelimiter {
    /// The delimiter kind (fields/lines/etc.).
    pub delimiter: HiveDelimiter,
    /// The delimiter character identifier.
    pub char: Ident,
}

impl fmt::Display for HiveRowDelimiter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} ", self.delimiter)?;
        write!(f, "{}", self.char)
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Kind of delimiter used in Hive `ROW FORMAT` definitions.
pub enum HiveDelimiter {
    /// Fields terminated by a delimiter.
    FieldsTerminatedBy,
    /// Fields escaped by a character.
    FieldsEscapedBy,
    /// Collection items terminated by a delimiter.
    CollectionItemsTerminatedBy,
    /// Map keys terminated by a delimiter.
    MapKeysTerminatedBy,
    /// Lines terminated by a delimiter.
    LinesTerminatedBy,
    /// Null represented by a specific token.
    NullDefinedAs,
}

impl fmt::Display for HiveDelimiter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use HiveDelimiter::*;
        f.write_str(match self {
            FieldsTerminatedBy => "FIELDS TERMINATED BY",
            FieldsEscapedBy => "ESCAPED BY",
            CollectionItemsTerminatedBy => "COLLECTION ITEMS TERMINATED BY",
            MapKeysTerminatedBy => "MAP KEYS TERMINATED BY",
            LinesTerminatedBy => "LINES TERMINATED BY",
            NullDefinedAs => "NULL DEFINED AS",
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Describe output format options for Hive `DESCRIBE`/`EXPLAIN`.
pub enum HiveDescribeFormat {
    /// Extended describe output.
    Extended,
    /// Formatted describe output.
    Formatted,
}

impl fmt::Display for HiveDescribeFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use HiveDescribeFormat::*;
        f.write_str(match self {
            Extended => "EXTENDED",
            Formatted => "FORMATTED",
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Aliases accepted for describe-style commands.
pub enum DescribeAlias {
    /// `DESCRIBE` alias.
    Describe,
    /// `EXPLAIN` alias.
    Explain,
    /// `DESC` alias.
    Desc,
}

impl fmt::Display for DescribeAlias {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use DescribeAlias::*;
        f.write_str(match self {
            Describe => "DESCRIBE",
            Explain => "EXPLAIN",
            Desc => "DESC",
        })
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[allow(clippy::large_enum_variant)]
/// Hive input/output format specification used in `CREATE TABLE`.
pub enum HiveIOFormat {
    /// Generic IO format with separate input and output expressions.
    IOF {
        /// Expression for the input format.
        input_format: Expr,
        /// Expression for the output format.
        output_format: Expr,
    },
    /// File format wrapper referencing a `FileFormat` variant.
    FileFormat {
        /// The file format used for storage.
        format: FileFormat,
    },
    /// `USING <format>` syntax used by Spark SQL.
    ///
    /// Example: `CREATE TABLE t (i INT) USING PARQUET`
    ///
    /// See <https://spark.apache.org/docs/latest/sql-ref-syntax-ddl-create-table-datasource.html>
    Using {
        /// The data source or format name, e.g. `parquet`, `delta`, `csv`.
        format: Ident,
    },
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Hive table format and storage-related options.
pub struct HiveFormat {
    /// Optional row format specification.
    pub row_format: Option<HiveRowFormat>,
    /// Optional SerDe properties expressed as SQL options.
    pub serde_properties: Option<Vec<SqlOption>>,
    /// Optional input/output storage format details.
    pub storage: Option<HiveIOFormat>,
    /// Optional location (URI or path) for table data.
    pub location: Option<String>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A clustered index column specification.
pub struct ClusteredIndex {
    /// Column identifier for the clustered index entry.
    pub name: Ident,
    /// Optional sort direction: `Some(true)` for ASC, `Some(false)` for DESC, `None` for unspecified.
    pub asc: Option<bool>,
}

impl fmt::Display for ClusteredIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name)?;
        match self.asc {
            Some(true) => write!(f, " ASC"),
            Some(false) => write!(f, " DESC"),
            _ => Ok(()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clustered options used for `CREATE TABLE` clustered/indexed storage.
pub enum TableOptionsClustered {
    /// Use a columnstore index.
    ColumnstoreIndex,
    /// Columnstore index with an explicit ordering of columns.
    ColumnstoreIndexOrder(Vec<Ident>),
    /// A named clustered index with one or more columns.
    Index(Vec<ClusteredIndex>),
}

impl fmt::Display for TableOptionsClustered {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TableOptionsClustered::ColumnstoreIndex => {
                write!(f, "CLUSTERED COLUMNSTORE INDEX")
            }
            TableOptionsClustered::ColumnstoreIndexOrder(values) => {
                write!(
                    f,
                    "CLUSTERED COLUMNSTORE INDEX ORDER ({})",
                    display_comma_separated(values)
                )
            }
            TableOptionsClustered::Index(values) => {
                write!(f, "CLUSTERED INDEX ({})", display_comma_separated(values))
            }
        }
    }
}

/// Specifies which partition the boundary values on table partitioning belongs to.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum PartitionRangeDirection {
    /// LEFT range direction.
    Left,
    /// RIGHT range direction.
    Right,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// SQL option syntax used in table and server definitions.
pub enum SqlOption {
    /// Clustered represents the clustered version of table storage for MSSQL.
    ///
    /// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TableOptions>
    Clustered(TableOptionsClustered),
    /// Single identifier options, e.g. `HEAP` for MSSQL.
    ///
    /// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TableOptions>
    Ident(Ident),
    /// Any option that consists of a key value pair where the value is an expression. e.g.
    ///
    ///   WITH(DISTRIBUTION = ROUND_ROBIN)
    KeyValue {
        /// The option key identifier.
        key: Ident,
        /// The expression value for the option.
        value: Expr,
    },
    /// One or more table partitions and represents which partition the boundary values belong to,
    /// e.g.
    ///
    ///   PARTITION (id RANGE LEFT FOR VALUES (10, 20, 30, 40))
    ///
    /// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TablePartitionOptions>
    Partition {
        /// The partition column name.
        column_name: Ident,
        /// Optional direction for the partition range (LEFT/RIGHT).
        range_direction: Option<PartitionRangeDirection>,
        /// Values that define the partition boundaries.
        for_values: Vec<Expr>,
    },
    /// Comment parameter (supports `=` and no `=` syntax)
    Comment(CommentDef),
    /// MySQL TableSpace option
    /// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
    TableSpace(TablespaceOption),
    /// An option representing a key value pair, where the value is a parenthesized list and with an optional name
    /// e.g.
    ///
    ///   UNION  = (tbl_name\[,tbl_name\]...) <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
    ///   ENGINE = ReplicatedMergeTree('/table_name','{replica}', ver) <https://clickhouse.com/docs/engines/table-engines/mergetree-family/replication>
    ///   ENGINE = SummingMergeTree(\[columns\]) <https://clickhouse.com/docs/engines/table-engines/mergetree-family/summingmergetree>
    NamedParenthesizedList(NamedParenthesizedList),
}

impl fmt::Display for SqlOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SqlOption::Clustered(c) => write!(f, "{c}"),
            SqlOption::Ident(ident) => {
                write!(f, "{ident}")
            }
            SqlOption::KeyValue { key: name, value } => {
                write!(f, "{name} = {value}")
            }
            SqlOption::Partition {
                column_name,
                range_direction,
                for_values,
            } => {
                let direction = match range_direction {
                    Some(PartitionRangeDirection::Left) => " LEFT",
                    Some(PartitionRangeDirection::Right) => " RIGHT",
                    None => "",
                };

                write!(
                    f,
                    "PARTITION ({} RANGE{} FOR VALUES ({}))",
                    column_name,
                    direction,
                    display_comma_separated(for_values)
                )
            }
            SqlOption::TableSpace(tablespace_option) => {
                write!(f, "TABLESPACE {}", tablespace_option.name)?;
                match tablespace_option.storage {
                    Some(StorageType::Disk) => write!(f, " STORAGE DISK"),
                    Some(StorageType::Memory) => write!(f, " STORAGE MEMORY"),
                    None => Ok(()),
                }
            }
            SqlOption::Comment(comment) => match comment {
                CommentDef::WithEq(comment) => {
                    write!(f, "COMMENT = '{comment}'")
                }
                CommentDef::WithoutEq(comment) => {
                    write!(f, "COMMENT '{comment}'")
                }
            },
            SqlOption::NamedParenthesizedList(value) => {
                write!(f, "{} = ", value.key)?;
                if let Some(key) = &value.name {
                    write!(f, "{key}")?;
                }
                if !value.values.is_empty() {
                    write!(f, "({})", display_comma_separated(&value.values))?
                }
                Ok(())
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Storage type options for a tablespace.
pub enum StorageType {
    /// Store on disk.
    Disk,
    /// Store in memory.
    Memory,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySql TableSpace option
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
pub struct TablespaceOption {
    /// Name of the tablespace.
    pub name: String,
    /// Optional storage type for the tablespace.
    pub storage: Option<StorageType>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A key/value identifier pair used for secret or key-based options.
pub struct SecretOption {
    /// The option key identifier.
    pub key: Ident,
    /// The option value identifier.
    pub value: Ident,
}

impl fmt::Display for SecretOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.key, self.value)
    }
}

/// A `CREATE SERVER` statement.
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateServerStatement {
    /// The server name.
    pub name: ObjectName,
    /// Whether `IF NOT EXISTS` was specified.
    pub if_not_exists: bool,
    /// Optional server type identifier.
    pub server_type: Option<Ident>,
    /// Optional server version identifier.
    pub version: Option<Ident>,
    /// Foreign-data wrapper object name.
    pub foreign_data_wrapper: ObjectName,
    /// Optional list of server options.
    pub options: Option<Vec<CreateServerOption>>,
}

impl fmt::Display for CreateServerStatement {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let CreateServerStatement {
            name,
            if_not_exists,
            server_type,
            version,
            foreign_data_wrapper,
            options,
        } = self;

        write!(
            f,
            "CREATE SERVER {if_not_exists}{name} ",
            if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
        )?;

        if let Some(st) = server_type {
            write!(f, "TYPE {st} ")?;
        }

        if let Some(v) = version {
            write!(f, "VERSION {v} ")?;
        }

        write!(f, "FOREIGN DATA WRAPPER {foreign_data_wrapper}")?;

        if let Some(o) = options {
            write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
        }

        Ok(())
    }
}

/// A key/value option for `CREATE SERVER`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateServerOption {
    /// Option key identifier.
    pub key: Ident,
    /// Option value identifier.
    pub value: Ident,
}

impl fmt::Display for CreateServerOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.key, self.value)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options supported by DuckDB for `ATTACH DATABASE`.
pub enum AttachDuckDBDatabaseOption {
    /// READ_ONLY option, optional boolean value.
    ReadOnly(Option<bool>),
    /// TYPE option specifying a database type identifier.
    Type(Ident),
}

impl fmt::Display for AttachDuckDBDatabaseOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AttachDuckDBDatabaseOption::ReadOnly(Some(true)) => write!(f, "READ_ONLY true"),
            AttachDuckDBDatabaseOption::ReadOnly(Some(false)) => write!(f, "READ_ONLY false"),
            AttachDuckDBDatabaseOption::ReadOnly(None) => write!(f, "READ_ONLY"),
            AttachDuckDBDatabaseOption::Type(t) => write!(f, "TYPE {t}"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Mode for transactions: access mode or isolation level.
pub enum TransactionMode {
    /// Access mode for a transaction (e.g. `READ ONLY` / `READ WRITE`).
    AccessMode(TransactionAccessMode),
    /// Isolation level for a transaction (e.g. `SERIALIZABLE`).
    IsolationLevel(TransactionIsolationLevel),
}

impl fmt::Display for TransactionMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use TransactionMode::*;
        match self {
            AccessMode(access_mode) => write!(f, "{access_mode}"),
            IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {iso_level}"),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Transaction access mode (READ ONLY / READ WRITE).
pub enum TransactionAccessMode {
    /// READ ONLY access mode.
    ReadOnly,
    /// READ WRITE access mode.
    ReadWrite,
}

impl fmt::Display for TransactionAccessMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use TransactionAccessMode::*;
        f.write_str(match self {
            ReadOnly => "READ ONLY",
            ReadWrite => "READ WRITE",
        })
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Transaction isolation levels.
pub enum TransactionIsolationLevel {
    /// READ UNCOMMITTED isolation level.
    ReadUncommitted,
    /// READ COMMITTED isolation level.
    ReadCommitted,
    /// REPEATABLE READ isolation level.
    RepeatableRead,
    /// SERIALIZABLE isolation level.
    Serializable,
    /// SNAPSHOT isolation level.
    Snapshot,
}

impl fmt::Display for TransactionIsolationLevel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use TransactionIsolationLevel::*;
        f.write_str(match self {
            ReadUncommitted => "READ UNCOMMITTED",
            ReadCommitted => "READ COMMITTED",
            RepeatableRead => "REPEATABLE READ",
            Serializable => "SERIALIZABLE",
            Snapshot => "SNAPSHOT",
        })
    }
}

/// Modifier for the transaction in the `BEGIN` syntax
///
/// SQLite: <https://sqlite.org/lang_transaction.html>
/// MS-SQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/try-catch-transact-sql>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TransactionModifier {
    /// DEFERRED transaction modifier.
    Deferred,
    /// IMMEDIATE transaction modifier.
    Immediate,
    /// EXCLUSIVE transaction modifier.
    Exclusive,
    /// TRY block modifier (MS-SQL style TRY/CATCH).
    Try,
    /// CATCH block modifier (MS-SQL style TRY/CATCH).
    Catch,
}

impl fmt::Display for TransactionModifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use TransactionModifier::*;
        f.write_str(match self {
            Deferred => "DEFERRED",
            Immediate => "IMMEDIATE",
            Exclusive => "EXCLUSIVE",
            Try => "TRY",
            Catch => "CATCH",
        })
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Filter forms usable in SHOW statements.
pub enum ShowStatementFilter {
    /// Filter using LIKE pattern.
    Like(String),
    /// Filter using ILIKE pattern.
    ILike(String),
    /// Filter using a WHERE expression.
    Where(Expr),
    /// Filter provided without a keyword (raw string).
    NoKeyword(String),
}

impl fmt::Display for ShowStatementFilter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ShowStatementFilter::*;
        match self {
            Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
            ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)),
            Where(expr) => write!(f, "WHERE {expr}"),
            NoKeyword(pattern) => write!(f, "'{}'", value::escape_single_quote_string(pattern)),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clause types used with SHOW ... IN/FROM.
pub enum ShowStatementInClause {
    /// Use the `IN` clause.
    IN,
    /// Use the `FROM` clause.
    FROM,
}

impl fmt::Display for ShowStatementInClause {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ShowStatementInClause::*;
        match self {
            FROM => write!(f, "FROM"),
            IN => write!(f, "IN"),
        }
    }
}

/// Sqlite specific syntax
///
/// See [Sqlite documentation](https://sqlite.org/lang_conflict.html)
/// for more details.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SqliteOnConflict {
    /// Use ROLLBACK on conflict.
    Rollback,
    /// Use ABORT on conflict.
    Abort,
    /// Use FAIL on conflict.
    Fail,
    /// Use IGNORE on conflict.
    Ignore,
    /// Use REPLACE on conflict.
    Replace,
}

impl fmt::Display for SqliteOnConflict {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use SqliteOnConflict::*;
        match self {
            Rollback => write!(f, "OR ROLLBACK"),
            Abort => write!(f, "OR ABORT"),
            Fail => write!(f, "OR FAIL"),
            Ignore => write!(f, "OR IGNORE"),
            Replace => write!(f, "OR REPLACE"),
        }
    }
}

/// Mysql specific syntax
///
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/replace.html)
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/insert.html)
/// for more details.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MysqlInsertPriority {
    /// LOW_PRIORITY modifier for INSERT/REPLACE.
    LowPriority,
    /// DELAYED modifier for INSERT/REPLACE.
    Delayed,
    /// HIGH_PRIORITY modifier for INSERT/REPLACE.
    HighPriority,
}

impl fmt::Display for crate::ast::MysqlInsertPriority {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use MysqlInsertPriority::*;
        match self {
            LowPriority => write!(f, "LOW_PRIORITY"),
            Delayed => write!(f, "DELAYED"),
            HighPriority => write!(f, "HIGH_PRIORITY"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Source for the `COPY` command: a table or a query.
pub enum CopySource {
    /// Copy from a table with optional column list.
    Table {
        /// The name of the table to copy from.
        table_name: ObjectName,
        /// A list of column names to copy. Empty list means that all columns
        /// are copied.
        columns: Vec<Ident>,
    },
    /// Copy from the results of a query.
    Query(Box<Query>),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Target for the `COPY` command: STDIN, STDOUT, a file, or a program.
pub enum CopyTarget {
    /// Use standard input as the source.
    Stdin,
    /// Use standard output as the target.
    Stdout,
    /// Read from or write to a file.
    File {
        /// The path name of the input or output file.
        filename: String,
    },
    /// Use a program as the source or target (shell command).
    Program {
        /// A command to execute
        command: String,
    },
}

impl fmt::Display for CopyTarget {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use CopyTarget::*;
        match self {
            Stdin => write!(f, "STDIN"),
            Stdout => write!(f, "STDOUT"),
            File { filename } => write!(f, "'{}'", value::escape_single_quote_string(filename)),
            Program { command } => write!(
                f,
                "PROGRAM '{}'",
                value::escape_single_quote_string(command)
            ),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Action to take `ON COMMIT` for temporary tables.
pub enum OnCommit {
    /// Delete rows on commit.
    DeleteRows,
    /// Preserve rows on commit.
    PreserveRows,
    /// Drop the table on commit.
    Drop,
}

/// An option in `COPY` statement.
///
/// <https://www.postgresql.org/docs/14/sql-copy.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyOption {
    /// FORMAT format_name
    Format(Ident),
    /// FREEZE \[ boolean \]
    Freeze(bool),
    /// DELIMITER 'delimiter_character'
    Delimiter(char),
    /// NULL 'null_string'
    Null(String),
    /// HEADER \[ boolean \]
    Header(bool),
    /// QUOTE 'quote_character'
    Quote(char),
    /// ESCAPE 'escape_character'
    Escape(char),
    /// FORCE_QUOTE { ( column_name [, ...] ) | * }
    ForceQuote(Vec<Ident>),
    /// FORCE_NOT_NULL ( column_name [, ...] )
    ForceNotNull(Vec<Ident>),
    /// FORCE_NULL ( column_name [, ...] )
    ForceNull(Vec<Ident>),
    /// ENCODING 'encoding_name'
    Encoding(String),
}

impl fmt::Display for CopyOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use CopyOption::*;
        match self {
            Format(name) => write!(f, "FORMAT {name}"),
            Freeze(true) => write!(f, "FREEZE"),
            Freeze(false) => write!(f, "FREEZE FALSE"),
            Delimiter(char) => write!(f, "DELIMITER '{char}'"),
            Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
            Header(true) => write!(f, "HEADER"),
            Header(false) => write!(f, "HEADER FALSE"),
            Quote(char) => write!(f, "QUOTE '{char}'"),
            Escape(char) => write!(f, "ESCAPE '{char}'"),
            ForceQuote(columns) => write!(f, "FORCE_QUOTE ({})", display_comma_separated(columns)),
            ForceNotNull(columns) => {
                write!(f, "FORCE_NOT_NULL ({})", display_comma_separated(columns))
            }
            ForceNull(columns) => write!(f, "FORCE_NULL ({})", display_comma_separated(columns)),
            Encoding(name) => write!(f, "ENCODING '{}'", value::escape_single_quote_string(name)),
        }
    }
}

/// An option in `COPY` statement before PostgreSQL version 9.0.
///
/// [PostgreSQL](https://www.postgresql.org/docs/8.4/sql-copy.html)
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_COPY-alphabetical-parm-list.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyLegacyOption {
    /// ACCEPTANYDATE
    AcceptAnyDate,
    /// ACCEPTINVCHARS
    AcceptInvChars(Option<String>),
    /// ADDQUOTES
    AddQuotes,
    /// ALLOWOVERWRITE
    AllowOverwrite,
    /// BINARY
    Binary,
    /// BLANKSASNULL
    BlankAsNull,
    /// BZIP2
    Bzip2,
    /// CLEANPATH
    CleanPath,
    /// COMPUPDATE [ PRESET | { ON | TRUE } | { OFF | FALSE } ]
    CompUpdate {
        /// Whether the COMPUPDATE PRESET option was used.
        preset: bool,
        /// Optional enabled flag for COMPUPDATE.
        enabled: Option<bool>,
    },
    /// CSV ...
    Csv(Vec<CopyLegacyCsvOption>),
    /// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
    DateFormat(Option<String>),
    /// DELIMITER \[ AS \] 'delimiter_character'
    Delimiter(char),
    /// EMPTYASNULL
    EmptyAsNull,
    /// `ENCRYPTED \[ AUTO \]`
    Encrypted {
        /// Whether `AUTO` was specified for encryption.
        auto: bool,
    },
    /// ESCAPE
    Escape,
    /// EXTENSION 'extension-name'
    Extension(String),
    /// FIXEDWIDTH \[ AS \] 'fixedwidth-spec'
    FixedWidth(String),
    /// GZIP
    Gzip,
    /// HEADER
    Header,
    /// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
    IamRole(IamRoleKind),
    /// IGNOREHEADER \[ AS \] number_rows
    IgnoreHeader(u64),
    /// JSON \[ AS \] 'json_option'
    Json(Option<String>),
    /// MANIFEST \[ VERBOSE \]
    Manifest {
        /// Whether the MANIFEST is verbose.
        verbose: bool,
    },
    /// MAXFILESIZE \[ AS \] max-size \[ MB | GB \]
    MaxFileSize(FileSize),
    /// `NULL \[ AS \] 'null_string'`
    Null(String),
    /// `PARALLEL [ { ON | TRUE } | { OFF | FALSE } ]`
    Parallel(Option<bool>),
    /// PARQUET
    Parquet,
    /// PARTITION BY ( column_name [, ... ] ) \[ INCLUDE \]
    PartitionBy(UnloadPartitionBy),
    /// REGION \[ AS \] 'aws-region' }
    Region(String),
    /// REMOVEQUOTES
    RemoveQuotes,
    /// ROWGROUPSIZE \[ AS \] size \[ MB | GB \]
    RowGroupSize(FileSize),
    /// STATUPDATE [ { ON | TRUE } | { OFF | FALSE } ]
    StatUpdate(Option<bool>),
    /// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
    TimeFormat(Option<String>),
    /// TRUNCATECOLUMNS
    TruncateColumns,
    /// ZSTD
    Zstd,
    /// Redshift `CREDENTIALS 'auth-args'`
    /// <https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html>
    Credentials(String),
}

impl fmt::Display for CopyLegacyOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use CopyLegacyOption::*;
        match self {
            AcceptAnyDate => write!(f, "ACCEPTANYDATE"),
            AcceptInvChars(ch) => {
                write!(f, "ACCEPTINVCHARS")?;
                if let Some(ch) = ch {
                    write!(f, " '{}'", value::escape_single_quote_string(ch))?;
                }
                Ok(())
            }
            AddQuotes => write!(f, "ADDQUOTES"),
            AllowOverwrite => write!(f, "ALLOWOVERWRITE"),
            Binary => write!(f, "BINARY"),
            BlankAsNull => write!(f, "BLANKSASNULL"),
            Bzip2 => write!(f, "BZIP2"),
            CleanPath => write!(f, "CLEANPATH"),
            CompUpdate { preset, enabled } => {
                write!(f, "COMPUPDATE")?;
                if *preset {
                    write!(f, " PRESET")?;
                } else if let Some(enabled) = enabled {
                    write!(
                        f,
                        "{}",
                        match enabled {
                            true => " TRUE",
                            false => " FALSE",
                        }
                    )?;
                }
                Ok(())
            }
            Csv(opts) => {
                write!(f, "CSV")?;
                if !opts.is_empty() {
                    write!(f, " {}", display_separated(opts, " "))?;
                }
                Ok(())
            }
            DateFormat(fmt) => {
                write!(f, "DATEFORMAT")?;
                if let Some(fmt) = fmt {
                    write!(f, " '{}'", value::escape_single_quote_string(fmt))?;
                }
                Ok(())
            }
            Delimiter(char) => write!(f, "DELIMITER '{char}'"),
            EmptyAsNull => write!(f, "EMPTYASNULL"),
            Encrypted { auto } => write!(f, "ENCRYPTED{}", if *auto { " AUTO" } else { "" }),
            Escape => write!(f, "ESCAPE"),
            Extension(ext) => write!(f, "EXTENSION '{}'", value::escape_single_quote_string(ext)),
            FixedWidth(spec) => write!(
                f,
                "FIXEDWIDTH '{}'",
                value::escape_single_quote_string(spec)
            ),
            Gzip => write!(f, "GZIP"),
            Header => write!(f, "HEADER"),
            IamRole(role) => write!(f, "IAM_ROLE {role}"),
            IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
            Json(opt) => {
                write!(f, "JSON")?;
                if let Some(opt) = opt {
                    write!(f, " AS '{}'", value::escape_single_quote_string(opt))?;
                }
                Ok(())
            }
            Manifest { verbose } => write!(f, "MANIFEST{}", if *verbose { " VERBOSE" } else { "" }),
            MaxFileSize(file_size) => write!(f, "MAXFILESIZE {file_size}"),
            Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
            Parallel(enabled) => {
                write!(
                    f,
                    "PARALLEL{}",
                    match enabled {
                        Some(true) => " TRUE",
                        Some(false) => " FALSE",
                        _ => "",
                    }
                )
            }
            Parquet => write!(f, "PARQUET"),
            PartitionBy(p) => write!(f, "{p}"),
            Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)),
            RemoveQuotes => write!(f, "REMOVEQUOTES"),
            RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"),
            StatUpdate(enabled) => {
                write!(
                    f,
                    "STATUPDATE{}",
                    match enabled {
                        Some(true) => " TRUE",
                        Some(false) => " FALSE",
                        _ => "",
                    }
                )
            }
            TimeFormat(fmt) => {
                write!(f, "TIMEFORMAT")?;
                if let Some(fmt) = fmt {
                    write!(f, " '{}'", value::escape_single_quote_string(fmt))?;
                }
                Ok(())
            }
            TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
            Zstd => write!(f, "ZSTD"),
            Credentials(s) => write!(f, "CREDENTIALS '{}'", value::escape_single_quote_string(s)),
        }
    }
}

/// ```sql
/// SIZE \[ MB | GB \]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FileSize {
    /// Numeric size value.
    pub size: ValueWithSpan,
    /// Optional unit for the size (MB or GB).
    pub unit: Option<FileSizeUnit>,
}

impl fmt::Display for FileSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.size)?;
        if let Some(unit) = &self.unit {
            write!(f, " {unit}")?;
        }
        Ok(())
    }
}

/// Units for `FileSize` (MB or GB).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FileSizeUnit {
    /// Megabytes.
    MB,
    /// Gigabytes.
    GB,
}

impl fmt::Display for FileSizeUnit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileSizeUnit::MB => write!(f, "MB"),
            FileSizeUnit::GB => write!(f, "GB"),
        }
    }
}

/// Specifies the partition keys for the unload operation
///
/// ```sql
/// PARTITION BY ( column_name [, ... ] ) [ INCLUDE ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UnloadPartitionBy {
    /// Columns used to partition the unload output.
    pub columns: Vec<Ident>,
    /// Whether to include the partition in the output.
    pub include: bool,
}

impl fmt::Display for UnloadPartitionBy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "PARTITION BY ({}){}",
            display_comma_separated(&self.columns),
            if self.include { " INCLUDE" } else { "" }
        )
    }
}

/// An `IAM_ROLE` option in the AWS ecosystem
///
/// [Redshift COPY](https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html#copy-iam-role)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IamRoleKind {
    /// Default role
    Default,
    /// Specific role ARN, for example: `arn:aws:iam::123456789:role/role1`
    Arn(String),
}

impl fmt::Display for IamRoleKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            IamRoleKind::Default => write!(f, "DEFAULT"),
            IamRoleKind::Arn(arn) => write!(f, "'{arn}'"),
        }
    }
}

/// A `CSV` option in `COPY` statement before PostgreSQL version 9.0.
///
/// <https://www.postgresql.org/docs/8.4/sql-copy.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyLegacyCsvOption {
    /// HEADER
    Header,
    /// QUOTE \[ AS \] 'quote_character'
    Quote(char),
    /// ESCAPE \[ AS \] 'escape_character'
    Escape(char),
    /// FORCE QUOTE { column_name [, ...] | * }
    ForceQuote(Vec<Ident>),
    /// FORCE NOT NULL column_name [, ...]
    ForceNotNull(Vec<Ident>),
}

impl fmt::Display for CopyLegacyCsvOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use CopyLegacyCsvOption::*;
        match self {
            Header => write!(f, "HEADER"),
            Quote(char) => write!(f, "QUOTE '{char}'"),
            Escape(char) => write!(f, "ESCAPE '{char}'"),
            ForceQuote(columns) => write!(f, "FORCE QUOTE {}", display_comma_separated(columns)),
            ForceNotNull(columns) => {
                write!(f, "FORCE NOT NULL {}", display_comma_separated(columns))
            }
        }
    }
}

/// Objects that can be discarded with `DISCARD`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DiscardObject {
    /// Discard all session state.
    ALL,
    /// Discard cached plans.
    PLANS,
    /// Discard sequence values.
    SEQUENCES,
    /// Discard temporary objects.
    TEMP,
}

impl fmt::Display for DiscardObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DiscardObject::ALL => f.write_str("ALL"),
            DiscardObject::PLANS => f.write_str("PLANS"),
            DiscardObject::SEQUENCES => f.write_str("SEQUENCES"),
            DiscardObject::TEMP => f.write_str("TEMP"),
        }
    }
}

/// Types of flush operations supported by `FLUSH`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushType {
    /// Flush binary logs.
    BinaryLogs,
    /// Flush engine logs.
    EngineLogs,
    /// Flush error logs.
    ErrorLogs,
    /// Flush general logs.
    GeneralLogs,
    /// Flush hosts information.
    Hosts,
    /// Flush logs.
    Logs,
    /// Flush privileges.
    Privileges,
    /// Flush optimizer costs.
    OptimizerCosts,
    /// Flush relay logs.
    RelayLogs,
    /// Flush slow logs.
    SlowLogs,
    /// Flush status.
    Status,
    /// Flush user resources.
    UserResources,
    /// Flush table data.
    Tables,
}

impl fmt::Display for FlushType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FlushType::BinaryLogs => f.write_str("BINARY LOGS"),
            FlushType::EngineLogs => f.write_str("ENGINE LOGS"),
            FlushType::ErrorLogs => f.write_str("ERROR LOGS"),
            FlushType::GeneralLogs => f.write_str("GENERAL LOGS"),
            FlushType::Hosts => f.write_str("HOSTS"),
            FlushType::Logs => f.write_str("LOGS"),
            FlushType::Privileges => f.write_str("PRIVILEGES"),
            FlushType::OptimizerCosts => f.write_str("OPTIMIZER_COSTS"),
            FlushType::RelayLogs => f.write_str("RELAY LOGS"),
            FlushType::SlowLogs => f.write_str("SLOW LOGS"),
            FlushType::Status => f.write_str("STATUS"),
            FlushType::UserResources => f.write_str("USER_RESOURCES"),
            FlushType::Tables => f.write_str("TABLES"),
        }
    }
}

/// Location modifier for flush commands.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushLocation {
    /// Do not write changes to the binary log.
    NoWriteToBinlog,
    /// Apply flush locally.
    Local,
}

impl fmt::Display for FlushLocation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FlushLocation::NoWriteToBinlog => f.write_str("NO_WRITE_TO_BINLOG"),
            FlushLocation::Local => f.write_str("LOCAL"),
        }
    }
}

/// Optional context modifier for statements that can be or `LOCAL`, `GLOBAL`, or `SESSION`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ContextModifier {
    /// `LOCAL` identifier, usually related to transactional states.
    Local,
    /// `SESSION` identifier
    Session,
    /// `GLOBAL` identifier
    Global,
}

impl fmt::Display for ContextModifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Local => {
                write!(f, "LOCAL ")
            }
            Self::Session => {
                write!(f, "SESSION ")
            }
            Self::Global => {
                write!(f, "GLOBAL ")
            }
        }
    }
}

/// Function describe in DROP FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DropFunctionOption {
    /// `RESTRICT` option for DROP FUNCTION.
    Restrict,
    /// `CASCADE` option for DROP FUNCTION.
    Cascade,
}

impl fmt::Display for DropFunctionOption {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DropFunctionOption::Restrict => write!(f, "RESTRICT "),
            DropFunctionOption::Cascade => write!(f, "CASCADE  "),
        }
    }
}

/// Generic function description for DROP FUNCTION and CREATE TRIGGER.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionDesc {
    /// The function name.
    pub name: ObjectName,
    /// Optional list of function arguments.
    pub args: Option<Vec<OperateFunctionArg>>,
}

impl fmt::Display for FunctionDesc {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name)?;
        if let Some(args) = &self.args {
            write!(f, "({})", display_comma_separated(args))?;
        }
        Ok(())
    }
}

/// Function argument in CREATE OR DROP FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OperateFunctionArg {
    /// Optional argument mode (`IN`, `OUT`, `INOUT`).
    pub mode: Option<ArgMode>,
    /// Optional argument identifier/name.
    pub name: Option<Ident>,
    /// The data type of the argument.
    pub data_type: DataType,
    /// Optional default expression for the argument.
    pub default_expr: Option<Expr>,
}

impl OperateFunctionArg {
    /// Returns an unnamed argument.
    pub fn unnamed(data_type: DataType) -> Self {
        Self {
            mode: None,
            name: None,
            data_type,
            default_expr: None,
        }
    }

    /// Returns an argument with name.
    pub fn with_name(name: &str, data_type: DataType) -> Self {
        Self {
            mode: None,
            name: Some(name.into()),
            data_type,
            default_expr: None,
        }
    }
}

impl fmt::Display for OperateFunctionArg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(mode) = &self.mode {
            write!(f, "{mode} ")?;
        }
        if let Some(name) = &self.name {
            write!(f, "{name} ")?;
        }
        write!(f, "{}", self.data_type)?;
        if let Some(default_expr) = &self.default_expr {
            write!(f, " = {default_expr}")?;
        }
        Ok(())
    }
}

/// The mode of an argument in CREATE FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ArgMode {
    /// `IN` mode.
    In,
    /// `OUT` mode.
    Out,
    /// `INOUT` mode.
    InOut,
    /// `VARIADIC` mode.
    Variadic,
}

impl fmt::Display for ArgMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ArgMode::In => write!(f, "IN"),
            ArgMode::Out => write!(f, "OUT"),
            ArgMode::InOut => write!(f, "INOUT"),
            ArgMode::Variadic => write!(f, "VARIADIC"),
        }
    }
}

/// These attributes inform the query optimizer about the behavior of the function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionBehavior {
    /// Function is immutable.
    Immutable,
    /// Function is stable.
    Stable,
    /// Function is volatile.
    Volatile,
}

impl fmt::Display for FunctionBehavior {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionBehavior::Immutable => write!(f, "IMMUTABLE"),
            FunctionBehavior::Stable => write!(f, "STABLE"),
            FunctionBehavior::Volatile => write!(f, "VOLATILE"),
        }
    }
}

/// Security attribute for functions: SECURITY DEFINER or SECURITY INVOKER.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionSecurity {
    /// Execute the function with the privileges of the user who defined it.
    Definer,
    /// Execute the function with the privileges of the user who invokes it.
    Invoker,
}

impl fmt::Display for FunctionSecurity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionSecurity::Definer => write!(f, "SECURITY DEFINER"),
            FunctionSecurity::Invoker => write!(f, "SECURITY INVOKER"),
        }
    }
}

/// Value for a SET configuration parameter in a CREATE FUNCTION statement.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionSetValue {
    /// SET param = DEFAULT / SET param TO DEFAULT
    Default,
    /// SET param = value1, value2, ...
    Values(Vec<Expr>),
    /// SET param FROM CURRENT
    FromCurrent,
}

/// A SET configuration_parameter clause in a CREATE FUNCTION statement.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionDefinitionSetParam {
    /// The name of the configuration parameter.
    pub name: ObjectName,
    /// The value to set for the parameter.
    pub value: FunctionSetValue,
}

impl fmt::Display for FunctionDefinitionSetParam {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SET {} ", self.name)?;
        match &self.value {
            FunctionSetValue::Default => write!(f, "= DEFAULT"),
            FunctionSetValue::Values(values) => {
                write!(f, "= {}", display_comma_separated(values))
            }
            FunctionSetValue::FromCurrent => write!(f, "FROM CURRENT"),
        }
    }
}

/// These attributes describe the behavior of the function when called with a null argument.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionCalledOnNull {
    /// Function is called even when inputs are null.
    CalledOnNullInput,
    /// Function returns null when any input is null.
    ReturnsNullOnNullInput,
    /// Function is strict about null inputs.
    Strict,
}

impl fmt::Display for FunctionCalledOnNull {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionCalledOnNull::CalledOnNullInput => write!(f, "CALLED ON NULL INPUT"),
            FunctionCalledOnNull::ReturnsNullOnNullInput => write!(f, "RETURNS NULL ON NULL INPUT"),
            FunctionCalledOnNull::Strict => write!(f, "STRICT"),
        }
    }
}

/// If it is safe for PostgreSQL to call the function from multiple threads at once
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionParallel {
    /// The function is not safe to run in parallel.
    Unsafe,
    /// The function is restricted for parallel execution.
    Restricted,
    /// The function is safe to run in parallel.
    Safe,
}

impl fmt::Display for FunctionParallel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionParallel::Unsafe => write!(f, "PARALLEL UNSAFE"),
            FunctionParallel::Restricted => write!(f, "PARALLEL RESTRICTED"),
            FunctionParallel::Safe => write!(f, "PARALLEL SAFE"),
        }
    }
}

/// [BigQuery] Determinism specifier used in a UDF definition.
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionDeterminismSpecifier {
    /// Function is deterministic.
    Deterministic,
    /// Function is not deterministic.
    NotDeterministic,
}

impl fmt::Display for FunctionDeterminismSpecifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FunctionDeterminismSpecifier::Deterministic => {
                write!(f, "DETERMINISTIC")
            }
            FunctionDeterminismSpecifier::NotDeterministic => {
                write!(f, "NOT DETERMINISTIC")
            }
        }
    }
}

/// Represent the expression body of a `CREATE FUNCTION` statement as well as
/// where within the statement, the body shows up.
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
/// [PostgreSQL]: https://www.postgresql.org/docs/15/sql-createfunction.html
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateFunctionBody {
    /// A function body expression using the 'AS' keyword and shows up
    /// before any `OPTIONS` clause.
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION myfunc(x FLOAT64, y FLOAT64) RETURNS FLOAT64
    /// AS (x * y)
    /// OPTIONS(description="desc");
    /// ```
    ///
    /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
    /// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
    AsBeforeOptions {
        /// The primary expression.
        body: Expr,
        /// Link symbol if the primary expression contains the name of shared library file.
        ///
        /// Example:
        /// ```sql
        /// CREATE FUNCTION cas_in(input cstring) RETURNS cas
        /// AS 'MODULE_PATHNAME', 'cas_in_wrapper'
        /// ```
        /// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
        link_symbol: Option<Expr>,
    },
    /// A function body expression using the 'AS' keyword and shows up
    /// after any `OPTIONS` clause.
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION myfunc(x FLOAT64, y FLOAT64) RETURNS FLOAT64
    /// OPTIONS(description="desc")
    /// AS (x * y);
    /// ```
    ///
    /// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
    AsAfterOptions(Expr),
    /// Function body with statements before the `RETURN` keyword.
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION my_scalar_udf(a INT, b INT)
    /// RETURNS INT
    /// AS
    /// BEGIN
    ///     DECLARE c INT;
    ///     SET c = a + b;
    ///     RETURN c;
    /// END
    /// ```
    ///
    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
    AsBeginEnd(BeginEndStatements),
    /// Function body expression using the 'RETURN' keyword.
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION myfunc(a INTEGER, IN b INTEGER = 1) RETURNS INTEGER
    /// LANGUAGE SQL
    /// RETURN a + b;
    /// ```
    ///
    /// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
    Return(Expr),

    /// Function body expression using the 'AS RETURN' keywords
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION myfunc(a INT, b INT)
    /// RETURNS TABLE
    /// AS RETURN (SELECT a + b AS sum);
    /// ```
    ///
    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
    AsReturnExpr(Expr),

    /// Function body expression using the 'AS RETURN' keywords, with an un-parenthesized SELECT query
    ///
    /// Example:
    /// ```sql
    /// CREATE FUNCTION myfunc(a INT, b INT)
    /// RETURNS TABLE
    /// AS RETURN SELECT a + b AS sum;
    /// ```
    ///
    /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-ver16#select_stmt
    AsReturnSelect(Select),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `USING` clause options for `CREATE FUNCTION` (e.g., JAR, FILE, ARCHIVE).
pub enum CreateFunctionUsing {
    /// Use a JAR file located at the given URI.
    Jar(String),
    /// Use a file located at the given URI.
    File(String),
    /// Use an archive located at the given URI.
    Archive(String),
}

impl fmt::Display for CreateFunctionUsing {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "USING ")?;
        match self {
            CreateFunctionUsing::Jar(uri) => write!(f, "JAR '{uri}'"),
            CreateFunctionUsing::File(uri) => write!(f, "FILE '{uri}'"),
            CreateFunctionUsing::Archive(uri) => write!(f, "ARCHIVE '{uri}'"),
        }
    }
}

/// `NAME = <EXPR>` arguments for DuckDB macros
///
/// See [Create Macro - DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
/// for more details
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MacroArg {
    /// The argument name.
    pub name: Ident,
    /// Optional default expression for the argument.
    pub default_expr: Option<Expr>,
}

impl MacroArg {
    /// Returns an argument with name.
    pub fn new(name: &str) -> Self {
        Self {
            name: name.into(),
            default_expr: None,
        }
    }
}

impl fmt::Display for MacroArg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name)?;
        if let Some(default_expr) = &self.default_expr {
            write!(f, " := {default_expr}")?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Definition for a DuckDB macro: either an expression or a table-producing query.
pub enum MacroDefinition {
    /// The macro is defined as an expression.
    Expr(Expr),
    /// The macro is defined as a table (query).
    Table(Box<Query>),
}

impl fmt::Display for MacroDefinition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MacroDefinition::Expr(expr) => write!(f, "{expr}")?,
            MacroDefinition::Table(query) => write!(f, "{query}")?,
        }
        Ok(())
    }
}

/// Schema possible naming variants ([1]).
///
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#schema-definition
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SchemaName {
    /// Only schema name specified: `<schema name>`.
    Simple(ObjectName),
    /// Only authorization identifier specified: `AUTHORIZATION <schema authorization identifier>`.
    UnnamedAuthorization(Ident),
    /// Both schema name and authorization identifier specified: `<schema name>  AUTHORIZATION <schema authorization identifier>`.
    NamedAuthorization(ObjectName, Ident),
}

impl fmt::Display for SchemaName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SchemaName::Simple(name) => {
                write!(f, "{name}")
            }
            SchemaName::UnnamedAuthorization(authorization) => {
                write!(f, "AUTHORIZATION {authorization}")
            }
            SchemaName::NamedAuthorization(name, authorization) => {
                write!(f, "{name} AUTHORIZATION {authorization}")
            }
        }
    }
}

/// Fulltext search modifiers ([1]).
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html#function_match
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SearchModifier {
    /// `IN NATURAL LANGUAGE MODE`.
    InNaturalLanguageMode,
    /// `IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION`.
    InNaturalLanguageModeWithQueryExpansion,
    ///`IN BOOLEAN MODE`.
    InBooleanMode,
    ///`WITH QUERY EXPANSION`.
    WithQueryExpansion,
}

impl fmt::Display for SearchModifier {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InNaturalLanguageMode => {
                write!(f, "IN NATURAL LANGUAGE MODE")?;
            }
            Self::InNaturalLanguageModeWithQueryExpansion => {
                write!(f, "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION")?;
            }
            Self::InBooleanMode => {
                write!(f, "IN BOOLEAN MODE")?;
            }
            Self::WithQueryExpansion => {
                write!(f, "WITH QUERY EXPANSION")?;
            }
        }

        Ok(())
    }
}

/// Represents a `LOCK TABLE` clause with optional alias and lock type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LockTable {
    /// The table identifier to lock.
    pub table: Ident,
    /// Optional alias for the table.
    pub alias: Option<Ident>,
    /// The type of lock to apply to the table.
    pub lock_type: LockTableType,
}

impl fmt::Display for LockTable {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let Self {
            table: tbl_name,
            alias,
            lock_type,
        } = self;

        write!(f, "{tbl_name} ")?;
        if let Some(alias) = alias {
            write!(f, "AS {alias} ")?;
        }
        write!(f, "{lock_type}")?;
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// The type of lock used in `LOCK TABLE` statements.
pub enum LockTableType {
    /// Shared/read lock. If `local` is true, it's a local read lock.
    Read {
        /// Whether the read lock is local.
        local: bool,
    },
    /// Exclusive/write lock. If `low_priority` is true, the write is low priority.
    Write {
        /// Whether the write lock is low priority.
        low_priority: bool,
    },
}

impl fmt::Display for LockTableType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Read { local } => {
                write!(f, "READ")?;
                if *local {
                    write!(f, " LOCAL")?;
                }
            }
            Self::Write { low_priority } => {
                if *low_priority {
                    write!(f, "LOW_PRIORITY ")?;
                }
                write!(f, "WRITE")?;
            }
        }

        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Hive-specific `SET LOCATION` helper used in some `LOAD DATA` statements.
pub struct HiveSetLocation {
    /// Whether the `SET` keyword was present.
    pub has_set: bool,
    /// The location identifier.
    pub location: Ident,
}

impl fmt::Display for HiveSetLocation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.has_set {
            write!(f, "SET ")?;
        }
        write!(f, "LOCATION {}", self.location)
    }
}

/// MySQL `ALTER TABLE` only  [FIRST | AFTER column_name]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `ALTER TABLE` column position specifier: `FIRST` or `AFTER <column>`.
pub enum MySQLColumnPosition {
    /// Place the column first in the table.
    First,
    /// Place the column after the specified identifier.
    After(Ident),
}

impl Display for MySQLColumnPosition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MySQLColumnPosition::First => write!(f, "FIRST"),
            MySQLColumnPosition::After(ident) => {
                let column_name = &ident.value;
                write!(f, "AFTER {column_name}")
            }
        }
    }
}

/// MySQL `CREATE VIEW` algorithm parameter: [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `CREATE VIEW` algorithm options.
pub enum CreateViewAlgorithm {
    /// `UNDEFINED` algorithm.
    Undefined,
    /// `MERGE` algorithm.
    Merge,
    /// `TEMPTABLE` algorithm.
    TempTable,
}

impl Display for CreateViewAlgorithm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateViewAlgorithm::Undefined => write!(f, "UNDEFINED"),
            CreateViewAlgorithm::Merge => write!(f, "MERGE"),
            CreateViewAlgorithm::TempTable => write!(f, "TEMPTABLE"),
        }
    }
}
/// MySQL `CREATE VIEW` security parameter: [SQL SECURITY { DEFINER | INVOKER }]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `CREATE VIEW` SQL SECURITY options.
pub enum CreateViewSecurity {
    /// The view runs with the privileges of the definer.
    Definer,
    /// The view runs with the privileges of the invoker.
    Invoker,
}

impl Display for CreateViewSecurity {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateViewSecurity::Definer => write!(f, "DEFINER"),
            CreateViewSecurity::Invoker => write!(f, "INVOKER"),
        }
    }
}

/// [MySQL] `CREATE VIEW` additional parameters
///
/// [MySQL]: https://dev.mysql.com/doc/refman/9.1/en/create-view.html
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateViewParams {
    /// Optional view algorithm (e.g., MERGE, TEMPTABLE).
    pub algorithm: Option<CreateViewAlgorithm>,
    /// Optional definer (the security principal that will own the view).
    pub definer: Option<GranteeName>,
    /// Optional SQL SECURITY setting for the view.
    pub security: Option<CreateViewSecurity>,
}

impl Display for CreateViewParams {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let CreateViewParams {
            algorithm,
            definer,
            security,
        } = self;
        if let Some(algorithm) = algorithm {
            write!(f, "ALGORITHM = {algorithm} ")?;
        }
        if let Some(definers) = definer {
            write!(f, "DEFINER = {definers} ")?;
        }
        if let Some(security) = security {
            write!(f, "SQL SECURITY {security} ")?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Key/Value, where the value is a (optionally named) list of identifiers
///
/// ```sql
/// UNION = (tbl_name[,tbl_name]...)
/// ENGINE = ReplicatedMergeTree('/table_name','{replica}', ver)
/// ENGINE = SummingMergeTree([columns])
/// ```
pub struct NamedParenthesizedList {
    /// The option key (identifier) for this named list.
    pub key: Ident,
    /// Optional secondary name associated with the key.
    pub name: Option<Ident>,
    /// The list of identifier values for the key.
    pub values: Vec<Ident>,
}

/// Snowflake `WITH ROW ACCESS POLICY policy_name ON (identifier, ...)`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
/// <https://docs.snowflake.com/en/user-guide/security-row-intro>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RowAccessPolicy {
    /// The fully-qualified policy object name.
    pub policy: ObjectName,
    /// Identifiers for the columns or objects the policy applies to.
    pub on: Vec<Ident>,
}

impl RowAccessPolicy {
    /// Create a new `RowAccessPolicy` for the given `policy` and `on` identifiers.
    pub fn new(policy: ObjectName, on: Vec<Ident>) -> Self {
        Self { policy, on }
    }
}

impl Display for RowAccessPolicy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "WITH ROW ACCESS POLICY {} ON ({})",
            self.policy,
            display_comma_separated(self.on.as_slice())
        )
    }
}

/// Snowflake `[ WITH ] STORAGE LIFECYCLE POLICY <policy_name> ON ( <col_name> [ , ... ] )`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct StorageLifecyclePolicy {
    /// The fully-qualified policy object name.
    pub policy: ObjectName,
    /// Column names the policy applies to.
    pub on: Vec<Ident>,
}

impl Display for StorageLifecyclePolicy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "WITH STORAGE LIFECYCLE POLICY {} ON ({})",
            self.policy,
            display_comma_separated(self.on.as_slice())
        )
    }
}

/// Snowflake `WITH TAG ( tag_name = '<tag_value>', ...)`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Tag {
    /// The tag key (can be qualified).
    pub key: ObjectName,
    /// The tag value as a string.
    pub value: String,
}

impl Tag {
    /// Create a new `Tag` with the given key and value.
    pub fn new(key: ObjectName, value: String) -> Self {
        Self { key, value }
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}='{}'", self.key, self.value)
    }
}

/// Snowflake `WITH CONTACT ( purpose = contact [ , purpose = contact ...] )`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ContactEntry {
    /// The purpose label for the contact entry.
    pub purpose: String,
    /// The contact information associated with the purpose.
    pub contact: String,
}

impl Display for ContactEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} = {}", self.purpose, self.contact)
    }
}

/// Helper to indicate if a comment includes the `=` in the display form
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CommentDef {
    /// Includes `=` when printing the comment, as `COMMENT = 'comment'`
    /// Does not include `=` when printing the comment, as `COMMENT 'comment'`
    WithEq(String),
    /// Comment variant that omits the `=` when displayed.
    WithoutEq(String),
}

impl Display for CommentDef {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CommentDef::WithEq(comment) | CommentDef::WithoutEq(comment) => write!(f, "{comment}"),
        }
    }
}

/// Helper to indicate if a collection should be wrapped by a symbol in the display form
///
/// [`Display`] is implemented for every [`Vec<T>`] where `T: Display`.
/// The string output is a comma separated list for the vec items
///
/// # Examples
/// ```
/// # use sqlparser::ast::WrappedCollection;
/// let items = WrappedCollection::Parentheses(vec!["one", "two", "three"]);
/// assert_eq!("(one, two, three)", items.to_string());
///
/// let items = WrappedCollection::NoWrapping(vec!["one", "two", "three"]);
/// assert_eq!("one, two, three", items.to_string());
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WrappedCollection<T> {
    /// Print the collection without wrapping symbols, as `item, item, item`
    NoWrapping(T),
    /// Wraps the collection in Parentheses, as `(item, item, item)`
    Parentheses(T),
}

impl<T> Display for WrappedCollection<Vec<T>>
where
    T: Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            WrappedCollection::NoWrapping(inner) => {
                write!(f, "{}", display_comma_separated(inner.as_slice()))
            }
            WrappedCollection::Parentheses(inner) => {
                write!(f, "({})", display_comma_separated(inner.as_slice()))
            }
        }
    }
}

/// Represents a single PostgreSQL utility option.
///
/// A utility option is a key-value pair where the key is an identifier (IDENT) and the value
/// can be one of the following:
/// - A number with an optional sign (`+` or `-`). Example: `+10`, `-10.2`, `3`
/// - A non-keyword string. Example: `option1`, `'option2'`, `"option3"`
/// - keyword: `TRUE`, `FALSE`, `ON` (`off` is also accept).
/// - Empty. Example: `ANALYZE` (identifier only)
///
/// Utility options are used in various PostgreSQL DDL statements, including statements such as
/// `CLUSTER`, `EXPLAIN`, `VACUUM`, and `REINDEX`. These statements format options as `( option [, ...] )`.
///
/// [CLUSTER](https://www.postgresql.org/docs/current/sql-cluster.html)
/// [EXPLAIN](https://www.postgresql.org/docs/current/sql-explain.html)
/// [VACUUM](https://www.postgresql.org/docs/current/sql-vacuum.html)
/// [REINDEX](https://www.postgresql.org/docs/current/sql-reindex.html)
///
/// For example, the `EXPLAIN` AND `VACUUM` statements with options might look like this:
/// ```sql
/// EXPLAIN (ANALYZE, VERBOSE TRUE, FORMAT TEXT) SELECT * FROM my_table;
///
/// VACUUM (VERBOSE, ANALYZE ON, PARALLEL 10) my_table;
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UtilityOption {
    /// The option name (identifier).
    pub name: Ident,
    /// Optional argument for the option (number, string, keyword, etc.).
    pub arg: Option<Expr>,
}

impl Display for UtilityOption {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref arg) = self.arg {
            write!(f, "{} {}", self.name, arg)
        } else {
            write!(f, "{}", self.name)
        }
    }
}

/// Represents the different options available for `SHOW`
/// statements to filter the results. Example from Snowflake:
/// <https://docs.snowflake.com/en/sql-reference/sql/show-tables>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowStatementOptions {
    /// Optional scope to show in (for example: TABLE, SCHEMA).
    pub show_in: Option<ShowStatementIn>,
    /// Optional `STARTS WITH` filter value.
    pub starts_with: Option<ValueWithSpan>,
    /// Optional `LIMIT` expression.
    pub limit: Option<Expr>,
    /// Optional `FROM` value used with `LIMIT`.
    pub limit_from: Option<ValueWithSpan>,
    /// Optional filter position (infix or suffix) for `LIKE`/`FILTER`.
    pub filter_position: Option<ShowStatementFilterPosition>,
}

impl Display for ShowStatementOptions {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let (like_in_infix, like_in_suffix) = match &self.filter_position {
            Some(ShowStatementFilterPosition::Infix(filter)) => {
                (format!(" {filter}"), "".to_string())
            }
            Some(ShowStatementFilterPosition::Suffix(filter)) => {
                ("".to_string(), format!(" {filter}"))
            }
            None => ("".to_string(), "".to_string()),
        };
        write!(
            f,
            "{like_in_infix}{show_in}{starts_with}{limit}{from}{like_in_suffix}",
            show_in = match &self.show_in {
                Some(i) => format!(" {i}"),
                None => String::new(),
            },
            starts_with = match &self.starts_with {
                Some(s) => format!(" STARTS WITH {s}"),
                None => String::new(),
            },
            limit = match &self.limit {
                Some(l) => format!(" LIMIT {l}"),
                None => String::new(),
            },
            from = match &self.limit_from {
                Some(f) => format!(" FROM {f}"),
                None => String::new(),
            }
        )?;
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Where a `SHOW` filter appears relative to the main clause.
pub enum ShowStatementFilterPosition {
    /// Put the filter in an infix position (e.g. `SHOW COLUMNS LIKE '%name%' IN TABLE tbl`).
    Infix(ShowStatementFilter), // For example: SHOW COLUMNS LIKE '%name%' IN TABLE tbl
    /// Put the filter in a suffix position (e.g. `SHOW COLUMNS IN tbl LIKE '%name%'`).
    Suffix(ShowStatementFilter), // For example: SHOW COLUMNS IN tbl LIKE '%name%'
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Parent object types usable with `SHOW ... IN <parent>` clauses.
pub enum ShowStatementInParentType {
    /// ACCOUNT parent type for SHOW statements.
    Account,
    /// DATABASE parent type for SHOW statements.
    Database,
    /// SCHEMA parent type for SHOW statements.
    Schema,
    /// TABLE parent type for SHOW statements.
    Table,
    /// VIEW parent type for SHOW statements.
    View,
}

impl fmt::Display for ShowStatementInParentType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ShowStatementInParentType::Account => write!(f, "ACCOUNT"),
            ShowStatementInParentType::Database => write!(f, "DATABASE"),
            ShowStatementInParentType::Schema => write!(f, "SCHEMA"),
            ShowStatementInParentType::Table => write!(f, "TABLE"),
            ShowStatementInParentType::View => write!(f, "VIEW"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents a `SHOW ... IN` clause with optional parent qualifier and name.
pub struct ShowStatementIn {
    /// The clause that specifies what to show (e.g. COLUMNS, TABLES).
    pub clause: ShowStatementInClause,
    /// Optional parent type qualifier (ACCOUNT/DATABASE/...).
    pub parent_type: Option<ShowStatementInParentType>,
    /// Optional parent object name for the SHOW clause.
    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
    pub parent_name: Option<ObjectName>,
}

impl fmt::Display for ShowStatementIn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.clause)?;
        if let Some(parent_type) = &self.parent_type {
            write!(f, " {parent_type}")?;
        }
        if let Some(parent_name) = &self.parent_name {
            write!(f, " {parent_name}")?;
        }
        Ok(())
    }
}

/// A Show Charset statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowCharset {
    /// The statement can be written as `SHOW CHARSET` or `SHOW CHARACTER SET`
    /// true means CHARSET was used and false means CHARACTER SET was used
    pub is_shorthand: bool,
    /// Optional `LIKE`/`WHERE`-style filter for the statement.
    pub filter: Option<ShowStatementFilter>,
}

impl fmt::Display for ShowCharset {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SHOW")?;
        if self.is_shorthand {
            write!(f, " CHARSET")?;
        } else {
            write!(f, " CHARACTER SET")?;
        }
        if let Some(filter) = &self.filter {
            write!(f, " {filter}")?;
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options for a `SHOW OBJECTS` statement.
pub struct ShowObjects {
    /// Whether to show terse output.
    pub terse: bool,
    /// Additional options controlling the SHOW output.
    pub show_options: ShowStatementOptions,
}

/// MSSQL's json null clause
///
/// ```plaintext
/// <json_null_clause> ::=
///       NULL ON NULL
///     | ABSENT ON NULL
/// ```
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16#json_null_clause>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonNullClause {
    /// `NULL ON NULL` behavior for JSON functions.
    NullOnNull,
    /// `ABSENT ON NULL` behavior for JSON functions.
    AbsentOnNull,
}

impl Display for JsonNullClause {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            JsonNullClause::NullOnNull => write!(f, "NULL ON NULL"),
            JsonNullClause::AbsentOnNull => write!(f, "ABSENT ON NULL"),
        }
    }
}

/// PostgreSQL JSON function RETURNING clause
///
/// Example:
/// ```sql
/// JSON_OBJECT('a': 1 RETURNING jsonb)
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct JsonReturningClause {
    /// The data type to return from the JSON function (e.g. JSON/JSONB).
    pub data_type: DataType,
}

impl Display for JsonReturningClause {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "RETURNING {}", self.data_type)
    }
}

/// rename object definition
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RenameTable {
    /// The current name of the object to rename.
    pub old_name: ObjectName,
    /// The new name for the object.
    pub new_name: ObjectName,
}

impl fmt::Display for RenameTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} TO {}", self.old_name, self.new_name)?;
        Ok(())
    }
}

/// Represents the referenced table in an `INSERT INTO` statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TableObject {
    /// Table specified by name.
    /// Example:
    /// ```sql
    /// INSERT INTO my_table
    /// ```
    TableName(#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] ObjectName),

    /// Table specified as a function.
    /// Example:
    /// ```sql
    /// INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table)
    /// ```
    /// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/table-functions)
    TableFunction(Function),

    /// Table specified through a sub-query
    /// Example:
    /// ```sql
    /// INSERT INTO
    /// (SELECT employee_id, last_name, email, hire_date, job_id,  salary, commission_pct FROM employees)
    /// VALUES (207, 'Gregory', 'pgregory@example.com', sysdate, 'PU_CLERK', 1.2E3, NULL);
    /// ```
    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/INSERT.html#GUID-903F8043-0254-4EE9-ACC1-CB8AC0AF3423__I2126242)
    TableQuery(Box<Query>),
}

impl fmt::Display for TableObject {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::TableName(table_name) => write!(f, "{table_name}"),
            Self::TableFunction(func) => write!(f, "FUNCTION {func}"),
            Self::TableQuery(table_query) => write!(f, "({table_query})"),
        }
    }
}

/// Represents a SET SESSION AUTHORIZATION statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct SetSessionAuthorizationParam {
    /// The scope for the `SET SESSION AUTHORIZATION` (e.g., GLOBAL/SESSION).
    pub scope: ContextModifier,
    /// The specific authorization parameter kind.
    pub kind: SetSessionAuthorizationParamKind,
}

impl fmt::Display for SetSessionAuthorizationParam {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}

/// Represents the parameter kind for SET SESSION AUTHORIZATION
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SetSessionAuthorizationParamKind {
    /// Default authorization
    Default,

    /// User name
    User(Ident),
}

impl fmt::Display for SetSessionAuthorizationParamKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SetSessionAuthorizationParamKind::Default => write!(f, "DEFAULT"),
            SetSessionAuthorizationParamKind::User(name) => write!(f, "{}", name),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Kind of session parameter being set by `SET SESSION`.
pub enum SetSessionParamKind {
    /// Generic session parameter (name/value pair).
    Generic(SetSessionParamGeneric),
    /// Identity insert related parameter.
    IdentityInsert(SetSessionParamIdentityInsert),
    /// Offsets-related parameter.
    Offsets(SetSessionParamOffsets),
    /// Statistics-related parameter.
    Statistics(SetSessionParamStatistics),
}

impl fmt::Display for SetSessionParamKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SetSessionParamKind::Generic(x) => write!(f, "{x}"),
            SetSessionParamKind::IdentityInsert(x) => write!(f, "{x}"),
            SetSessionParamKind::Offsets(x) => write!(f, "{x}"),
            SetSessionParamKind::Statistics(x) => write!(f, "{x}"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Generic `SET SESSION` parameter represented as name(s) and value.
pub struct SetSessionParamGeneric {
    /// Names of the session parameters being set.
    pub names: Vec<String>,
    /// The value to assign to the parameter(s).
    pub value: String,
}

impl fmt::Display for SetSessionParamGeneric {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", display_comma_separated(&self.names), self.value)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `IDENTITY_INSERT` session parameter for a specific object.
pub struct SetSessionParamIdentityInsert {
    /// Object name targeted by `IDENTITY_INSERT`.
    pub obj: ObjectName,
    /// Value (ON/OFF) for the identity insert setting.
    pub value: SessionParamValue,
}

impl fmt::Display for SetSessionParamIdentityInsert {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "IDENTITY_INSERT {} {}", self.obj, self.value)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Offsets-related session parameter with keywords and a value.
pub struct SetSessionParamOffsets {
    /// Keywords specifying which offsets to modify.
    pub keywords: Vec<String>,
    /// Value (ON/OFF) for the offsets setting.
    pub value: SessionParamValue,
}

impl fmt::Display for SetSessionParamOffsets {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "OFFSETS {} {}",
            display_comma_separated(&self.keywords),
            self.value
        )
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Statistics-related session parameter specifying topic and value.
pub struct SetSessionParamStatistics {
    /// Statistics topic to set (IO/PROFILE/TIME/XML).
    pub topic: SessionParamStatsTopic,
    /// Value (ON/OFF) for the statistics topic.
    pub value: SessionParamValue,
}

impl fmt::Display for SetSessionParamStatistics {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "STATISTICS {} {}", self.topic, self.value)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Topics available for session statistics configuration.
pub enum SessionParamStatsTopic {
    /// Input/output statistics.
    IO,
    /// Profile statistics.
    Profile,
    /// Time statistics.
    Time,
    /// XML-related statistics.
    Xml,
}

impl fmt::Display for SessionParamStatsTopic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SessionParamStatsTopic::IO => write!(f, "IO"),
            SessionParamStatsTopic::Profile => write!(f, "PROFILE"),
            SessionParamStatsTopic::Time => write!(f, "TIME"),
            SessionParamStatsTopic::Xml => write!(f, "XML"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Value for a session boolean-like parameter (ON/OFF).
pub enum SessionParamValue {
    /// Session parameter enabled.
    On,
    /// Session parameter disabled.
    Off,
}

impl fmt::Display for SessionParamValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SessionParamValue::On => write!(f, "ON"),
            SessionParamValue::Off => write!(f, "OFF"),
        }
    }
}

/// Snowflake StorageSerializationPolicy for Iceberg Tables
/// ```sql
/// [ STORAGE_SERIALIZATION_POLICY = { COMPATIBLE | OPTIMIZED } ]
/// ```
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum StorageSerializationPolicy {
    /// Use compatible serialization mode.
    Compatible,
    /// Use optimized serialization mode.
    Optimized,
}

impl Display for StorageSerializationPolicy {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            StorageSerializationPolicy::Compatible => write!(f, "COMPATIBLE"),
            StorageSerializationPolicy::Optimized => write!(f, "OPTIMIZED"),
        }
    }
}

/// Snowflake CatalogSyncNamespaceMode
/// ```sql
/// [ CATALOG_SYNC_NAMESPACE_MODE = { NEST | FLATTEN } ]
/// ```
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CatalogSyncNamespaceMode {
    /// Nest namespaces when syncing catalog.
    Nest,
    /// Flatten namespaces when syncing catalog.
    Flatten,
}

impl Display for CatalogSyncNamespaceMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CatalogSyncNamespaceMode::Nest => write!(f, "NEST"),
            CatalogSyncNamespaceMode::Flatten => write!(f, "FLATTEN"),
        }
    }
}

/// Variants of the Snowflake `COPY INTO` statement
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyIntoSnowflakeKind {
    /// Loads data from files to a table
    /// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
    Table,
    /// Unloads data from a table or query to external files
    /// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
    Location,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `PRINT` statement for producing debug/output messages.
pub struct PrintStatement {
    /// The expression producing the message to print.
    pub message: Box<Expr>,
}

impl fmt::Display for PrintStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "PRINT {}", self.message)
    }
}

/// The type of `WAITFOR` statement (MSSQL).
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WaitForType {
    /// `WAITFOR DELAY 'time_to_pass'`
    Delay,
    /// `WAITFOR TIME 'time_to_execute'`
    Time,
}

impl fmt::Display for WaitForType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WaitForType::Delay => write!(f, "DELAY"),
            WaitForType::Time => write!(f, "TIME"),
        }
    }
}

/// MSSQL `WAITFOR` statement.
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WaitForStatement {
    /// `DELAY` or `TIME`.
    pub wait_type: WaitForType,
    /// The time expression.
    pub expr: Expr,
}

impl fmt::Display for WaitForStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "WAITFOR {} {}", self.wait_type, self.expr)
    }
}

/// Represents a `Return` statement.
///
/// [MsSql triggers](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql)
/// [MsSql functions](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ReturnStatement {
    /// Optional return value expression.
    pub value: Option<ReturnStatementValue>,
}

impl fmt::Display for ReturnStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.value {
            Some(ReturnStatementValue::Expr(expr)) => write!(f, "RETURN {expr}"),
            None => write!(f, "RETURN"),
        }
    }
}

/// Variants of a `RETURN` statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ReturnStatementValue {
    /// Return an expression from a function or trigger.
    Expr(Expr),
}

/// Represents an `OPEN` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OpenStatement {
    /// Cursor name
    pub cursor_name: Ident,
}

impl fmt::Display for OpenStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "OPEN {}", self.cursor_name)
    }
}

/// Specifies Include / Exclude NULL within UNPIVOT command.
/// For example
/// `UNPIVOT (column1 FOR new_column IN (col3, col4, col5, col6))`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum NullInclusion {
    /// Include NULL values in the UNPIVOT output.
    IncludeNulls,
    /// Exclude NULL values from the UNPIVOT output.
    ExcludeNulls,
}

impl fmt::Display for NullInclusion {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            NullInclusion::IncludeNulls => write!(f, "INCLUDE NULLS"),
            NullInclusion::ExcludeNulls => write!(f, "EXCLUDE NULLS"),
        }
    }
}

/// Checks membership of a value in a JSON array
///
/// Syntax:
/// ```sql
/// <value> MEMBER OF(<array>)
/// ```
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#operator_member-of)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MemberOf {
    /// The value to check for membership.
    pub value: Box<Expr>,
    /// The JSON array expression to check against.
    pub array: Box<Expr>,
}

impl fmt::Display for MemberOf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} MEMBER OF({})", self.value, self.array)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents an `EXPORT DATA` statement.
pub struct ExportData {
    /// Options for the export operation.
    pub options: Vec<SqlOption>,
    /// The query producing the data to export.
    pub query: Box<Query>,
    /// Optional named connection to use for export.
    pub connection: Option<ObjectName>,
}

impl fmt::Display for ExportData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(connection) = &self.connection {
            write!(
                f,
                "EXPORT DATA WITH CONNECTION {connection} OPTIONS({}) AS {}",
                display_comma_separated(&self.options),
                self.query
            )
        } else {
            write!(
                f,
                "EXPORT DATA OPTIONS({}) AS {}",
                display_comma_separated(&self.options),
                self.query
            )
        }
    }
}
/// Creates a user
///
/// Syntax:
/// ```sql
/// CREATE [OR REPLACE] USER [IF NOT EXISTS] <name> [OPTIONS]
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateUser {
    /// Replace existing user if present.
    pub or_replace: bool,
    /// Only create the user if it does not already exist.
    pub if_not_exists: bool,
    /// The name of the user to create.
    pub name: Ident,
    /// Key/value options for user creation.
    pub options: KeyValueOptions,
    /// Whether tags are specified using `WITH TAG`.
    pub with_tags: bool,
    /// Tags for the user.
    pub tags: KeyValueOptions,
}

impl fmt::Display for CreateUser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CREATE")?;
        if self.or_replace {
            write!(f, " OR REPLACE")?;
        }
        write!(f, " USER")?;
        if self.if_not_exists {
            write!(f, " IF NOT EXISTS")?;
        }
        write!(f, " {}", self.name)?;
        if !self.options.options.is_empty() {
            write!(f, " {}", self.options)?;
        }
        if !self.tags.options.is_empty() {
            if self.with_tags {
                write!(f, " WITH")?;
            }
            write!(f, " TAG ({})", self.tags)?;
        }
        Ok(())
    }
}

/// Modifies the properties of a user
///
/// [Snowflake Syntax:](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
/// ```
///
/// [PostgreSQL Syntax:](https://www.postgresql.org/docs/current/sql-alteruser.html)
/// ```sql
/// ALTER USER <role_specification> [ WITH ] option [ ... ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUser {
    /// Whether to only alter the user if it exists.
    pub if_exists: bool,
    /// The name of the user to alter.
    pub name: Ident,
    /// Optional new name for the user (Snowflake-specific).
    /// See: <https://docs.snowflake.com/en/sql-reference/sql/alter-user#syntax>
    pub rename_to: Option<Ident>,
    /// Reset the user's password.
    pub reset_password: bool,
    /// Abort all running queries for the user.
    pub abort_all_queries: bool,
    /// Optionally add a delegated role authorization.
    pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
    /// Optionally remove a delegated role authorization.
    pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
    /// Enroll the user in MFA.
    pub enroll_mfa: bool,
    /// Set the default MFA method for the user.
    pub set_default_mfa_method: Option<MfaMethodKind>,
    /// Remove the user's default MFA method.
    pub remove_mfa_method: Option<MfaMethodKind>,
    /// Modify an MFA method for the user.
    pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
    /// Add an MFA OTP method with optional count.
    pub add_mfa_method_otp: Option<AlterUserAddMfaMethodOtp>,
    /// Set a user policy.
    pub set_policy: Option<AlterUserSetPolicy>,
    /// Unset a user policy.
    pub unset_policy: Option<UserPolicyKind>,
    /// Key/value tag options to set on the user.
    pub set_tag: KeyValueOptions,
    /// Tags to unset on the user.
    pub unset_tag: Vec<String>,
    /// Key/value properties to set on the user.
    pub set_props: KeyValueOptions,
    /// Properties to unset on the user.
    pub unset_props: Vec<String>,
    /// The following options are PostgreSQL-specific: <https://www.postgresql.org/docs/current/sql-alteruser.html>
    pub password: Option<AlterUserPassword>,
}

/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserAddRoleDelegation {
    /// Role name to delegate.
    pub role: Ident,
    /// Security integration receiving the delegation.
    pub integration: Ident,
}

/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserRemoveRoleDelegation {
    /// Optional role name to remove delegation for.
    pub role: Option<Ident>,
    /// Security integration from which to remove delegation.
    pub integration: Ident,
}

/// ```sql
/// ADD MFA METHOD OTP [ COUNT = number ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserAddMfaMethodOtp {
    /// Optional OTP count parameter.
    pub count: Option<ValueWithSpan>,
}

/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserModifyMfaMethod {
    /// The MFA method being modified.
    pub method: MfaMethodKind,
    /// The new comment for the MFA method.
    pub comment: String,
}

/// Types of MFA methods
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MfaMethodKind {
    /// PassKey (hardware or platform passkey) MFA method.
    PassKey,
    /// Time-based One-Time Password (TOTP) MFA method.
    Totp,
    /// Duo Security MFA method.
    Duo,
}

impl fmt::Display for MfaMethodKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MfaMethodKind::PassKey => write!(f, "PASSKEY"),
            MfaMethodKind::Totp => write!(f, "TOTP"),
            MfaMethodKind::Duo => write!(f, "DUO"),
        }
    }
}

/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserSetPolicy {
    /// The kind of user policy being set (authentication/password/session).
    pub policy_kind: UserPolicyKind,
    /// The identifier of the policy to apply.
    pub policy: Ident,
}

/// Types of user-based policies
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum UserPolicyKind {
    /// Authentication policy.
    Authentication,
    /// Password policy.
    Password,
    /// Session policy.
    Session,
}

impl fmt::Display for UserPolicyKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
            UserPolicyKind::Password => write!(f, "PASSWORD"),
            UserPolicyKind::Session => write!(f, "SESSION"),
        }
    }
}

impl fmt::Display for AlterUser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ALTER")?;
        write!(f, " USER")?;
        if self.if_exists {
            write!(f, " IF EXISTS")?;
        }
        write!(f, " {}", self.name)?;
        if let Some(new_name) = &self.rename_to {
            write!(f, " RENAME TO {new_name}")?;
        }
        if self.reset_password {
            write!(f, " RESET PASSWORD")?;
        }
        if self.abort_all_queries {
            write!(f, " ABORT ALL QUERIES")?;
        }
        if let Some(role_delegation) = &self.add_role_delegation {
            let role = &role_delegation.role;
            let integration = &role_delegation.integration;
            write!(
                f,
                " ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
            )?;
        }
        if let Some(role_delegation) = &self.remove_role_delegation {
            write!(f, " REMOVE DELEGATED")?;
            match &role_delegation.role {
                Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
                None => write!(f, " AUTHORIZATIONS")?,
            }
            let integration = &role_delegation.integration;
            write!(f, " FROM SECURITY INTEGRATION {integration}")?;
        }
        if self.enroll_mfa {
            write!(f, " ENROLL MFA")?;
        }
        if let Some(method) = &self.set_default_mfa_method {
            write!(f, " SET DEFAULT_MFA_METHOD {method}")?
        }
        if let Some(method) = &self.remove_mfa_method {
            write!(f, " REMOVE MFA METHOD {method}")?;
        }
        if let Some(modify) = &self.modify_mfa_method {
            let method = &modify.method;
            let comment = &modify.comment;
            write!(
                f,
                " MODIFY MFA METHOD {method} SET COMMENT '{}'",
                value::escape_single_quote_string(comment)
            )?;
        }
        if let Some(add_mfa_method_otp) = &self.add_mfa_method_otp {
            write!(f, " ADD MFA METHOD OTP")?;
            if let Some(count) = &add_mfa_method_otp.count {
                write!(f, " COUNT = {count}")?;
            }
        }
        if let Some(policy) = &self.set_policy {
            let policy_kind = &policy.policy_kind;
            let name = &policy.policy;
            write!(f, " SET {policy_kind} POLICY {name}")?;
        }
        if let Some(policy_kind) = &self.unset_policy {
            write!(f, " UNSET {policy_kind} POLICY")?;
        }
        if !self.set_tag.options.is_empty() {
            write!(f, " SET TAG {}", self.set_tag)?;
        }
        if !self.unset_tag.is_empty() {
            write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
        }
        let has_props = !self.set_props.options.is_empty();
        if has_props {
            write!(f, " SET")?;
            write!(f, " {}", &self.set_props)?;
        }
        if !self.unset_props.is_empty() {
            write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
        }
        if let Some(password) = &self.password {
            write!(f, " {}", password)?;
        }
        Ok(())
    }
}

/// ```sql
/// ALTER USER <role_specification> [ WITH ] PASSWORD { 'password' | NULL }``
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserPassword {
    /// Whether the password is encrypted.
    pub encrypted: bool,
    /// The password string, or `None` for `NULL`.
    pub password: Option<String>,
}

impl Display for AlterUserPassword {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.encrypted {
            write!(f, "ENCRYPTED ")?;
        }
        write!(f, "PASSWORD")?;
        match &self.password {
            None => write!(f, " NULL")?,
            Some(password) => write!(f, " '{}'", value::escape_single_quote_string(password))?,
        }
        Ok(())
    }
}

/// Specifies how to create a new table based on an existing table's schema.
/// '''sql
/// CREATE TABLE new LIKE old ...
/// '''
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateTableLikeKind {
    /// '''sql
    /// CREATE TABLE new (LIKE old ...)
    /// '''
    /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html)
    Parenthesized(CreateTableLike),
    /// '''sql
    /// CREATE TABLE new LIKE old ...
    /// '''
    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-table#label-create-table-like)
    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_like)
    Plain(CreateTableLike),
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Controls whether defaults are included when creating a table FROM/LILE another.
pub enum CreateTableLikeDefaults {
    /// Include default values from the source table.
    Including,
    /// Exclude default values from the source table.
    Excluding,
}

impl fmt::Display for CreateTableLikeDefaults {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateTableLikeDefaults::Including => write!(f, "INCLUDING DEFAULTS"),
            CreateTableLikeDefaults::Excluding => write!(f, "EXCLUDING DEFAULTS"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents the `LIKE` clause of a `CREATE TABLE` statement.
pub struct CreateTableLike {
    /// The source table name to copy the schema from.
    pub name: ObjectName,
    /// Optional behavior controlling whether defaults are copied.
    pub defaults: Option<CreateTableLikeDefaults>,
}

impl fmt::Display for CreateTableLike {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "LIKE {}", self.name)?;
        if let Some(defaults) = &self.defaults {
            write!(f, " {defaults}")?;
        }
        Ok(())
    }
}

/// Specifies the refresh mode for the dynamic table.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum RefreshModeKind {
    /// Automatic refresh mode (`AUTO`).
    Auto,
    /// Full refresh mode (`FULL`).
    Full,
    /// Incremental refresh mode (`INCREMENTAL`).
    Incremental,
}

impl fmt::Display for RefreshModeKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RefreshModeKind::Auto => write!(f, "AUTO"),
            RefreshModeKind::Full => write!(f, "FULL"),
            RefreshModeKind::Incremental => write!(f, "INCREMENTAL"),
        }
    }
}

/// Specifies the behavior of the initial refresh of the dynamic table.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum InitializeKind {
    /// Initialize on creation (`ON CREATE`).
    OnCreate,
    /// Initialize on schedule (`ON SCHEDULE`).
    OnSchedule,
}

impl fmt::Display for InitializeKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            InitializeKind::OnCreate => write!(f, "ON_CREATE"),
            InitializeKind::OnSchedule => write!(f, "ON_SCHEDULE"),
        }
    }
}

/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
///
/// '''sql
/// VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ] [ \[ table_name \] [ TO threshold PERCENT ] \[ BOOST \] ]
/// '''
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct VacuumStatement {
    /// Whether `FULL` was specified.
    pub full: bool,
    /// Whether `SORT ONLY` was specified.
    pub sort_only: bool,
    /// Whether `DELETE ONLY` was specified.
    pub delete_only: bool,
    /// Whether `REINDEX` was specified.
    pub reindex: bool,
    /// Whether `RECLUSTER` was specified.
    pub recluster: bool,
    /// Optional table to run `VACUUM` on.
    pub table_name: Option<ObjectName>,
    /// Optional threshold value (percent) for `TO threshold PERCENT`.
    pub threshold: Option<ValueWithSpan>,
    /// Whether `BOOST` was specified.
    pub boost: bool,
}

impl fmt::Display for VacuumStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "VACUUM{}{}{}{}{}",
            if self.full { " FULL" } else { "" },
            if self.sort_only { " SORT ONLY" } else { "" },
            if self.delete_only { " DELETE ONLY" } else { "" },
            if self.reindex { " REINDEX" } else { "" },
            if self.recluster { " RECLUSTER" } else { "" },
        )?;
        if let Some(table_name) = &self.table_name {
            write!(f, " {table_name}")?;
        }
        if let Some(threshold) = &self.threshold {
            write!(f, " TO {threshold} PERCENT")?;
        }
        if self.boost {
            write!(f, " BOOST")?;
        }
        Ok(())
    }
}

/// Variants of the RESET statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Reset {
    /// Resets all session parameters to their default values.
    ALL,

    /// Resets a specific session parameter to its default value.
    ConfigurationParameter(ObjectName),
}

/// Resets a session parameter to its default value.
/// ```sql
/// RESET { ALL | <configuration_parameter> }
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ResetStatement {
    /// The reset action to perform (either `ALL` or a specific configuration parameter).
    pub reset: Reset,
}

/// Query optimizer hints are optionally supported comments after the
/// `SELECT`, `INSERT`, `UPDATE`, `REPLACE`, `MERGE`, and `DELETE` keywords in
/// the corresponding statements.
///
/// See [Select::optimizer_hints]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OptimizerHint {
    /// An optional prefix between the comment marker and `+`.
    ///
    /// Standard optimizer hints like `/*+ ... */` have an empty prefix,
    /// while system-specific hints like `/*abc+ ... */` have `prefix = "abc"`.
    /// The prefix is any sequence of ASCII alphanumeric characters
    /// immediately before the `+` marker.
    pub prefix: String,
    /// the raw text of the optimizer hint without its markers
    pub text: String,
    /// the style of the comment which `text` was extracted from,
    /// e.g. `/*+...*/` or `--+...`
    ///
    /// Not all dialects support all styles, though.
    pub style: OptimizerHintStyle,
}

/// The commentary style of an [optimizer hint](OptimizerHint)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OptimizerHintStyle {
    /// A hint corresponding to a single line comment,
    /// e.g. `--+ LEADING(v.e v.d t)`
    SingleLine {
        /// the comment prefix, e.g. `--`
        prefix: String,
    },
    /// A hint corresponding to a multi line comment,
    /// e.g. `/*+ LEADING(v.e v.d t) */`
    MultiLine,
}

impl fmt::Display for OptimizerHint {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.style {
            OptimizerHintStyle::SingleLine { prefix } => {
                f.write_str(prefix)?;
                f.write_str(&self.prefix)?;
                f.write_str("+")?;
                f.write_str(&self.text)
            }
            OptimizerHintStyle::MultiLine => {
                f.write_str("/*")?;
                f.write_str(&self.prefix)?;
                f.write_str("+")?;
                f.write_str(&self.text)?;
                f.write_str("*/")
            }
        }
    }
}

impl fmt::Display for ResetStatement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.reset {
            Reset::ALL => write!(f, "RESET ALL"),
            Reset::ConfigurationParameter(param) => write!(f, "RESET {}", param),
        }
    }
}

impl From<Set> for Statement {
    fn from(s: Set) -> Self {
        Self::Set(s)
    }
}

impl From<Query> for Statement {
    fn from(q: Query) -> Self {
        Box::new(q).into()
    }
}

impl From<Box<Query>> for Statement {
    fn from(q: Box<Query>) -> Self {
        Self::Query(q)
    }
}

impl From<Insert> for Statement {
    fn from(i: Insert) -> Self {
        Self::Insert(i)
    }
}

impl From<Update> for Statement {
    fn from(u: Update) -> Self {
        Self::Update(u)
    }
}

impl From<CreateView> for Statement {
    fn from(cv: CreateView) -> Self {
        Self::CreateView(cv)
    }
}

impl From<CreateRole> for Statement {
    fn from(cr: CreateRole) -> Self {
        Self::CreateRole(cr)
    }
}

impl From<AlterTable> for Statement {
    fn from(at: AlterTable) -> Self {
        Self::AlterTable(at)
    }
}

impl From<DropFunction> for Statement {
    fn from(df: DropFunction) -> Self {
        Self::DropFunction(df)
    }
}

impl From<CreateExtension> for Statement {
    fn from(ce: CreateExtension) -> Self {
        Self::CreateExtension(ce)
    }
}

impl From<CreateCollation> for Statement {
    fn from(c: CreateCollation) -> Self {
        Self::CreateCollation(c)
    }
}

impl From<DropExtension> for Statement {
    fn from(de: DropExtension) -> Self {
        Self::DropExtension(de)
    }
}

impl From<CaseStatement> for Statement {
    fn from(c: CaseStatement) -> Self {
        Self::Case(c)
    }
}

impl From<IfStatement> for Statement {
    fn from(i: IfStatement) -> Self {
        Self::If(i)
    }
}

impl From<WhileStatement> for Statement {
    fn from(w: WhileStatement) -> Self {
        Self::While(w)
    }
}

impl From<RaiseStatement> for Statement {
    fn from(r: RaiseStatement) -> Self {
        Self::Raise(r)
    }
}

impl From<ThrowStatement> for Statement {
    fn from(t: ThrowStatement) -> Self {
        Self::Throw(t)
    }
}

impl From<Function> for Statement {
    fn from(f: Function) -> Self {
        Self::Call(f)
    }
}

impl From<OpenStatement> for Statement {
    fn from(o: OpenStatement) -> Self {
        Self::Open(o)
    }
}

impl From<Delete> for Statement {
    fn from(d: Delete) -> Self {
        Self::Delete(d)
    }
}

impl From<CreateTable> for Statement {
    fn from(c: CreateTable) -> Self {
        Self::CreateTable(c)
    }
}

impl From<CreateIndex> for Statement {
    fn from(c: CreateIndex) -> Self {
        Self::CreateIndex(c)
    }
}

impl From<CreateServerStatement> for Statement {
    fn from(c: CreateServerStatement) -> Self {
        Self::CreateServer(c)
    }
}

impl From<CreateConnector> for Statement {
    fn from(c: CreateConnector) -> Self {
        Self::CreateConnector(c)
    }
}

impl From<CreateOperator> for Statement {
    fn from(c: CreateOperator) -> Self {
        Self::CreateOperator(c)
    }
}

impl From<CreateOperatorFamily> for Statement {
    fn from(c: CreateOperatorFamily) -> Self {
        Self::CreateOperatorFamily(c)
    }
}

impl From<CreateOperatorClass> for Statement {
    fn from(c: CreateOperatorClass) -> Self {
        Self::CreateOperatorClass(c)
    }
}

impl From<AlterSchema> for Statement {
    fn from(a: AlterSchema) -> Self {
        Self::AlterSchema(a)
    }
}

impl From<AlterFunction> for Statement {
    fn from(a: AlterFunction) -> Self {
        Self::AlterFunction(a)
    }
}

impl From<AlterType> for Statement {
    fn from(a: AlterType) -> Self {
        Self::AlterType(a)
    }
}

impl From<AlterCollation> for Statement {
    fn from(a: AlterCollation) -> Self {
        Self::AlterCollation(a)
    }
}

impl From<AlterOperator> for Statement {
    fn from(a: AlterOperator) -> Self {
        Self::AlterOperator(a)
    }
}

impl From<AlterOperatorFamily> for Statement {
    fn from(a: AlterOperatorFamily) -> Self {
        Self::AlterOperatorFamily(a)
    }
}

impl From<AlterOperatorClass> for Statement {
    fn from(a: AlterOperatorClass) -> Self {
        Self::AlterOperatorClass(a)
    }
}

impl From<Merge> for Statement {
    fn from(m: Merge) -> Self {
        Self::Merge(m)
    }
}

impl From<AlterUser> for Statement {
    fn from(a: AlterUser) -> Self {
        Self::AlterUser(a)
    }
}

impl From<DropDomain> for Statement {
    fn from(d: DropDomain) -> Self {
        Self::DropDomain(d)
    }
}

impl From<ShowCharset> for Statement {
    fn from(s: ShowCharset) -> Self {
        Self::ShowCharset(s)
    }
}

impl From<ShowObjects> for Statement {
    fn from(s: ShowObjects) -> Self {
        Self::ShowObjects(s)
    }
}

impl From<Use> for Statement {
    fn from(u: Use) -> Self {
        Self::Use(u)
    }
}

impl From<CreateFunction> for Statement {
    fn from(c: CreateFunction) -> Self {
        Self::CreateFunction(c)
    }
}

impl From<CreateTrigger> for Statement {
    fn from(c: CreateTrigger) -> Self {
        Self::CreateTrigger(c)
    }
}

impl From<DropTrigger> for Statement {
    fn from(d: DropTrigger) -> Self {
        Self::DropTrigger(d)
    }
}

impl From<DropOperator> for Statement {
    fn from(d: DropOperator) -> Self {
        Self::DropOperator(d)
    }
}

impl From<DropOperatorFamily> for Statement {
    fn from(d: DropOperatorFamily) -> Self {
        Self::DropOperatorFamily(d)
    }
}

impl From<DropOperatorClass> for Statement {
    fn from(d: DropOperatorClass) -> Self {
        Self::DropOperatorClass(d)
    }
}

impl From<DenyStatement> for Statement {
    fn from(d: DenyStatement) -> Self {
        Self::Deny(d)
    }
}

impl From<CreateDomain> for Statement {
    fn from(c: CreateDomain) -> Self {
        Self::CreateDomain(c)
    }
}

impl From<RenameTable> for Statement {
    fn from(r: RenameTable) -> Self {
        vec![r].into()
    }
}

impl From<Vec<RenameTable>> for Statement {
    fn from(r: Vec<RenameTable>) -> Self {
        Self::RenameTable(r)
    }
}

impl From<PrintStatement> for Statement {
    fn from(p: PrintStatement) -> Self {
        Self::Print(p)
    }
}

impl From<ReturnStatement> for Statement {
    fn from(r: ReturnStatement) -> Self {
        Self::Return(r)
    }
}

impl From<ExportData> for Statement {
    fn from(e: ExportData) -> Self {
        Self::ExportData(e)
    }
}

impl From<CreateUser> for Statement {
    fn from(c: CreateUser) -> Self {
        Self::CreateUser(c)
    }
}

impl From<VacuumStatement> for Statement {
    fn from(v: VacuumStatement) -> Self {
        Self::Vacuum(v)
    }
}

impl From<ResetStatement> for Statement {
    fn from(r: ResetStatement) -> Self {
        Self::Reset(r)
    }
}

#[cfg(test)]
mod tests {
    use crate::tokenizer::Location;

    use super::*;

    #[test]
    fn test_window_frame_default() {
        let window_frame = WindowFrame::default();
        assert_eq!(WindowFrameBound::Preceding(None), window_frame.start_bound);
    }

    #[test]
    fn test_grouping_sets_display() {
        // a and b in different group
        let grouping_sets = Expr::GroupingSets(vec![
            vec![Expr::Identifier(Ident::new("a"))],
            vec![Expr::Identifier(Ident::new("b"))],
        ]);
        assert_eq!("GROUPING SETS ((a), (b))", format!("{grouping_sets}"));

        // a and b in the same group
        let grouping_sets = Expr::GroupingSets(vec![vec![
            Expr::Identifier(Ident::new("a")),
            Expr::Identifier(Ident::new("b")),
        ]]);
        assert_eq!("GROUPING SETS ((a, b))", format!("{grouping_sets}"));

        // (a, b) and (c, d) in different group
        let grouping_sets = Expr::GroupingSets(vec![
            vec![
                Expr::Identifier(Ident::new("a")),
                Expr::Identifier(Ident::new("b")),
            ],
            vec![
                Expr::Identifier(Ident::new("c")),
                Expr::Identifier(Ident::new("d")),
            ],
        ]);
        assert_eq!("GROUPING SETS ((a, b), (c, d))", format!("{grouping_sets}"));
    }

    #[test]
    fn test_rollup_display() {
        let rollup = Expr::Rollup(vec![vec![Expr::Identifier(Ident::new("a"))]]);
        assert_eq!("ROLLUP (a)", format!("{rollup}"));

        let rollup = Expr::Rollup(vec![vec![
            Expr::Identifier(Ident::new("a")),
            Expr::Identifier(Ident::new("b")),
        ]]);
        assert_eq!("ROLLUP ((a, b))", format!("{rollup}"));

        let rollup = Expr::Rollup(vec![
            vec![Expr::Identifier(Ident::new("a"))],
            vec![Expr::Identifier(Ident::new("b"))],
        ]);
        assert_eq!("ROLLUP (a, b)", format!("{rollup}"));

        let rollup = Expr::Rollup(vec![
            vec![Expr::Identifier(Ident::new("a"))],
            vec![
                Expr::Identifier(Ident::new("b")),
                Expr::Identifier(Ident::new("c")),
            ],
            vec![Expr::Identifier(Ident::new("d"))],
        ]);
        assert_eq!("ROLLUP (a, (b, c), d)", format!("{rollup}"));
    }

    #[test]
    fn test_cube_display() {
        let cube = Expr::Cube(vec![vec![Expr::Identifier(Ident::new("a"))]]);
        assert_eq!("CUBE (a)", format!("{cube}"));

        let cube = Expr::Cube(vec![vec![
            Expr::Identifier(Ident::new("a")),
            Expr::Identifier(Ident::new("b")),
        ]]);
        assert_eq!("CUBE ((a, b))", format!("{cube}"));

        let cube = Expr::Cube(vec![
            vec![Expr::Identifier(Ident::new("a"))],
            vec![Expr::Identifier(Ident::new("b"))],
        ]);
        assert_eq!("CUBE (a, b)", format!("{cube}"));

        let cube = Expr::Cube(vec![
            vec![Expr::Identifier(Ident::new("a"))],
            vec![
                Expr::Identifier(Ident::new("b")),
                Expr::Identifier(Ident::new("c")),
            ],
            vec![Expr::Identifier(Ident::new("d"))],
        ]);
        assert_eq!("CUBE (a, (b, c), d)", format!("{cube}"));
    }

    #[test]
    fn test_interval_display() {
        let interval = Expr::Interval(Interval {
            value: Box::new(Expr::Value(
                Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(),
            )),
            leading_field: Some(DateTimeField::Minute),
            leading_precision: Some(10),
            last_field: Some(DateTimeField::Second),
            fractional_seconds_precision: Some(9),
        });
        assert_eq!(
            "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)",
            format!("{interval}"),
        );

        let interval = Expr::Interval(Interval {
            value: Box::new(Expr::Value(
                Value::SingleQuotedString(String::from("5")).with_empty_span(),
            )),
            leading_field: Some(DateTimeField::Second),
            leading_precision: Some(1),
            last_field: None,
            fractional_seconds_precision: Some(3),
        });
        assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}"));
    }

    #[test]
    fn test_one_or_many_with_parens_deref() {
        use core::ops::Index;

        let one = OneOrManyWithParens::One("a");

        assert_eq!(one.deref(), &["a"]);
        assert_eq!(<OneOrManyWithParens<_> as Deref>::deref(&one), &["a"]);

        assert_eq!(one[0], "a");
        assert_eq!(one.index(0), &"a");
        assert_eq!(
            <<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&one, 0),
            &"a"
        );

        assert_eq!(one.len(), 1);
        assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&one), 1);

        let many1 = OneOrManyWithParens::Many(vec!["b"]);

        assert_eq!(many1.deref(), &["b"]);
        assert_eq!(<OneOrManyWithParens<_> as Deref>::deref(&many1), &["b"]);

        assert_eq!(many1[0], "b");
        assert_eq!(many1.index(0), &"b");
        assert_eq!(
            <<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many1, 0),
            &"b"
        );

        assert_eq!(many1.len(), 1);
        assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&many1), 1);

        let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);

        assert_eq!(many2.deref(), &["c", "d"]);
        assert_eq!(
            <OneOrManyWithParens<_> as Deref>::deref(&many2),
            &["c", "d"]
        );

        assert_eq!(many2[0], "c");
        assert_eq!(many2.index(0), &"c");
        assert_eq!(
            <<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many2, 0),
            &"c"
        );

        assert_eq!(many2[1], "d");
        assert_eq!(many2.index(1), &"d");
        assert_eq!(
            <<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many2, 1),
            &"d"
        );

        assert_eq!(many2.len(), 2);
        assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&many2), 2);
    }

    #[test]
    fn test_one_or_many_with_parens_as_ref() {
        let one = OneOrManyWithParens::One("a");

        assert_eq!(one.as_ref(), &["a"]);
        assert_eq!(<OneOrManyWithParens<_> as AsRef<_>>::as_ref(&one), &["a"]);

        let many1 = OneOrManyWithParens::Many(vec!["b"]);

        assert_eq!(many1.as_ref(), &["b"]);
        assert_eq!(<OneOrManyWithParens<_> as AsRef<_>>::as_ref(&many1), &["b"]);

        let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);

        assert_eq!(many2.as_ref(), &["c", "d"]);
        assert_eq!(
            <OneOrManyWithParens<_> as AsRef<_>>::as_ref(&many2),
            &["c", "d"]
        );
    }

    #[test]
    fn test_one_or_many_with_parens_ref_into_iter() {
        let one = OneOrManyWithParens::One("a");

        assert_eq!(Vec::from_iter(&one), vec![&"a"]);

        let many1 = OneOrManyWithParens::Many(vec!["b"]);

        assert_eq!(Vec::from_iter(&many1), vec![&"b"]);

        let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);

        assert_eq!(Vec::from_iter(&many2), vec![&"c", &"d"]);
    }

    #[test]
    fn test_one_or_many_with_parens_value_into_iter() {
        use core::iter::once;

        //tests that our iterator implemented methods behaves exactly as it's inner iterator, at every step up to n calls to next/next_back
        fn test_steps<I>(ours: OneOrManyWithParens<usize>, inner: I, n: usize)
        where
            I: IntoIterator<Item = usize, IntoIter: DoubleEndedIterator + Clone> + Clone,
        {
            fn checks<I>(ours: OneOrManyWithParensIntoIter<usize>, inner: I)
            where
                I: Iterator<Item = usize> + Clone + DoubleEndedIterator,
            {
                assert_eq!(ours.size_hint(), inner.size_hint());
                assert_eq!(ours.clone().count(), inner.clone().count());

                assert_eq!(
                    ours.clone().fold(1, |a, v| a + v),
                    inner.clone().fold(1, |a, v| a + v)
                );

                assert_eq!(Vec::from_iter(ours.clone()), Vec::from_iter(inner.clone()));
                assert_eq!(
                    Vec::from_iter(ours.clone().rev()),
                    Vec::from_iter(inner.clone().rev())
                );
            }

            let mut ours_next = ours.clone().into_iter();
            let mut inner_next = inner.clone().into_iter();

            for _ in 0..n {
                checks(ours_next.clone(), inner_next.clone());

                assert_eq!(ours_next.next(), inner_next.next());
            }

            let mut ours_next_back = ours.clone().into_iter();
            let mut inner_next_back = inner.clone().into_iter();

            for _ in 0..n {
                checks(ours_next_back.clone(), inner_next_back.clone());

                assert_eq!(ours_next_back.next_back(), inner_next_back.next_back());
            }

            let mut ours_mixed = ours.clone().into_iter();
            let mut inner_mixed = inner.clone().into_iter();

            for i in 0..n {
                checks(ours_mixed.clone(), inner_mixed.clone());

                if i % 2 == 0 {
                    assert_eq!(ours_mixed.next_back(), inner_mixed.next_back());
                } else {
                    assert_eq!(ours_mixed.next(), inner_mixed.next());
                }
            }

            let mut ours_mixed2 = ours.into_iter();
            let mut inner_mixed2 = inner.into_iter();

            for i in 0..n {
                checks(ours_mixed2.clone(), inner_mixed2.clone());

                if i % 2 == 0 {
                    assert_eq!(ours_mixed2.next(), inner_mixed2.next());
                } else {
                    assert_eq!(ours_mixed2.next_back(), inner_mixed2.next_back());
                }
            }
        }

        test_steps(OneOrManyWithParens::One(1), once(1), 3);
        test_steps(OneOrManyWithParens::Many(vec![2]), vec![2], 3);
        test_steps(OneOrManyWithParens::Many(vec![3, 4]), vec![3, 4], 4);
    }

    // Tests that the position in the code of an `Ident` does not affect its
    // ordering.
    #[test]
    fn test_ident_ord() {
        let mut a = Ident::with_span(Span::new(Location::new(1, 1), Location::new(1, 1)), "a");
        let mut b = Ident::with_span(Span::new(Location::new(2, 2), Location::new(2, 2)), "b");

        assert!(a < b);
        std::mem::swap(&mut a.span, &mut b.span);
        assert!(a < b);
    }
}
