| // 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 core::ops::ControlFlow; | |
| #[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, MapBracketKind, 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, AlterTextSearch, AlterTextSearchOperation, | |
| AlterTextSearchOption, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition, | |
| AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef, | |
| ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty, | |
| ConstraintCharacteristics, CreateCollation, CreateCollationDefinition, CreateConnector, | |
| CreateDomain, CreateExtension, CreateForeignTable, CreateFunction, CreateIndex, CreateOperator, | |
| CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType, | |
| CreateTable, CreateTextSearch, 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, SqliteTableOption, SqliteTableOptions, TagsColumnOption, TextSearchObjectType, | |
| TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef, | |
| UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, | |
| UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef, WithData, | |
| }; | |
| pub use self::dml::{ | |
| Delete, Insert, InsertOverriding, Merge, MergeAction, MergeClause, MergeClauseKind, | |
| MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, 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, | |
| OrderBySort, 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, ExcludeConstraint, ExcludeConstraintElement, | |
| ExcludeConstraintOperator, 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))] | |
| 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(), | |
| } | |
| } | |
| } | |
| pub(crate) fn fmt_ident( | |
| f: &mut fmt::Formatter, | |
| value: &str, | |
| quote_style: Option<char>, | |
| ) -> fmt::Result { | |
| match quote_style { | |
| Some('[') => write!(f, "[{value}]"), | |
| Some(q) => { | |
| let escaped = value::escape_quoted_string(value, q); | |
| write!(f, "{q}{escaped}{q}") | |
| } | |
| None => f.write_str(value), | |
| } | |
| } | |
| impl fmt::Display for Ident { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| fmt_ident(f, &self.value, self.quote_style) | |
| } | |
| } | |
| #[cfg(feature = "visitor")] | |
| impl Visit for Ident { | |
| fn visit<V: Visitor>(&self, visitor: &mut V) -> ControlFlow<V::Break> { | |
| visitor.pre_visit_ident(self)?; | |
| visitor.post_visit_ident(self) | |
| } | |
| } | |
| #[cfg(feature = "visitor")] | |
| impl VisitMut for Ident { | |
| fn visit<V: VisitorMut>(&mut self, visitor: &mut V) -> ControlFlow<V::Break> { | |
| visitor.pre_visit_ident(self)?; | |
| visitor.post_visit_ident(self) | |
| } | |
| } | |
| /// 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] JSON [VALUE|SCALAR|ARRAY|OBJECT] [WITH|WITHOUT UNIQUE [KEYS]]` | |
| IsJson { | |
| /// Expression being tested. | |
| expr: Box<Expr>, | |
| /// Optional JSON shape constraint. | |
| kind: Option<JsonPredicateType>, | |
| /// Optional duplicate-key handling constraint for JSON objects. | |
| unique_keys: Option<JsonKeyUniqueness>, | |
| /// `true` when `NOT` is present. | |
| negated: bool, | |
| }, | |
| /// `<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, | |
| }, | |
| /// `[ GLOBAL ] [ 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, | |
| /// `true` when the ClickHouse `GLOBAL` modifier is present. | |
| global: bool, | |
| }, | |
| /// `[ GLOBAL ] [ 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, | |
| /// `true` when the ClickHouse `GLOBAL` modifier is present. | |
| global: 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<Box<Expr>>, | |
| }, | |
| /// `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<Box<Expr>>, | |
| }, | |
| /// `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<Box<Expr>>, | |
| }, | |
| /// 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, | |
| /// 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>, | |
| }, | |
| /// A named expression: `1 AS A`. Used in `BigQuery` typeless structs [1] | |
| /// and in aliased function arguments, e.g. `XMLFOREST(a AS x)` in | |
| /// PostgreSQL [2]. | |
| /// | |
| /// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type | |
| /// [2]: https://www.postgresql.org/docs/current/functions-xml.html#FUNCTIONS-PRODUCING-XML-XMLFOREST | |
| 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), | |
| } | |
| const fn is_number_expr(expr: &Expr) -> bool { | |
| matches!(expr, Expr::Value(v) if matches!(v.value, Value::Number(_, _))) | |
| } | |
| impl fmt::Display for AccessExpr { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| AccessExpr::Dot(expr) if is_number_expr(expr) => write!(f, " . {expr}"), | |
| 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}")?; | |
| let mut prev_is_number = is_number_expr(root); | |
| for field in access_chain { | |
| match field { | |
| AccessExpr::Dot(expr) if prev_is_number => write!(f, " . {expr}")?, | |
| _ => write!(f, "{field}")?, | |
| } | |
| prev_is_number = matches!(field, AccessExpr::Dot(expr) if is_number_expr(expr)); | |
| } | |
| 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::IsJson { | |
| expr, | |
| kind, | |
| unique_keys, | |
| negated, | |
| } => { | |
| write!(f, "{expr} IS ")?; | |
| if *negated { | |
| write!(f, "NOT ")?; | |
| } | |
| write!(f, "JSON")?; | |
| if let Some(kind) = kind { | |
| write!(f, " {kind}")?; | |
| } | |
| if let Some(unique_keys) = unique_keys { | |
| write!(f, " {unique_keys}")?; | |
| } | |
| Ok(()) | |
| } | |
| Expr::InList { | |
| expr, | |
| list, | |
| negated, | |
| global, | |
| } => write!( | |
| f, | |
| "{} {}{}IN ({})", | |
| expr, | |
| if *global { "GLOBAL " } else { "" }, | |
| if *negated { "NOT " } else { "" }, | |
| display_comma_separated(list) | |
| ), | |
| Expr::InSubquery { | |
| expr, | |
| subquery, | |
| negated, | |
| global, | |
| } => write!( | |
| f, | |
| "{} {}{}IN ({})", | |
| expr, | |
| if *global { "GLOBAL " } else { "" }, | |
| 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 let Some(form) = form { | |
| write!(f, "{} IS {}{} NORMALIZED", expr, not_, form) | |
| } else { | |
| write!(f, "{expr} IS {not_}NORMALIZED") | |
| } | |
| } | |
| 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 } => match op { | |
| UnaryOperator::PGPostfixFactorial => { | |
| if matches!( | |
| expr.as_ref(), | |
| Expr::UnaryOp { | |
| op: UnaryOperator::PGPostfixFactorial, | |
| .. | |
| } | |
| ) { | |
| write!(f, "{expr} {op}") | |
| } else { | |
| write!(f, "{expr}{op}") | |
| } | |
| } | |
| UnaryOperator::Not | |
| | UnaryOperator::BitwiseNot | |
| | UnaryOperator::Hash | |
| | UnaryOperator::AtDashAt | |
| | UnaryOperator::DoubleAt | |
| | UnaryOperator::PGAbs | |
| | UnaryOperator::QuestionDash | |
| | UnaryOperator::QuestionPipe | |
| | UnaryOperator::PGSquareRoot | |
| | UnaryOperator::PGCubeRoot => write!(f, "{op} {expr}"), | |
| UnaryOperator::Minus | UnaryOperator::BangNot => { | |
| if starts_with_operator_char(expr) { | |
| write!(f, "{op} {expr}") | |
| } else { | |
| write!(f, "{op}{expr}") | |
| } | |
| } | |
| UnaryOperator::Plus | UnaryOperator::PGPrefixFactorial => 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, | |
| format, | |
| } => match kind { | |
| CastKind::Cast => { | |
| if matches!(data_type, DataType::Unspecified) { | |
| write!(f, "CAST({expr} AS")?; | |
| } else { | |
| write!(f, "CAST({expr} AS {data_type}")?; | |
| } | |
| 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.) | |
| #[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 FOREIGN TABLE | |
| /// ``` | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html) | |
| CreateForeignTable(CreateForeignTable), | |
| /// ```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), | |
| /// A `CREATE TEXT SEARCH` statement. | |
| /// | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/textsearch-intro.html) | |
| CreateTextSearch(CreateTextSearch), | |
| /// ```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), | |
| /// An `ALTER TEXT SEARCH` statement. | |
| /// | |
| /// See [PostgreSQL](https://www.postgresql.org/docs/current/textsearch-configuration.html) | |
| AlterTextSearch(AlterTextSearch), | |
| /// ```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 `OR REPLACE` was present. | |
| or_replace: bool, | |
| /// `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 | |
| /// CREATE [ OR REPLACE ] [ { TEMP | TEMPORARY | VOLATILE } ] FILE FORMAT [ IF NOT EXISTS ] <name> | |
| /// [ TYPE = { CSV | JSON | AVRO | ORC | PARQUET | XML } [ formatTypeOptions ] ] | |
| /// [ COMMENT = '<string_literal>' ] | |
| /// ``` | |
| /// See <https://docs.snowflake.com/en/sql-reference/sql/create-file-format> | |
| CreateFileFormat { | |
| /// `OR REPLACE` flag. | |
| or_replace: bool, | |
| /// Whether file format is temporary. | |
| temporary: bool, | |
| /// Whether file format is volatile. | |
| volatile: bool, | |
| /// `IF NOT EXISTS` flag. | |
| if_not_exists: bool, | |
| /// File format name. | |
| name: ObjectName, | |
| /// Format type options (e.g. `TYPE`, `FIELD_DELIMITER`, `COMPRESSION`, ...). | |
| options: KeyValueOptions, | |
| /// Optional comment. | |
| comment: Option<String>, | |
| }, | |
| /// ```sql | |
| /// CREATE [ OR REPLACE ] WAREHOUSE [ IF NOT EXISTS ] <name> | |
| /// [ [ WITH ] <property> = <value> [ ... ] ] | |
| /// ``` | |
| /// Snowflake-specific statement to create a virtual warehouse. | |
| /// | |
| /// See <https://docs.snowflake.com/en/sql-reference/sql/create-warehouse> | |
| CreateWarehouse(CreateWarehouse), | |
| /// ```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 `PUT` | |
| /// ```sql | |
| /// PUT 'file://<path>' <internalStage> [ <option> = <value> ... ] | |
| /// ``` | |
| /// Options include `PARALLEL`, `AUTO_COMPRESS`, `SOURCE_COMPRESSION`, `OVERWRITE`. | |
| /// See: <https://docs.snowflake.com/en/sql-reference/sql/put> | |
| Put { | |
| /// Local source URI as written in the statement, e.g. `file:///tmp/data.csv`. | |
| source: String, | |
| /// Target internal stage (e.g. `@mystage`, `@~`, `@%table`). | |
| stage: ObjectName, | |
| /// Trailing options (`PARALLEL=4`, `AUTO_COMPRESS=TRUE`, ...). | |
| options: KeyValueOptions, | |
| }, | |
| /// 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"#); | |
| /// ``` | |
| #[expect( | |
| clippy::cognitive_complexity, | |
| reason = "splitting this match would need a struct for each `Statement` variant" | |
| )] | |
| 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 { "" }, | |
| )?; | |
| write!( | |
| f, | |
| "({})", | |
| display_comma_separated(args.as_deref().unwrap_or(&[])) | |
| )?; | |
| 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::CreateForeignTable(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::CreateTextSearch(create_text_search) => create_text_search.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::AlterTextSearch(alter_text_search) => write!(f, "{alter_text_search}"), | |
| 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 let Some(filter) = filter { | |
| write!(f, " {}", filter)?; | |
| } | |
| Ok(()) | |
| } | |
| Statement::ShowVariables { | |
| filter, | |
| global, | |
| session, | |
| } => { | |
| write!(f, "SHOW")?; | |
| if *global { | |
| write!(f, " GLOBAL")?; | |
| } | |
| if *session { | |
| write!(f, " SESSION")?; | |
| } | |
| write!(f, " VARIABLES")?; | |
| if let Some(filter) = filter { | |
| write!(f, " {}", filter)?; | |
| } | |
| 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, | |
| or_replace, | |
| if_not_exists, | |
| with, | |
| options, | |
| default_collate_spec, | |
| clone, | |
| } => { | |
| write!( | |
| f, | |
| "CREATE {or_replace}SCHEMA {if_not_exists}{name}", | |
| or_replace = if *or_replace { "OR REPLACE " } else { "" }, | |
| 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 let Some(comment) = comment { | |
| write!(f, " COMMENT='{}'", comment)?; | |
| } | |
| Ok(()) | |
| } | |
| Statement::CreateFileFormat { | |
| or_replace, | |
| temporary, | |
| volatile, | |
| if_not_exists, | |
| name, | |
| options, | |
| comment, | |
| } => { | |
| write!( | |
| f, | |
| "CREATE {or_replace}{temp}{volatile}FILE FORMAT {if_not_exists}{name}", | |
| or_replace = if *or_replace { "OR REPLACE " } else { "" }, | |
| temp = if *temporary { "TEMPORARY " } else { "" }, | |
| volatile = if *volatile { "VOLATILE " } else { "" }, | |
| if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, | |
| )?; | |
| if !options.options.is_empty() { | |
| write!(f, " {options}")?; | |
| } | |
| if let Some(comment) = comment { | |
| write!(f, " COMMENT='{}'", comment)?; | |
| } | |
| Ok(()) | |
| } | |
| Statement::CreateWarehouse(s) => write!(f, "{s}"), | |
| 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 let Some(value) = value { | |
| if *is_eq { | |
| write!(f, " = {value}")?; | |
| } else { | |
| write!(f, "({value})")?; | |
| } | |
| } | |
| 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::Put { | |
| source, | |
| stage, | |
| options, | |
| } => { | |
| write!(f, "PUT '{source}' {stage}")?; | |
| if !options.options.is_empty() { | |
| write!(f, " {options}")?; | |
| } | |
| Ok(()) | |
| } | |
| 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 { | |
| let keyword = match self.grantee_type { | |
| GranteesType::Role => "ROLE", | |
| GranteesType::Share => "SHARE", | |
| GranteesType::User => "USER", | |
| GranteesType::Group => "GROUP", | |
| GranteesType::Public => "PUBLIC", | |
| GranteesType::DatabaseRole => "DATABASE ROLE", | |
| GranteesType::Application => "APPLICATION", | |
| GranteesType::ApplicationRole => "APPLICATION ROLE", | |
| GranteesType::None => "", | |
| }; | |
| f.write_str(keyword)?; | |
| if let Some(name) = &self.name { | |
| if !keyword.is_empty() { | |
| f.write_str(" ")?; | |
| } | |
| 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, | |
| /// function(arg1 value1), with no operator between the name and the value, | |
| /// as in PostgreSQL `XMLPARSE(DOCUMENT value)` | |
| /// | |
| /// [PostgreSQL](https://www.postgresql.org/docs/current/datatype-xml.html#DATATYPE-XML-CREATING) | |
| Space, | |
| } | |
| 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"), | |
| FunctionArgOperator::Space => Ok(()), | |
| } | |
| } | |
| } | |
| #[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, | |
| } => fmt_named_function_arg(f, name, operator, arg), | |
| FunctionArg::ExprNamed { | |
| name, | |
| arg, | |
| operator, | |
| } => fmt_named_function_arg(f, name, operator, arg), | |
| FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_arg}"), | |
| } | |
| } | |
| } | |
| /// Whether `expr` renders with an operator character first. A prefix `-` or `!` | |
| /// must not abut one, since `--` starts a line comment and compound tokens | |
| /// like `!!` or `!~` alter the parsed AST or fail to parse. | |
| fn starts_with_operator_char(mut expr: &Expr) -> bool { | |
| loop { | |
| match expr { | |
| Expr::UnaryOp { op, .. } => return !matches!(op, UnaryOperator::Not), | |
| Expr::BinaryOp { left, .. } => expr = left, | |
| _ => return false, | |
| } | |
| } | |
| } | |
| /// `FunctionArgOperator::Space` has no token of its own, so the name and the | |
| /// value are separated by a single space instead. | |
| fn fmt_named_function_arg( | |
| f: &mut fmt::Formatter, | |
| name: &impl fmt::Display, | |
| operator: &FunctionArgOperator, | |
| arg: &FunctionArgExpr, | |
| ) -> fmt::Result { | |
| match operator { | |
| FunctionArgOperator::Space => write!(f, "{name} {arg}"), | |
| _ => write!(f, "{name} {operator} {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, | |
| /// 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>, | |
| /// 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>, | |
| } | |
| 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), | |
| /// The inline `WHERE` filter clause on an aggregate call, e.g. | |
| /// `COUNT(* WHERE cond)` / `SUM(x WHERE cond)` / `ARRAY_AGG(x WHERE cond ORDER BY ..)`. | |
| /// Popularized by [GoogleSQL]; equivalent to the standard `AGG(x) FILTER (WHERE cond)`. | |
| /// Accepted for all dialects since `WHERE` cannot otherwise begin a function argument. | |
| /// | |
| /// [GoogleSQL]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#grouping_and_filtering | |
| Where(Expr), | |
| /// 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::Where(expr) => write!(f, "WHERE {expr}"), | |
| 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, | |
| } | |
| /// Optional type constraint for `IS JSON`. | |
| #[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 JsonPredicateType { | |
| /// `VALUE` form. | |
| Value, | |
| /// `SCALAR` form. | |
| Scalar, | |
| /// `ARRAY` form. | |
| Array, | |
| /// `OBJECT` form. | |
| Object, | |
| } | |
| impl fmt::Display for JsonPredicateType { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| JsonPredicateType::Value => write!(f, "VALUE"), | |
| JsonPredicateType::Scalar => write!(f, "SCALAR"), | |
| JsonPredicateType::Array => write!(f, "ARRAY"), | |
| JsonPredicateType::Object => write!(f, "OBJECT"), | |
| } | |
| } | |
| } | |
| /// Optional duplicate-key handling for `IS JSON`. | |
| #[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 JsonKeyUniqueness { | |
| /// `WITH UNIQUE KEYS` form. | |
| WithUniqueKeys, | |
| /// `WITHOUT UNIQUE KEYS` form. | |
| WithoutUniqueKeys, | |
| } | |
| impl fmt::Display for JsonKeyUniqueness { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| JsonKeyUniqueness::WithUniqueKeys => write!(f, "WITH UNIQUE KEYS"), | |
| JsonKeyUniqueness::WithoutUniqueKeys => write!(f, "WITHOUT UNIQUE KEYS"), | |
| } | |
| } | |
| } | |
| 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, | |
| /// A warehouse. | |
| Warehouse, | |
| } | |
| 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", | |
| ObjectType::Warehouse => "WAREHOUSE", | |
| }) | |
| } | |
| } | |
| #[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))] | |
| /// 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 entry in a Postgres `OPTIONS ( ... )` clause. | |
| #[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] | |
| #[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) => write!(f, "AFTER {ident}"), | |
| } | |
| } | |
| } | |
| /// 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(()) | |
| } | |
| } | |
| /// ```sql | |
| /// CREATE [ OR REPLACE ] WAREHOUSE [ IF NOT EXISTS ] <name> | |
| /// [ [ WITH ] <property> = <value> [ ... ] ] | |
| /// ``` | |
| /// Snowflake-specific statement to create a virtual warehouse. | |
| /// | |
| /// See <https://docs.snowflake.com/en/sql-reference/sql/create-warehouse> | |
| #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] | |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | |
| #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] | |
| pub struct CreateWarehouse { | |
| /// `OR REPLACE` flag. | |
| pub or_replace: bool, | |
| /// `IF NOT EXISTS` flag. | |
| pub if_not_exists: bool, | |
| /// Warehouse name. | |
| pub name: ObjectName, | |
| /// Warehouse properties and parameters (e.g. `WAREHOUSE_SIZE = 'XSMALL'`). | |
| pub options: KeyValueOptions, | |
| } | |
| impl fmt::Display for CreateWarehouse { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!(f, "CREATE")?; | |
| if self.or_replace { | |
| write!(f, " OR REPLACE")?; | |
| } | |
| write!(f, " WAREHOUSE")?; | |
| if self.if_not_exists { | |
| write!(f, " IF NOT EXISTS")?; | |
| } | |
| write!(f, " {}", self.name)?; | |
| if !self.options.options.is_empty() { | |
| write!(f, " {}", self.options)?; | |
| } | |
| 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 session authorization to the session user. | |
| SessionAuthorization, | |
| /// Resets a specific session parameter to its default value. | |
| ConfigurationParameter(ObjectName), | |
| } | |
| /// Resets a session parameter to its default value. | |
| /// ```sql | |
| /// RESET { ALL | SESSION AUTHORIZATION | <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)?; | |
| f.write_str("\n") | |
| } | |
| 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::SessionAuthorization => write!(f, "RESET SESSION AUTHORIZATION"), | |
| 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<CreateTextSearch> for Statement { | |
| fn from(c: CreateTextSearch) -> Self { | |
| Self::CreateTextSearch(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<AlterTextSearch> for Statement { | |
| fn from(a: AlterTextSearch) -> Self { | |
| Self::AlterTextSearch(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<CreateWarehouse> for Statement { | |
| fn from(c: CreateWarehouse) -> Self { | |
| Self::CreateWarehouse(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); | |
| } | |
| } |