blob: 7be23f4b3c76f5310dca27e1118e03e2cd2b1318 [file]
// 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;
use core::{
fmt::{self, Display},
hash,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
use crate::{
display_utils::SpaceOrNewline,
tokenizer::{Span, Token},
};
use crate::{
display_utils::{Indent, NewLine},
keywords::Keyword,
};
pub use self::data_type::{
ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember,
ExactNumberInfo, IntervalFields, StructBracketKind, TimezoneInfo,
};
pub use self::dcl::{
AlterRoleOperation, CreateRole, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
SetConfigValue, Use,
};
pub use self::ddl::{
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterFunction, AlterFunctionAction,
AlterFunctionKind, AlterFunctionOperation, AlterIndexOperation, AlterOperator,
AlterOperatorClass, AlterOperatorClassOperation, AlterOperatorFamily,
AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicy, AlterPolicyOperation,
AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock,
AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction,
CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreatePolicy,
CreatePolicyCommand, CreatePolicyType, CreateTable, CreateTrigger, CreateView, Deduplicate,
DeferrableInitial, DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator,
DropOperatorClass, DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger,
ForValues, FunctionReturnType, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem,
OperatorOption, OperatorPurpose, Owner, Partition, PartitionBoundValue, ProcedureParam,
ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind,
Truncate, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
UserDefinedTypeStorage, ViewColumnDef,
};
pub use self::dml::{
Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
MergeInsertKind, MergeUpdateExpr, MultiTableInsertIntoClause, MultiTableInsertType,
MultiTableInsertValue, MultiTableInsertValues, MultiTableInsertWhenClause, OutputClause,
Update,
};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectByKind, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, ExprWithAliasAndOrderBy, Fetch, ForClause,
ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias,
IlikeSelectItem, InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint,
JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn,
JsonTableNestedColumn, LateralView, LimitClause, LockClause, LockType, MatchRecognizePattern,
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
OffsetRows, OpenJsonTableColumn, OrderBy, OrderByExpr, OrderByKind, OrderByOptions,
PipeOperator, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
SelectFlavor, SelectInto, SelectItem, SelectItemQualifiedWildcardKind, SelectModifiers,
SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table, TableAlias,
TableAliasColumnDef, TableFactor, TableFunctionArgs, TableIndexHintForClause,
TableIndexHintType, TableIndexHints, TableIndexType, TableSample, TableSampleBucket,
TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed,
TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity,
UpdateTableFromKind, ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn,
XmlTableColumnOption,
};
pub use self::trigger::{
TriggerEvent, TriggerExecBody, TriggerExecBodyType, TriggerObject, TriggerPeriod,
TriggerReferencing, TriggerReferencingType,
};
pub use self::value::{
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,
NormalizationForm, QuoteDelimitedString, TrimWhereField, Value, ValueWithSpan,
};
use crate::ast::helpers::key_value_options::KeyValueOptions;
use crate::ast::helpers::stmt_data_loading::StageParamsObject;
#[cfg(feature = "visitor")]
pub use visitor::*;
pub use self::data_type::GeometricTypeKind;
mod data_type;
mod dcl;
mod ddl;
mod dml;
/// Helper modules for building and manipulating AST nodes.
pub mod helpers;
pub mod table_constraints;
pub use table_constraints::{
CheckConstraint, ConstraintUsingIndex, ForeignKeyConstraint, FullTextOrSpatialConstraint,
IndexConstraint, PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
};
mod operator;
mod query;
mod spans;
pub use spans::Spanned;
pub mod comments;
mod trigger;
mod value;
#[cfg(feature = "visitor")]
mod visitor;
/// Helper used to format a slice using a separator string (e.g., `", "`).
pub struct DisplaySeparated<'a, T>
where
T: fmt::Display,
{
slice: &'a [T],
sep: &'static str,
}
impl<T> fmt::Display for DisplaySeparated<'_, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut delim = "";
for t in self.slice {
f.write_str(delim)?;
delim = self.sep;
t.fmt(f)?;
}
Ok(())
}
}
pub(crate) fn display_separated<'a, T>(slice: &'a [T], sep: &'static str) -> DisplaySeparated<'a, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep }
}
pub(crate) fn display_comma_separated<T>(slice: &[T]) -> DisplaySeparated<'_, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep: ", " }
}
/// Writes the given statements to the formatter, each ending with
/// a semicolon and space separated.
fn format_statement_list(f: &mut fmt::Formatter, statements: &[Statement]) -> fmt::Result {
write!(f, "{}", display_separated(statements, "; "))?;
// We manually insert semicolon for the last statement,
// since display_separated doesn't handle that case.
write!(f, ";")
}
/// An identifier, decomposed into its value or character data and the quote style.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Ident {
/// The value of the identifier without quotes.
pub value: String,
/// The starting quote if any. Valid quote characters are the single quote,
/// double quote, backtick, and opening square bracket.
pub quote_style: Option<char>,
/// The span of the identifier in the original SQL string.
pub span: Span,
}
impl PartialEq for Ident {
fn eq(&self, other: &Self) -> bool {
let Ident {
value,
quote_style,
// exhaustiveness check; we ignore spans in comparisons
span: _,
} = self;
value == &other.value && quote_style == &other.quote_style
}
}
impl core::hash::Hash for Ident {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
let Ident {
value,
quote_style,
// exhaustiveness check; we ignore spans in hashes
span: _,
} = self;
value.hash(state);
quote_style.hash(state);
}
}
impl Eq for Ident {}
impl PartialOrd for Ident {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Ident {
fn cmp(&self, other: &Self) -> Ordering {
let Ident {
value,
quote_style,
// exhaustiveness check; we ignore spans in ordering
span: _,
} = self;
let Ident {
value: other_value,
quote_style: other_quote_style,
// exhaustiveness check; we ignore spans in ordering
span: _,
} = other;
// First compare by value, then by quote_style
value
.cmp(other_value)
.then_with(|| quote_style.cmp(other_quote_style))
}
}
impl Ident {
/// Create a new identifier with the given value and no quotes and an empty span.
pub fn new<S>(value: S) -> Self
where
S: Into<String>,
{
Ident {
value: value.into(),
quote_style: None,
span: Span::empty(),
}
}
/// Create a new quoted identifier with the given quote and value. This function
/// panics if the given quote is not a valid quote character.
pub fn with_quote<S>(quote: char, value: S) -> Self
where
S: Into<String>,
{
assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '[');
Ident {
value: value.into(),
quote_style: Some(quote),
span: Span::empty(),
}
}
/// Create an `Ident` with the given `span` and `value` (unquoted).
pub fn with_span<S>(span: Span, value: S) -> Self
where
S: Into<String>,
{
Ident {
value: value.into(),
quote_style: None,
span,
}
}
/// Create a quoted `Ident` with the given `quote` and `span`.
pub fn with_quote_and_span<S>(quote: char, span: Span, value: S) -> Self
where
S: Into<String>,
{
assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '[');
Ident {
value: value.into(),
quote_style: Some(quote),
span,
}
}
}
impl From<&str> for Ident {
fn from(value: &str) -> Self {
Ident {
value: value.to_string(),
quote_style: None,
span: Span::empty(),
}
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.quote_style {
Some(q) if q == '"' || q == '\'' || q == '`' => {
let escaped = value::escape_quoted_string(&self.value, q);
write!(f, "{q}{escaped}{q}")
}
Some('[') => write!(f, "[{}]", self.value),
None => f.write_str(&self.value),
_ => panic!("unexpected quote style"),
}
}
}
/// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ObjectName(pub Vec<ObjectNamePart>);
impl From<Vec<Ident>> for ObjectName {
fn from(idents: Vec<Ident>) -> Self {
ObjectName(idents.into_iter().map(ObjectNamePart::Identifier).collect())
}
}
impl From<Ident> for ObjectName {
fn from(ident: Ident) -> Self {
ObjectName(vec![ObjectNamePart::Identifier(ident)])
}
}
impl fmt::Display for ObjectName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", display_separated(&self.0, "."))
}
}
/// A single part of an ObjectName
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ObjectNamePart {
/// A single identifier part, e.g. `schema` or `table`.
Identifier(Ident),
/// A function that returns an identifier (dialect-specific).
Function(ObjectNamePartFunction),
}
impl ObjectNamePart {
/// Return the identifier if this is an `Identifier` variant.
pub fn as_ident(&self) -> Option<&Ident> {
match self {
ObjectNamePart::Identifier(ident) => Some(ident),
ObjectNamePart::Function(_) => None,
}
}
}
impl fmt::Display for ObjectNamePart {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ObjectNamePart::Identifier(ident) => write!(f, "{ident}"),
ObjectNamePart::Function(func) => write!(f, "{func}"),
}
}
}
/// An object name part that consists of a function that dynamically
/// constructs identifiers.
///
/// - [Snowflake](https://docs.snowflake.com/en/sql-reference/identifier-literal)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ObjectNamePartFunction {
/// The function name that produces the object name part.
pub name: Ident,
/// Function arguments used to compute the identifier.
pub args: Vec<FunctionArg>,
}
impl fmt::Display for ObjectNamePartFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}(", self.name)?;
write!(f, "{})", display_comma_separated(&self.args))
}
}
/// Represents an Array Expression, either
/// `ARRAY[..]`, or `[..]`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Array {
/// The list of expressions between brackets
pub elem: Vec<Expr>,
/// `true` for `ARRAY[..]`, `false` for `[..]`
pub named: bool,
}
impl fmt::Display for Array {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}[{}]",
if self.named { "ARRAY" } else { "" },
display_comma_separated(&self.elem)
)
}
}
/// Represents an INTERVAL expression, roughly in the following format:
/// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
/// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
///
/// The parser does not validate the `<value>`, nor does it ensure
/// that the `<leading_field>` units >= the units in `<last_field>`,
/// so the user will have to reject intervals like `HOUR TO YEAR`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Interval {
/// The interval value expression (commonly a string literal).
pub value: Box<Expr>,
/// Optional leading time unit (e.g., `HOUR`, `MINUTE`).
pub leading_field: Option<DateTimeField>,
/// Optional leading precision for the leading field.
pub leading_precision: Option<u64>,
/// Optional trailing time unit for a range (e.g., `SECOND`).
pub last_field: Option<DateTimeField>,
/// The fractional seconds precision, when specified.
///
/// See SQL `SECOND(n)` or `SECOND(m, n)` forms.
pub fractional_seconds_precision: Option<u64>,
}
impl fmt::Display for Interval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = self.value.as_ref();
match (
&self.leading_field,
self.leading_precision,
self.fractional_seconds_precision,
) {
(
Some(DateTimeField::Second),
Some(leading_precision),
Some(fractional_seconds_precision),
) => {
// When the leading field is SECOND, the parser guarantees that
// the last field is None.
assert!(self.last_field.is_none());
write!(
f,
"INTERVAL {value} SECOND ({leading_precision}, {fractional_seconds_precision})"
)
}
_ => {
write!(f, "INTERVAL {value}")?;
if let Some(leading_field) = &self.leading_field {
write!(f, " {leading_field}")?;
}
if let Some(leading_precision) = self.leading_precision {
write!(f, " ({leading_precision})")?;
}
if let Some(last_field) = &self.last_field {
write!(f, " TO {last_field}")?;
}
if let Some(fractional_seconds_precision) = self.fractional_seconds_precision {
write!(f, " ({fractional_seconds_precision})")?;
}
Ok(())
}
}
}
}
/// A field definition within a struct
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct StructField {
/// Optional name of the struct field.
pub field_name: Option<Ident>,
/// The field data type.
pub field_type: DataType,
/// Struct field options (e.g., `OPTIONS(...)` on BigQuery).
/// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_name_and_column_schema)
pub options: Option<Vec<SqlOption>>,
}
impl fmt::Display for StructField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(name) = &self.field_name {
write!(f, "{name} {}", self.field_type)?;
} else {
write!(f, "{}", self.field_type)?;
}
if let Some(options) = &self.options {
write!(f, " OPTIONS({})", display_separated(options, ", "))
} else {
Ok(())
}
}
}
/// A field definition within a union
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/union.html
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UnionField {
/// Name of the union field.
pub field_name: Ident,
/// Type of the union field.
pub field_type: DataType,
}
impl fmt::Display for UnionField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.field_name, self.field_type)
}
}
/// A dictionary field within a dictionary.
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DictionaryField {
/// Dictionary key identifier.
pub key: Ident,
/// Value expression for the dictionary entry.
pub value: Box<Expr>,
}
impl fmt::Display for DictionaryField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.key, self.value)
}
}
/// Represents a Map expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Map {
/// Entries of the map as key/value pairs.
pub entries: Vec<MapEntry>,
}
impl Display for Map {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAP {{{}}}", display_comma_separated(&self.entries))
}
}
/// A map field within a map.
///
/// [DuckDB]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MapEntry {
/// Key expression of the map entry.
pub key: Box<Expr>,
/// Value expression of the map entry.
pub value: Box<Expr>,
}
impl fmt::Display for MapEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.key, self.value)
}
}
/// Options for `CAST` / `TRY_CAST`
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CastFormat {
/// A simple cast format specified by a `Value`.
Value(ValueWithSpan),
/// A cast format with an explicit time zone: `(format, timezone)`.
ValueAtTimeZone(ValueWithSpan, ValueWithSpan),
}
/// An element of a JSON path.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonPathElem {
/// Accesses an object field using dot notation, e.g. `obj:foo.bar.baz`.
///
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured#dot-notation>.
Dot {
/// The object key text (without quotes).
key: String,
/// `true` when the key was quoted in the source.
quoted: bool,
},
/// Accesses an object field or array element using bracket notation,
/// e.g. `obj['foo']`.
///
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured#bracket-notation>.
Bracket {
/// The expression used as the bracket key (string or numeric expression).
key: Expr,
},
/// Access an object field using colon bracket notation
/// e.g. `obj:['foo']`
///
/// See <https://docs.databricks.com/en/sql/language-manual/functions/colonsign.html>
ColonBracket {
/// The expression used as the bracket key (string or numeric expression).
key: Expr,
},
}
/// A JSON path.
///
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured>.
/// See <https://docs.databricks.com/en/sql/language-manual/sql-ref-json-path-expression.html>.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct JsonPath {
/// Sequence of path elements that form the JSON path.
pub path: Vec<JsonPathElem>,
}
impl fmt::Display for JsonPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, elem) in self.path.iter().enumerate() {
match elem {
JsonPathElem::Dot { key, quoted } => {
if i == 0 {
write!(f, ":")?;
} else {
write!(f, ".")?;
}
if *quoted {
write!(f, "\"{}\"", escape_double_quote_string(key))?;
} else {
write!(f, "{key}")?;
}
}
JsonPathElem::Bracket { key } => {
write!(f, "[{key}]")?;
}
JsonPathElem::ColonBracket { key } => {
write!(f, ":[{key}]")?;
}
}
}
Ok(())
}
}
/// The syntax used for in a cast expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CastKind {
/// The standard SQL cast syntax, e.g. `CAST(<expr> as <datatype>)`
Cast,
/// A cast that returns `NULL` on failure, e.g. `TRY_CAST(<expr> as <datatype>)`.
///
/// See <https://docs.snowflake.com/en/sql-reference/functions/try_cast>.
/// See <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql>.
TryCast,
/// A cast that returns `NULL` on failure, bigQuery-specific , e.g. `SAFE_CAST(<expr> as <datatype>)`.
///
/// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting>.
SafeCast,
/// `<expr> :: <datatype>`
DoubleColon,
}
/// `MATCH` type for constraint references
///
/// See: <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ConstraintReferenceMatchKind {
/// `MATCH FULL`
Full,
/// `MATCH PARTIAL`
Partial,
/// `MATCH SIMPLE`
Simple,
}
impl fmt::Display for ConstraintReferenceMatchKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Full => write!(f, "MATCH FULL"),
Self::Partial => write!(f, "MATCH PARTIAL"),
Self::Simple => write!(f, "MATCH SIMPLE"),
}
}
}
/// `EXTRACT` syntax variants.
///
/// In Snowflake dialect, the `EXTRACT` expression can support either the `from` syntax
/// or the comma syntax.
///
/// See <https://docs.snowflake.com/en/sql-reference/functions/extract>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ExtractSyntax {
/// `EXTRACT( <date_or_time_part> FROM <date_or_time_expr> )`
From,
/// `EXTRACT( <date_or_time_part> , <date_or_timestamp_expr> )`
Comma,
}
/// The syntax used in a CEIL or FLOOR expression.
///
/// The `CEIL/FLOOR(<datetime value expression> TO <time unit>)` is an Amazon Kinesis Data Analytics extension.
/// See <https://docs.aws.amazon.com/kinesisanalytics/latest/sqlref/sql-reference-ceil.html> for
/// details.
///
/// Other dialects either support `CEIL/FLOOR( <expr> [, <scale>])` format or just
/// `CEIL/FLOOR(<expr>)`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CeilFloorKind {
/// `CEIL( <expr> TO <DateTimeField>)`
DateTimeField(DateTimeField),
/// `CEIL( <expr> [, <scale>])`
Scale(ValueWithSpan),
}
/// A WHEN clause in a CASE expression containing both
/// the condition and its corresponding result
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CaseWhen {
/// The `WHEN` condition expression.
pub condition: Expr,
/// The expression returned when `condition` matches.
pub result: Expr,
}
impl fmt::Display for CaseWhen {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("WHEN ")?;
self.condition.fmt(f)?;
f.write_str(" THEN")?;
SpaceOrNewline.fmt(f)?;
Indent(&self.result).fmt(f)?;
Ok(())
}
}
/// An SQL expression of any type.
///
/// # Semantics / Type Checking
///
/// The parser does not distinguish between expressions of different types
/// (e.g. boolean vs string). The caller is responsible for detecting and
/// validating types as necessary (for example `WHERE 1` vs `SELECT 1=1`)
/// See the [README.md] for more details.
///
/// [README.md]: https://github.com/apache/datafusion-sqlparser-rs/blob/main/README.md#syntax-vs-semantics
///
/// # Equality and Hashing Does not Include Source Locations
///
/// The `Expr` type implements `PartialEq` and `Eq` based on the semantic value
/// of the expression (not bitwise comparison). This means that `Expr` instances
/// that are semantically equivalent but have different spans (locations in the
/// source tree) will compare as equal.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "visitor",
derive(Visit, VisitMut),
visit(with = "visit_expr")
)]
pub enum Expr {
/// Identifier e.g. table name or column name
Identifier(Ident),
/// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col`
CompoundIdentifier(Vec<Ident>),
/// Multi-part expression access.
///
/// This structure represents an access chain in structured / nested types
/// such as maps, arrays, and lists:
/// - Array
/// - A 1-dim array `a[1]` will be represented like:
/// `CompoundFieldAccess(Ident('a'), vec![Subscript(1)]`
/// - A 2-dim array `a[1][2]` will be represented like:
/// `CompoundFieldAccess(Ident('a'), vec![Subscript(1), Subscript(2)]`
/// - Map or Struct (Bracket-style)
/// - A map `a['field1']` will be represented like:
/// `CompoundFieldAccess(Ident('a'), vec![Subscript('field')]`
/// - A 2-dim map `a['field1']['field2']` will be represented like:
/// `CompoundFieldAccess(Ident('a'), vec![Subscript('field1'), Subscript('field2')]`
/// - Struct (Dot-style) (only effect when the chain contains both subscript and expr)
/// - A struct access `a[field1].field2` will be represented like:
/// `CompoundFieldAccess(Ident('a'), vec![Subscript('field1'), Ident('field2')]`
/// - If a struct access likes `a.field1.field2`, it will be represented by CompoundIdentifier([a, field1, field2])
CompoundFieldAccess {
/// The base expression being accessed.
root: Box<Expr>,
/// Sequence of access operations (subscript or identifier accesses).
access_chain: Vec<AccessExpr>,
},
/// Access data nested in a value containing semi-structured data, such as
/// the `VARIANT` type on Snowflake. for example `src:customer[0].name`.
///
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured>.
/// See <https://docs.databricks.com/en/sql/language-manual/functions/colonsign.html>.
JsonAccess {
/// The value being queried.
value: Box<Expr>,
/// The path to the data to extract.
path: JsonPath,
},
/// `IS FALSE` operator
IsFalse(Box<Expr>),
/// `IS NOT FALSE` operator
IsNotFalse(Box<Expr>),
/// `IS TRUE` operator
IsTrue(Box<Expr>),
/// `IS NOT TRUE` operator
IsNotTrue(Box<Expr>),
/// `IS NULL` operator
IsNull(Box<Expr>),
/// `IS NOT NULL` operator
IsNotNull(Box<Expr>),
/// `IS UNKNOWN` operator
IsUnknown(Box<Expr>),
/// `IS NOT UNKNOWN` operator
IsNotUnknown(Box<Expr>),
/// `IS DISTINCT FROM` operator
IsDistinctFrom(Box<Expr>, Box<Expr>),
/// `IS NOT DISTINCT FROM` operator
IsNotDistinctFrom(Box<Expr>, Box<Expr>),
/// `<expr> IS [ NOT ] [ form ] NORMALIZED`
IsNormalized {
/// Expression being tested.
expr: Box<Expr>,
/// Optional normalization `form` (e.g., NFC, NFD).
form: Option<NormalizationForm>,
/// `true` when `NOT` is present.
negated: bool,
},
/// `[ NOT ] IN (val1, val2, ...)`
InList {
/// Left-hand expression to test for membership.
expr: Box<Expr>,
/// Literal list of expressions to check against.
list: Vec<Expr>,
/// `true` when the `NOT` modifier is present.
negated: bool,
},
/// `[ NOT ] IN (SELECT ...)`
InSubquery {
/// Left-hand expression to test for membership.
expr: Box<Expr>,
/// The subquery providing the candidate values.
subquery: Box<Query>,
/// `true` when the `NOT` modifier is present.
negated: bool,
},
/// `[ NOT ] IN UNNEST(array_expression)`
InUnnest {
/// Left-hand expression to test for membership.
expr: Box<Expr>,
/// Array expression being unnested.
array_expr: Box<Expr>,
/// `true` when the `NOT` modifier is present.
negated: bool,
},
/// `<expr> [ NOT ] BETWEEN <low> AND <high>`
Between {
/// Expression being compared.
expr: Box<Expr>,
/// `true` when the `NOT` modifier is present.
negated: bool,
/// Lower bound.
low: Box<Expr>,
/// Upper bound.
high: Box<Expr>,
},
/// Binary operation e.g. `1 + 1` or `foo > bar`
BinaryOp {
/// Left operand.
left: Box<Expr>,
/// Operator between operands.
op: BinaryOperator,
/// Right operand.
right: Box<Expr>,
},
/// `[NOT] LIKE <pattern> [ESCAPE <escape_character>]`
Like {
/// `true` when `NOT` is present.
negated: bool,
/// Snowflake supports the ANY keyword to match against a list of patterns
/// <https://docs.snowflake.com/en/sql-reference/functions/like_any>
any: bool,
/// Expression to match.
expr: Box<Expr>,
/// Pattern expression.
pattern: Box<Expr>,
/// Optional escape character.
escape_char: Option<ValueWithSpan>,
},
/// `ILIKE` (case-insensitive `LIKE`)
ILike {
/// `true` when `NOT` is present.
negated: bool,
/// Snowflake supports the ANY keyword to match against a list of patterns
/// <https://docs.snowflake.com/en/sql-reference/functions/like_any>
any: bool,
/// Expression to match.
expr: Box<Expr>,
/// Pattern expression.
pattern: Box<Expr>,
/// Optional escape character.
escape_char: Option<ValueWithSpan>,
},
/// `SIMILAR TO` regex
SimilarTo {
/// `true` when `NOT` is present.
negated: bool,
/// Expression to test.
expr: Box<Expr>,
/// Pattern expression.
pattern: Box<Expr>,
/// Optional escape character.
escape_char: Option<ValueWithSpan>,
},
/// MySQL: `RLIKE` regex or `REGEXP` regex
RLike {
/// `true` when `NOT` is present.
negated: bool,
/// Expression to test.
expr: Box<Expr>,
/// Pattern expression.
pattern: Box<Expr>,
/// true for REGEXP, false for RLIKE (no difference in semantics)
regexp: bool,
},
/// `ANY` operation e.g. `foo > ANY(bar)`, comparison operator is one of `[=, >, <, =>, =<, !=]`
/// <https://docs.snowflake.com/en/sql-reference/operators-subquery#all-any>
AnyOp {
/// Left operand.
left: Box<Expr>,
/// Comparison operator.
compare_op: BinaryOperator,
/// Right-hand subquery expression.
right: Box<Expr>,
/// ANY and SOME are synonymous: <https://docs.cloudera.com/cdw-runtime/cloud/using-hiveql/topics/hive_comparison_predicates.html>
is_some: bool,
},
/// `ALL` operation e.g. `foo > ALL(bar)`, comparison operator is one of `[=, >, <, =>, =<, !=]`
/// <https://docs.snowflake.com/en/sql-reference/operators-subquery#all-any>
AllOp {
/// Left operand.
left: Box<Expr>,
/// Comparison operator.
compare_op: BinaryOperator,
/// Right-hand subquery expression.
right: Box<Expr>,
},
/// Unary operation e.g. `NOT foo`
UnaryOp {
/// The unary operator (e.g., `NOT`, `-`).
op: UnaryOperator,
/// Operand expression.
expr: Box<Expr>,
},
/// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)`
Convert {
/// CONVERT (false) or TRY_CONVERT (true)
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql?view=sql-server-ver16>
is_try: bool,
/// The expression to convert.
expr: Box<Expr>,
/// The target data type, if provided.
data_type: Option<DataType>,
/// Optional target character encoding (e.g., `utf8mb4`).
charset: Option<ObjectName>,
/// `true` when target precedes the value (MSSQL syntax).
target_before_value: bool,
/// How to translate the expression.
///
/// [MSSQL]: https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16#style
styles: Vec<Expr>,
},
/// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
Cast {
/// The cast kind (e.g., `CAST`, `TRY_CAST`).
kind: CastKind,
/// Expression being cast.
expr: Box<Expr>,
/// Target data type.
data_type: DataType,
/// [MySQL] allows CAST(... AS type ARRAY) in functional index definitions for InnoDB
/// multi-valued indices. It's not really a datatype, and is only allowed in `CAST` in key
/// specifications, so it's a flag here.
///
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/cast-functions.html#function_cast
array: bool,
/// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by [BigQuery]
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
format: Option<CastFormat>,
},
/// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
AtTimeZone {
/// Timestamp expression to shift.
timestamp: Box<Expr>,
/// Time zone expression to apply.
time_zone: Box<Expr>,
},
/// Extract a field from a timestamp e.g. `EXTRACT(MONTH FROM foo)`
/// Or `EXTRACT(MONTH, foo)`
///
/// Syntax:
/// ```sql
/// EXTRACT(DateTimeField FROM <expr>) | EXTRACT(DateTimeField, <expr>)
/// ```
Extract {
/// Which datetime field is being extracted.
field: DateTimeField,
/// Syntax variant used (`From` or `Comma`).
syntax: ExtractSyntax,
/// Expression to extract from.
expr: Box<Expr>,
},
/// ```sql
/// CEIL(<expr> [TO DateTimeField])
/// ```
/// ```sql
/// CEIL( <input_expr> [, <scale_expr> ] )
/// ```
Ceil {
/// Expression to ceil.
expr: Box<Expr>,
/// The CEIL/FLOOR kind (datetime field or scale).
field: CeilFloorKind,
},
/// ```sql
/// FLOOR(<expr> [TO DateTimeField])
/// ```
/// ```sql
/// FLOOR( <input_expr> [, <scale_expr> ] )
///
Floor {
/// Expression to floor.
expr: Box<Expr>,
/// The CEIL/FLOOR kind (datetime field or scale).
field: CeilFloorKind,
},
/// ```sql
/// POSITION(<expr> in <expr>)
/// ```
Position {
/// Expression to search for.
expr: Box<Expr>,
/// Expression to search in.
r#in: Box<Expr>,
},
/// ```sql
/// SUBSTRING(<expr> [FROM <expr>] [FOR <expr>])
/// ```
/// or
/// ```sql
/// SUBSTRING(<expr>, <expr>, <expr>)
/// ```
Substring {
/// Source expression.
expr: Box<Expr>,
/// Optional `FROM` expression.
substring_from: Option<Box<Expr>>,
/// Optional `FOR` expression.
substring_for: Option<Box<Expr>>,
/// false if the expression is represented using the `SUBSTRING(expr [FROM start] [FOR len])` syntax
/// true if the expression is represented using the `SUBSTRING(expr, start, len)` syntax
/// This flag is used for formatting.
special: bool,
/// true if the expression is represented using the `SUBSTR` shorthand
/// This flag is used for formatting.
shorthand: bool,
},
/// ```sql
/// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)
/// TRIM(<expr>)
/// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic
/// ```
Trim {
/// Which side to trim: `BOTH`, `LEADING`, or `TRAILING`.
trim_where: Option<TrimWhereField>,
/// Optional expression specifying what to trim from the value `expr`.
trim_what: Option<Box<Expr>>,
/// The expression to trim from.
expr: Box<Expr>,
/// Optional list of characters to trim (dialect-specific).
trim_characters: Option<Vec<Expr>>,
},
/// ```sql
/// OVERLAY(<expr> PLACING <expr> FROM <expr>[ FOR <expr> ]
/// ```
Overlay {
/// The target expression being overlayed.
expr: Box<Expr>,
/// The expression to place into the target.
overlay_what: Box<Expr>,
/// The `FROM` position expression indicating where to start overlay.
overlay_from: Box<Expr>,
/// Optional `FOR` length expression limiting the overlay span.
overlay_for: Option<Box<Expr>>,
},
/// `expr COLLATE collation`
Collate {
/// The expression being collated.
expr: Box<Expr>,
/// The collation name to apply to the expression.
collation: ObjectName,
},
/// Nested expression e.g. `(foo > bar)` or `(1)`
Nested(Box<Expr>),
/// A literal value, such as string, number, date or NULL
Value(ValueWithSpan),
/// Prefixed expression, e.g. introducer strings, projection prefix
/// <https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html>
/// <https://docs.snowflake.com/en/sql-reference/constructs/connect-by>
Prefixed {
/// The prefix identifier (introducer or projection prefix).
prefix: Ident,
/// The value expression being prefixed.
/// Hint: you can unwrap the string value using `value.into_string()`.
value: Box<Expr>,
},
/// A constant of form `<data_type> 'value'`.
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
/// as well as constants of other types (a non-standard PostgreSQL extension).
TypedString(TypedString),
/// Scalar function call e.g. `LEFT(foo, 5)`
Function(Function),
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
///
/// Note we only recognize a complete single expression as `<condition>`,
/// not `< 0` nor `1, 2, 3` as allowed in a `<simple when clause>` per
/// <https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#simple-when-clause>
Case {
/// The attached `CASE` token (keeps original spacing/comments).
case_token: AttachedToken,
/// The attached `END` token (keeps original spacing/comments).
end_token: AttachedToken,
/// Optional operand expression after `CASE` (for simple CASE).
operand: Option<Box<Expr>>,
/// The `WHEN ... THEN` conditions and results.
conditions: Vec<CaseWhen>,
/// Optional `ELSE` result expression.
else_result: Option<Box<Expr>>,
},
/// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like
/// `WHERE [ NOT ] EXISTS (SELECT ...)`.
Exists {
/// The subquery checked by `EXISTS`.
subquery: Box<Query>,
/// Whether the `EXISTS` is negated (`NOT EXISTS`).
negated: bool,
},
/// A parenthesized subquery `(SELECT ...)`, used in expression like
/// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
Subquery(Box<Query>),
/// The `GROUPING SETS` expr.
GroupingSets(Vec<Vec<Expr>>),
/// The `CUBE` expr.
Cube(Vec<Vec<Expr>>),
/// The `ROLLUP` expr.
Rollup(Vec<Vec<Expr>>),
/// ROW / TUPLE a single value, such as `SELECT (1, 2)`
Tuple(Vec<Expr>),
/// `Struct` literal expression
/// Syntax:
/// ```sql
/// STRUCT<[field_name] field_type, ...>( expr1 [, ... ])
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type)
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/functions/struct.html)
/// ```
Struct {
/// Struct values.
values: Vec<Expr>,
/// Struct field definitions.
fields: Vec<StructField>,
},
/// `BigQuery` specific: An named expression in a typeless struct [1]
///
/// Syntax
/// ```sql
/// 1 AS A
/// ```
/// [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
Named {
/// The expression being named.
expr: Box<Expr>,
/// The assigned identifier name for the expression.
name: Ident,
},
/// `DuckDB` specific `Struct` literal expression [1]
///
/// Syntax:
/// ```sql
/// syntax: {'field_name': expr1[, ... ]}
/// ```
/// [1]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
Dictionary(Vec<DictionaryField>),
/// `DuckDB` specific `Map` literal expression [1]
///
/// Syntax:
/// ```sql
/// syntax: Map {key1: value1[, ... ]}
/// ```
/// [1]: https://duckdb.org/docs/sql/data_types/map#creating-maps
Map(Map),
/// An array expression e.g. `ARRAY[1, 2]`
Array(Array),
/// An interval expression e.g. `INTERVAL '1' YEAR`
Interval(Interval),
/// `MySQL` specific text search function [(1)].
///
/// Syntax:
/// ```sql
/// MATCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
///
/// <col> = CompoundIdentifier
/// <expr> = String literal
/// ```
/// [(1)]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html#function_match
MatchAgainst {
/// `(<col>, <col>, ...)`.
columns: Vec<ObjectName>,
/// `<expr>`.
match_value: ValueWithSpan,
/// `<search modifier>`
opt_search_modifier: Option<SearchModifier>,
},
/// An unqualified `*` wildcard token (e.g. `*`).
Wildcard(AttachedToken),
/// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
/// (Same caveats apply to `QualifiedWildcard` as to `Wildcard`.)
QualifiedWildcard(ObjectName, AttachedToken),
/// Some dialects support an older syntax for outer joins where columns are
/// marked with the `(+)` operator in the WHERE clause, for example:
///
/// ```sql
/// SELECT t1.c1, t2.c2 FROM t1, t2 WHERE t1.c1 = t2.c2 (+)
/// ```
///
/// which is equivalent to
///
/// ```sql
/// SELECT t1.c1, t2.c2 FROM t1 LEFT OUTER JOIN t2 ON t1.c1 = t2.c2
/// ```
///
/// See <https://docs.snowflake.com/en/sql-reference/constructs/where#joins-in-the-where-clause>.
OuterJoin(Box<Expr>),
/// A reference to the prior level in a CONNECT BY clause.
Prior(Box<Expr>),
/// A lambda function.
///
/// Syntax:
/// ```plaintext
/// param -> expr | (param1, ...) -> expr
/// ```
///
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function)
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
/// [DuckDB](https://duckdb.org/docs/stable/sql/functions/lambda)
Lambda(LambdaFunction),
/// Checks membership of a value in a JSON array
MemberOf(MemberOf),
}
impl Expr {
/// Creates a new [`Expr::Value`]
pub fn value(value: impl Into<ValueWithSpan>) -> Self {
Expr::Value(value.into())
}
}
/// The contents inside the `[` and `]` in a subscript expression.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Subscript {
/// Accesses the element of the array at the given index.
Index {
/// The index expression used to access the array element.
index: Expr,
},
/// Accesses a slice of an array on PostgreSQL, e.g.
///
/// ```plaintext
/// => select (array[1,2,3,4,5,6])[2:5];
/// -----------
/// {2,3,4,5}
/// ```
///
/// The lower and/or upper bound can be omitted to slice from the start or
/// end of the array respectively.
///
/// See <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-ACCESSING>.
///
/// Also supports an optional "stride" as the last element (this is not
/// supported by postgres), e.g.
///
/// ```plaintext
/// => select (array[1,2,3,4,5,6])[1:6:2];
/// -----------
/// {1,3,5}
/// ```
Slice {
/// Optional lower bound for the slice (inclusive).
lower_bound: Option<Expr>,
/// Optional upper bound for the slice (inclusive).
upper_bound: Option<Expr>,
/// Optional stride for the slice (step size).
stride: Option<Expr>,
},
}
impl fmt::Display for Subscript {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Subscript::Index { index } => write!(f, "{index}"),
Subscript::Slice {
lower_bound,
upper_bound,
stride,
} => {
if let Some(lower) = lower_bound {
write!(f, "{lower}")?;
}
write!(f, ":")?;
if let Some(upper) = upper_bound {
write!(f, "{upper}")?;
}
if let Some(stride) = stride {
write!(f, ":")?;
write!(f, "{stride}")?;
}
Ok(())
}
}
}
}
/// An element of a [`Expr::CompoundFieldAccess`].
/// It can be an expression or a subscript.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AccessExpr {
/// Accesses a field using dot notation, e.g. `foo.bar.baz`.
Dot(Expr),
/// Accesses a field or array element using bracket notation, e.g. `foo['bar']`.
Subscript(Subscript),
}
impl fmt::Display for AccessExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccessExpr::Dot(expr) => write!(f, ".{expr}"),
AccessExpr::Subscript(subscript) => write!(f, "[{subscript}]"),
}
}
}
/// A lambda function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LambdaFunction {
/// The parameters to the lambda function.
pub params: OneOrManyWithParens<LambdaFunctionParameter>,
/// The body of the lambda function.
pub body: Box<Expr>,
/// The syntax style used to write the lambda function.
pub syntax: LambdaSyntax,
}
impl fmt::Display for LambdaFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.syntax {
LambdaSyntax::Arrow => write!(f, "{} -> {}", self.params, self.body),
LambdaSyntax::LambdaKeyword => {
// For lambda keyword syntax, display params without parentheses
// e.g., `lambda x, y : expr` not `lambda (x, y) : expr`
write!(f, "lambda ")?;
match &self.params {
OneOrManyWithParens::One(p) => write!(f, "{p}")?,
OneOrManyWithParens::Many(ps) => write!(f, "{}", display_comma_separated(ps))?,
};
write!(f, " : {}", self.body)
}
}
}
}
/// A parameter to a lambda function, optionally with a data type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LambdaFunctionParameter {
/// The name of the parameter
pub name: Ident,
/// The optional data type of the parameter
/// [Snowflake Syntax](https://docs.snowflake.com/en/sql-reference/functions/filter#arguments)
pub data_type: Option<DataType>,
}
impl fmt::Display for LambdaFunctionParameter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.data_type {
Some(dt) => write!(f, "{} {}", self.name, dt),
None => write!(f, "{}", self.name),
}
}
}
/// The syntax style for a lambda function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum LambdaSyntax {
/// Arrow syntax: `param -> expr` or `(param1, param2) -> expr`
///
/// <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-lambda-functions>
///
/// Supported, but deprecated in DuckDB:
/// <https://duckdb.org/docs/stable/sql/functions/lambda>
Arrow,
/// Lambda keyword syntax: `lambda param : expr` or `lambda param1, param2 : expr`
///
/// Recommended in DuckDB:
/// <https://duckdb.org/docs/stable/sql/functions/lambda>
LambdaKeyword,
}
/// Encapsulates the common pattern in SQL where either one unparenthesized item
/// such as an identifier or expression is permitted, or multiple of the same
/// item in a parenthesized list. For accessing items regardless of the form,
/// `OneOrManyWithParens` implements `Deref<Target = [T]>` and `IntoIterator`,
/// so you can call slice methods on it and iterate over items
/// # Examples
/// Accessing as a slice:
/// ```
/// # use sqlparser::ast::OneOrManyWithParens;
/// let one = OneOrManyWithParens::One("a");
///
/// assert_eq!(one[0], "a");
/// assert_eq!(one.len(), 1);
/// ```
/// Iterating:
/// ```
/// # use sqlparser::ast::OneOrManyWithParens;
/// let one = OneOrManyWithParens::One("a");
/// let many = OneOrManyWithParens::Many(vec!["a", "b"]);
///
/// assert_eq!(one.into_iter().chain(many).collect::<Vec<_>>(), vec!["a", "a", "b"] );
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OneOrManyWithParens<T> {
/// A single `T`, unparenthesized.
One(T),
/// One or more `T`s, parenthesized.
Many(Vec<T>),
}
impl<T> Deref for OneOrManyWithParens<T> {
type Target = [T];
fn deref(&self) -> &[T] {
match self {
OneOrManyWithParens::One(one) => core::slice::from_ref(one),
OneOrManyWithParens::Many(many) => many,
}
}
}
impl<T> AsRef<[T]> for OneOrManyWithParens<T> {
fn as_ref(&self) -> &[T] {
self
}
}
impl<'a, T> IntoIterator for &'a OneOrManyWithParens<T> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// Owned iterator implementation of `OneOrManyWithParens`
#[derive(Debug, Clone)]
pub struct OneOrManyWithParensIntoIter<T> {
inner: OneOrManyWithParensIntoIterInner<T>,
}
#[derive(Debug, Clone)]
enum OneOrManyWithParensIntoIterInner<T> {
One(core::iter::Once<T>),
Many(<Vec<T> as IntoIterator>::IntoIter),
}
impl<T> core::iter::FusedIterator for OneOrManyWithParensIntoIter<T>
where
core::iter::Once<T>: core::iter::FusedIterator,
<Vec<T> as IntoIterator>::IntoIter: core::iter::FusedIterator,
{
}
impl<T> core::iter::ExactSizeIterator for OneOrManyWithParensIntoIter<T>
where
core::iter::Once<T>: core::iter::ExactSizeIterator,
<Vec<T> as IntoIterator>::IntoIter: core::iter::ExactSizeIterator,
{
}
impl<T> core::iter::Iterator for OneOrManyWithParensIntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
OneOrManyWithParensIntoIterInner::One(one) => one.next(),
OneOrManyWithParensIntoIterInner::Many(many) => many.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
OneOrManyWithParensIntoIterInner::One(one) => one.size_hint(),
OneOrManyWithParensIntoIterInner::Many(many) => many.size_hint(),
}
}
fn count(self) -> usize
where
Self: Sized,
{
match self.inner {
OneOrManyWithParensIntoIterInner::One(one) => one.count(),
OneOrManyWithParensIntoIterInner::Many(many) => many.count(),
}
}
fn fold<B, F>(mut self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
match &mut self.inner {
OneOrManyWithParensIntoIterInner::One(one) => one.fold(init, f),
OneOrManyWithParensIntoIterInner::Many(many) => many.fold(init, f),
}
}
}
impl<T> core::iter::DoubleEndedIterator for OneOrManyWithParensIntoIter<T> {
fn next_back(&mut self) -> Option<Self::Item> {
match &mut self.inner {
OneOrManyWithParensIntoIterInner::One(one) => one.next_back(),
OneOrManyWithParensIntoIterInner::Many(many) => many.next_back(),
}
}
}
impl<T> IntoIterator for OneOrManyWithParens<T> {
type Item = T;
type IntoIter = OneOrManyWithParensIntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
let inner = match self {
OneOrManyWithParens::One(one) => {
OneOrManyWithParensIntoIterInner::One(core::iter::once(one))
}
OneOrManyWithParens::Many(many) => {
OneOrManyWithParensIntoIterInner::Many(many.into_iter())
}
};
OneOrManyWithParensIntoIter { inner }
}
}
impl<T> fmt::Display for OneOrManyWithParens<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OneOrManyWithParens::One(value) => write!(f, "{value}"),
OneOrManyWithParens::Many(values) => {
write!(f, "({})", display_comma_separated(values))
}
}
}
}
impl fmt::Display for CastFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CastFormat::Value(v) => write!(f, "{v}"),
CastFormat::ValueAtTimeZone(v, tz) => write!(f, "{v} AT TIME ZONE {tz}"),
}
}
}
impl fmt::Display for Expr {
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Identifier(s) => write!(f, "{s}"),
Expr::Wildcard(_) => f.write_str("*"),
Expr::QualifiedWildcard(prefix, _) => write!(f, "{prefix}.*"),
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
Expr::CompoundFieldAccess { root, access_chain } => {
write!(f, "{root}")?;
for field in access_chain {
write!(f, "{field}")?;
}
Ok(())
}
Expr::IsTrue(ast) => write!(f, "{ast} IS TRUE"),
Expr::IsNotTrue(ast) => write!(f, "{ast} IS NOT TRUE"),
Expr::IsFalse(ast) => write!(f, "{ast} IS FALSE"),
Expr::IsNotFalse(ast) => write!(f, "{ast} IS NOT FALSE"),
Expr::IsNull(ast) => write!(f, "{ast} IS NULL"),
Expr::IsNotNull(ast) => write!(f, "{ast} IS NOT NULL"),
Expr::IsUnknown(ast) => write!(f, "{ast} IS UNKNOWN"),
Expr::IsNotUnknown(ast) => write!(f, "{ast} IS NOT UNKNOWN"),
Expr::InList {
expr,
list,
negated,
} => write!(
f,
"{} {}IN ({})",
expr,
if *negated { "NOT " } else { "" },
display_comma_separated(list)
),
Expr::InSubquery {
expr,
subquery,
negated,
} => write!(
f,
"{} {}IN ({})",
expr,
if *negated { "NOT " } else { "" },
subquery
),
Expr::InUnnest {
expr,
array_expr,
negated,
} => write!(
f,
"{} {}IN UNNEST({})",
expr,
if *negated { "NOT " } else { "" },
array_expr
),
Expr::Between {
expr,
negated,
low,
high,
} => write!(
f,
"{} {}BETWEEN {} AND {}",
expr,
if *negated { "NOT " } else { "" },
low,
high
),
Expr::BinaryOp { left, op, right } => write!(f, "{left} {op} {right}"),
Expr::Like {
negated,
expr,
pattern,
escape_char,
any,
} => match escape_char {
Some(ch) => write!(
f,
"{} {}LIKE {}{} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern,
ch
),
_ => write!(
f,
"{} {}LIKE {}{}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern
),
},
Expr::ILike {
negated,
expr,
pattern,
escape_char,
any,
} => match escape_char {
Some(ch) => write!(
f,
"{} {}ILIKE {}{} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY" } else { "" },
pattern,
ch
),
_ => write!(
f,
"{} {}ILIKE {}{}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern
),
},
Expr::RLike {
negated,
expr,
pattern,
regexp,
} => write!(
f,
"{} {}{} {}",
expr,
if *negated { "NOT " } else { "" },
if *regexp { "REGEXP" } else { "RLIKE" },
pattern
),
Expr::IsNormalized {
expr,
form,
negated,
} => {
let not_ = if *negated { "NOT " } else { "" };
if form.is_none() {
write!(f, "{expr} IS {not_}NORMALIZED")
} else {
write!(
f,
"{} IS {}{} NORMALIZED",
expr,
not_,
form.as_ref().unwrap()
)
}
}
Expr::SimilarTo {
negated,
expr,
pattern,
escape_char,
} => match escape_char {
Some(ch) => write!(
f,
"{} {}SIMILAR TO {} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
pattern,
ch
),
_ => write!(
f,
"{} {}SIMILAR TO {}",
expr,
if *negated { "NOT " } else { "" },
pattern
),
},
Expr::AnyOp {
left,
compare_op,
right,
is_some,
} => {
let add_parens = !matches!(right.as_ref(), Expr::Subquery(_));
write!(
f,
"{left} {compare_op} {}{}{right}{}",
if *is_some { "SOME" } else { "ANY" },
if add_parens { "(" } else { "" },
if add_parens { ")" } else { "" },
)
}
Expr::AllOp {
left,
compare_op,
right,
} => {
let add_parens = !matches!(right.as_ref(), Expr::Subquery(_));
write!(
f,
"{left} {compare_op} ALL{}{right}{}",
if add_parens { "(" } else { "" },
if add_parens { ")" } else { "" },
)
}
Expr::UnaryOp { op, expr } => {
if op == &UnaryOperator::PGPostfixFactorial {
write!(f, "{expr}{op}")
} else if matches!(
op,
UnaryOperator::Not
| UnaryOperator::Hash
| UnaryOperator::AtDashAt
| UnaryOperator::DoubleAt
| UnaryOperator::QuestionDash
| UnaryOperator::QuestionPipe
) {
write!(f, "{op} {expr}")
} else {
write!(f, "{op}{expr}")
}
}
Expr::Convert {
is_try,
expr,
target_before_value,
data_type,
charset,
styles,
} => {
write!(f, "{}CONVERT(", if *is_try { "TRY_" } else { "" })?;
if let Some(data_type) = data_type {
if let Some(charset) = charset {
write!(f, "{expr}, {data_type} CHARACTER SET {charset}")
} else if *target_before_value {
write!(f, "{data_type}, {expr}")
} else {
write!(f, "{expr}, {data_type}")
}
} else if let Some(charset) = charset {
write!(f, "{expr} USING {charset}")
} else {
write!(f, "{expr}") // This should never happen
}?;
if !styles.is_empty() {
write!(f, ", {}", display_comma_separated(styles))?;
}
write!(f, ")")
}
Expr::Cast {
kind,
expr,
data_type,
array,
format,
} => match kind {
CastKind::Cast => {
write!(f, "CAST({expr} AS {data_type}")?;
if *array {
write!(f, " ARRAY")?;
}
if let Some(format) = format {
write!(f, " FORMAT {format}")?;
}
write!(f, ")")
}
CastKind::TryCast => {
if let Some(format) = format {
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
} else {
write!(f, "TRY_CAST({expr} AS {data_type})")
}
}
CastKind::SafeCast => {
if let Some(format) = format {
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
} else {
write!(f, "SAFE_CAST({expr} AS {data_type})")
}
}
CastKind::DoubleColon => {
write!(f, "{expr}::{data_type}")
}
},
Expr::Extract {
field,
syntax,
expr,
} => match syntax {
ExtractSyntax::From => write!(f, "EXTRACT({field} FROM {expr})"),
ExtractSyntax::Comma => write!(f, "EXTRACT({field}, {expr})"),
},
Expr::Ceil { expr, field } => match field {
CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => {
write!(f, "CEIL({expr})")
}
CeilFloorKind::DateTimeField(dt_field) => write!(f, "CEIL({expr} TO {dt_field})"),
CeilFloorKind::Scale(s) => write!(f, "CEIL({expr}, {s})"),
},
Expr::Floor { expr, field } => match field {
CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) => {
write!(f, "FLOOR({expr})")
}
CeilFloorKind::DateTimeField(dt_field) => write!(f, "FLOOR({expr} TO {dt_field})"),
CeilFloorKind::Scale(s) => write!(f, "FLOOR({expr}, {s})"),
},
Expr::Position { expr, r#in } => write!(f, "POSITION({expr} IN {in})"),
Expr::Collate { expr, collation } => write!(f, "{expr} COLLATE {collation}"),
Expr::Nested(ast) => write!(f, "({ast})"),
Expr::Value(v) => write!(f, "{v}"),
Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"),
Expr::TypedString(ts) => ts.fmt(f),
Expr::Function(fun) => fun.fmt(f),
Expr::Case {
case_token: _,
end_token: _,
operand,
conditions,
else_result,
} => {
f.write_str("CASE")?;
if let Some(operand) = operand {
f.write_str(" ")?;
operand.fmt(f)?;
}
for when in conditions {
SpaceOrNewline.fmt(f)?;
Indent(when).fmt(f)?;
}
if let Some(else_result) = else_result {
SpaceOrNewline.fmt(f)?;
Indent("ELSE").fmt(f)?;
SpaceOrNewline.fmt(f)?;
Indent(Indent(else_result)).fmt(f)?;
}
SpaceOrNewline.fmt(f)?;
f.write_str("END")
}
Expr::Exists { subquery, negated } => write!(
f,
"{}EXISTS ({})",
if *negated { "NOT " } else { "" },
subquery
),
Expr::Subquery(s) => write!(f, "({s})"),
Expr::GroupingSets(sets) => {
write!(f, "GROUPING SETS (")?;
let mut sep = "";
for set in sets {
write!(f, "{sep}")?;
sep = ", ";
write!(f, "({})", display_comma_separated(set))?;
}
write!(f, ")")
}
Expr::Cube(sets) => {
write!(f, "CUBE (")?;
let mut sep = "";
for set in sets {
write!(f, "{sep}")?;
sep = ", ";
if set.len() == 1 {
write!(f, "{}", set[0])?;
} else {
write!(f, "({})", display_comma_separated(set))?;
}
}
write!(f, ")")
}
Expr::Rollup(sets) => {
write!(f, "ROLLUP (")?;
let mut sep = "";
for set in sets {
write!(f, "{sep}")?;
sep = ", ";
if set.len() == 1 {
write!(f, "{}", set[0])?;
} else {
write!(f, "({})", display_comma_separated(set))?;
}
}
write!(f, ")")
}
Expr::Substring {
expr,
substring_from,
substring_for,
special,
shorthand,
} => {
f.write_str("SUBSTR")?;
if !*shorthand {
f.write_str("ING")?;
}
write!(f, "({expr}")?;
if let Some(from_part) = substring_from {
if *special {
write!(f, ", {from_part}")?;
} else {
write!(f, " FROM {from_part}")?;
}
}
if let Some(for_part) = substring_for {
if *special {
write!(f, ", {for_part}")?;
} else {
write!(f, " FOR {for_part}")?;
}
}
write!(f, ")")
}
Expr::Overlay {
expr,
overlay_what,
overlay_from,
overlay_for,
} => {
write!(
f,
"OVERLAY({expr} PLACING {overlay_what} FROM {overlay_from}"
)?;
if let Some(for_part) = overlay_for {
write!(f, " FOR {for_part}")?;
}
write!(f, ")")
}
Expr::IsDistinctFrom(a, b) => write!(f, "{a} IS DISTINCT FROM {b}"),
Expr::IsNotDistinctFrom(a, b) => write!(f, "{a} IS NOT DISTINCT FROM {b}"),
Expr::Trim {
expr,
trim_where,
trim_what,
trim_characters,
} => {
write!(f, "TRIM(")?;
if let Some(ident) = trim_where {
write!(f, "{ident} ")?;
}
if let Some(trim_char) = trim_what {
write!(f, "{trim_char} FROM {expr}")?;
} else {
write!(f, "{expr}")?;
}
if let Some(characters) = trim_characters {
write!(f, ", {}", display_comma_separated(characters))?;
}
write!(f, ")")
}
Expr::Tuple(exprs) => {
write!(f, "({})", display_comma_separated(exprs))
}
Expr::Struct { values, fields } => {
if !fields.is_empty() {
write!(
f,
"STRUCT<{}>({})",
display_comma_separated(fields),
display_comma_separated(values)
)
} else {
write!(f, "STRUCT({})", display_comma_separated(values))
}
}
Expr::Named { expr, name } => {
write!(f, "{expr} AS {name}")
}
Expr::Dictionary(fields) => {
write!(f, "{{{}}}", display_comma_separated(fields))
}
Expr::Map(map) => {
write!(f, "{map}")
}
Expr::Array(set) => {
write!(f, "{set}")
}
Expr::JsonAccess { value, path } => {
write!(f, "{value}{path}")
}
Expr::AtTimeZone {
timestamp,
time_zone,
} => {
write!(f, "{timestamp} AT TIME ZONE {time_zone}")
}
Expr::Interval(interval) => {
write!(f, "{interval}")
}
Expr::MatchAgainst {
columns,
match_value: match_expr,
opt_search_modifier,
} => {
write!(f, "MATCH ({}) AGAINST ", display_comma_separated(columns),)?;
if let Some(search_modifier) = opt_search_modifier {
write!(f, "({match_expr} {search_modifier})")?;
} else {
write!(f, "({match_expr})")?;
}
Ok(())
}
Expr::OuterJoin(expr) => {
write!(f, "{expr} (+)")
}
Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
Expr::Lambda(lambda) => write!(f, "{lambda}"),
Expr::MemberOf(member_of) => write!(f, "{member_of}"),
}
}
}
/// The type of a window used in `OVER` clauses.
///
/// A window can be either an inline specification (`WindowSpec`) or a
/// reference to a previously defined named window.
///
/// - `WindowSpec(WindowSpec)`: An inline window specification, e.g.
/// `OVER (PARTITION BY ... ORDER BY ...)`.
/// - `NamedWindow(Ident)`: A reference to a named window declared elsewhere.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WindowType {
/// An inline window specification.
WindowSpec(WindowSpec),
/// A reference to a previously defined named window.
NamedWindow(Ident),
}
impl Display for WindowType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WindowType::WindowSpec(spec) => {
f.write_str("(")?;
NewLine.fmt(f)?;
Indent(spec).fmt(f)?;
NewLine.fmt(f)?;
f.write_str(")")
}
WindowType::NamedWindow(name) => name.fmt(f),
}
}
}
/// A window specification (i.e. `OVER ([window_name] PARTITION BY .. ORDER BY .. etc.)`)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WindowSpec {
/// Optional window name.
///
/// You can find it at least in [MySQL][1], [BigQuery][2], [PostgreSQL][3]
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/window-functions-named-windows.html
/// [2]: https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls
/// [3]: https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS
pub window_name: Option<Ident>,
/// `OVER (PARTITION BY ...)`
pub partition_by: Vec<Expr>,
/// `OVER (ORDER BY ...)`
pub order_by: Vec<OrderByExpr>,
/// `OVER (window frame)`
pub window_frame: Option<WindowFrame>,
}
impl fmt::Display for WindowSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut is_first = true;
if let Some(window_name) = &self.window_name {
if !is_first {
SpaceOrNewline.fmt(f)?;
}
is_first = false;
write!(f, "{window_name}")?;
}
if !self.partition_by.is_empty() {
if !is_first {
SpaceOrNewline.fmt(f)?;
}
is_first = false;
write!(
f,
"PARTITION BY {}",
display_comma_separated(&self.partition_by)
)?;
}
if !self.order_by.is_empty() {
if !is_first {
SpaceOrNewline.fmt(f)?;
}
is_first = false;
write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?;
}
if let Some(window_frame) = &self.window_frame {
if !is_first {
SpaceOrNewline.fmt(f)?;
}
if let Some(end_bound) = &window_frame.end_bound {
write!(
f,
"{} BETWEEN {} AND {}",
window_frame.units, window_frame.start_bound, end_bound
)?;
} else {
write!(f, "{} {}", window_frame.units, window_frame.start_bound)?;
}
}
Ok(())
}
}
/// Specifies the data processed by a window function, e.g.
/// `RANGE UNBOUNDED PRECEDING` or `ROWS BETWEEN 5 PRECEDING AND CURRENT ROW`.
///
/// Note: The parser does not validate the specified bounds; the caller should
/// reject invalid bounds like `ROWS UNBOUNDED FOLLOWING` before execution.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WindowFrame {
/// Units for the frame (e.g. `ROWS`, `RANGE`, `GROUPS`).
pub units: WindowFrameUnits,
/// The start bound of the window frame.
pub start_bound: WindowFrameBound,
/// The right bound of the `BETWEEN .. AND` clause. The end bound of `None`
/// indicates the shorthand form (e.g. `ROWS 1 PRECEDING`), which must
/// behave the same as `end_bound = WindowFrameBound::CurrentRow`.
pub end_bound: Option<WindowFrameBound>,
// TBD: EXCLUDE
}
impl Default for WindowFrame {
/// Returns default value for window frame
///
/// See [this page](https://www.sqlite.org/windowfunctions.html#frame_specifications) for more details.
fn default() -> Self {
Self {
units: WindowFrameUnits::Range,
start_bound: WindowFrameBound::Preceding(None),
end_bound: None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Units used to describe the window frame scope.
pub enum WindowFrameUnits {
/// `ROWS` unit.
Rows,
/// `RANGE` unit.
Range,
/// `GROUPS` unit.
Groups,
}
impl fmt::Display for WindowFrameUnits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
WindowFrameUnits::Rows => "ROWS",
WindowFrameUnits::Range => "RANGE",
WindowFrameUnits::Groups => "GROUPS",
})
}
}
/// Specifies Ignore / Respect NULL within window functions.
/// For example
/// `FIRST_VALUE(column2) IGNORE NULLS OVER (PARTITION BY column1)`
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How NULL values are treated in certain window functions.
pub enum NullTreatment {
/// Ignore NULL values (e.g. `IGNORE NULLS`).
IgnoreNulls,
/// Respect NULL values (e.g. `RESPECT NULLS`).
RespectNulls,
}
impl fmt::Display for NullTreatment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
NullTreatment::IgnoreNulls => "IGNORE NULLS",
NullTreatment::RespectNulls => "RESPECT NULLS",
})
}
}
/// Specifies [WindowFrame]'s `start_bound` and `end_bound`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WindowFrameBound {
/// `CURRENT ROW`
CurrentRow,
/// `<N> PRECEDING` or `UNBOUNDED PRECEDING`
Preceding(Option<Box<Expr>>),
/// `<N> FOLLOWING` or `UNBOUNDED FOLLOWING`.
Following(Option<Box<Expr>>),
}
impl fmt::Display for WindowFrameBound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
WindowFrameBound::Preceding(Some(n)) => write!(f, "{n} PRECEDING"),
WindowFrameBound::Following(Some(n)) => write!(f, "{n} FOLLOWING"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Indicates partition operation type for partition management statements.
pub enum AddDropSync {
/// Add partitions.
ADD,
/// Drop partitions.
DROP,
/// Sync partitions.
SYNC,
}
impl fmt::Display for AddDropSync {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AddDropSync::SYNC => f.write_str("SYNC PARTITIONS"),
AddDropSync::DROP => f.write_str("DROP PARTITIONS"),
AddDropSync::ADD => f.write_str("ADD PARTITIONS"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Object kinds supported by `SHOW CREATE` statements.
pub enum ShowCreateObject {
/// An event object for `SHOW CREATE EVENT`.
Event,
/// A function object for `SHOW CREATE FUNCTION`.
Function,
/// A procedure object for `SHOW CREATE PROCEDURE`.
Procedure,
/// A table object for `SHOW CREATE TABLE`.
Table,
/// A trigger object for `SHOW CREATE TRIGGER`.
Trigger,
/// A view object for `SHOW CREATE VIEW`.
View,
}
impl fmt::Display for ShowCreateObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ShowCreateObject::Event => f.write_str("EVENT"),
ShowCreateObject::Function => f.write_str("FUNCTION"),
ShowCreateObject::Procedure => f.write_str("PROCEDURE"),
ShowCreateObject::Table => f.write_str("TABLE"),
ShowCreateObject::Trigger => f.write_str("TRIGGER"),
ShowCreateObject::View => f.write_str("VIEW"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Objects that can be targeted by a `COMMENT` statement.
pub enum CommentObject {
/// A 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::Column => f.write_str("COLUMN"),
CommentObject::Database => f.write_str("DATABASE"),
CommentObject::Domain => f.write_str("DOMAIN"),
CommentObject::Extension => f.write_str("EXTENSION"),
CommentObject::Function => f.write_str("FUNCTION"),
CommentObject::Index => f.write_str("INDEX"),
CommentObject::MaterializedView => f.write_str("MATERIALIZED VIEW"),
CommentObject::Procedure => f.write_str("PROCEDURE"),
CommentObject::Role => f.write_str("ROLE"),
CommentObject::Schema => f.write_str("SCHEMA"),
CommentObject::Sequence => f.write_str("SEQUENCE"),
CommentObject::Table => f.write_str("TABLE"),
CommentObject::Type => f.write_str("TYPE"),
CommentObject::User => f.write_str("USER"),
CommentObject::View => f.write_str("VIEW"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Password specification variants used in user-related statements.
pub enum Password {
/// A concrete password expression.
Password(Expr),
/// Represents a `NULL` password.
NullPassword,
}
/// A `CASE` statement.
///
/// Examples:
/// ```sql
/// CASE
/// WHEN EXISTS(SELECT 1)
/// THEN SELECT 1 FROM T;
/// WHEN EXISTS(SELECT 2)
/// THEN SELECT 1 FROM U;
/// ELSE
/// SELECT 1 FROM V;
/// END CASE;
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#case_search_expression)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/case)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CaseStatement {
/// The `CASE` token that starts the statement.
pub case_token: AttachedToken,
/// Optional expression to match against in `CASE ... WHEN`.
pub match_expr: Option<Expr>,
/// The `WHEN ... THEN` blocks of the `CASE` statement.
pub when_blocks: Vec<ConditionalStatementBlock>,
/// Optional `ELSE` block for the `CASE` statement.
pub else_block: Option<ConditionalStatementBlock>,
/// The last token of the statement (`END` or `CASE`).
pub end_case_token: AttachedToken,
}
impl fmt::Display for CaseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let CaseStatement {
case_token: _,
match_expr,
when_blocks,
else_block,
end_case_token: AttachedToken(end),
} = self;
write!(f, "CASE")?;
if let Some(expr) = match_expr {
write!(f, " {expr}")?;
}
if !when_blocks.is_empty() {
write!(f, " {}", display_separated(when_blocks, " "))?;
}
if let Some(else_block) = else_block {
write!(f, " {else_block}")?;
}
write!(f, " END")?;
if let Token::Word(w) = &end.token {
if w.keyword == Keyword::CASE {
write!(f, " CASE")?;
}
}
Ok(())
}
}
/// An `IF` statement.
///
/// Example (BigQuery or Snowflake):
/// ```sql
/// IF TRUE THEN
/// SELECT 1;
/// SELECT 2;
/// ELSEIF TRUE THEN
/// SELECT 3;
/// ELSE
/// SELECT 4;
/// END IF
/// ```
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#if)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/if)
///
/// Example (MSSQL):
/// ```sql
/// IF 1=1 SELECT 1 ELSE SELECT 2
/// ```
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql?view=sql-server-ver16)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct IfStatement {
/// The initial `IF` block containing the condition and statements.
pub if_block: ConditionalStatementBlock,
/// Additional `ELSEIF` blocks.
pub elseif_blocks: Vec<ConditionalStatementBlock>,
/// Optional `ELSE` block.
pub else_block: Option<ConditionalStatementBlock>,
/// Optional trailing `END` token for the `IF` statement.
pub end_token: Option<AttachedToken>,
}
impl fmt::Display for IfStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let IfStatement {
if_block,
elseif_blocks,
else_block,
end_token,
} = self;
write!(f, "{if_block}")?;
for elseif_block in elseif_blocks {
write!(f, " {elseif_block}")?;
}
if let Some(else_block) = else_block {
write!(f, " {else_block}")?;
}
if let Some(AttachedToken(end_token)) = end_token {
write!(f, " END {end_token}")?;
}
Ok(())
}
}
/// A `WHILE` statement.
///
/// Example:
/// ```sql
/// WHILE @@FETCH_STATUS = 0
/// BEGIN
/// FETCH NEXT FROM c1 INTO @var1, @var2;
/// END
/// ```
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/while-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WhileStatement {
/// Block executed while the condition holds.
pub while_block: ConditionalStatementBlock,
}
impl fmt::Display for WhileStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let WhileStatement { while_block } = self;
write!(f, "{while_block}")?;
Ok(())
}
}
/// A block within a [Statement::Case] or [Statement::If] or [Statement::While]-like statement
///
/// Example 1:
/// ```sql
/// WHEN EXISTS(SELECT 1) THEN SELECT 1;
/// ```
///
/// Example 2:
/// ```sql
/// IF TRUE THEN SELECT 1; SELECT 2;
/// ```
///
/// Example 3:
/// ```sql
/// ELSE SELECT 1; SELECT 2;
/// ```
///
/// Example 4:
/// ```sql
/// WHILE @@FETCH_STATUS = 0
/// BEGIN
/// FETCH NEXT FROM c1 INTO @var1, @var2;
/// END
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ConditionalStatementBlock {
/// Token representing the start of the block (e.g., WHEN/IF/WHILE).
pub start_token: AttachedToken,
/// Optional condition expression for the block.
pub condition: Option<Expr>,
/// Optional token for the `THEN` keyword.
pub then_token: Option<AttachedToken>,
/// The statements contained in this conditional block.
pub conditional_statements: ConditionalStatements,
}
impl ConditionalStatementBlock {
/// Get the statements in this conditional block.
pub fn statements(&self) -> &Vec<Statement> {
self.conditional_statements.statements()
}
}
impl fmt::Display for ConditionalStatementBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ConditionalStatementBlock {
start_token: AttachedToken(start_token),
condition,
then_token,
conditional_statements,
} = self;
write!(f, "{start_token}")?;
if let Some(condition) = condition {
write!(f, " {condition}")?;
}
if then_token.is_some() {
write!(f, " THEN")?;
}
if !conditional_statements.statements().is_empty() {
write!(f, " {conditional_statements}")?;
}
Ok(())
}
}
/// A list of statements in a [ConditionalStatementBlock].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Statements used inside conditional blocks (`IF`, `WHEN`, `WHILE`).
pub enum ConditionalStatements {
/// Simple sequence of statements (no `BEGIN`/`END`).
Sequence {
/// The statements in the sequence.
statements: Vec<Statement>,
},
/// Block enclosed by `BEGIN` and `END`.
BeginEnd(BeginEndStatements),
}
impl ConditionalStatements {
/// Get the statements in this conditional statements block.
pub fn statements(&self) -> &Vec<Statement> {
match self {
ConditionalStatements::Sequence { statements } => statements,
ConditionalStatements::BeginEnd(bes) => &bes.statements,
}
}
}
impl fmt::Display for ConditionalStatements {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConditionalStatements::Sequence { statements } => {
if !statements.is_empty() {
format_statement_list(f, statements)?;
}
Ok(())
}
ConditionalStatements::BeginEnd(bes) => write!(f, "{bes}"),
}
}
}
/// Represents a list of statements enclosed within `BEGIN` and `END` keywords.
/// Example:
/// ```sql
/// BEGIN
/// SELECT 1;
/// SELECT 2;
/// END
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct BeginEndStatements {
/// Token representing the `BEGIN` keyword (may include span info).
pub begin_token: AttachedToken,
/// Statements contained within the block.
pub statements: Vec<Statement>,
/// Token representing the `END` keyword (may include span info).
pub end_token: AttachedToken,
}
impl fmt::Display for BeginEndStatements {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let BeginEndStatements {
begin_token: AttachedToken(begin_token),
statements,
end_token: AttachedToken(end_token),
} = self;
if begin_token.token != Token::EOF {
write!(f, "{begin_token} ")?;
}
if !statements.is_empty() {
format_statement_list(f, statements)?;
}
if end_token.token != Token::EOF {
write!(f, " {end_token}")?;
}
Ok(())
}
}
/// A `RAISE` statement.
///
/// Examples:
/// ```sql
/// RAISE USING MESSAGE = 'error';
///
/// RAISE myerror;
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#raise)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/raise)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RaiseStatement {
/// Optional value provided to the RAISE statement.
pub value: Option<RaiseStatementValue>,
}
impl fmt::Display for RaiseStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let RaiseStatement { value } = self;
write!(f, "RAISE")?;
if let Some(value) = value {
write!(f, " {value}")?;
}
Ok(())
}
}
/// Represents the error value of a [RaiseStatement].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum RaiseStatementValue {
/// `RAISE USING MESSAGE = 'error'`
UsingMessage(Expr),
/// `RAISE myerror`
Expr(Expr),
}
impl fmt::Display for RaiseStatementValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RaiseStatementValue::Expr(expr) => write!(f, "{expr}"),
RaiseStatementValue::UsingMessage(expr) => write!(f, "USING MESSAGE = {expr}"),
}
}
}
/// A MSSQL `THROW` statement.
///
/// ```sql
/// THROW [ error_number, message, state ]
/// ```
///
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ThrowStatement {
/// Error number expression.
pub error_number: Option<Box<Expr>>,
/// Error message expression.
pub message: Option<Box<Expr>>,
/// State expression.
pub state: Option<Box<Expr>>,
}
impl fmt::Display for ThrowStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ThrowStatement {
error_number,
message,
state,
} = self;
write!(f, "THROW")?;
if let (Some(error_number), Some(message), Some(state)) = (error_number, message, state) {
write!(f, " {error_number}, {message}, {state}")?;
}
Ok(())
}
}
/// Represents an expression assignment within a variable `DECLARE` statement.
///
/// Examples:
/// ```sql
/// DECLARE variable_name := 42
/// DECLARE variable_name DEFAULT 42
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DeclareAssignment {
/// Plain expression specified.
Expr(Box<Expr>),
/// Expression assigned via the `DEFAULT` keyword
Default(Box<Expr>),
/// Expression assigned via the `:=` syntax
///
/// Example:
/// ```sql
/// DECLARE variable_name := 42;
/// ```
DuckAssignment(Box<Expr>),
/// Expression via the `FOR` keyword
///
/// Example:
/// ```sql
/// DECLARE c1 CURSOR FOR res
/// ```
For(Box<Expr>),
/// Expression via the `=` syntax.
///
/// Example:
/// ```sql
/// DECLARE @variable AS INT = 100
/// ```
MsSqlAssignment(Box<Expr>),
}
impl fmt::Display for DeclareAssignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DeclareAssignment::Expr(expr) => {
write!(f, "{expr}")
}
DeclareAssignment::Default(expr) => {
write!(f, "DEFAULT {expr}")
}
DeclareAssignment::DuckAssignment(expr) => {
write!(f, ":= {expr}")
}
DeclareAssignment::MsSqlAssignment(expr) => {
write!(f, "= {expr}")
}
DeclareAssignment::For(expr) => {
write!(f, "FOR {expr}")
}
}
}
}
/// Represents the type of a `DECLARE` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DeclareType {
/// Cursor variable type. e.g. [Snowflake] [PostgreSQL] [MsSql]
///
/// [Snowflake]: https://docs.snowflake.com/en/developer-guide/snowflake-scripting/cursors#declaring-a-cursor
/// [PostgreSQL]: https://www.postgresql.org/docs/current/plpgsql-cursors.html
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-cursor-transact-sql
Cursor,
/// Result set variable type. [Snowflake]
///
/// Syntax:
/// ```text
/// <resultset_name> RESULTSET [ { DEFAULT | := } ( <query> ) ] ;
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare#resultset-declaration-syntax
ResultSet,
/// Exception declaration syntax. [Snowflake]
///
/// Syntax:
/// ```text
/// <exception_name> EXCEPTION [ ( <exception_number> , '<exception_message>' ) ] ;
/// ```
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare#exception-declaration-syntax
Exception,
}
impl fmt::Display for DeclareType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DeclareType::Cursor => {
write!(f, "CURSOR")
}
DeclareType::ResultSet => {
write!(f, "RESULTSET")
}
DeclareType::Exception => {
write!(f, "EXCEPTION")
}
}
}
}
/// A `DECLARE` statement.
/// [PostgreSQL] [Snowflake] [BigQuery]
///
/// Examples:
/// ```sql
/// DECLARE variable_name := 42
/// DECLARE liahona CURSOR FOR SELECT * FROM films;
/// ```
///
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-declare.html
/// [Snowflake]: https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#declare
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Declare {
/// The name(s) being declared.
/// Example: `DECLARE a, b, c DEFAULT 42;
pub names: Vec<Ident>,
/// Data-type assigned to the declared variable.
/// Example: `DECLARE x INT64 DEFAULT 42;
pub data_type: Option<DataType>,
/// Expression being assigned to the declared variable.
pub assignment: Option<DeclareAssignment>,
/// Represents the type of the declared variable.
pub declare_type: Option<DeclareType>,
/// Causes the cursor to return data in binary rather than in text format.
pub binary: Option<bool>,
/// None = Not specified
/// Some(true) = INSENSITIVE
/// Some(false) = ASENSITIVE
pub sensitive: Option<bool>,
/// None = Not specified
/// Some(true) = SCROLL
/// Some(false) = NO SCROLL
pub scroll: Option<bool>,
/// None = Not specified
/// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
/// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
pub hold: Option<bool>,
/// `FOR <query>` clause in a CURSOR declaration.
pub for_query: Option<Box<Query>>,
}
impl fmt::Display for Declare {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Declare {
names,
data_type,
assignment,
declare_type,
binary,
sensitive,
scroll,
hold,
for_query,
} = self;
write!(f, "{}", display_comma_separated(names))?;
if let Some(true) = binary {
write!(f, " BINARY")?;
}
if let Some(sensitive) = sensitive {
if *sensitive {
write!(f, " INSENSITIVE")?;
} else {
write!(f, " ASENSITIVE")?;
}
}
if let Some(scroll) = scroll {
if *scroll {
write!(f, " SCROLL")?;
} else {
write!(f, " NO SCROLL")?;
}
}
if let Some(declare_type) = declare_type {
write!(f, " {declare_type}")?;
}
if let Some(hold) = hold {
if *hold {
write!(f, " WITH HOLD")?;
} else {
write!(f, " WITHOUT HOLD")?;
}
}
if let Some(query) = for_query {
write!(f, " FOR {query}")?;
}
if let Some(data_type) = data_type {
write!(f, " {data_type}")?;
}
if let Some(expr) = assignment {
write!(f, " {expr}")?;
}
Ok(())
}
}
/// Sql options of a `CREATE TABLE` statement.
#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options allowed within a `CREATE TABLE` statement.
pub enum CreateTableOptions {
/// No options specified.
#[default]
None,
/// Options specified using the `WITH` keyword, e.g. `WITH (k = v)`.
With(Vec<SqlOption>),
/// Options specified using the `OPTIONS(...)` clause.
Options(Vec<SqlOption>),
/// Plain space-separated options.
Plain(Vec<SqlOption>),
/// Table properties (e.g., TBLPROPERTIES / storage properties).
TableProperties(Vec<SqlOption>),
}
impl fmt::Display for CreateTableOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateTableOptions::With(with_options) => {
write!(f, "WITH ({})", display_comma_separated(with_options))
}
CreateTableOptions::Options(options) => {
write!(f, "OPTIONS({})", display_comma_separated(options))
}
CreateTableOptions::TableProperties(options) => {
write!(f, "TBLPROPERTIES ({})", display_comma_separated(options))
}
CreateTableOptions::Plain(options) => {
write!(f, "{}", display_separated(options, " "))
}
CreateTableOptions::None => Ok(()),
}
}
}
/// A `FROM` clause within a `DELETE` statement.
///
/// Syntax
/// ```sql
/// [FROM] table
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FromTable {
/// An explicit `FROM` keyword was specified.
WithFromKeyword(Vec<TableWithJoins>),
/// BigQuery: `FROM` keyword was omitted.
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#delete_statement>
WithoutKeyword(Vec<TableWithJoins>),
}
impl Display for FromTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FromTable::WithFromKeyword(tables) => {
write!(f, "FROM {}", display_comma_separated(tables))
}
FromTable::WithoutKeyword(tables) => {
write!(f, "{}", display_comma_separated(tables))
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Variants for the `SET` family of statements.
pub enum Set {
/// SQL Standard-style
/// SET a = 1;
/// `SET var = value` (standard SQL-style assignment).
SingleAssignment {
/// Optional scope modifier (`SESSION` / `LOCAL`).
scope: Option<ContextModifier>,
/// Whether this is a Hive-style `HIVEVAR:` assignment.
hivevar: bool,
/// Variable name to assign.
variable: ObjectName,
/// Values assigned to the variable.
values: Vec<Expr>,
},
/// Snowflake-style
/// SET (a, b, ..) = (1, 2, ..);
/// `SET (a, b) = (1, 2)` (tuple assignment syntax).
ParenthesizedAssignments {
/// Variables being assigned in tuple form.
variables: Vec<ObjectName>,
/// Corresponding values for the variables.
values: Vec<Expr>,
},
/// MySQL-style
/// SET a = 1, b = 2, ..;
/// `SET a = 1, b = 2` (MySQL-style comma-separated assignments).
MultipleAssignments {
/// List of `SET` assignments (MySQL-style comma-separated).
assignments: Vec<SetAssignment>,
},
/// Session authorization for Postgres/Redshift
///
/// ```sql
/// SET SESSION AUTHORIZATION { user_name | DEFAULT }
/// ```
///
/// See <https://www.postgresql.org/docs/current/sql-set-session-authorization.html>
/// See <https://docs.aws.amazon.com/redshift/latest/dg/r_SET_SESSION_AUTHORIZATION.html>
SetSessionAuthorization(SetSessionAuthorizationParam),
/// MS-SQL session
///
/// See <https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql>
SetSessionParam(SetSessionParamKind),
/// ```sql
/// SET [ SESSION | LOCAL ] ROLE role_name
/// ```
///
/// Sets session state. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4]
///
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
/// [2]: https://www.postgresql.org/docs/14/sql-set-role.html
/// [3]: https://dev.mysql.com/doc/refman/8.0/en/set-role.html
/// [4]: https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10004.htm
SetRole {
/// Non-ANSI optional identifier to inform if the role is defined inside the current session (`SESSION`) or transaction (`LOCAL`).
context_modifier: Option<ContextModifier>,
/// Role name. If NONE is specified, then the current role name is removed.
role_name: Option<Ident>,
},
/// ```sql
/// SET TIME ZONE <value>
/// ```
///
/// Note: this is a PostgreSQL-specific statements
/// `SET TIME ZONE <value>` is an alias for `SET timezone TO <value>` in PostgreSQL
/// However, we allow it for all dialects.
/// `SET TIME ZONE` statement. `local` indicates the `LOCAL` keyword.
/// `SET TIME ZONE <value>` statement.
SetTimeZone {
/// Whether the `LOCAL` keyword was specified.
local: bool,
/// Time zone expression value.
value: Expr,
},
/// ```sql
/// SET NAMES 'charset_name' [COLLATE 'collation_name']
/// ```
SetNames {
/// Character set name to set.
charset_name: Ident,
/// Optional collation name.
collation_name: Option<String>,
},
/// ```sql
/// SET NAMES DEFAULT
/// ```
///
/// Note: this is a MySQL-specific statement.
SetNamesDefault {},
/// ```sql
/// SET TRANSACTION ...
/// ```
SetTransaction {
/// Transaction modes (e.g., ISOLATION LEVEL, READ ONLY).
modes: Vec<TransactionMode>,
/// Optional snapshot value for transaction snapshot control.
snapshot: Option<ValueWithSpan>,
/// `true` when the `SESSION` keyword was used.
session: bool,
},
}
impl Display for Set {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ParenthesizedAssignments { variables, values } => write!(
f,
"SET ({}) = ({})",
display_comma_separated(variables),
display_comma_separated(values)
),
Self::MultipleAssignments { assignments } => {
write!(f, "SET {}", display_comma_separated(assignments))
}
Self::SetRole {
context_modifier,
role_name,
} => {
let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE"));
write!(
f,
"SET {modifier}ROLE {role_name}",
modifier = context_modifier.map(|m| format!("{m}")).unwrap_or_default()
)
}
Self::SetSessionAuthorization(kind) => write!(f, "SET SESSION AUTHORIZATION {kind}"),
Self::SetSessionParam(kind) => write!(f, "SET {kind}"),
Self::SetTransaction {
modes,
snapshot,
session,
} => {
if *session {
write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?;
} else {
write!(f, "SET TRANSACTION")?;
}
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
if let Some(snapshot_id) = snapshot {
write!(f, " SNAPSHOT {snapshot_id}")?;
}
Ok(())
}
Self::SetTimeZone { local, value } => {
f.write_str("SET ")?;
if *local {
f.write_str("LOCAL ")?;
}
write!(f, "TIME ZONE {value}")
}
Self::SetNames {
charset_name,
collation_name,
} => {
write!(f, "SET NAMES {charset_name}")?;
if let Some(collation) = collation_name {
f.write_str(" COLLATE ")?;
f.write_str(collation)?;
};
Ok(())
}
Self::SetNamesDefault {} => {
f.write_str("SET NAMES DEFAULT")?;
Ok(())
}
Set::SingleAssignment {
scope,
hivevar,
variable,
values,
} => {
write!(
f,
"SET {}{}{} = {}",
scope.map(|s| format!("{s}")).unwrap_or_default(),
if *hivevar { "HIVEVAR:" } else { "" },
variable,
display_comma_separated(values)
)
}
}
}
}
/// A representation of a `WHEN` arm with all the identifiers catched and the statements to execute
/// for the arm.
///
/// Snowflake: <https://docs.snowflake.com/en/sql-reference/snowflake-scripting/exception>
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#beginexceptionend>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ExceptionWhen {
/// Identifiers that trigger this branch (error conditions).
pub idents: Vec<Ident>,
/// Statements to execute when the condition matches.
pub statements: Vec<Statement>,
}
impl Display for ExceptionWhen {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WHEN {idents} THEN",
idents = display_separated(&self.idents, " OR ")
)?;
if !self.statements.is_empty() {
write!(f, " ")?;
format_statement_list(f, &self.statements)?;
}
Ok(())
}
}
/// ANALYZE statement
///
/// Supported syntax varies by dialect:
/// - Hive: `ANALYZE TABLE t [PARTITION (...)] COMPUTE STATISTICS [NOSCAN] [FOR COLUMNS [col1, ...]] [CACHE METADATA]`
/// - PostgreSQL: `ANALYZE [VERBOSE] [t [(col1, ...)]]` See <https://www.postgresql.org/docs/current/sql-analyze.html>
/// - General: `ANALYZE [TABLE] t`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Analyze {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
/// Name of the table to analyze. `None` for bare `ANALYZE`.
pub table_name: Option<ObjectName>,
/// Optional partition expressions to restrict the analysis.
pub partitions: Option<Vec<Expr>>,
/// `true` when analyzing specific columns (Hive `FOR COLUMNS` syntax).
pub for_columns: bool,
/// Columns to analyze.
pub columns: Vec<Ident>,
/// Whether to cache metadata before analyzing.
pub cache_metadata: bool,
/// Whether to skip scanning the table.
pub noscan: bool,
/// Whether to compute statistics during analysis.
pub compute_statistics: bool,
/// Whether the `TABLE` keyword was present.
pub has_table_keyword: bool,
}
impl fmt::Display for Analyze {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ANALYZE")?;
if let Some(ref table_name) = self.table_name {
if self.has_table_keyword {
write!(f, " TABLE")?;
}
write!(f, " {table_name}")?;
}
if !self.for_columns && !self.columns.is_empty() {
write!(f, " ({})", display_comma_separated(&self.columns))?;
}
if let Some(ref parts) = self.partitions {
if !parts.is_empty() {
write!(f, " PARTITION ({})", display_comma_separated(parts))?;
}
}
if self.compute_statistics {
write!(f, " COMPUTE STATISTICS")?;
}
if self.noscan {
write!(f, " NOSCAN")?;
}
if self.cache_metadata {
write!(f, " CACHE METADATA")?;
}
if self.for_columns {
write!(f, " FOR COLUMNS")?;
if !self.columns.is_empty() {
write!(f, " {}", display_comma_separated(&self.columns))?;
}
}
Ok(())
}
}
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "visitor",
derive(Visit, VisitMut),
visit(with = "visit_statement")
)]
pub enum Statement {
/// ```sql
/// ANALYZE
/// ```
/// Analyze (Hive)
Analyze(Analyze),
/// `SET` statements (session, transaction, timezone, etc.).
Set(Set),
/// ```sql
/// TRUNCATE
/// ```
/// Truncate (Hive)
Truncate(Truncate),
/// ```sql
/// MSCK
/// ```
/// Msck (Hive)
Msck(Msck),
/// ```sql
/// SELECT
/// ```
Query(Box<Query>),
/// ```sql
/// INSERT
/// ```
Insert(Insert),
/// ```sql
/// INSTALL
/// ```
Install {
/// Only for DuckDB
extension_name: Ident,
},
/// ```sql
/// LOAD
/// ```
Load {
/// Only for DuckDB
extension_name: Ident,
},
// TODO: Support ROW FORMAT
/// LOAD DATA from a directory or query source.
Directory {
/// Whether to overwrite existing files.
overwrite: bool,
/// Whether the directory is local to the server.
local: bool,
/// Path to the directory or files.
path: String,
/// Optional file format for the data.
file_format: Option<FileFormat>,
/// Source query providing data to load.
source: Box<Query>,
},
/// A `CASE` statement.
Case(CaseStatement),
/// An `IF` statement.
If(IfStatement),
/// A `WHILE` statement.
While(WhileStatement),
/// A `RAISE` statement.
Raise(RaiseStatement),
/// ```sql
/// CALL <function>
/// ```
Call(Function),
/// ```sql
/// COPY [TO | FROM] ...
/// ```
Copy {
/// The source of 'COPY TO', or the target of 'COPY FROM'
source: CopySource,
/// If true, is a 'COPY TO' statement. If false is a 'COPY FROM'
to: bool,
/// The target of 'COPY TO', or the source of 'COPY FROM'
target: CopyTarget,
/// WITH options (from PostgreSQL version 9.0)
options: Vec<CopyOption>,
/// WITH options (before PostgreSQL version 9.0)
legacy_options: Vec<CopyLegacyOption>,
/// VALUES a vector of values to be copied
values: Vec<Option<String>>,
},
/// ```sql
/// COPY INTO <table> | <location>
/// ```
/// See:
/// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
/// <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
///
/// Copy Into syntax available for Snowflake is different than the one implemented in
/// Postgres. Although they share common prefix, it is reasonable to implement them
/// in different enums. This can be refactored later once custom dialects
/// are allowed to have custom Statements.
CopyIntoSnowflake {
/// Kind of COPY INTO operation (table or location).
kind: CopyIntoSnowflakeKind,
/// Target object for the COPY INTO operation.
into: ObjectName,
/// Optional list of target columns.
into_columns: Option<Vec<Ident>>,
/// Optional source object name (staged data).
from_obj: Option<ObjectName>,
/// Optional alias for the source object.
from_obj_alias: Option<Ident>,
/// Stage-specific parameters (e.g., credentials, path).
stage_params: StageParamsObject,
/// Optional list of transformations applied when loading.
from_transformations: Option<Vec<StageLoadSelectItemKind>>,
/// Optional source query instead of a staged object.
from_query: Option<Box<Query>>,
/// Optional list of specific file names to load.
files: Option<Vec<String>>,
/// Optional filename matching pattern.
pattern: Option<String>,
/// File format options.
file_format: KeyValueOptions,
/// Additional copy options.
copy_options: KeyValueOptions,
/// Optional validation mode string.
validation_mode: Option<String>,
/// Optional partition expression for loading.
partition: Option<Box<Expr>>,
},
/// ```sql
/// OPEN cursor_name
/// ```
/// Opens a cursor.
Open(OpenStatement),
/// ```sql
/// CLOSE
/// ```
/// Closes the portal underlying an open cursor.
Close {
/// Cursor name
cursor: CloseCursor,
},
/// ```sql
/// UPDATE
/// ```
Update(Update),
/// ```sql
/// DELETE
/// ```
Delete(Delete),
/// ```sql
/// CREATE VIEW
/// ```
CreateView(CreateView),
/// ```sql
/// CREATE TABLE
/// ```
CreateTable(CreateTable),
/// ```sql
/// CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
/// ```
/// Sqlite specific statement
CreateVirtualTable {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
/// Name of the virtual table module instance.
name: ObjectName,
/// `true` when `IF NOT EXISTS` was specified.
if_not_exists: bool,
/// Module name used by the virtual table.
module_name: Ident,
/// Arguments passed to the module.
module_args: Vec<Ident>,
},
/// ```sql
/// `CREATE INDEX`
/// ```
CreateIndex(CreateIndex),
/// ```sql
/// CREATE ROLE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.html)
CreateRole(CreateRole),
/// ```sql
/// CREATE SECRET
/// ```
/// See [DuckDB](https://duckdb.org/docs/sql/statements/create_secret.html)
CreateSecret {
/// `true` when `OR REPLACE` was specified.
or_replace: bool,
/// Optional `TEMPORARY` flag.
temporary: Option<bool>,
/// `true` when `IF NOT EXISTS` was present.
if_not_exists: bool,
/// Optional secret name.
name: Option<Ident>,
/// Optional storage specifier identifier.
storage_specifier: Option<Ident>,
/// The secret type identifier.
secret_type: Ident,
/// Additional secret options.
options: Vec<SecretOption>,
},
/// A `CREATE SERVER` statement.
CreateServer(CreateServerStatement),
/// ```sql
/// CREATE POLICY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
CreatePolicy(CreatePolicy),
/// ```sql
/// CREATE CONNECTOR
/// ```
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector)
CreateConnector(CreateConnector),
/// ```sql
/// CREATE OPERATOR
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperator.html)
CreateOperator(CreateOperator),
/// ```sql
/// CREATE OPERATOR FAMILY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopfamily.html)
CreateOperatorFamily(CreateOperatorFamily),
/// ```sql
/// CREATE OPERATOR CLASS
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopclass.html)
CreateOperatorClass(CreateOperatorClass),
/// ```sql
/// ALTER TABLE
/// ```
AlterTable(AlterTable),
/// ```sql
/// ALTER SCHEMA
/// ```
/// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#alter_schema_collate_statement)
AlterSchema(AlterSchema),
/// ```sql
/// ALTER INDEX
/// ```
AlterIndex {
/// Name of the index to alter.
name: ObjectName,
/// The operation to perform on the index.
operation: AlterIndexOperation,
},
/// ```sql
/// ALTER VIEW
/// ```
AlterView {
/// View name being altered.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
name: ObjectName,
/// Optional new column list for the view.
columns: Vec<Ident>,
/// Replacement query for the view definition.
query: Box<Query>,
/// Additional WITH options for the view.
with_options: Vec<SqlOption>,
},
/// ```sql
/// ALTER FUNCTION
/// ALTER AGGREGATE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alterfunction.html)
/// and [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteraggregate.html)
AlterFunction(AlterFunction),
/// ```sql
/// ALTER TYPE
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertype.html)
/// ```
AlterType(AlterType),
/// ```sql
/// ALTER OPERATOR
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteroperator.html)
AlterOperator(AlterOperator),
/// ```sql
/// ALTER OPERATOR FAMILY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropfamily.html)
AlterOperatorFamily(AlterOperatorFamily),
/// ```sql
/// ALTER OPERATOR CLASS
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropclass.html)
AlterOperatorClass(AlterOperatorClass),
/// ```sql
/// ALTER ROLE
/// ```
AlterRole {
/// Role name being altered.
name: Ident,
/// Operation to perform on the role.
operation: AlterRoleOperation,
},
/// ```sql
/// ALTER POLICY <NAME> ON <TABLE NAME> [<OPERATION>]
/// ```
/// (Postgresql-specific)
AlterPolicy(AlterPolicy),
/// ```sql
/// ALTER CONNECTOR connector_name SET DCPROPERTIES(property_name=property_value, ...);
/// or
/// ALTER CONNECTOR connector_name SET URL new_url;
/// or
/// ALTER CONNECTOR connector_name SET OWNER [USER|ROLE] user_or_role;
/// ```
/// (Hive-specific)
AlterConnector {
/// Name of the connector to alter.
name: Ident,
/// Optional connector properties to set.
properties: Option<Vec<SqlOption>>,
/// Optional new URL for the connector.
url: Option<String>,
/// Optional new owner specification.
owner: Option<ddl::AlterConnectorOwner>,
},
/// ```sql
/// ALTER SESSION SET sessionParam
/// ALTER SESSION UNSET <param_name> [ , <param_name> , ... ]
/// ```
/// See <https://docs.snowflake.com/en/sql-reference/sql/alter-session>
AlterSession {
/// true is to set for the session parameters, false is to unset
set: bool,
/// The session parameters to set or unset
session_params: KeyValueOptions,
},
/// ```sql
/// ATTACH DATABASE 'path/to/file' AS alias
/// ```
/// (SQLite-specific)
AttachDatabase {
/// The name to bind to the newly attached database
schema_name: Ident,
/// An expression that indicates the path to the database file
database_file_name: Expr,
/// true if the syntax is 'ATTACH DATABASE', false if it's just 'ATTACH'
database: bool,
},
/// (DuckDB-specific)
/// ```sql
/// ATTACH 'sqlite_file.db' AS sqlite_db (READ_ONLY, TYPE SQLITE);
/// ```
/// See <https://duckdb.org/docs/sql/statements/attach.html>
AttachDuckDBDatabase {
/// `true` when `IF NOT EXISTS` was present.
if_not_exists: bool,
/// `true` if the syntax used `ATTACH DATABASE` rather than `ATTACH`.
database: bool,
/// The path identifier to the database file being attached.
database_path: Ident,
/// Optional alias assigned to the attached database.
database_alias: Option<Ident>,
/// Dialect-specific attach options (e.g., `READ_ONLY`).
attach_options: Vec<AttachDuckDBDatabaseOption>,
},
/// (DuckDB-specific)
/// ```sql
/// DETACH db_alias;
/// ```
/// See <https://duckdb.org/docs/sql/statements/attach.html>
DetachDuckDBDatabase {
/// `true` when `IF EXISTS` was present.
if_exists: bool,
/// `true` if the syntax used `DETACH DATABASE` rather than `DETACH`.
database: bool,
/// Alias of the database to detach.
database_alias: Ident,
},
/// ```sql
/// DROP [TABLE, VIEW, ...]
/// ```
Drop {
/// The type of the object to drop: TABLE, VIEW, etc.
object_type: ObjectType,
/// An optional `IF EXISTS` clause. (Non-standard.)
if_exists: bool,
/// One or more objects to drop. (ANSI SQL requires exactly one.)
names: Vec<ObjectName>,
/// Whether `CASCADE` was specified. This will be `false` when
/// `RESTRICT` or no drop behavior at all was specified.
cascade: bool,
/// Whether `RESTRICT` was specified. This will be `false` when
/// `CASCADE` or no drop behavior at all was specified.
restrict: bool,
/// Hive allows you specify whether the table's stored data will be
/// deleted along with the dropped table
purge: bool,
/// MySQL-specific "TEMPORARY" keyword
temporary: bool,
/// MySQL-specific drop index syntax, which requires table specification
/// See <https://dev.mysql.com/doc/refman/8.4/en/drop-index.html>
table: Option<ObjectName>,
},
/// ```sql
/// DROP FUNCTION
/// ```
DropFunction(DropFunction),
/// ```sql
/// DROP DOMAIN
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-dropdomain.html)
///
/// DROP DOMAIN [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
///
DropDomain(DropDomain),
/// ```sql
/// DROP PROCEDURE
/// ```
DropProcedure {
/// `true` when `IF EXISTS` was present.
if_exists: bool,
/// One or more functions/procedures to drop.
proc_desc: Vec<FunctionDesc>,
/// Optional drop behavior (`CASCADE` or `RESTRICT`).
drop_behavior: Option<DropBehavior>,
},
/// ```sql
/// DROP SECRET
/// ```
DropSecret {
/// `true` when `IF EXISTS` was present.
if_exists: bool,
/// Optional `TEMPORARY` marker.
temporary: Option<bool>,
/// Name of the secret to drop.
name: Ident,
/// Optional storage specifier identifier.
storage_specifier: Option<Ident>,
},
///```sql
/// DROP POLICY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droppolicy.html)
DropPolicy(DropPolicy),
/// ```sql
/// DROP CONNECTOR
/// ```
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-DropConnector)
DropConnector {
/// `true` when `IF EXISTS` was present.
if_exists: bool,
/// Name of the connector to drop.
name: Ident,
},
/// ```sql
/// DECLARE
/// ```
/// Declare Cursor Variables
///
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
Declare {
/// Cursor declaration statements collected by `DECLARE`.
stmts: Vec<Declare>,
},
/// ```sql
/// CREATE EXTENSION [ IF NOT EXISTS ] extension_name
/// [ WITH ] [ SCHEMA schema_name ]
/// [ VERSION version ]
/// [ CASCADE ]
/// ```
///
/// Note: this is a PostgreSQL-specific statement,
CreateExtension(CreateExtension),
/// ```sql
/// 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 DATABASES
/// ```
ShowDatabases {
/// `true` when terse output format was requested.
terse: bool,
/// `true` when history information was requested.
history: bool,
/// Additional options for `SHOW DATABASES`.
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW [FULL] PROCESSLIST
/// ```
///
/// Note: this is a MySQL-specific statement.
ShowProcessList {
/// `true` when full process information was requested.
full: bool,
},
/// ```sql
/// SHOW SCHEMAS
/// ```
ShowSchemas {
/// `true` when terse (compact) output was requested.
terse: bool,
/// `true` when history information was requested.
history: bool,
/// Additional options for `SHOW SCHEMAS`.
show_options: ShowStatementOptions,
},
// ```sql
// SHOW {CHARACTER SET | CHARSET}
// ```
// [MySQL]:
// <https://dev.mysql.com/doc/refman/8.4/en/show.html#:~:text=SHOW%20%7BCHARACTER%20SET%20%7C%20CHARSET%7D%20%5Blike_or_where%5D>
/// Show the available character sets (alias `CHARSET`).
ShowCharset(ShowCharset),
/// ```sql
/// SHOW OBJECTS LIKE 'line%' IN mydb.public
/// ```
/// Snowflake-specific statement
/// <https://docs.snowflake.com/en/sql-reference/sql/show-objects>
ShowObjects(ShowObjects),
/// ```sql
/// SHOW TABLES
/// ```
ShowTables {
/// `true` when terse output format was requested (compact listing).
terse: bool,
/// `true` when history rows are requested.
history: bool,
/// `true` when extended information should be shown.
extended: bool,
/// `true` when a full listing was requested.
full: bool,
/// `true` when external tables should be included.
external: bool,
/// Additional options for `SHOW` statements.
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW VIEWS
/// ```
ShowViews {
/// `true` when terse output format was requested.
terse: bool,
/// `true` when materialized views should be included.
materialized: bool,
/// Additional options for `SHOW` statements.
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW COLLATION
/// ```
///
/// Note: this is a MySQL-specific statement.
ShowCollation {
/// Optional filter for which collations to display.
filter: Option<ShowStatementFilter>,
},
/// ```sql
/// `USE ...`
/// ```
Use(Use),
/// ```sql
/// START [ TRANSACTION | WORK ] | START TRANSACTION } ...
/// ```
/// If `begin` is false.
///
/// ```sql
/// `BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
/// ```
/// If `begin` is true
StartTransaction {
/// Transaction modes such as `ISOLATION LEVEL` or `READ WRITE`.
modes: Vec<TransactionMode>,
/// `true` when this was parsed as `BEGIN` instead of `START`.
begin: bool,
/// Optional specific keyword used: `TRANSACTION` or `WORK`.
transaction: Option<BeginTransactionKind>,
/// Optional transaction modifier (e.g., `AND NO CHAIN`).
modifier: Option<TransactionModifier>,
/// List of statements belonging to the `BEGIN` block.
/// Example:
/// ```sql
/// BEGIN
/// SELECT 1;
/// SELECT 2;
/// END;
/// ```
statements: Vec<Statement>,
/// Exception handling with exception clauses.
/// Example:
/// ```sql
/// EXCEPTION
/// WHEN EXCEPTION_1 THEN
/// SELECT 2;
/// WHEN EXCEPTION_2 OR EXCEPTION_3 THEN
/// SELECT 3;
/// WHEN OTHER THEN
/// SELECT 4;
/// ```
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#beginexceptionend>
/// <https://docs.snowflake.com/en/sql-reference/snowflake-scripting/exception>
exception: Option<Vec<ExceptionWhen>>,
/// TRUE if the statement has an `END` keyword.
has_end_keyword: bool,
},
/// ```sql
/// COMMENT ON ...
/// ```
///
/// Note: this is a PostgreSQL-specific statement.
Comment {
/// Type of object being commented (table, column, etc.).
object_type: CommentObject,
/// Name of the object the comment applies to.
object_name: ObjectName,
/// Optional comment text (None to remove comment).
comment: Option<String>,
/// An optional `IF EXISTS` clause. (Non-standard.)
/// See <https://docs.snowflake.com/en/sql-reference/sql/comment>
if_exists: bool,
},
/// ```sql
/// COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]
/// ```
/// If `end` is false
///
/// ```sql
/// END [ TRY | CATCH ]
/// ```
/// If `end` is true
Commit {
/// `true` when `AND [ NO ] CHAIN` was present.
chain: bool,
/// `true` when this `COMMIT` was parsed as an `END` block terminator.
end: bool,
/// Optional transaction modifier for commit semantics.
modifier: Option<TransactionModifier>,
},
/// ```sql
/// ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ] [ TO [ SAVEPOINT ] savepoint_name ]
/// ```
Rollback {
/// `true` when `AND [ NO ] CHAIN` was present.
chain: bool,
/// Optional savepoint name to roll back to.
savepoint: Option<Ident>,
},
/// ```sql
/// CREATE SCHEMA
/// ```
CreateSchema {
/// `<schema name> | AUTHORIZATION <schema authorization identifier> | <schema name> AUTHORIZATION <schema authorization identifier>`
schema_name: SchemaName,
/// `true` when `IF NOT EXISTS` was present.
if_not_exists: bool,
/// Schema properties.
///
/// ```sql
/// CREATE SCHEMA myschema WITH (key1='value1');
/// ```
///
/// [Trino](https://trino.io/docs/current/sql/create-schema.html)
with: Option<Vec<SqlOption>>,
/// Schema options.
///
/// ```sql
/// CREATE SCHEMA myschema OPTIONS(key1='value1');
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_schema_statement)
options: Option<Vec<SqlOption>>,
/// Default collation specification for the schema.
///
/// ```sql
/// CREATE SCHEMA myschema DEFAULT COLLATE 'und:ci';
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_schema_statement)
default_collate_spec: Option<Expr>,
/// Clones a schema
///
/// ```sql
/// CREATE SCHEMA myschema CLONE otherschema
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-clone#databases-schemas)
clone: Option<ObjectName>,
},
/// ```sql
/// CREATE DATABASE
/// ```
/// See:
/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
CreateDatabase {
/// Database name.
db_name: ObjectName,
/// `IF NOT EXISTS` flag.
if_not_exists: bool,
/// Optional location URI.
location: Option<String>,
/// Optional managed location.
managed_location: Option<String>,
/// `OR REPLACE` flag.
or_replace: bool,
/// `TRANSIENT` flag.
transient: bool,
/// Optional clone source.
clone: Option<ObjectName>,
/// Optional data retention time in days.
data_retention_time_in_days: Option<u64>,
/// Optional maximum data extension time in days.
max_data_extension_time_in_days: Option<u64>,
/// Optional external volume identifier.
external_volume: Option<String>,
/// Optional catalog name.
catalog: Option<String>,
/// Whether to replace invalid characters.
replace_invalid_characters: Option<bool>,
/// Default DDL collation string.
default_ddl_collation: Option<String>,
/// Storage serialization policy.
storage_serialization_policy: Option<StorageSerializationPolicy>,
/// Optional comment.
comment: Option<String>,
/// Optional default character set (MySQL).
default_charset: Option<String>,
/// Optional default collation (MySQL).
default_collation: Option<String>,
/// Optional catalog sync identifier.
catalog_sync: Option<String>,
/// Catalog sync namespace mode.
catalog_sync_namespace_mode: Option<CatalogSyncNamespaceMode>,
/// Optional flatten delimiter for namespace sync.
catalog_sync_namespace_flatten_delimiter: Option<String>,
/// Optional tags for the database.
with_tags: Option<Vec<Tag>>,
/// Optional contact entries for the database.
with_contacts: Option<Vec<ContactEntry>>,
},
/// ```sql
/// CREATE FUNCTION
/// ```
///
/// Supported variants:
/// 1. [Hive](https://cwiki.apache.org/confluence/display/hive/languagemanual+ddl#LanguageManualDDL-Create/Drop/ReloadFunction)
/// 2. [PostgreSQL](https://www.postgresql.org/docs/15/sql-createfunction.html)
/// 3. [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement)
/// 4. [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql)
CreateFunction(CreateFunction),
/// CREATE TRIGGER statement. See struct [CreateTrigger] for details.
CreateTrigger(CreateTrigger),
/// DROP TRIGGER statement. See struct [DropTrigger] for details.
DropTrigger(DropTrigger),
/// ```sql
/// CREATE PROCEDURE
/// ```
CreateProcedure {
/// `OR ALTER` flag.
or_alter: bool,
/// Procedure name.
name: ObjectName,
/// Optional procedure parameters.
params: Option<Vec<ProcedureParam>>,
/// Optional language identifier.
language: Option<Ident>,
/// Procedure body statements.
body: ConditionalStatements,
},
/// ```sql
/// CREATE MACRO
/// ```
///
/// Supported variants:
/// 1. [DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
CreateMacro {
/// `OR REPLACE` flag.
or_replace: bool,
/// Whether macro is temporary.
temporary: bool,
/// Macro name.
name: ObjectName,
/// Optional macro arguments.
args: Option<Vec<MacroArg>>,
/// Macro definition body.
definition: MacroDefinition,
},
/// ```sql
/// CREATE STAGE
/// ```
/// See <https://docs.snowflake.com/en/sql-reference/sql/create-stage>
CreateStage {
/// `OR REPLACE` flag for stage.
or_replace: bool,
/// Whether stage is temporary.
temporary: bool,
/// `IF NOT EXISTS` flag.
if_not_exists: bool,
/// Stage name.
name: ObjectName,
/// Stage parameters.
stage_params: StageParamsObject,
/// Directory table parameters.
directory_table_params: KeyValueOptions,
/// File format options.
file_format: KeyValueOptions,
/// Copy options for stage.
copy_options: KeyValueOptions,
/// Optional comment.
comment: Option<String>,
},
/// ```sql
/// ASSERT <condition> [AS <message>]
/// ```
Assert {
/// Assertion condition expression.
condition: Expr,
/// Optional message expression.
message: Option<Expr>,
},
/// ```sql
/// GRANT privileges ON objects TO grantees
/// ```
Grant(Grant),
/// ```sql
/// DENY privileges ON object TO grantees
/// ```
Deny(DenyStatement),
/// ```sql
/// REVOKE privileges ON objects FROM grantees
/// ```
Revoke(Revoke),
/// ```sql
/// DEALLOCATE [ PREPARE ] { name | ALL }
/// ```
///
/// Note: this is a PostgreSQL-specific statement.
Deallocate {
/// Name to deallocate (or `ALL`).
name: Ident,
/// Whether `PREPARE` keyword was present.
prepare: bool,
},
/// ```sql
/// An `EXECUTE` statement
/// ```
///
/// Postgres: <https://www.postgresql.org/docs/current/sql-execute.html>
/// MSSQL: <https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure>
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#execute_immediate>
/// Snowflake: <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
Execute {
/// Optional function/procedure name.
name: Option<ObjectName>,
/// Parameter expressions passed to execute.
parameters: Vec<Expr>,
/// Whether parentheses were present around `parameters`.
has_parentheses: bool,
/// Is this an `EXECUTE IMMEDIATE`.
immediate: bool,
/// Identifiers to capture results into.
into: Vec<Ident>,
/// `USING` expressions with optional aliases.
using: Vec<ExprWithAlias>,
/// Whether the last parameter is the return value of the procedure
/// MSSQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql?view=sql-server-ver17#output>
output: bool,
/// Whether to invoke the procedure with the default parameter values
/// MSSQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql?view=sql-server-ver17#default>
default: bool,
},
/// ```sql
/// PREPARE name [ ( data_type [, ...] ) ] AS statement
/// ```
///
/// Note: this is a PostgreSQL-specific statement.
Prepare {
/// Name of the prepared statement.
name: Ident,
/// Optional data types for parameters.
data_types: Vec<DataType>,
/// Statement being prepared.
statement: Box<Statement>,
},
/// ```sql
/// KILL [CONNECTION | QUERY | MUTATION]
/// ```
///
/// See <https://clickhouse.com/docs/en/sql-reference/statements/kill/>
/// See <https://dev.mysql.com/doc/refman/8.0/en/kill.html>
Kill {
/// Optional kill modifier (CONNECTION, QUERY, MUTATION).
modifier: Option<KillType>,
// processlist_id
/// The id of the process to kill.
id: u64,
},
/// ```sql
/// [EXPLAIN | DESC | DESCRIBE] TABLE
/// ```
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/explain.html>
ExplainTable {
/// `EXPLAIN | DESC | DESCRIBE`
describe_alias: DescribeAlias,
/// Hive style `FORMATTED | EXTENDED`
hive_format: Option<HiveDescribeFormat>,
/// Snowflake and ClickHouse support `DESC|DESCRIBE TABLE <table_name>` syntax
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/desc-table.html)
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/describe-table)
has_table_keyword: bool,
/// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
table_name: ObjectName,
},
/// ```sql
/// [EXPLAIN | DESC | DESCRIBE] <statement>
/// ```
Explain {
/// `EXPLAIN | DESC | DESCRIBE`
describe_alias: DescribeAlias,
/// Carry out the command and show actual run times and other statistics.
analyze: bool,
/// Display additional information regarding the plan.
verbose: bool,
/// `EXPLAIN QUERY PLAN`
/// Display the query plan without running the query.
///
/// [SQLite](https://sqlite.org/lang_explain.html)
query_plan: bool,
/// `EXPLAIN ESTIMATE`
/// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/statements/explain#explain-estimate)
estimate: bool,
/// A SQL query that specifies what to explain
statement: Box<Statement>,
/// Optional output format of explain
format: Option<AnalyzeFormatKind>,
/// Postgres style utility options, `(analyze, verbose true)`
options: Option<Vec<UtilityOption>>,
},
/// ```sql
/// SAVEPOINT
/// ```
/// Define a new savepoint within the current transaction
Savepoint {
/// Name of the savepoint being defined.
name: Ident,
},
/// ```sql
/// RELEASE [ SAVEPOINT ] savepoint_name
/// ```
ReleaseSavepoint {
/// Name of the savepoint to release.
name: Ident,
},
/// A `MERGE` statement.
///
/// ```sql
/// MERGE INTO <target_table> USING <source> ON <join_expr> { matchedClause | notMatchedClause } [ ... ]
/// ```
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver16)
Merge(Merge),
/// ```sql
/// CACHE [ FLAG ] TABLE <table_name> [ OPTIONS('K1' = 'V1', 'K2' = V2) ] [ AS ] [ <query> ]
/// ```
///
/// See [Spark SQL docs] for more details.
///
/// [Spark SQL docs]: https://docs.databricks.com/spark/latest/spark-sql/language-manual/sql-ref-syntax-aux-cache-cache-table.html
Cache {
/// Table flag
table_flag: Option<ObjectName>,
/// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
table_name: ObjectName,
/// `true` if `AS` keyword was present before the query.
has_as: bool,
/// Table confs
options: Vec<SqlOption>,
/// Cache table as a Query
query: Option<Box<Query>>,
},
/// ```sql
/// UNCACHE TABLE [ IF EXISTS ] <table_name>
/// ```
UNCache {
/// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
table_name: ObjectName,
/// `true` when `IF EXISTS` was present.
if_exists: bool,
},
/// ```sql
/// CREATE [ { TEMPORARY | TEMP } ] SEQUENCE [ IF NOT EXISTS ] <sequence_name>
/// ```
/// Define a new sequence:
CreateSequence {
/// Whether the sequence is temporary.
temporary: bool,
/// `IF NOT EXISTS` flag.
if_not_exists: bool,
/// Sequence name.
name: ObjectName,
/// Optional data type for the sequence.
data_type: Option<DataType>,
/// Sequence options (INCREMENT, MINVALUE, etc.).
sequence_options: Vec<SequenceOptions>,
/// Optional `OWNED BY` target.
owned_by: Option<ObjectName>,
},
/// A `CREATE DOMAIN` statement.
CreateDomain(CreateDomain),
/// ```sql
/// CREATE TYPE <name>
/// ```
CreateType {
/// Type name to create.
name: ObjectName,
/// Optional type representation details.
representation: Option<UserDefinedTypeRepresentation>,
},
/// ```sql
/// PRAGMA <schema-name>.<pragma-name> = <pragma-value>
/// ```
Pragma {
/// Pragma name (possibly qualified).
name: ObjectName,
/// Optional pragma value.
value: Option<ValueWithSpan>,
/// Whether the pragma used `=`.
is_eq: bool,
},
/// ```sql
/// LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]
/// ```
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
Lock(Lock),
/// ```sql
/// LOCK TABLES <table_name> [READ [LOCAL] | [LOW_PRIORITY] WRITE]
/// ```
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
LockTables {
/// List of tables to lock with modes.
tables: Vec<LockTable>,
},
/// ```sql
/// UNLOCK TABLES
/// ```
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
UnlockTables,
/// Unloads the result of a query to file
///
/// [Athena](https://docs.aws.amazon.com/athena/latest/ug/unload.html):
/// ```sql
/// UNLOAD(statement) TO <destination> [ WITH options ]
/// ```
///
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_UNLOAD.html):
/// ```sql
/// UNLOAD('statement') TO <destination> [ OPTIONS ]
/// ```
Unload {
/// Optional query AST to unload.
query: Option<Box<Query>>,
/// Optional original query text.
query_text: Option<String>,
/// Destination identifier.
to: Ident,
/// Optional IAM role/auth information.
auth: Option<IamRoleKind>,
/// Additional `WITH` options.
with: Vec<SqlOption>,
/// Legacy copy-style options.
options: Vec<CopyLegacyOption>,
},
/// ClickHouse:
/// ```sql
/// OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
/// ```
/// See ClickHouse <https://clickhouse.com/docs/en/sql-reference/statements/optimize>
///
/// Databricks:
/// ```sql
/// OPTIMIZE table_name [WHERE predicate] [ZORDER BY (col_name1 [, ...])]
/// ```
/// See Databricks <https://docs.databricks.com/en/sql/language-manual/delta-optimize.html>
OptimizeTable {
/// Table name to optimize.
name: ObjectName,
/// Whether the `TABLE` keyword was present (ClickHouse uses `OPTIMIZE TABLE`, Databricks uses `OPTIMIZE`).
has_table_keyword: bool,
/// Optional cluster identifier.
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
on_cluster: Option<Ident>,
/// Optional partition spec.
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
partition: Option<Partition>,
/// Whether `FINAL` was specified.
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
include_final: bool,
/// Optional deduplication settings.
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/optimize)
deduplicate: Option<Deduplicate>,
/// Optional WHERE predicate.
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-optimize.html)
predicate: Option<Expr>,
/// Optional ZORDER BY columns.
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-optimize.html)
zorder: Option<Vec<Expr>>,
},
/// ```sql
/// LISTEN
/// ```
/// listen for a notification channel
///
/// See Postgres <https://www.postgresql.org/docs/current/sql-listen.html>
LISTEN {
/// Notification channel identifier.
channel: Ident,
},
/// ```sql
/// UNLISTEN
/// ```
/// stop listening for a notification
///
/// See Postgres <https://www.postgresql.org/docs/current/sql-unlisten.html>
UNLISTEN {
/// Notification channel identifier.
channel: Ident,
},
/// ```sql
/// NOTIFY channel [ , payload ]
/// ```
/// send a notification event together with an optional "payload" string to channel
///
/// See Postgres <https://www.postgresql.org/docs/current/sql-notify.html>
NOTIFY {
/// Notification channel identifier.
channel: Ident,
/// Optional payload string.
payload: Option<String>,
},
/// ```sql
/// LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename
/// [PARTITION (partcol1=val1, partcol2=val2 ...)]
/// [INPUTFORMAT 'inputformat' SERDE 'serde']
/// ```
/// Loading files into tables
///
/// See Hive <https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362036#LanguageManualDML-Loadingfilesintotables>
LoadData {
/// Whether `LOCAL` is present.
local: bool,
/// Input path for files to load.
inpath: String,
/// Whether `OVERWRITE` was specified.
overwrite: bool,
/// Target table name to load into.
table_name: ObjectName,
/// Optional partition specification.
partitioned: Option<Vec<Expr>>,
/// Optional table format information.
table_format: Option<HiveLoadDataFormat>,
},
/// ```sql
/// Rename TABLE tbl_name TO new_tbl_name[, tbl_name2 TO new_tbl_name2] ...
/// ```
/// Renames one or more tables
///
/// See Mysql <https://dev.mysql.com/doc/refman/9.1/en/rename-table.html>
RenameTable(Vec<RenameTable>),
/// Snowflake `LIST`
/// See: <https://docs.snowflake.com/en/sql-reference/sql/list>
List(FileStagingCommand),
/// Snowflake `REMOVE`
/// See: <https://docs.snowflake.com/en/sql-reference/sql/remove>
Remove(FileStagingCommand),
/// RaiseError (MSSQL)
/// RAISERROR ( { msg_id | msg_str | @local_variable }
/// { , severity , state }
/// [ , argument [ , ...n ] ] )
/// [ WITH option [ , ...n ] ]
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16>
RaisError {
/// Error message expression or identifier.
message: Box<Expr>,
/// Severity expression.
severity: Box<Expr>,
/// State expression.
state: Box<Expr>,
/// Substitution arguments for the message.
arguments: Vec<Expr>,
/// Additional `WITH` options for RAISERROR.
options: Vec<RaisErrorOption>,
},
/// A MSSQL `THROW` statement.
Throw(ThrowStatement),
/// ```sql
/// PRINT msg_str | @local_variable | string_expr
/// ```
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/print-transact-sql>
Print(PrintStatement),
/// MSSQL `WAITFOR` statement.
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
WaitFor(WaitForStatement),
/// ```sql
/// RETURN [ expression ]
/// ```
///
/// See [ReturnStatement]
Return(ReturnStatement),
/// Export data statement
///
/// Example:
/// ```sql
/// EXPORT DATA OPTIONS(uri='gs://bucket/folder/*', format='PARQUET', overwrite=true) AS
/// SELECT field1, field2 FROM mydataset.table1 ORDER BY field1 LIMIT 10
/// ```
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/export-statements)
ExportData(ExportData),
/// ```sql
/// CREATE [OR REPLACE] USER <user> [IF NOT EXISTS]
/// ```
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
CreateUser(CreateUser),
/// ```sql
/// ALTER USER \[ IF EXISTS \] \[ <name> \]
/// ```
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
AlterUser(AlterUser),
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
///
/// ```sql
/// VACUUM tbl
/// ```
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
Vacuum(VacuumStatement),
/// Restore the value of a run-time parameter to the default value.
///
/// ```sql
/// RESET configuration_parameter;
/// RESET ALL;
/// ```
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-reset.html)
Reset(ResetStatement),
}
impl From<Analyze> for Statement {
fn from(analyze: Analyze) -> Self {
Statement::Analyze(analyze)
}
}
impl From<ddl::Truncate> for Statement {
fn from(truncate: ddl::Truncate) -> Self {
Statement::Truncate(truncate)
}
}
impl From<Lock> for Statement {
fn from(lock: Lock) -> Self {
Statement::Lock(lock)
}
}
impl From<ddl::Msck> for Statement {
fn from(msck: ddl::Msck) -> Self {
Statement::Msck(msck)
}
}
/// ```sql
/// {COPY | REVOKE} CURRENT GRANTS
/// ```
///
/// - [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#optional-parameters)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CurrentGrantsKind {
/// `COPY CURRENT GRANTS` (copy current grants to target).
CopyCurrentGrants,
/// `REVOKE CURRENT GRANTS` (revoke current grants from target).
RevokeCurrentGrants,
}
impl fmt::Display for CurrentGrantsKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CurrentGrantsKind::CopyCurrentGrants => write!(f, "COPY CURRENT GRANTS"),
CurrentGrantsKind::RevokeCurrentGrants => write!(f, "REVOKE CURRENT GRANTS"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `RAISERROR` options
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql?view=sql-server-ver16#options>
pub enum RaisErrorOption {
/// Log the error.
Log,
/// Do not wait for completion.
NoWait,
/// Set the error state.
SetError,
}
impl fmt::Display for RaisErrorOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RaisErrorOption::Log => write!(f, "LOG"),
RaisErrorOption::NoWait => write!(f, "NOWAIT"),
RaisErrorOption::SetError => write!(f, "SETERROR"),
}
}
}
impl fmt::Display for Statement {
/// Formats a SQL statement with support for pretty printing.
///
/// When using the alternate flag (`{:#}`), the statement will be formatted with proper
/// indentation and line breaks. For example:
///
/// ```
/// # use sqlparser::dialect::GenericDialect;
/// # use sqlparser::parser::Parser;
/// let sql = "SELECT a, b FROM table_1";
/// let ast = Parser::parse_sql(&GenericDialect, sql).unwrap();
///
/// // Regular formatting
/// assert_eq!(format!("{}", ast[0]), "SELECT a, b FROM table_1");
///
/// // Pretty printing
/// assert_eq!(format!("{:#}", ast[0]),
/// r#"SELECT
/// a,
/// b
/// FROM
/// table_1"#);
/// ```
// Clippy thinks this function is too complicated, but it is painful to
// split up without extracting structs for each `Statement` variant.
#[allow(clippy::cognitive_complexity)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Statement::Flush {
object_type,
location,
channel,
read_lock,
export,
tables,
} => {
write!(f, "FLUSH")?;
if let Some(location) = location {
f.write_str(" ")?;
location.fmt(f)?;
}
write!(f, " {object_type}")?;
if let Some(channel) = channel {
write!(f, " FOR CHANNEL {channel}")?;
}
write!(
f,
"{tables}{read}{export}",
tables = if !tables.is_empty() {
format!(" {}", display_comma_separated(tables))
} else {
String::new()
},
export = if *export { " FOR EXPORT" } else { "" },
read = if *read_lock { " WITH READ LOCK" } else { "" }
)
}
Statement::Kill { modifier, id } => {
write!(f, "KILL ")?;
if let Some(m) = modifier {
write!(f, "{m} ")?;
}
write!(f, "{id}")
}
Statement::ExplainTable {
describe_alias,
hive_format,
has_table_keyword,
table_name,
} => {
write!(f, "{describe_alias} ")?;
if let Some(format) = hive_format {
write!(f, "{format} ")?;
}
if *has_table_keyword {
write!(f, "TABLE ")?;
}
write!(f, "{table_name}")
}
Statement::Explain {
describe_alias,
verbose,
analyze,
query_plan,
estimate,
statement,
format,
options,
} => {
write!(f, "{describe_alias} ")?;
if *query_plan {
write!(f, "QUERY PLAN ")?;
}
if *analyze {
write!(f, "ANALYZE ")?;
}
if *estimate {
write!(f, "ESTIMATE ")?;
}
if *verbose {
write!(f, "VERBOSE ")?;
}
if let Some(format) = format {
write!(f, "{format} ")?;
}
if let Some(options) = options {
write!(f, "({}) ", display_comma_separated(options))?;
}
write!(f, "{statement}")
}
Statement::Query(s) => s.fmt(f),
Statement::Declare { stmts } => {
write!(f, "DECLARE ")?;
write!(f, "{}", display_separated(stmts, "; "))
}
Statement::Fetch {
name,
direction,
position,
into,
} => {
write!(f, "FETCH {direction} {position} {name}")?;
if let Some(into) = into {
write!(f, " INTO {into}")?;
}
Ok(())
}
Statement::Directory {
overwrite,
local,
path,
file_format,
source,
} => {
write!(
f,
"INSERT{overwrite}{local} DIRECTORY '{path}'",
overwrite = if *overwrite { " OVERWRITE" } else { "" },
local = if *local { " LOCAL" } else { "" },
path = path
)?;
if let Some(ref ff) = file_format {
write!(f, " STORED AS {ff}")?
}
write!(f, " {source}")
}
Statement::Msck(msck) => msck.fmt(f),
Statement::Truncate(truncate) => truncate.fmt(f),
Statement::Case(stmt) => {
write!(f, "{stmt}")
}
Statement::If(stmt) => {
write!(f, "{stmt}")
}
Statement::While(stmt) => {
write!(f, "{stmt}")
}
Statement::Raise(stmt) => {
write!(f, "{stmt}")
}
Statement::AttachDatabase {
schema_name,
database_file_name,
database,
} => {
let keyword = if *database { "DATABASE " } else { "" };
write!(f, "ATTACH {keyword}{database_file_name} AS {schema_name}")
}
Statement::AttachDuckDBDatabase {
if_not_exists,
database,
database_path,
database_alias,
attach_options,
} => {
write!(
f,
"ATTACH{database}{if_not_exists} {database_path}",
database = if *database { " DATABASE" } else { "" },
if_not_exists = if *if_not_exists { " IF NOT EXISTS" } else { "" },
)?;
if let Some(alias) = database_alias {
write!(f, " AS {alias}")?;
}
if !attach_options.is_empty() {
write!(f, " ({})", display_comma_separated(attach_options))?;
}
Ok(())
}
Statement::DetachDuckDBDatabase {
if_exists,
database,
database_alias,
} => {
write!(
f,
"DETACH{database}{if_exists} {database_alias}",
database = if *database { " DATABASE" } else { "" },
if_exists = if *if_exists { " IF EXISTS" } else { "" },
)?;
Ok(())
}
Statement::Analyze(analyze) => analyze.fmt(f),
Statement::Insert(insert) => insert.fmt(f),
Statement::Install {
extension_name: name,
} => write!(f, "INSTALL {name}"),
Statement::Load {
extension_name: name,
} => write!(f, "LOAD {name}"),
Statement::Call(function) => write!(f, "CALL {function}"),
Statement::Copy {
source,
to,
target,
options,
legacy_options,
values,
} => {
write!(f, "COPY")?;
match source {
CopySource::Query(query) => write!(f, " ({query})")?,
CopySource::Table {
table_name,
columns,
} => {
write!(f, " {table_name}")?;
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
}
}
write!(f, " {} {}", if *to { "TO" } else { "FROM" }, target)?;
if !options.is_empty() {
write!(f, " ({})", display_comma_separated(options))?;
}
if !legacy_options.is_empty() {
write!(f, " {}", display_separated(legacy_options, " "))?;
}
if !values.is_empty() {
writeln!(f, ";")?;
let mut delim = "";
for v in values {
write!(f, "{delim}")?;
delim = "\t";
if let Some(v) = v {
write!(f, "{v}")?;
} else {
write!(f, "\\N")?;
}
}
write!(f, "\n\\.")?;
}
Ok(())
}
Statement::Update(update) => update.fmt(f),
Statement::Delete(delete) => delete.fmt(f),
Statement::Open(open) => open.fmt(f),
Statement::Close { cursor } => {
write!(f, "CLOSE {cursor}")?;
Ok(())
}
Statement::CreateDatabase {
db_name,
if_not_exists,
location,
managed_location,
or_replace,
transient,
clone,
data_retention_time_in_days,
max_data_extension_time_in_days,
external_volume,
catalog,
replace_invalid_characters,
default_ddl_collation,
storage_serialization_policy,
comment,
default_charset,
default_collation,
catalog_sync,
catalog_sync_namespace_mode,
catalog_sync_namespace_flatten_delimiter,
with_tags,
with_contacts,
} => {
write!(
f,
"CREATE {or_replace}{transient}DATABASE {if_not_exists}{name}",
or_replace = if *or_replace { "OR REPLACE " } else { "" },
transient = if *transient { "TRANSIENT " } else { "" },
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
name = db_name,
)?;
if let Some(l) = location {
write!(f, " LOCATION '{l}'")?;
}
if let Some(ml) = managed_location {
write!(f, " MANAGEDLOCATION '{ml}'")?;
}
if let Some(clone) = clone {
write!(f, " CLONE {clone}")?;
}
if let Some(value) = data_retention_time_in_days {
write!(f, " DATA_RETENTION_TIME_IN_DAYS = {value}")?;
}
if let Some(value) = max_data_extension_time_in_days {
write!(f, " MAX_DATA_EXTENSION_TIME_IN_DAYS = {value}")?;
}
if let Some(vol) = external_volume {
write!(f, " EXTERNAL_VOLUME = '{vol}'")?;
}
if let Some(cat) = catalog {
write!(f, " CATALOG = '{cat}'")?;
}
if let Some(true) = replace_invalid_characters {
write!(f, " REPLACE_INVALID_CHARACTERS = TRUE")?;
} else if let Some(false) = replace_invalid_characters {
write!(f, " REPLACE_INVALID_CHARACTERS = FALSE")?;
}
if let Some(collation) = default_ddl_collation {
write!(f, " DEFAULT_DDL_COLLATION = '{collation}'")?;
}
if let Some(policy) = storage_serialization_policy {
write!(f, " STORAGE_SERIALIZATION_POLICY = {policy}")?;
}
if let Some(comment) = comment {
write!(f, " COMMENT = '{comment}'")?;
}
if let Some(charset) = default_charset {
write!(f, " DEFAULT CHARACTER SET {charset}")?;
}
if let Some(collation) = default_collation {
write!(f, " DEFAULT COLLATE {collation}")?;
}
if let Some(sync) = catalog_sync {
write!(f, " CATALOG_SYNC = '{sync}'")?;
}
if let Some(mode) = catalog_sync_namespace_mode {
write!(f, " CATALOG_SYNC_NAMESPACE_MODE = {mode}")?;
}
if let Some(delim) = catalog_sync_namespace_flatten_delimiter {
write!(f, " CATALOG_SYNC_NAMESPACE_FLATTEN_DELIMITER = '{delim}'")?;
}
if let Some(tags) = with_tags {
write!(f, " WITH TAG ({})", display_comma_separated(tags))?;
}
if let Some(contacts) = with_contacts {
write!(f, " WITH CONTACT ({})", display_comma_separated(contacts))?;
}
Ok(())
}
Statement::CreateFunction(create_function) => create_function.fmt(f),
Statement::CreateDomain(create_domain) => create_domain.fmt(f),
Statement::CreateTrigger(create_trigger) => create_trigger.fmt(f),
Statement::DropTrigger(drop_trigger) => drop_trigger.fmt(f),
Statement::CreateProcedure {
name,
or_alter,
params,
language,
body,
} => {
write!(
f,
"CREATE {or_alter}PROCEDURE {name}",
or_alter = if *or_alter { "OR ALTER " } else { "" },
name = name
)?;
if let Some(p) = params {
if !p.is_empty() {
write!(f, " ({})", display_comma_separated(p))?;
}
}
if let Some(language) = language {
write!(f, " LANGUAGE {language}")?;
}
write!(f, " AS {body}")
}
Statement::CreateMacro {
or_replace,
temporary,
name,
args,
definition,
} => {
write!(
f,
"CREATE {or_replace}{temp}MACRO {name}",
temp = if *temporary { "TEMPORARY " } else { "" },
or_replace = if *or_replace { "OR REPLACE " } else { "" },
)?;
if let Some(args) = args {
write!(f, "({})", display_comma_separated(args))?;
}
match definition {
MacroDefinition::Expr(expr) => write!(f, " AS {expr}")?,
MacroDefinition::Table(query) => write!(f, " AS TABLE {query}")?,
}
Ok(())
}
Statement::CreateView(create_view) => create_view.fmt(f),
Statement::CreateTable(create_table) => create_table.fmt(f),
Statement::LoadData {
local,
inpath,
overwrite,
table_name,
partitioned,
table_format,
} => {
write!(
f,
"LOAD DATA {local}INPATH '{inpath}' {overwrite}INTO TABLE {table_name}",
local = if *local { "LOCAL " } else { "" },
inpath = inpath,
overwrite = if *overwrite { "OVERWRITE " } else { "" },
table_name = table_name,
)?;
if let Some(ref parts) = &partitioned {
if !parts.is_empty() {
write!(f, " PARTITION ({})", display_comma_separated(parts))?;
}
}
if let Some(HiveLoadDataFormat {
serde,
input_format,
}) = &table_format
{
write!(f, " INPUTFORMAT {input_format} SERDE {serde}")?;
}
Ok(())
}
Statement::CreateVirtualTable {
name,
if_not_exists,
module_name,
module_args,
} => {
write!(
f,
"CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
name = name,
module_name = module_name
)?;
if !module_args.is_empty() {
write!(f, " ({})", display_comma_separated(module_args))?;
}
Ok(())
}
Statement::CreateIndex(create_index) => create_index.fmt(f),
Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
Statement::DropOperatorFamily(drop_operator_family) => {
write!(f, "{drop_operator_family}")
}
Statement::DropOperatorClass(drop_operator_class) => {
write!(f, "{drop_operator_class}")
}
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
Statement::CreateSecret {
or_replace,
temporary,
if_not_exists,
name,
storage_specifier,
secret_type,
options,
} => {
write!(
f,
"CREATE {or_replace}",
or_replace = if *or_replace { "OR REPLACE " } else { "" },
)?;
if let Some(t) = temporary {
write!(f, "{}", if *t { "TEMPORARY " } else { "PERSISTENT " })?;
}
write!(
f,
"SECRET {if_not_exists}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
)?;
if let Some(n) = name {
write!(f, "{n} ")?;
};
if let Some(s) = storage_specifier {
write!(f, "IN {s} ")?;
}
write!(f, "( TYPE {secret_type}",)?;
if !options.is_empty() {
write!(f, ", {o}", o = display_comma_separated(options))?;
}
write!(f, " )")?;
Ok(())
}
Statement::CreateServer(stmt) => {
write!(f, "{stmt}")
}
Statement::CreatePolicy(policy) => write!(f, "{policy}"),
Statement::CreateConnector(create_connector) => create_connector.fmt(f),
Statement::CreateOperator(create_operator) => create_operator.fmt(f),
Statement::CreateOperatorFamily(create_operator_family) => {
create_operator_family.fmt(f)
}
Statement::CreateOperatorClass(create_operator_class) => create_operator_class.fmt(f),
Statement::AlterTable(alter_table) => write!(f, "{alter_table}"),
Statement::AlterIndex { name, operation } => {
write!(f, "ALTER INDEX {name} {operation}")
}
Statement::AlterView {
name,
columns,
query,
with_options,
} => {
write!(f, "ALTER VIEW {name}")?;
if !with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_options))?;
}
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
write!(f, " AS {query}")
}
Statement::AlterFunction(alter_function) => write!(f, "{alter_function}"),
Statement::AlterType(AlterType { name, operation }) => {
write!(f, "ALTER TYPE {name} {operation}")
}
Statement::AlterOperator(alter_operator) => write!(f, "{alter_operator}"),
Statement::AlterOperatorFamily(alter_operator_family) => {
write!(f, "{alter_operator_family}")
}
Statement::AlterOperatorClass(alter_operator_class) => {
write!(f, "{alter_operator_class}")
}
Statement::AlterRole { name, operation } => {
write!(f, "ALTER ROLE {name} {operation}")
}
Statement::AlterPolicy(alter_policy) => write!(f, "{alter_policy}"),
Statement::AlterConnector {
name,
properties,
url,
owner,
} => {
write!(f, "ALTER CONNECTOR {name}")?;
if let Some(properties) = properties {
write!(
f,
" SET DCPROPERTIES({})",
display_comma_separated(properties)
)?;
}
if let Some(url) = url {
write!(f, " SET URL '{url}'")?;
}
if let Some(owner) = owner {
write!(f, " SET OWNER {owner}")?;
}
Ok(())
}
Statement::AlterSession {
set,
session_params,
} => {
write!(
f,
"ALTER SESSION {set}",
set = if *set { "SET" } else { "UNSET" }
)?;
if !session_params.options.is_empty() {
if *set {
write!(f, " {session_params}")?;
} else {
let options = session_params
.options
.iter()
.map(|p| p.option_name.clone())
.collect::<Vec<_>>();
write!(f, " {}", display_separated(&options, ", "))?;
}
}
Ok(())
}
Statement::Drop {
object_type,
if_exists,
names,
cascade,
restrict,
purge,
temporary,
table,
} => {
write!(
f,
"DROP {}{}{} {}{}{}{}",
if *temporary { "TEMPORARY " } else { "" },
object_type,
if *if_exists { " IF EXISTS" } else { "" },
display_comma_separated(names),
if *cascade { " CASCADE" } else { "" },
if *restrict { " RESTRICT" } else { "" },
if *purge { " PURGE" } else { "" },
)?;
if let Some(table_name) = table.as_ref() {
write!(f, " ON {table_name}")?;
};
Ok(())
}
Statement::DropFunction(drop_function) => write!(f, "{drop_function}"),
Statement::DropDomain(DropDomain {
if_exists,
name,
drop_behavior,
}) => {
write!(
f,
"DROP DOMAIN{} {name}",
if *if_exists { " IF EXISTS" } else { "" },
)?;
if let Some(op) = drop_behavior {
write!(f, " {op}")?;
}
Ok(())
}
Statement::DropProcedure {
if_exists,
proc_desc,
drop_behavior,
} => {
write!(
f,
"DROP PROCEDURE{} {}",
if *if_exists { " IF EXISTS" } else { "" },
display_comma_separated(proc_desc),
)?;
if let Some(op) = drop_behavior {
write!(f, " {op}")?;
}
Ok(())
}
Statement::DropSecret {
if_exists,
temporary,
name,
storage_specifier,
} => {
write!(f, "DROP ")?;
if let Some(t) = temporary {
write!(f, "{}", if *t { "TEMPORARY " } else { "PERSISTENT " })?;
}
write!(
f,
"SECRET {if_exists}{name}",
if_exists = if *if_exists { "IF EXISTS " } else { "" },
)?;
if let Some(s) = storage_specifier {
write!(f, " FROM {s}")?;
}
Ok(())
}
Statement::DropPolicy(policy) => write!(f, "{policy}"),
Statement::DropConnector { if_exists, name } => {
write!(
f,
"DROP CONNECTOR {if_exists}{name}",
if_exists = if *if_exists { "IF EXISTS " } else { "" }
)?;
Ok(())
}
Statement::Discard { object_type } => {
write!(f, "DISCARD {object_type}")?;
Ok(())
}
Self::Set(set) => write!(f, "{set}"),
Statement::ShowVariable { variable } => {
write!(f, "SHOW")?;
if !variable.is_empty() {
write!(f, " {}", display_separated(variable, " "))?;
}
Ok(())
}
Statement::ShowStatus {
filter,
global,
session,
} => {
write!(f, "SHOW")?;
if *global {
write!(f, " GLOBAL")?;
}
if *session {
write!(f, " SESSION")?;
}
write!(f, " STATUS")?;
if filter.is_some() {
write!(f, " {}", filter.as_ref().unwrap())?;
}
Ok(())
}
Statement::ShowVariables {
filter,
global,
session,
} => {
write!(f, "SHOW")?;
if *global {
write!(f, " GLOBAL")?;
}
if *session {
write!(f, " SESSION")?;
}
write!(f, " VARIABLES")?;
if filter.is_some() {
write!(f, " {}", filter.as_ref().unwrap())?;
}
Ok(())
}
Statement::ShowCreate { obj_type, obj_name } => {
write!(f, "SHOW CREATE {obj_type} {obj_name}",)?;
Ok(())
}
Statement::ShowColumns {
extended,
full,
show_options,
} => {
write!(
f,
"SHOW {extended}{full}COLUMNS{show_options}",
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
)?;
Ok(())
}
Statement::ShowDatabases {
terse,
history,
show_options,
} => {
write!(
f,
"SHOW {terse}DATABASES{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
history = if *history { " HISTORY" } else { "" },
)?;
Ok(())
}
Statement::ShowProcessList { full } => {
write!(
f,
"SHOW {full}PROCESSLIST",
full = if *full { "FULL " } else { "" },
)?;
Ok(())
}
Statement::ShowSchemas {
terse,
history,
show_options,
} => {
write!(
f,
"SHOW {terse}SCHEMAS{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
history = if *history { " HISTORY" } else { "" },
)?;
Ok(())
}
Statement::ShowObjects(ShowObjects {
terse,
show_options,
}) => {
write!(
f,
"SHOW {terse}OBJECTS{show_options}",
terse = if *terse { "TERSE " } else { "" },
)?;
Ok(())
}
Statement::ShowTables {
terse,
history,
extended,
full,
external,
show_options,
} => {
write!(
f,
"SHOW {terse}{extended}{full}{external}TABLES{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
external = if *external { "EXTERNAL " } else { "" },
history = if *history { " HISTORY" } else { "" },
)?;
Ok(())
}
Statement::ShowViews {
terse,
materialized,
show_options,
} => {
write!(
f,
"SHOW {terse}{materialized}VIEWS{show_options}",
terse = if *terse { "TERSE " } else { "" },
materialized = if *materialized { "MATERIALIZED " } else { "" }
)?;
Ok(())
}
Statement::ShowFunctions { filter } => {
write!(f, "SHOW FUNCTIONS")?;
if let Some(filter) = filter {
write!(f, " {filter}")?;
}
Ok(())
}
Statement::Use(use_expr) => use_expr.fmt(f),
Statement::ShowCollation { filter } => {
write!(f, "SHOW COLLATION")?;
if let Some(filter) = filter {
write!(f, " {filter}")?;
}
Ok(())
}
Statement::ShowCharset(show_stm) => show_stm.fmt(f),
Statement::StartTransaction {
modes,
begin: syntax_begin,
transaction,
modifier,
statements,
exception,
has_end_keyword,
} => {
if *syntax_begin {
if let Some(modifier) = *modifier {
write!(f, "BEGIN {modifier}")?;
} else {
write!(f, "BEGIN")?;
}
} else {
write!(f, "START")?;
}
if let Some(transaction) = transaction {
write!(f, " {transaction}")?;
}
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
if !statements.is_empty() {
write!(f, " ")?;
format_statement_list(f, statements)?;
}
if let Some(exception_when) = exception {
write!(f, " EXCEPTION")?;
for when in exception_when {
write!(f, " {when}")?;
}
}
if *has_end_keyword {
write!(f, " END")?;
}
Ok(())
}
Statement::Commit {
chain,
end: end_syntax,
modifier,
} => {
if *end_syntax {
write!(f, "END")?;
if let Some(modifier) = *modifier {
write!(f, " {modifier}")?;
}
if *chain {
write!(f, " AND CHAIN")?;
}
} else {
write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" })?;
}
Ok(())
}
Statement::Rollback { chain, savepoint } => {
write!(f, "ROLLBACK")?;
if *chain {
write!(f, " AND CHAIN")?;
}
if let Some(savepoint) = savepoint {
write!(f, " TO SAVEPOINT {savepoint}")?;
}
Ok(())
}
Statement::CreateSchema {
schema_name,
if_not_exists,
with,
options,
default_collate_spec,
clone,
} => {
write!(
f,
"CREATE SCHEMA {if_not_exists}{name}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
name = schema_name
)?;
if let Some(collate) = default_collate_spec {
write!(f, " DEFAULT COLLATE {collate}")?;
}
if let Some(with) = with {
write!(f, " WITH ({})", display_comma_separated(with))?;
}
if let Some(options) = options {
write!(f, " OPTIONS({})", display_comma_separated(options))?;
}
if let Some(clone) = clone {
write!(f, " CLONE {clone}")?;
}
Ok(())
}
Statement::Assert { condition, message } => {
write!(f, "ASSERT {condition}")?;
if let Some(m) = message {
write!(f, " AS {m}")?;
}
Ok(())
}
Statement::Grant(grant) => write!(f, "{grant}"),
Statement::Deny(s) => write!(f, "{s}"),
Statement::Revoke(revoke) => write!(f, "{revoke}"),
Statement::Deallocate { name, prepare } => write!(
f,
"DEALLOCATE {prepare}{name}",
prepare = if *prepare { "PREPARE " } else { "" },
name = name,
),
Statement::Execute {
name,
parameters,
has_parentheses,
immediate,
into,
using,
output,
default,
} => {
let (open, close) = if *has_parentheses {
// Space before `(` only when there is no name directly preceding it.
(if name.is_some() { "(" } else { " (" }, ")")
} else {
(if parameters.is_empty() { "" } else { " " }, "")
};
write!(f, "EXECUTE")?;
if *immediate {
write!(f, " IMMEDIATE")?;
}
if let Some(name) = name {
write!(f, " {name}")?;
}
write!(f, "{open}{}{close}", display_comma_separated(parameters),)?;
if !into.is_empty() {
write!(f, " INTO {}", display_comma_separated(into))?;
}
if !using.is_empty() {
write!(f, " USING {}", display_comma_separated(using))?;
};
if *output {
write!(f, " OUTPUT")?;
}
if *default {
write!(f, " DEFAULT")?;
}
Ok(())
}
Statement::Prepare {
name,
data_types,
statement,
} => {
write!(f, "PREPARE {name} ")?;
if !data_types.is_empty() {
write!(f, "({}) ", display_comma_separated(data_types))?;
}
write!(f, "AS {statement}")
}
Statement::Comment {
object_type,
object_name,
comment,
if_exists,
} => {
write!(f, "COMMENT ")?;
if *if_exists {
write!(f, "IF EXISTS ")?
};
write!(f, "ON {object_type} {object_name} IS ")?;
if let Some(c) = comment {
write!(f, "'{c}'")
} else {
write!(f, "NULL")
}
}
Statement::Savepoint { name } => {
write!(f, "SAVEPOINT ")?;
write!(f, "{name}")
}
Statement::ReleaseSavepoint { name } => {
write!(f, "RELEASE SAVEPOINT {name}")
}
Statement::Merge(merge) => merge.fmt(f),
Statement::Cache {
table_name,
table_flag,
has_as,
options,
query,
} => {
if let Some(table_flag) = table_flag {
write!(f, "CACHE {table_flag} TABLE {table_name}")?;
} else {
write!(f, "CACHE TABLE {table_name}")?;
}
if !options.is_empty() {
write!(f, " OPTIONS({})", display_comma_separated(options))?;
}
match (*has_as, query) {
(true, Some(query)) => write!(f, " AS {query}"),
(true, None) => f.write_str(" AS"),
(false, Some(query)) => write!(f, " {query}"),
(false, None) => Ok(()),
}
}
Statement::UNCache {
table_name,
if_exists,
} => {
if *if_exists {
write!(f, "UNCACHE TABLE IF EXISTS {table_name}")
} else {
write!(f, "UNCACHE TABLE {table_name}")
}
}
Statement::CreateSequence {
temporary,
if_not_exists,
name,
data_type,
sequence_options,
owned_by,
} => {
let as_type: String = if let Some(dt) = data_type.as_ref() {
//Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi
// " AS ".to_owned() + &dt.to_string()
[" AS ", &dt.to_string()].concat()
} else {
"".to_string()
};
write!(
f,
"CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
temporary = if *temporary { "TEMPORARY " } else { "" },
name = name,
as_type = as_type
)?;
for sequence_option in sequence_options {
write!(f, "{sequence_option}")?;
}
if let Some(ob) = owned_by.as_ref() {
write!(f, " OWNED BY {ob}")?;
}
write!(f, "")
}
Statement::CreateStage {
or_replace,
temporary,
if_not_exists,
name,
stage_params,
directory_table_params,
file_format,
copy_options,
comment,
..
} => {
write!(
f,
"CREATE {or_replace}{temp}STAGE {if_not_exists}{name}{stage_params}",
temp = if *temporary { "TEMPORARY " } else { "" },
or_replace = if *or_replace { "OR REPLACE " } else { "" },
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
)?;
if !directory_table_params.options.is_empty() {
write!(f, " DIRECTORY=({directory_table_params})")?;
}
if !file_format.options.is_empty() {
write!(f, " FILE_FORMAT=({file_format})")?;
}
if !copy_options.options.is_empty() {
write!(f, " COPY_OPTIONS=({copy_options})")?;
}
if comment.is_some() {
write!(f, " COMMENT='{}'", comment.as_ref().unwrap())?;
}
Ok(())
}
Statement::CopyIntoSnowflake {
kind,
into,
into_columns,
from_obj,
from_obj_alias,
stage_params,
from_transformations,
from_query,
files,
pattern,
file_format,
copy_options,
validation_mode,
partition,
} => {
write!(f, "COPY INTO {into}")?;
if let Some(into_columns) = into_columns {
write!(f, " ({})", display_comma_separated(into_columns))?;
}
if let Some(from_transformations) = from_transformations {
// Data load with transformation
if let Some(from_stage) = from_obj {
write!(
f,
" FROM (SELECT {} FROM {}{}",
display_separated(from_transformations, ", "),
from_stage,
stage_params
)?;
}
if let Some(from_obj_alias) = from_obj_alias {
write!(f, " AS {from_obj_alias}")?;
}
write!(f, ")")?;
} else if let Some(from_obj) = from_obj {
// Standard data load
write!(f, " FROM {from_obj}{stage_params}")?;
if let Some(from_obj_alias) = from_obj_alias {
write!(f, " AS {from_obj_alias}")?;
}
} else if let Some(from_query) = from_query {
// Data unload from query
write!(f, " FROM ({from_query})")?;
}
if let Some(files) = files {
write!(f, " FILES = ('{}')", display_separated(files, "', '"))?;
}
if let Some(pattern) = pattern {
write!(f, " PATTERN = '{pattern}'")?;
}
if let Some(partition) = partition {
write!(f, " PARTITION BY {partition}")?;
}
if !file_format.options.is_empty() {
write!(f, " FILE_FORMAT=({file_format})")?;
}
if !copy_options.options.is_empty() {
match kind {
CopyIntoSnowflakeKind::Table => {
write!(f, " COPY_OPTIONS=({copy_options})")?
}
CopyIntoSnowflakeKind::Location => write!(f, " {copy_options}")?,
}
}
if let Some(validation_mode) = validation_mode {
write!(f, " VALIDATION_MODE = {validation_mode}")?;
}
Ok(())
}
Statement::CreateType {
name,
representation,
} => {
write!(f, "CREATE TYPE {name}")?;
if let Some(repr) = representation {
write!(f, " {repr}")?;
}
Ok(())
}
Statement::Pragma { name, value, is_eq } => {
write!(f, "PRAGMA {name}")?;
if value.is_some() {
let val = value.as_ref().unwrap();
if *is_eq {
write!(f, " = {val}")?;
} else {
write!(f, "({val})")?;
}
}
Ok(())
}
Statement::Lock(lock) => lock.fmt(f),
Statement::LockTables { tables } => {
write!(f, "LOCK TABLES {}", display_comma_separated(tables))
}
Statement::UnlockTables => {
write!(f, "UNLOCK TABLES")
}
Statement::Unload {
query,
query_text,
to,
auth,
with,
options,
} => {
write!(f, "UNLOAD(")?;
if let Some(query) = query {
write!(f, "{query}")?;
}
if let Some(query_text) = query_text {
write!(f, "'{query_text}'")?;
}
write!(f, ") TO {to}")?;
if let Some(auth) = auth {
write!(f, " IAM_ROLE {auth}")?;
}
if !with.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with))?;
}
if !options.is_empty() {
write!(f, " {}", display_separated(options, " "))?;
}
Ok(())
}
Statement::OptimizeTable {
name,
has_table_keyword,
on_cluster,
partition,
include_final,
deduplicate,
predicate,
zorder,
} => {
write!(f, "OPTIMIZE")?;
if *has_table_keyword {
write!(f, " TABLE")?;
}
write!(f, " {name}")?;
if let Some(on_cluster) = on_cluster {
write!(f, " ON CLUSTER {on_cluster}")?;
}
if let Some(partition) = partition {
write!(f, " {partition}")?;
}
if *include_final {
write!(f, " FINAL")?;
}
if let Some(deduplicate) = deduplicate {
write!(f, " {deduplicate}")?;
}
if let Some(predicate) = predicate {
write!(f, " WHERE {predicate}")?;
}
if let Some(zorder) = zorder {
write!(f, " ZORDER BY ({})", display_comma_separated(zorder))?;
}
Ok(())
}
Statement::LISTEN { channel } => {
write!(f, "LISTEN {channel}")?;
Ok(())
}
Statement::UNLISTEN { channel } => {
write!(f, "UNLISTEN {channel}")?;
Ok(())
}
Statement::NOTIFY { channel, payload } => {
write!(f, "NOTIFY {channel}")?;
if let Some(payload) = payload {
write!(f, ", '{payload}'")?;
}
Ok(())
}
Statement::RenameTable(rename_tables) => {
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
}
Statement::RaisError {
message,
severity,
state,
arguments,
options,
} => {
write!(f, "RAISERROR({message}, {severity}, {state}")?;
if !arguments.is_empty() {
write!(f, ", {}", display_comma_separated(arguments))?;
}
write!(f, ")")?;
if !options.is_empty() {
write!(f, " WITH {}", display_comma_separated(options))?;
}
Ok(())
}
Statement::Throw(s) => write!(f, "{s}"),
Statement::Print(s) => write!(f, "{s}"),
Statement::WaitFor(s) => write!(f, "{s}"),
Statement::Return(r) => write!(f, "{r}"),
Statement::List(command) => write!(f, "LIST {command}"),
Statement::Remove(command) => write!(f, "REMOVE {command}"),
Statement::ExportData(e) => write!(f, "{e}"),
Statement::CreateUser(s) => write!(f, "{s}"),
Statement::AlterSchema(s) => write!(f, "{s}"),
Statement::Vacuum(s) => write!(f, "{s}"),
Statement::AlterUser(s) => write!(f, "{s}"),
Statement::Reset(s) => write!(f, "{s}"),
}
}
}
/// Can use to describe options in create sequence or table column type identity
/// ```sql
/// [ INCREMENT [ BY ] increment ]
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
/// [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SequenceOptions {
/// `INCREMENT [BY] <expr>` option; second value indicates presence of `BY` keyword.
IncrementBy(Expr, bool),
/// `MINVALUE <expr>` or `NO MINVALUE`.
MinValue(Option<Expr>),
/// `MAXVALUE <expr>` or `NO MAXVALUE`.
MaxValue(Option<Expr>),
/// `START [WITH] <expr>`; second value indicates presence of `WITH`.
StartWith(Expr, bool),
/// `CACHE <expr>` option.
Cache(Expr),
/// `CYCLE` or `NO CYCLE` option.
Cycle(bool),
}
impl fmt::Display for SequenceOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SequenceOptions::IncrementBy(increment, by) => {
write!(
f,
" INCREMENT{by} {increment}",
by = if *by { " BY" } else { "" },
increment = increment
)
}
SequenceOptions::MinValue(Some(expr)) => {
write!(f, " MINVALUE {expr}")
}
SequenceOptions::MinValue(None) => {
write!(f, " NO MINVALUE")
}
SequenceOptions::MaxValue(Some(expr)) => {
write!(f, " MAXVALUE {expr}")
}
SequenceOptions::MaxValue(None) => {
write!(f, " NO MAXVALUE")
}
SequenceOptions::StartWith(start, with) => {
write!(
f,
" START{with} {start}",
with = if *with { " WITH" } else { "" },
start = start
)
}
SequenceOptions::Cache(cache) => {
write!(f, " CACHE {}", *cache)
}
SequenceOptions::Cycle(no) => {
write!(f, " {}CYCLE", if *no { "NO " } else { "" })
}
}
}
}
/// Assignment for a `SET` statement (name [=|TO] value)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct SetAssignment {
/// Optional context scope (e.g., SESSION or LOCAL).
pub scope: Option<ContextModifier>,
/// Assignment target name.
pub name: ObjectName,
/// Assigned expression value.
pub value: Expr,
}
impl fmt::Display for SetAssignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}{} = {}",
self.scope.map(|s| format!("{s}")).unwrap_or_default(),
self.name,
self.value
)
}
}
/// Target of a `TRUNCATE TABLE` command
///
/// Note this is its own struct because `visit_relation` requires an `ObjectName` (not a `Vec<ObjectName>`)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TruncateTableTarget {
/// name of the table being truncated
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub name: ObjectName,
/// Postgres-specific option: explicitly exclude descendants (also default without ONLY)
/// ```sql
/// TRUNCATE TABLE ONLY name
/// ```
/// <https://www.postgresql.org/docs/current/sql-truncate.html>
pub only: bool,
/// Postgres-specific option: asterisk after table name to explicitly indicate descendants
/// ```sql
/// TRUNCATE TABLE name [ * ]
/// ```
/// <https://www.postgresql.org/docs/current/sql-truncate.html>
pub has_asterisk: bool,
}
impl fmt::Display for TruncateTableTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.only {
write!(f, "ONLY ")?;
};
write!(f, "{}", self.name)?;
if self.has_asterisk {
write!(f, " *")?;
};
Ok(())
}
}
/// A `LOCK` statement.
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Lock {
/// List of tables to lock.
pub tables: Vec<LockTableTarget>,
/// Lock mode.
pub lock_mode: Option<LockTableMode>,
/// Whether `NOWAIT` was specified.
pub nowait: bool,
}
impl fmt::Display for Lock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LOCK TABLE {}", display_comma_separated(&self.tables))?;
if let Some(lock_mode) = &self.lock_mode {
write!(f, " IN {lock_mode} MODE")?;
}
if self.nowait {
write!(f, " NOWAIT")?;
}
Ok(())
}
}
/// Target of a `LOCK TABLE` command
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LockTableTarget {
/// Name of the table being locked.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub name: ObjectName,
/// Whether `ONLY` was specified to exclude descendant tables.
pub only: bool,
/// Whether `*` was specified to explicitly include descendant tables.
pub has_asterisk: bool,
}
impl fmt::Display for LockTableTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.only {
write!(f, "ONLY ")?;
}
write!(f, "{}", self.name)?;
if self.has_asterisk {
write!(f, " *")?;
}
Ok(())
}
}
/// PostgreSQL lock modes for `LOCK TABLE`.
///
/// See <https://www.postgresql.org/docs/current/sql-lock.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum LockTableMode {
/// `ACCESS SHARE`
AccessShare,
/// `ROW SHARE`
RowShare,
/// `ROW EXCLUSIVE`
RowExclusive,
/// `SHARE UPDATE EXCLUSIVE`
ShareUpdateExclusive,
/// `SHARE`
Share,
/// `SHARE ROW EXCLUSIVE`
ShareRowExclusive,
/// `EXCLUSIVE`
Exclusive,
/// `ACCESS EXCLUSIVE`
AccessExclusive,
}
impl fmt::Display for LockTableMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
Self::AccessShare => "ACCESS SHARE",
Self::RowShare => "ROW SHARE",
Self::RowExclusive => "ROW EXCLUSIVE",
Self::ShareUpdateExclusive => "SHARE UPDATE EXCLUSIVE",
Self::Share => "SHARE",
Self::ShareRowExclusive => "SHARE ROW EXCLUSIVE",
Self::Exclusive => "EXCLUSIVE",
Self::AccessExclusive => "ACCESS EXCLUSIVE",
};
write!(f, "{text}")
}
}
/// PostgreSQL identity option for TRUNCATE table
/// [ RESTART IDENTITY | CONTINUE IDENTITY ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TruncateIdentityOption {
/// Restart identity values (RESTART IDENTITY).
Restart,
/// Continue identity values (CONTINUE IDENTITY).
Continue,
}
/// Cascade/restrict option for Postgres TRUNCATE table, MySQL GRANT/REVOKE, etc.
/// [ CASCADE | RESTRICT ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CascadeOption {
/// Apply cascading action (e.g., CASCADE).
Cascade,
/// Restrict the action (e.g., RESTRICT).
Restrict,
}
impl Display for CascadeOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CascadeOption::Cascade => write!(f, "CASCADE"),
CascadeOption::Restrict => write!(f, "RESTRICT"),
}
}
}
/// Transaction started with [ TRANSACTION | WORK | TRAN ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum BeginTransactionKind {
/// Standard `TRANSACTION` keyword.
Transaction,
/// Alternate `WORK` keyword.
Work,
/// MSSQL shorthand `TRAN` keyword.
/// See <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/begin-transaction-transact-sql>
Tran,
}
impl Display for BeginTransactionKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BeginTransactionKind::Transaction => write!(f, "TRANSACTION"),
BeginTransactionKind::Work => write!(f, "WORK"),
BeginTransactionKind::Tran => write!(f, "TRAN"),
}
}
}
/// Can use to describe options in create sequence or table column type identity
/// [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MinMaxValue {
/// Clause is not specified.
Empty,
/// NO MINVALUE / NO MAXVALUE.
None,
/// `MINVALUE <expr>` / `MAXVALUE <expr>`.
Some(Expr),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[non_exhaustive]
/// Behavior to apply for `INSERT` when a conflict occurs.
pub enum OnInsert {
/// ON DUPLICATE KEY UPDATE (MySQL when the key already exists, then execute an update instead)
DuplicateKeyUpdate(Vec<Assignment>),
/// ON CONFLICT is a PostgreSQL and Sqlite extension
OnConflict(OnConflict),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Optional aliases for `INSERT` targets: row alias and optional column aliases.
pub struct InsertAliases {
/// Row alias (table-style alias) for the inserted values.
pub row_alias: ObjectName,
/// Optional list of column aliases for the inserted values.
pub col_aliases: Option<Vec<Ident>>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Optional alias for an `INSERT` table; i.e. the table to be inserted into
pub struct TableAliasWithoutColumns {
/// `true` if the aliases was explicitly introduced with the "AS" keyword
pub explicit: bool,
/// the alias name itself
pub alias: Ident,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `ON CONFLICT` clause representation.
pub struct OnConflict {
/// Optional conflict target specifying columns or constraint.
pub conflict_target: Option<ConflictTarget>,
/// Action to take when a conflict occurs.
pub action: OnConflictAction,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Target specification for an `ON CONFLICT` clause.
pub enum ConflictTarget {
/// Target specified as a list of columns.
Columns(Vec<Ident>),
/// Target specified as a named constraint.
OnConstraint(ObjectName),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Action to perform when an `ON CONFLICT` target is matched.
pub enum OnConflictAction {
/// Do nothing on conflict.
DoNothing,
/// Perform an update on conflict.
DoUpdate(DoUpdate),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Details for `DO UPDATE` action of an `ON CONFLICT` clause.
pub struct DoUpdate {
/// Column assignments to perform on update.
pub assignments: Vec<Assignment>,
/// Optional WHERE clause limiting the update.
pub selection: Option<Expr>,
}
impl fmt::Display for OnInsert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::DuplicateKeyUpdate(expr) => write!(
f,
" ON DUPLICATE KEY UPDATE {}",
display_comma_separated(expr)
),
Self::OnConflict(o) => write!(f, "{o}"),
}
}
}
impl fmt::Display for OnConflict {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, " ON CONFLICT")?;
if let Some(target) = &self.conflict_target {
write!(f, "{target}")?;
}
write!(f, " {}", self.action)
}
}
impl fmt::Display for ConflictTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConflictTarget::Columns(cols) => write!(f, "({})", display_comma_separated(cols)),
ConflictTarget::OnConstraint(name) => write!(f, " ON CONSTRAINT {name}"),
}
}
}
impl fmt::Display for OnConflictAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::DoNothing => write!(f, "DO NOTHING"),
Self::DoUpdate(do_update) => {
write!(f, "DO UPDATE")?;
if !do_update.assignments.is_empty() {
write!(
f,
" SET {}",
display_comma_separated(&do_update.assignments)
)?;
}
if let Some(selection) = &do_update.selection {
write!(f, " WHERE {selection}")?;
}
Ok(())
}
}
}
}
/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Privileges {
/// All privileges applicable to the object type
All {
/// Optional keyword from the spec, ignored in practice
with_privileges_keyword: bool,
},
/// Specific privileges (e.g. `SELECT`, `INSERT`)
Actions(Vec<Action>),
}
impl fmt::Display for Privileges {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Privileges::All {
with_privileges_keyword,
} => {
write!(
f,
"ALL{}",
if *with_privileges_keyword {
" PRIVILEGES"
} else {
""
}
)
}
Privileges::Actions(actions) => {
write!(f, "{}", display_comma_separated(actions))
}
}
}
}
/// Specific direction for FETCH statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FetchDirection {
/// Fetch a specific count of rows.
Count {
/// The limit value for the count.
limit: ValueWithSpan,
},
/// Fetch the next row.
Next,
/// Fetch the prior row.
Prior,
/// Fetch the first row.
First,
/// Fetch the last row.
Last,
/// Fetch an absolute row by index.
Absolute {
/// The absolute index value.
limit: ValueWithSpan,
},
/// Fetch a row relative to the current position.
Relative {
/// The relative offset value.
limit: ValueWithSpan,
},
/// Fetch all rows.
All,
// FORWARD
// FORWARD count
/// Fetch forward by an optional limit.
Forward {
/// Optional forward limit.
limit: Option<ValueWithSpan>,
},
/// Fetch all forward rows.
ForwardAll,
// BACKWARD
// BACKWARD count
/// Fetch backward by an optional limit.
Backward {
/// Optional backward limit.
limit: Option<ValueWithSpan>,
},
/// Fetch all backward rows.
BackwardAll,
}
impl fmt::Display for FetchDirection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
FetchDirection::Next => f.write_str("NEXT")?,
FetchDirection::Prior => f.write_str("PRIOR")?,
FetchDirection::First => f.write_str("FIRST")?,
FetchDirection::Last => f.write_str("LAST")?,
FetchDirection::Absolute { limit } => {
f.write_str("ABSOLUTE ")?;
f.write_str(&limit.to_string())?;
}
FetchDirection::Relative { limit } => {
f.write_str("RELATIVE ")?;
f.write_str(&limit.to_string())?;
}
FetchDirection::All => f.write_str("ALL")?,
FetchDirection::Forward { limit } => {
f.write_str("FORWARD")?;
if let Some(l) = limit {
f.write_str(" ")?;
f.write_str(&l.to_string())?;
}
}
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
FetchDirection::Backward { limit } => {
f.write_str("BACKWARD")?;
if let Some(l) = limit {
f.write_str(" ")?;
f.write_str(&l.to_string())?;
}
}
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
};
Ok(())
}
}
/// The "position" for a FETCH statement.
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/fetch-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FetchPosition {
/// Use `FROM <pos>` position specifier.
From,
/// Use `IN <pos>` position specifier.
In,
}
impl fmt::Display for FetchPosition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FetchPosition::From => f.write_str("FROM")?,
FetchPosition::In => f.write_str("IN")?,
};
Ok(())
}
}
/// A privilege on a database object (table, sequence, etc.).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Action {
/// Add a search optimization.
AddSearchOptimization,
/// Apply an `APPLY` operation with a specific type.
Apply {
/// The type of apply operation.
apply_type: ActionApplyType,
},
/// Apply a budget operation.
ApplyBudget,
/// Attach a listing.
AttachListing,
/// Attach a policy.
AttachPolicy,
/// Audit operation.
Audit,
/// Bind a service endpoint.
BindServiceEndpoint,
/// Connect permission.
Connect,
/// Create action, optionally specifying an object type.
Create {
/// Optional object type to create.
obj_type: Option<ActionCreateObjectType>,
},
/// Actions related to database roles.
DatabaseRole {
/// The role name.
role: ObjectName,
},
/// Delete permission.
Delete,
/// Drop permission.
Drop,
/// Evolve schema permission.
EvolveSchema,
/// Exec action (execute) with optional object type.
Exec {
/// Optional execute object type.
obj_type: Option<ActionExecuteObjectType>,
},
/// Execute action with optional object type.
Execute {
/// Optional execute object type.
obj_type: Option<ActionExecuteObjectType>,
},
/// Failover operation.
Failover,
/// Use imported privileges.
ImportedPrivileges,
/// Import a share.
ImportShare,
/// Insert rows with optional column list.
Insert {
/// Optional list of target columns for insert.
columns: Option<Vec<Ident>>,
},
/// Manage operation with a specific manage type.
Manage {
/// The specific manage sub-type.
manage_type: ActionManageType,
},
/// Manage releases.
ManageReleases,
/// Manage versions.
ManageVersions,
/// Modify operation with an optional modify type.
Modify {
/// The optional modify sub-type.
modify_type: Option<ActionModifyType>,
},
/// Monitor operation with an optional monitor type.
Monitor {
/// The optional monitor sub-type.
monitor_type: Option<ActionMonitorType>,
},
/// Operate permission.
Operate,
/// Override share restrictions.
OverrideShareRestrictions,
/// Ownership permission.
Ownership,
/// Purchase a data exchange listing.
PurchaseDataExchangeListing,
/// Read access.
Read,
/// Read session-level access.
ReadSession,
/// References with optional column list.
References {
/// Optional list of referenced column identifiers.
columns: Option<Vec<Ident>>,
},
/// Replication permission.
Replicate,
/// Resolve all references.
ResolveAll,
/// Role-related permission with target role name.
Role {
/// The target role name.
role: ObjectName,
},
/// Select permission with optional column list.
Select {
/// Optional list of selected columns.
columns: Option<Vec<Ident>>,
},
/// Temporary object permission.
Temporary,
/// Trigger-related permission.
Trigger,
/// Truncate permission.
Truncate,
/// Update permission with optional affected columns.
Update {
/// Optional list of columns affected by update.
columns: Option<Vec<Ident>>,
},
/// Usage permission.
Usage,
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Action::AddSearchOptimization => f.write_str("ADD SEARCH OPTIMIZATION")?,
Action::Apply { apply_type } => write!(f, "APPLY {apply_type}")?,
Action::ApplyBudget => f.write_str("APPLYBUDGET")?,
Action::AttachListing => f.write_str("ATTACH LISTING")?,
Action::AttachPolicy => f.write_str("ATTACH POLICY")?,
Action::Audit => f.write_str("AUDIT")?,
Action::BindServiceEndpoint => f.write_str("BIND SERVICE ENDPOINT")?,
Action::Connect => f.write_str("CONNECT")?,
Action::Create { obj_type } => {
f.write_str("CREATE")?;
if let Some(obj_type) = obj_type {
write!(f, " {obj_type}")?
}
}
Action::DatabaseRole { role } => write!(f, "DATABASE ROLE {role}")?,
Action::Delete => f.write_str("DELETE")?,
Action::Drop => f.write_str("DROP")?,
Action::EvolveSchema => f.write_str("EVOLVE SCHEMA")?,
Action::Exec { obj_type } => {
f.write_str("EXEC")?;
if let Some(obj_type) = obj_type {
write!(f, " {obj_type}")?
}
}
Action::Execute { obj_type } => {
f.write_str("EXECUTE")?;
if let Some(obj_type) = obj_type {
write!(f, " {obj_type}")?
}
}
Action::Failover => f.write_str("FAILOVER")?,
Action::ImportedPrivileges => f.write_str("IMPORTED PRIVILEGES")?,
Action::ImportShare => f.write_str("IMPORT SHARE")?,
Action::Insert { .. } => f.write_str("INSERT")?,
Action::Manage { manage_type } => write!(f, "MANAGE {manage_type}")?,
Action::ManageReleases => f.write_str("MANAGE RELEASES")?,
Action::ManageVersions => f.write_str("MANAGE VERSIONS")?,
Action::Modify { modify_type } => {
write!(f, "MODIFY")?;
if let Some(modify_type) = modify_type {
write!(f, " {modify_type}")?;
}
}
Action::Monitor { monitor_type } => {
write!(f, "MONITOR")?;
if let Some(monitor_type) = monitor_type {
write!(f, " {monitor_type}")?
}
}
Action::Operate => f.write_str("OPERATE")?,
Action::OverrideShareRestrictions => f.write_str("OVERRIDE SHARE RESTRICTIONS")?,
Action::Ownership => f.write_str("OWNERSHIP")?,
Action::PurchaseDataExchangeListing => f.write_str("PURCHASE DATA EXCHANGE LISTING")?,
Action::Read => f.write_str("READ")?,
Action::ReadSession => f.write_str("READ SESSION")?,
Action::References { .. } => f.write_str("REFERENCES")?,
Action::Replicate => f.write_str("REPLICATE")?,
Action::ResolveAll => f.write_str("RESOLVE ALL")?,
Action::Role { role } => write!(f, "ROLE {role}")?,
Action::Select { .. } => f.write_str("SELECT")?,
Action::Temporary => f.write_str("TEMPORARY")?,
Action::Trigger => f.write_str("TRIGGER")?,
Action::Truncate => f.write_str("TRUNCATE")?,
Action::Update { .. } => f.write_str("UPDATE")?,
Action::Usage => f.write_str("USAGE")?,
};
match self {
Action::Insert { columns }
| Action::References { columns }
| Action::Select { columns }
| Action::Update { columns } => {
if let Some(columns) = columns {
write!(f, " ({})", display_comma_separated(columns))?;
}
}
_ => (),
};
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `CREATE` privilege.
pub enum ActionCreateObjectType {
/// An account-level object.
Account,
/// An application object.
Application,
/// An application package object.
ApplicationPackage,
/// A compute pool object.
ComputePool,
/// A data exchange listing.
DataExchangeListing,
/// A database object.
Database,
/// An external volume object.
ExternalVolume,
/// A failover group object.
FailoverGroup,
/// An integration object.
Integration,
/// A network policy object.
NetworkPolicy,
/// An organization listing.
OrganiationListing,
/// A replication group object.
ReplicationGroup,
/// A role object.
Role,
/// A schema object.
Schema,
/// A share object.
Share,
/// A user object.
User,
/// A warehouse object.
Warehouse,
}
impl fmt::Display for ActionCreateObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionCreateObjectType::Account => write!(f, "ACCOUNT"),
ActionCreateObjectType::Application => write!(f, "APPLICATION"),
ActionCreateObjectType::ApplicationPackage => write!(f, "APPLICATION PACKAGE"),
ActionCreateObjectType::ComputePool => write!(f, "COMPUTE POOL"),
ActionCreateObjectType::DataExchangeListing => write!(f, "DATA EXCHANGE LISTING"),
ActionCreateObjectType::Database => write!(f, "DATABASE"),
ActionCreateObjectType::ExternalVolume => write!(f, "EXTERNAL VOLUME"),
ActionCreateObjectType::FailoverGroup => write!(f, "FAILOVER GROUP"),
ActionCreateObjectType::Integration => write!(f, "INTEGRATION"),
ActionCreateObjectType::NetworkPolicy => write!(f, "NETWORK POLICY"),
ActionCreateObjectType::OrganiationListing => write!(f, "ORGANIZATION LISTING"),
ActionCreateObjectType::ReplicationGroup => write!(f, "REPLICATION GROUP"),
ActionCreateObjectType::Role => write!(f, "ROLE"),
ActionCreateObjectType::Schema => write!(f, "SCHEMA"),
ActionCreateObjectType::Share => write!(f, "SHARE"),
ActionCreateObjectType::User => write!(f, "USER"),
ActionCreateObjectType::Warehouse => write!(f, "WAREHOUSE"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `APPLY` privilege.
pub enum ActionApplyType {
/// Apply an aggregation policy.
AggregationPolicy,
/// Apply an authentication policy.
AuthenticationPolicy,
/// Apply a join policy.
JoinPolicy,
/// Apply a masking policy.
MaskingPolicy,
/// Apply a packages policy.
PackagesPolicy,
/// Apply a password policy.
PasswordPolicy,
/// Apply a projection policy.
ProjectionPolicy,
/// Apply a row access policy.
RowAccessPolicy,
/// Apply a session policy.
SessionPolicy,
/// Apply a tag.
Tag,
}
impl fmt::Display for ActionApplyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionApplyType::AggregationPolicy => write!(f, "AGGREGATION POLICY"),
ActionApplyType::AuthenticationPolicy => write!(f, "AUTHENTICATION POLICY"),
ActionApplyType::JoinPolicy => write!(f, "JOIN POLICY"),
ActionApplyType::MaskingPolicy => write!(f, "MASKING POLICY"),
ActionApplyType::PackagesPolicy => write!(f, "PACKAGES POLICY"),
ActionApplyType::PasswordPolicy => write!(f, "PASSWORD POLICY"),
ActionApplyType::ProjectionPolicy => write!(f, "PROJECTION POLICY"),
ActionApplyType::RowAccessPolicy => write!(f, "ROW ACCESS POLICY"),
ActionApplyType::SessionPolicy => write!(f, "SESSION POLICY"),
ActionApplyType::Tag => write!(f, "TAG"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `EXECUTE` privilege.
pub enum ActionExecuteObjectType {
/// Alert object.
Alert,
/// Data metric function object.
DataMetricFunction,
/// Managed alert object.
ManagedAlert,
/// Managed task object.
ManagedTask,
/// Task object.
Task,
}
impl fmt::Display for ActionExecuteObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionExecuteObjectType::Alert => write!(f, "ALERT"),
ActionExecuteObjectType::DataMetricFunction => write!(f, "DATA METRIC FUNCTION"),
ActionExecuteObjectType::ManagedAlert => write!(f, "MANAGED ALERT"),
ActionExecuteObjectType::ManagedTask => write!(f, "MANAGED TASK"),
ActionExecuteObjectType::Task => write!(f, "TASK"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MANAGE` privilege.
pub enum ActionManageType {
/// Account support cases management.
AccountSupportCases,
/// Event sharing management.
EventSharing,
/// Grants management.
Grants,
/// Listing auto-fulfillment management.
ListingAutoFulfillment,
/// Organization support cases management.
OrganizationSupportCases,
/// User support cases management.
UserSupportCases,
/// Warehouses management.
Warehouses,
}
impl fmt::Display for ActionManageType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionManageType::AccountSupportCases => write!(f, "ACCOUNT SUPPORT CASES"),
ActionManageType::EventSharing => write!(f, "EVENT SHARING"),
ActionManageType::Grants => write!(f, "GRANTS"),
ActionManageType::ListingAutoFulfillment => write!(f, "LISTING AUTO FULFILLMENT"),
ActionManageType::OrganizationSupportCases => write!(f, "ORGANIZATION SUPPORT CASES"),
ActionManageType::UserSupportCases => write!(f, "USER SUPPORT CASES"),
ActionManageType::Warehouses => write!(f, "WAREHOUSES"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MODIFY` privilege.
pub enum ActionModifyType {
/// Modify log level.
LogLevel,
/// Modify trace level.
TraceLevel,
/// Modify session log level.
SessionLogLevel,
/// Modify session trace level.
SessionTraceLevel,
}
impl fmt::Display for ActionModifyType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionModifyType::LogLevel => write!(f, "LOG LEVEL"),
ActionModifyType::TraceLevel => write!(f, "TRACE LEVEL"),
ActionModifyType::SessionLogLevel => write!(f, "SESSION LOG LEVEL"),
ActionModifyType::SessionTraceLevel => write!(f, "SESSION TRACE LEVEL"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// See <https://docs.snowflake.com/en/sql-reference/sql/grant-privilege>
/// under `globalPrivileges` in the `MONITOR` privilege.
pub enum ActionMonitorType {
/// Monitor execution.
Execution,
/// Monitor security.
Security,
/// Monitor usage.
Usage,
}
impl fmt::Display for ActionMonitorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ActionMonitorType::Execution => write!(f, "EXECUTION"),
ActionMonitorType::Security => write!(f, "SECURITY"),
ActionMonitorType::Usage => write!(f, "USAGE"),
}
}
}
/// The principal that receives the privileges
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Grantee {
/// The category/type of grantee (role, user, share, etc.).
pub grantee_type: GranteesType,
/// Optional name of the grantee (identifier or user@host).
pub name: Option<GranteeName>,
}
impl fmt::Display for Grantee {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.grantee_type {
GranteesType::Role => {
write!(f, "ROLE ")?;
}
GranteesType::Share => {
write!(f, "SHARE ")?;
}
GranteesType::User => {
write!(f, "USER ")?;
}
GranteesType::Group => {
write!(f, "GROUP ")?;
}
GranteesType::Public => {
write!(f, "PUBLIC ")?;
}
GranteesType::DatabaseRole => {
write!(f, "DATABASE ROLE ")?;
}
GranteesType::Application => {
write!(f, "APPLICATION ")?;
}
GranteesType::ApplicationRole => {
write!(f, "APPLICATION ROLE ")?;
}
GranteesType::None => (),
}
if let Some(ref name) = self.name {
name.fmt(f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// The kind of principal receiving privileges.
pub enum GranteesType {
/// A role principal.
Role,
/// A share principal.
Share,
/// A user principal.
User,
/// A group principal.
Group,
/// The public principal.
Public,
/// A database role principal.
DatabaseRole,
/// An application principal.
Application,
/// An application role principal.
ApplicationRole,
/// No specific principal (e.g. `NONE`).
None,
}
/// Users/roles designated in a GRANT/REVOKE
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum GranteeName {
/// A bare identifier
ObjectName(ObjectName),
/// A MySQL user/host pair such as 'root'@'%'
UserHost {
/// The user identifier portion.
user: Ident,
/// The host identifier portion.
host: Ident,
},
}
impl fmt::Display for GranteeName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GranteeName::ObjectName(name) => name.fmt(f),
GranteeName::UserHost { user, host } => {
write!(f, "{user}@{host}")
}
}
}
}
/// Objects on which privileges are granted in a GRANT statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum GrantObjects {
/// Grant privileges on `ALL SEQUENCES IN SCHEMA <schema_name> [, ...]`
AllSequencesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `ALL TABLES IN SCHEMA <schema_name> [, ...]`
AllTablesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `ALL VIEWS IN SCHEMA <schema_name> [, ...]`
AllViewsInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `ALL MATERIALIZED VIEWS IN SCHEMA <schema_name> [, ...]`
AllMaterializedViewsInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `ALL EXTERNAL TABLES IN SCHEMA <schema_name> [, ...]`
AllExternalTablesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `ALL FUNCTIONS IN SCHEMA <schema_name> [, ...]`
AllFunctionsInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE SCHEMAS IN DATABASE <database_name> [, ...]`
FutureSchemasInDatabase {
/// The target database names.
databases: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE TABLES IN SCHEMA <schema_name> [, ...]`
FutureTablesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE VIEWS IN SCHEMA <schema_name> [, ...]`
FutureViewsInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE EXTERNAL TABLES IN SCHEMA <schema_name> [, ...]`
FutureExternalTablesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE MATERIALIZED VIEWS IN SCHEMA <schema_name> [, ...]`
FutureMaterializedViewsInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on `FUTURE SEQUENCES IN SCHEMA <schema_name> [, ...]`
FutureSequencesInSchema {
/// The target schema names.
schemas: Vec<ObjectName>,
},
/// Grant privileges on specific databases
Databases(Vec<ObjectName>),
/// Grant privileges on specific schemas
Schemas(Vec<ObjectName>),
/// Grant privileges on specific sequences
Sequences(Vec<ObjectName>),
/// Grant privileges on specific tables
Tables(Vec<ObjectName>),
/// Grant privileges on specific views
Views(Vec<ObjectName>),
/// Grant privileges on specific warehouses
Warehouses(Vec<ObjectName>),
/// Grant privileges on specific integrations
Integrations(Vec<ObjectName>),
/// Grant privileges on resource monitors
ResourceMonitors(Vec<ObjectName>),
/// Grant privileges on users
Users(Vec<ObjectName>),
/// Grant privileges on compute pools
ComputePools(Vec<ObjectName>),
/// Grant privileges on connections
Connections(Vec<ObjectName>),
/// Grant privileges on failover groups
FailoverGroup(Vec<ObjectName>),
/// Grant privileges on replication group
ReplicationGroup(Vec<ObjectName>),
/// Grant privileges on external volumes
ExternalVolumes(Vec<ObjectName>),
/// Grant privileges on a procedure. In dialects that
/// support overloading, the argument types must be specified.
///
/// For example:
/// `GRANT USAGE ON PROCEDURE foo(varchar) TO ROLE role1`
Procedure {
/// The procedure name.
name: ObjectName,
/// Optional argument types for overloaded procedures.
arg_types: Vec<DataType>,
},
/// Grant privileges on a function. In dialects that
/// support overloading, the argument types must be specified.
///
/// For example:
/// `GRANT USAGE ON FUNCTION foo(varchar) TO ROLE role1`
Function {
/// The function name.
name: ObjectName,
/// Optional argument types for overloaded functions.
arg_types: Vec<DataType>,
},
}
impl fmt::Display for GrantObjects {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GrantObjects::Sequences(sequences) => {
write!(f, "SEQUENCE {}", display_comma_separated(sequences))
}
GrantObjects::Databases(databases) => {
write!(f, "DATABASE {}", display_comma_separated(databases))
}
GrantObjects::Schemas(schemas) => {
write!(f, "SCHEMA {}", display_comma_separated(schemas))
}
GrantObjects::Tables(tables) => {
write!(f, "{}", display_comma_separated(tables))
}
GrantObjects::Views(views) => {
write!(f, "VIEW {}", display_comma_separated(views))
}
GrantObjects::Warehouses(warehouses) => {
write!(f, "WAREHOUSE {}", display_comma_separated(warehouses))
}
GrantObjects::Integrations(integrations) => {
write!(f, "INTEGRATION {}", display_comma_separated(integrations))
}
GrantObjects::AllSequencesInSchema { schemas } => {
write!(
f,
"ALL SEQUENCES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllTablesInSchema { schemas } => {
write!(
f,
"ALL TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllExternalTablesInSchema { schemas } => {
write!(
f,
"ALL EXTERNAL TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllViewsInSchema { schemas } => {
write!(
f,
"ALL VIEWS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllMaterializedViewsInSchema { schemas } => {
write!(
f,
"ALL MATERIALIZED VIEWS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllFunctionsInSchema { schemas } => {
write!(
f,
"ALL FUNCTIONS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureSchemasInDatabase { databases } => {
write!(
f,
"FUTURE SCHEMAS IN DATABASE {}",
display_comma_separated(databases)
)
}
GrantObjects::FutureTablesInSchema { schemas } => {
write!(
f,
"FUTURE TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureExternalTablesInSchema { schemas } => {
write!(
f,
"FUTURE EXTERNAL TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureViewsInSchema { schemas } => {
write!(
f,
"FUTURE VIEWS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureMaterializedViewsInSchema { schemas } => {
write!(
f,
"FUTURE MATERIALIZED VIEWS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureSequencesInSchema { schemas } => {
write!(
f,
"FUTURE SEQUENCES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::ResourceMonitors(objects) => {
write!(f, "RESOURCE MONITOR {}", display_comma_separated(objects))
}
GrantObjects::Users(objects) => {
write!(f, "USER {}", display_comma_separated(objects))
}
GrantObjects::ComputePools(objects) => {
write!(f, "COMPUTE POOL {}", display_comma_separated(objects))
}
GrantObjects::Connections(objects) => {
write!(f, "CONNECTION {}", display_comma_separated(objects))
}
GrantObjects::FailoverGroup(objects) => {
write!(f, "FAILOVER GROUP {}", display_comma_separated(objects))
}
GrantObjects::ReplicationGroup(objects) => {
write!(f, "REPLICATION GROUP {}", display_comma_separated(objects))
}
GrantObjects::ExternalVolumes(objects) => {
write!(f, "EXTERNAL VOLUME {}", display_comma_separated(objects))
}
GrantObjects::Procedure { name, arg_types } => {
write!(f, "PROCEDURE {name}")?;
if !arg_types.is_empty() {
write!(f, "({})", display_comma_separated(arg_types))?;
}
Ok(())
}
GrantObjects::Function { name, arg_types } => {
write!(f, "FUNCTION {name}")?;
if !arg_types.is_empty() {
write!(f, "({})", display_comma_separated(arg_types))?;
}
Ok(())
}
}
}
}
/// A `DENY` statement
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/deny-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DenyStatement {
/// The privileges to deny.
pub privileges: Privileges,
/// The objects the privileges apply to.
pub objects: GrantObjects,
/// The grantees (users/roles) to whom the denial applies.
pub grantees: Vec<Grantee>,
/// Optional identifier of the principal that performed the grant.
pub granted_by: Option<Ident>,
/// Optional cascade option controlling dependent objects.
pub cascade: Option<CascadeOption>,
}
impl fmt::Display for DenyStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DENY {}", self.privileges)?;
write!(f, " ON {}", self.objects)?;
if !self.grantees.is_empty() {
write!(f, " TO {}", display_comma_separated(&self.grantees))?;
}
if let Some(cascade) = &self.cascade {
write!(f, " {cascade}")?;
}
if let Some(granted_by) = &self.granted_by {
write!(f, " AS {granted_by}")?;
}
Ok(())
}
}
/// SQL assignment `foo = expr` as used in SQLUpdate
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Assignment {
/// The left-hand side of the assignment.
pub target: AssignmentTarget,
/// The expression assigned to the target.
pub value: Expr,
}
impl fmt::Display for Assignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.target, self.value)
}
}
/// Left-hand side of an assignment in an UPDATE statement,
/// e.g. `foo` in `foo = 5` (ColumnName assignment) or
/// `(a, b)` in `(a, b) = (1, 2)` (Tuple assignment).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AssignmentTarget {
/// A single column
ColumnName(ObjectName),
/// A tuple of columns
Tuple(Vec<ObjectName>),
}
impl fmt::Display for AssignmentTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AssignmentTarget::ColumnName(column) => write!(f, "{column}"),
AssignmentTarget::Tuple(columns) => write!(f, "({})", display_comma_separated(columns)),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Expression forms allowed as a function argument.
pub enum FunctionArgExpr {
/// A normal expression argument.
Expr(Expr),
/// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
QualifiedWildcard(ObjectName),
/// An unqualified `*` wildcard.
Wildcard,
/// An unqualified `*` wildcard with additional options, e.g. `* EXCLUDE(col)`.
///
/// Used in Snowflake to support expressions like `HASH(* EXCLUDE(col))`.
WildcardWithOptions(WildcardAdditionalOptions),
}
impl From<Expr> for FunctionArgExpr {
fn from(wildcard_expr: Expr) -> Self {
match wildcard_expr {
Expr::QualifiedWildcard(prefix, _) => Self::QualifiedWildcard(prefix),
Expr::Wildcard(_) => Self::Wildcard,
expr => Self::Expr(expr),
}
}
}
impl fmt::Display for FunctionArgExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionArgExpr::Expr(expr) => write!(f, "{expr}"),
FunctionArgExpr::QualifiedWildcard(prefix) => write!(f, "{prefix}.*"),
FunctionArgExpr::Wildcard => f.write_str("*"),
FunctionArgExpr::WildcardWithOptions(opts) => write!(f, "*{opts}"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Operator used to separate function arguments
pub enum FunctionArgOperator {
/// function(arg1 = value1)
Equals,
/// function(arg1 => value1)
RightArrow,
/// function(arg1 := value1)
Assignment,
/// function(arg1 : value1)
Colon,
/// function(arg1 VALUE value1)
Value,
}
impl fmt::Display for FunctionArgOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionArgOperator::Equals => f.write_str("="),
FunctionArgOperator::RightArrow => f.write_str("=>"),
FunctionArgOperator::Assignment => f.write_str(":="),
FunctionArgOperator::Colon => f.write_str(":"),
FunctionArgOperator::Value => f.write_str("VALUE"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Forms of function arguments (named, expression-named, or positional).
pub enum FunctionArg {
/// `name` is identifier
///
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'false'
Named {
/// The identifier name of the argument.
name: Ident,
/// The argument expression or wildcard form.
arg: FunctionArgExpr,
/// The operator separating name and value.
operator: FunctionArgOperator,
},
/// `name` is arbitrary expression
///
/// Enabled when `Dialect::supports_named_fn_args_with_expr_name` returns 'true'
ExprNamed {
/// The expression used as the argument name.
name: Expr,
/// The argument expression or wildcard form.
arg: FunctionArgExpr,
/// The operator separating name and value.
operator: FunctionArgOperator,
},
/// An unnamed argument (positional), given by expression or wildcard.
Unnamed(FunctionArgExpr),
}
impl fmt::Display for FunctionArg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionArg::Named {
name,
arg,
operator,
} => write!(f, "{name} {operator} {arg}"),
FunctionArg::ExprNamed {
name,
arg,
operator,
} => write!(f, "{name} {operator} {arg}"),
FunctionArg::Unnamed(unnamed_arg) => write!(f, "{unnamed_arg}"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Which cursor(s) to close.
pub enum CloseCursor {
/// Close all cursors.
All,
/// Close a specific cursor by name.
Specific {
/// The name of the cursor to close.
name: Ident,
},
}
impl fmt::Display for CloseCursor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CloseCursor::All => write!(f, "ALL"),
CloseCursor::Specific { name } => write!(f, "{name}"),
}
}
}
/// A Drop Domain statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DropDomain {
/// Whether to drop the domain if it exists
pub if_exists: bool,
/// The name of the domain to drop
pub name: ObjectName,
/// The behavior to apply when dropping the domain
pub drop_behavior: Option<DropBehavior>,
}
/// A constant of form `<data_type> 'value'`.
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
/// as well as constants of other types (a non-standard PostgreSQL extension).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct TypedString {
/// The data type of the typed string (e.g. DATE, TIME, TIMESTAMP).
pub data_type: DataType,
/// The value of the constant.
/// Hint: you can unwrap the string value using `value.into_string()`.
pub value: ValueWithSpan,
/// Flags whether this TypedString uses the [ODBC syntax].
///
/// Example:
/// ```sql
/// -- An ODBC date literal:
/// SELECT {d '2025-07-16'}
/// -- This is equivalent to the standard ANSI SQL literal:
/// SELECT DATE '2025-07-16'
///
/// [ODBC syntax]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals?view=sql-server-2017
pub uses_odbc_syntax: bool,
}
impl fmt::Display for TypedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data_type = &self.data_type;
let value = &self.value;
match self.uses_odbc_syntax {
false => {
write!(f, "{data_type}")?;
write!(f, " {value}")
}
true => {
let prefix = match data_type {
DataType::Date => "d",
DataType::Time(..) => "t",
DataType::Timestamp(..) => "ts",
_ => "?",
};
write!(f, "{{{prefix} {value}}}")
}
}
}
}
/// A function call
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Function {
/// The function name (may be qualified).
pub name: ObjectName,
/// Flags whether this function call uses the [ODBC syntax].
///
/// Example:
/// ```sql
/// SELECT {fn CONCAT('foo', 'bar')}
/// ```
///
/// [ODBC syntax]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/scalar-function-calls?view=sql-server-2017
pub uses_odbc_syntax: bool,
/// The parameters to the function, including any options specified within the
/// delimiting parentheses.
///
/// Example:
/// ```plaintext
/// HISTOGRAM(0.5, 0.6)(x, y)
/// ```
///
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions)
pub parameters: FunctionArguments,
/// The arguments to the function, including any options specified within the
/// delimiting parentheses.
pub args: FunctionArguments,
/// e.g. `x > 5` in `COUNT(x) FILTER (WHERE x > 5)`
pub filter: Option<Box<Expr>>,
/// Indicates how `NULL`s should be handled in the calculation.
///
/// Example:
/// ```plaintext
/// FIRST_VALUE( <expr> ) [ { IGNORE | RESPECT } NULLS ] OVER ...
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/first_value)
pub null_treatment: Option<NullTreatment>,
/// The `OVER` clause, indicating a window function call.
pub over: Option<WindowType>,
/// A clause used with certain aggregate functions to control the ordering
/// within grouped sets before the function is applied.
///
/// Syntax:
/// ```plaintext
/// <aggregate_function>(expression) WITHIN GROUP (ORDER BY key [ASC | DESC], ...)
/// ```
pub within_group: Vec<OrderByExpr>,
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.uses_odbc_syntax {
write!(f, "{{fn ")?;
}
write!(f, "{}{}{}", self.name, self.parameters, self.args)?;
if !self.within_group.is_empty() {
write!(
f,
" WITHIN GROUP (ORDER BY {})",
display_comma_separated(&self.within_group)
)?;
}
if let Some(filter_cond) = &self.filter {
write!(f, " FILTER (WHERE {filter_cond})")?;
}
if let Some(null_treatment) = &self.null_treatment {
write!(f, " {null_treatment}")?;
}
if let Some(o) = &self.over {
f.write_str(" OVER ")?;
o.fmt(f)?;
}
if self.uses_odbc_syntax {
write!(f, "}}")?;
}
Ok(())
}
}
/// The arguments passed to a function call.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArguments {
/// Used for special functions like `CURRENT_TIMESTAMP` that are invoked
/// without parentheses.
None,
/// On some dialects, a subquery can be passed without surrounding
/// parentheses if it's the sole argument to the function.
Subquery(Box<Query>),
/// A normal function argument list, including any clauses within it such as
/// `DISTINCT` or `ORDER BY`.
List(FunctionArgumentList),
}
impl fmt::Display for FunctionArguments {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArguments::None => Ok(()),
FunctionArguments::Subquery(query) => write!(f, "({query})"),
FunctionArguments::List(args) => write!(f, "({args})"),
}
}
}
/// This represents everything inside the parentheses when calling a function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionArgumentList {
/// `[ ALL | DISTINCT ]`
pub duplicate_treatment: Option<DuplicateTreatment>,
/// The function arguments.
pub args: Vec<FunctionArg>,
/// Additional clauses specified within the argument list.
pub clauses: Vec<FunctionArgumentClause>,
}
impl fmt::Display for FunctionArgumentList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(duplicate_treatment) = self.duplicate_treatment {
write!(f, "{duplicate_treatment} ")?;
}
write!(f, "{}", display_comma_separated(&self.args))?;
if !self.clauses.is_empty() {
if !self.args.is_empty() {
write!(f, " ")?;
}
write!(f, "{}", display_separated(&self.clauses, " "))?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clauses that can appear inside a function argument list.
pub enum FunctionArgumentClause {
/// Indicates how `NULL`s should be handled in the calculation, e.g. in `FIRST_VALUE` on [BigQuery].
///
/// Syntax:
/// ```plaintext
/// { IGNORE | RESPECT } NULLS ]
/// ```
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value
IgnoreOrRespectNulls(NullTreatment),
/// Specifies the the ordering for some ordered set aggregates, e.g. `ARRAY_AGG` on [BigQuery].
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#array_agg
OrderBy(Vec<OrderByExpr>),
/// Specifies a limit for the `ARRAY_AGG` and `ARRAY_CONCAT_AGG` functions on BigQuery.
Limit(Expr),
/// Specifies the behavior on overflow of the `LISTAGG` function.
///
/// See <https://trino.io/docs/current/functions/aggregate.html>.
OnOverflow(ListAggOnOverflow),
/// Specifies a minimum or maximum bound on the input to [`ANY_VALUE`] on BigQuery.
///
/// Syntax:
/// ```plaintext
/// HAVING { MAX | MIN } expression
/// ```
///
/// [`ANY_VALUE`]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#any_value
Having(HavingBound),
/// The `SEPARATOR` clause to the [`GROUP_CONCAT`] function in MySQL.
///
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
Separator(ValueWithSpan),
/// The `ON NULL` clause for some JSON functions.
///
/// [MSSQL `JSON_ARRAY`](https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16)
/// [MSSQL `JSON_OBJECT`](https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16>)
/// [PostgreSQL JSON functions](https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-PROCESSING)
JsonNullClause(JsonNullClause),
/// The `RETURNING` clause for some JSON functions in PostgreSQL
///
/// [`JSON_OBJECT`](https://www.postgresql.org/docs/current/functions-json.html#:~:text=json_object)
JsonReturningClause(JsonReturningClause),
}
impl fmt::Display for FunctionArgumentClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => {
write!(f, "{null_treatment}")
}
FunctionArgumentClause::OrderBy(order_by) => {
write!(f, "ORDER BY {}", display_comma_separated(order_by))
}
FunctionArgumentClause::Limit(limit) => write!(f, "LIMIT {limit}"),
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
FunctionArgumentClause::JsonNullClause(null_clause) => write!(f, "{null_clause}"),
FunctionArgumentClause::JsonReturningClause(returning_clause) => {
write!(f, "{returning_clause}")
}
}
}
}
/// A method call
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Method {
/// The expression on which the method is invoked.
pub expr: Box<Expr>,
// always non-empty
/// The sequence of chained method calls.
pub method_chain: Vec<Function>,
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}.{}",
self.expr,
display_separated(&self.method_chain, ".")
)
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How duplicate values are treated inside function argument lists.
pub enum DuplicateTreatment {
/// Consider only unique values.
Distinct,
/// Retain all duplicate values (the default).
All,
}
impl fmt::Display for DuplicateTreatment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DuplicateTreatment::Distinct => write!(f, "DISTINCT"),
DuplicateTreatment::All => write!(f, "ALL"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// How the `ANALYZE`/`EXPLAIN ANALYZE` format is specified.
pub enum AnalyzeFormatKind {
/// Format provided as a keyword, e.g. `FORMAT JSON`.
Keyword(AnalyzeFormat),
/// Format provided as an assignment, e.g. `FORMAT=JSON`.
Assignment(AnalyzeFormat),
}
impl fmt::Display for AnalyzeFormatKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
AnalyzeFormatKind::Keyword(format) => write!(f, "FORMAT {format}"),
AnalyzeFormatKind::Assignment(format) => write!(f, "FORMAT={format}"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Output formats supported for `ANALYZE`/`EXPLAIN ANALYZE`.
pub enum AnalyzeFormat {
/// Plain text format.
TEXT,
/// Graphviz DOT format.
GRAPHVIZ,
/// JSON format.
JSON,
/// Traditional explain output.
TRADITIONAL,
/// Tree-style explain output.
TREE,
}
impl fmt::Display for AnalyzeFormat {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
AnalyzeFormat::TEXT => "TEXT",
AnalyzeFormat::GRAPHVIZ => "GRAPHVIZ",
AnalyzeFormat::JSON => "JSON",
AnalyzeFormat::TRADITIONAL => "TRADITIONAL",
AnalyzeFormat::TREE => "TREE",
})
}
}
/// External table's available file format
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FileFormat {
/// Text file format.
TEXTFILE,
/// Sequence file format.
SEQUENCEFILE,
/// ORC file format.
ORC,
/// Parquet file format.
PARQUET,
/// Avro file format.
AVRO,
/// RCFile format.
RCFILE,
/// JSON file format.
JSONFILE,
}
impl fmt::Display for FileFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::FileFormat::*;
f.write_str(match self {
TEXTFILE => "TEXTFILE",
SEQUENCEFILE => "SEQUENCEFILE",
ORC => "ORC",
PARQUET => "PARQUET",
AVRO => "AVRO",
RCFILE => "RCFILE",
JSONFILE => "JSONFILE",
})
}
}
/// The `ON OVERFLOW` clause of a LISTAGG invocation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ListAggOnOverflow {
/// `ON OVERFLOW ERROR`
Error,
/// `ON OVERFLOW TRUNCATE [ <filler> ] WITH[OUT] COUNT`
Truncate {
/// Optional filler expression used when truncating.
filler: Option<Box<Expr>>,
/// Whether to include a count when truncating.
with_count: bool,
},
}
impl fmt::Display for ListAggOnOverflow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ON OVERFLOW")?;
match self {
ListAggOnOverflow::Error => write!(f, " ERROR"),
ListAggOnOverflow::Truncate { filler, with_count } => {
write!(f, " TRUNCATE")?;
if let Some(filler) = filler {
write!(f, " {filler}")?;
}
if *with_count {
write!(f, " WITH")?;
} else {
write!(f, " WITHOUT")?;
}
write!(f, " COUNT")
}
}
}
}
/// The `HAVING` clause in a call to `ANY_VALUE` on BigQuery.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct HavingBound(pub HavingBoundKind, pub Expr);
impl fmt::Display for HavingBound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HAVING {} {}", self.0, self.1)
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Which bound is used in a HAVING clause for ANY_VALUE on BigQuery.
pub enum HavingBoundKind {
/// The minimum bound.
Min,
/// The maximum bound.
Max,
}
impl fmt::Display for HavingBoundKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HavingBoundKind::Min => write!(f, "MIN"),
HavingBoundKind::Max => write!(f, "MAX"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Types of database objects referenced by DDL statements.
pub enum ObjectType {
/// A table.
Table,
/// A view.
View,
/// A materialized view.
MaterializedView,
/// An index.
Index,
/// A schema.
Schema,
/// A database.
Database,
/// A role.
Role,
/// A sequence.
Sequence,
/// A stage.
Stage,
/// A type definition.
Type,
/// A user.
User,
/// A stream.
Stream,
}
impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
ObjectType::Table => "TABLE",
ObjectType::View => "VIEW",
ObjectType::MaterializedView => "MATERIALIZED VIEW",
ObjectType::Index => "INDEX",
ObjectType::Schema => "SCHEMA",
ObjectType::Database => "DATABASE",
ObjectType::Role => "ROLE",
ObjectType::Sequence => "SEQUENCE",
ObjectType::Stage => "STAGE",
ObjectType::Type => "TYPE",
ObjectType::User => "USER",
ObjectType::Stream => "STREAM",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Types supported by `KILL` statements.
pub enum KillType {
/// Kill a connection.
Connection,
/// Kill a running query.
Query,
/// Kill a mutation (ClickHouse).
Mutation,
}
impl fmt::Display for KillType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
// MySQL
KillType::Connection => "CONNECTION",
KillType::Query => "QUERY",
// Clickhouse supports Mutation
KillType::Mutation => "MUTATION",
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Distribution style options for Hive tables.
pub enum HiveDistributionStyle {
/// Partitioned distribution with the given columns.
PARTITIONED {
/// Columns used for partitioning.
columns: Vec<ColumnDef>,
},
/// Skewed distribution definition.
SKEWED {
/// Columns participating in the skew definition.
columns: Vec<ColumnDef>,
/// Columns listed in the `ON` clause for skewing.
on: Vec<ColumnDef>,
/// Whether skewed data is stored as directories.
stored_as_directories: bool,
},
/// No distribution style specified.
NONE,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Row format specification for Hive tables (SERDE or DELIMITED).
pub enum HiveRowFormat {
/// SerDe class specification with the implementing class name.
SERDE {
/// The SerDe implementation class name.
class: String,
},
/// Delimited row format with one or more delimiter specifications.
DELIMITED {
/// The list of delimiters used for delimiting fields/lines.
delimiters: Vec<HiveRowDelimiter>,
},
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Format specification for `LOAD DATA` Hive operations.
pub struct HiveLoadDataFormat {
/// SerDe expression used for the table.
pub serde: Expr,
/// Input format expression.
pub input_format: Expr,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A single row delimiter specification for Hive `ROW FORMAT`.
pub struct HiveRowDelimiter {
/// The delimiter kind (fields/lines/etc.).
pub delimiter: HiveDelimiter,
/// The delimiter character identifier.
pub char: Ident,
}
impl fmt::Display for HiveRowDelimiter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ", self.delimiter)?;
write!(f, "{}", self.char)
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Kind of delimiter used in Hive `ROW FORMAT` definitions.
pub enum HiveDelimiter {
/// Fields terminated by a delimiter.
FieldsTerminatedBy,
/// Fields escaped by a character.
FieldsEscapedBy,
/// Collection items terminated by a delimiter.
CollectionItemsTerminatedBy,
/// Map keys terminated by a delimiter.
MapKeysTerminatedBy,
/// Lines terminated by a delimiter.
LinesTerminatedBy,
/// Null represented by a specific token.
NullDefinedAs,
}
impl fmt::Display for HiveDelimiter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use HiveDelimiter::*;
f.write_str(match self {
FieldsTerminatedBy => "FIELDS TERMINATED BY",
FieldsEscapedBy => "ESCAPED BY",
CollectionItemsTerminatedBy => "COLLECTION ITEMS TERMINATED BY",
MapKeysTerminatedBy => "MAP KEYS TERMINATED BY",
LinesTerminatedBy => "LINES TERMINATED BY",
NullDefinedAs => "NULL DEFINED AS",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Describe output format options for Hive `DESCRIBE`/`EXPLAIN`.
pub enum HiveDescribeFormat {
/// Extended describe output.
Extended,
/// Formatted describe output.
Formatted,
}
impl fmt::Display for HiveDescribeFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use HiveDescribeFormat::*;
f.write_str(match self {
Extended => "EXTENDED",
Formatted => "FORMATTED",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Aliases accepted for describe-style commands.
pub enum DescribeAlias {
/// `DESCRIBE` alias.
Describe,
/// `EXPLAIN` alias.
Explain,
/// `DESC` alias.
Desc,
}
impl fmt::Display for DescribeAlias {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DescribeAlias::*;
f.write_str(match self {
Describe => "DESCRIBE",
Explain => "EXPLAIN",
Desc => "DESC",
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[allow(clippy::large_enum_variant)]
/// Hive input/output format specification used in `CREATE TABLE`.
pub enum HiveIOFormat {
/// Generic IO format with separate input and output expressions.
IOF {
/// Expression for the input format.
input_format: Expr,
/// Expression for the output format.
output_format: Expr,
},
/// File format wrapper referencing a `FileFormat` variant.
FileFormat {
/// The file format used for storage.
format: FileFormat,
},
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Hive table format and storage-related options.
pub struct HiveFormat {
/// Optional row format specification.
pub row_format: Option<HiveRowFormat>,
/// Optional SerDe properties expressed as SQL options.
pub serde_properties: Option<Vec<SqlOption>>,
/// Optional input/output storage format details.
pub storage: Option<HiveIOFormat>,
/// Optional location (URI or path) for table data.
pub location: Option<String>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A clustered index column specification.
pub struct ClusteredIndex {
/// Column identifier for the clustered index entry.
pub name: Ident,
/// Optional sort direction: `Some(true)` for ASC, `Some(false)` for DESC, `None` for unspecified.
pub asc: Option<bool>,
}
impl fmt::Display for ClusteredIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
match self.asc {
Some(true) => write!(f, " ASC"),
Some(false) => write!(f, " DESC"),
_ => Ok(()),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clustered options used for `CREATE TABLE` clustered/indexed storage.
pub enum TableOptionsClustered {
/// Use a columnstore index.
ColumnstoreIndex,
/// Columnstore index with an explicit ordering of columns.
ColumnstoreIndexOrder(Vec<Ident>),
/// A named clustered index with one or more columns.
Index(Vec<ClusteredIndex>),
}
impl fmt::Display for TableOptionsClustered {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableOptionsClustered::ColumnstoreIndex => {
write!(f, "CLUSTERED COLUMNSTORE INDEX")
}
TableOptionsClustered::ColumnstoreIndexOrder(values) => {
write!(
f,
"CLUSTERED COLUMNSTORE INDEX ORDER ({})",
display_comma_separated(values)
)
}
TableOptionsClustered::Index(values) => {
write!(f, "CLUSTERED INDEX ({})", display_comma_separated(values))
}
}
}
}
/// Specifies which partition the boundary values on table partitioning belongs to.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum PartitionRangeDirection {
/// LEFT range direction.
Left,
/// RIGHT range direction.
Right,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// SQL option syntax used in table and server definitions.
pub enum SqlOption {
/// Clustered represents the clustered version of table storage for MSSQL.
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TableOptions>
Clustered(TableOptionsClustered),
/// Single identifier options, e.g. `HEAP` for MSSQL.
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TableOptions>
Ident(Ident),
/// Any option that consists of a key value pair where the value is an expression. e.g.
///
/// WITH(DISTRIBUTION = ROUND_ROBIN)
KeyValue {
/// The option key identifier.
key: Ident,
/// The expression value for the option.
value: Expr,
},
/// One or more table partitions and represents which partition the boundary values belong to,
/// e.g.
///
/// PARTITION (id RANGE LEFT FOR VALUES (10, 20, 30, 40))
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-azure-sql-data-warehouse?view=aps-pdw-2016-au7#TablePartitionOptions>
Partition {
/// The partition column name.
column_name: Ident,
/// Optional direction for the partition range (LEFT/RIGHT).
range_direction: Option<PartitionRangeDirection>,
/// Values that define the partition boundaries.
for_values: Vec<Expr>,
},
/// Comment parameter (supports `=` and no `=` syntax)
Comment(CommentDef),
/// MySQL TableSpace option
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
TableSpace(TablespaceOption),
/// An option representing a key value pair, where the value is a parenthesized list and with an optional name
/// e.g.
///
/// UNION = (tbl_name\[,tbl_name\]...) <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
/// ENGINE = ReplicatedMergeTree('/table_name','{replica}', ver) <https://clickhouse.com/docs/engines/table-engines/mergetree-family/replication>
/// ENGINE = SummingMergeTree(\[columns\]) <https://clickhouse.com/docs/engines/table-engines/mergetree-family/summingmergetree>
NamedParenthesizedList(NamedParenthesizedList),
}
impl fmt::Display for SqlOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SqlOption::Clustered(c) => write!(f, "{c}"),
SqlOption::Ident(ident) => {
write!(f, "{ident}")
}
SqlOption::KeyValue { key: name, value } => {
write!(f, "{name} = {value}")
}
SqlOption::Partition {
column_name,
range_direction,
for_values,
} => {
let direction = match range_direction {
Some(PartitionRangeDirection::Left) => " LEFT",
Some(PartitionRangeDirection::Right) => " RIGHT",
None => "",
};
write!(
f,
"PARTITION ({} RANGE{} FOR VALUES ({}))",
column_name,
direction,
display_comma_separated(for_values)
)
}
SqlOption::TableSpace(tablespace_option) => {
write!(f, "TABLESPACE {}", tablespace_option.name)?;
match tablespace_option.storage {
Some(StorageType::Disk) => write!(f, " STORAGE DISK"),
Some(StorageType::Memory) => write!(f, " STORAGE MEMORY"),
None => Ok(()),
}
}
SqlOption::Comment(comment) => match comment {
CommentDef::WithEq(comment) => {
write!(f, "COMMENT = '{comment}'")
}
CommentDef::WithoutEq(comment) => {
write!(f, "COMMENT '{comment}'")
}
},
SqlOption::NamedParenthesizedList(value) => {
write!(f, "{} = ", value.key)?;
if let Some(key) = &value.name {
write!(f, "{key}")?;
}
if !value.values.is_empty() {
write!(f, "({})", display_comma_separated(&value.values))?
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Storage type options for a tablespace.
pub enum StorageType {
/// Store on disk.
Disk,
/// Store in memory.
Memory,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySql TableSpace option
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
pub struct TablespaceOption {
/// Name of the tablespace.
pub name: String,
/// Optional storage type for the tablespace.
pub storage: Option<StorageType>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A key/value identifier pair used for secret or key-based options.
pub struct SecretOption {
/// The option key identifier.
pub key: Ident,
/// The option value identifier.
pub value: Ident,
}
impl fmt::Display for SecretOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.key, self.value)
}
}
/// A `CREATE SERVER` statement.
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createserver.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateServerStatement {
/// The server name.
pub name: ObjectName,
/// Whether `IF NOT EXISTS` was specified.
pub if_not_exists: bool,
/// Optional server type identifier.
pub server_type: Option<Ident>,
/// Optional server version identifier.
pub version: Option<Ident>,
/// Foreign-data wrapper object name.
pub foreign_data_wrapper: ObjectName,
/// Optional list of server options.
pub options: Option<Vec<CreateServerOption>>,
}
impl fmt::Display for CreateServerStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let CreateServerStatement {
name,
if_not_exists,
server_type,
version,
foreign_data_wrapper,
options,
} = self;
write!(
f,
"CREATE SERVER {if_not_exists}{name} ",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
)?;
if let Some(st) = server_type {
write!(f, "TYPE {st} ")?;
}
if let Some(v) = version {
write!(f, "VERSION {v} ")?;
}
write!(f, "FOREIGN DATA WRAPPER {foreign_data_wrapper}")?;
if let Some(o) = options {
write!(f, " OPTIONS ({o})", o = display_comma_separated(o))?;
}
Ok(())
}
}
/// A key/value option for `CREATE SERVER`.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateServerOption {
/// Option key identifier.
pub key: Ident,
/// Option value identifier.
pub value: Ident,
}
impl fmt::Display for CreateServerOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.key, self.value)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options supported by DuckDB for `ATTACH DATABASE`.
pub enum AttachDuckDBDatabaseOption {
/// READ_ONLY option, optional boolean value.
ReadOnly(Option<bool>),
/// TYPE option specifying a database type identifier.
Type(Ident),
}
impl fmt::Display for AttachDuckDBDatabaseOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AttachDuckDBDatabaseOption::ReadOnly(Some(true)) => write!(f, "READ_ONLY true"),
AttachDuckDBDatabaseOption::ReadOnly(Some(false)) => write!(f, "READ_ONLY false"),
AttachDuckDBDatabaseOption::ReadOnly(None) => write!(f, "READ_ONLY"),
AttachDuckDBDatabaseOption::Type(t) => write!(f, "TYPE {t}"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Mode for transactions: access mode or isolation level.
pub enum TransactionMode {
/// Access mode for a transaction (e.g. `READ ONLY` / `READ WRITE`).
AccessMode(TransactionAccessMode),
/// Isolation level for a transaction (e.g. `SERIALIZABLE`).
IsolationLevel(TransactionIsolationLevel),
}
impl fmt::Display for TransactionMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionMode::*;
match self {
AccessMode(access_mode) => write!(f, "{access_mode}"),
IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {iso_level}"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Transaction access mode (READ ONLY / READ WRITE).
pub enum TransactionAccessMode {
/// READ ONLY access mode.
ReadOnly,
/// READ WRITE access mode.
ReadWrite,
}
impl fmt::Display for TransactionAccessMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionAccessMode::*;
f.write_str(match self {
ReadOnly => "READ ONLY",
ReadWrite => "READ WRITE",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Transaction isolation levels.
pub enum TransactionIsolationLevel {
/// READ UNCOMMITTED isolation level.
ReadUncommitted,
/// READ COMMITTED isolation level.
ReadCommitted,
/// REPEATABLE READ isolation level.
RepeatableRead,
/// SERIALIZABLE isolation level.
Serializable,
/// SNAPSHOT isolation level.
Snapshot,
}
impl fmt::Display for TransactionIsolationLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionIsolationLevel::*;
f.write_str(match self {
ReadUncommitted => "READ UNCOMMITTED",
ReadCommitted => "READ COMMITTED",
RepeatableRead => "REPEATABLE READ",
Serializable => "SERIALIZABLE",
Snapshot => "SNAPSHOT",
})
}
}
/// Modifier for the transaction in the `BEGIN` syntax
///
/// SQLite: <https://sqlite.org/lang_transaction.html>
/// MS-SQL: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/try-catch-transact-sql>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TransactionModifier {
/// DEFERRED transaction modifier.
Deferred,
/// IMMEDIATE transaction modifier.
Immediate,
/// EXCLUSIVE transaction modifier.
Exclusive,
/// TRY block modifier (MS-SQL style TRY/CATCH).
Try,
/// CATCH block modifier (MS-SQL style TRY/CATCH).
Catch,
}
impl fmt::Display for TransactionModifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionModifier::*;
f.write_str(match self {
Deferred => "DEFERRED",
Immediate => "IMMEDIATE",
Exclusive => "EXCLUSIVE",
Try => "TRY",
Catch => "CATCH",
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Filter forms usable in SHOW statements.
pub enum ShowStatementFilter {
/// Filter using LIKE pattern.
Like(String),
/// Filter using ILIKE pattern.
ILike(String),
/// Filter using a WHERE expression.
Where(Expr),
/// Filter provided without a keyword (raw string).
NoKeyword(String),
}
impl fmt::Display for ShowStatementFilter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ShowStatementFilter::*;
match self {
Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)),
Where(expr) => write!(f, "WHERE {expr}"),
NoKeyword(pattern) => write!(f, "'{}'", value::escape_single_quote_string(pattern)),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Clause types used with SHOW ... IN/FROM.
pub enum ShowStatementInClause {
/// Use the `IN` clause.
IN,
/// Use the `FROM` clause.
FROM,
}
impl fmt::Display for ShowStatementInClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ShowStatementInClause::*;
match self {
FROM => write!(f, "FROM"),
IN => write!(f, "IN"),
}
}
}
/// Sqlite specific syntax
///
/// See [Sqlite documentation](https://sqlite.org/lang_conflict.html)
/// for more details.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SqliteOnConflict {
/// Use ROLLBACK on conflict.
Rollback,
/// Use ABORT on conflict.
Abort,
/// Use FAIL on conflict.
Fail,
/// Use IGNORE on conflict.
Ignore,
/// Use REPLACE on conflict.
Replace,
}
impl fmt::Display for SqliteOnConflict {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use SqliteOnConflict::*;
match self {
Rollback => write!(f, "OR ROLLBACK"),
Abort => write!(f, "OR ABORT"),
Fail => write!(f, "OR FAIL"),
Ignore => write!(f, "OR IGNORE"),
Replace => write!(f, "OR REPLACE"),
}
}
}
/// Mysql specific syntax
///
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/replace.html)
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/insert.html)
/// for more details.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MysqlInsertPriority {
/// LOW_PRIORITY modifier for INSERT/REPLACE.
LowPriority,
/// DELAYED modifier for INSERT/REPLACE.
Delayed,
/// HIGH_PRIORITY modifier for INSERT/REPLACE.
HighPriority,
}
impl fmt::Display for crate::ast::MysqlInsertPriority {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MysqlInsertPriority::*;
match self {
LowPriority => write!(f, "LOW_PRIORITY"),
Delayed => write!(f, "DELAYED"),
HighPriority => write!(f, "HIGH_PRIORITY"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Source for the `COPY` command: a table or a query.
pub enum CopySource {
/// Copy from a table with optional column list.
Table {
/// The name of the table to copy from.
table_name: ObjectName,
/// A list of column names to copy. Empty list means that all columns
/// are copied.
columns: Vec<Ident>,
},
/// Copy from the results of a query.
Query(Box<Query>),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Target for the `COPY` command: STDIN, STDOUT, a file, or a program.
pub enum CopyTarget {
/// Use standard input as the source.
Stdin,
/// Use standard output as the target.
Stdout,
/// Read from or write to a file.
File {
/// The path name of the input or output file.
filename: String,
},
/// Use a program as the source or target (shell command).
Program {
/// A command to execute
command: String,
},
}
impl fmt::Display for CopyTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CopyTarget::*;
match self {
Stdin => write!(f, "STDIN"),
Stdout => write!(f, "STDOUT"),
File { filename } => write!(f, "'{}'", value::escape_single_quote_string(filename)),
Program { command } => write!(
f,
"PROGRAM '{}'",
value::escape_single_quote_string(command)
),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Action to take `ON COMMIT` for temporary tables.
pub enum OnCommit {
/// Delete rows on commit.
DeleteRows,
/// Preserve rows on commit.
PreserveRows,
/// Drop the table on commit.
Drop,
}
/// An option in `COPY` statement.
///
/// <https://www.postgresql.org/docs/14/sql-copy.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyOption {
/// FORMAT format_name
Format(Ident),
/// FREEZE \[ boolean \]
Freeze(bool),
/// DELIMITER 'delimiter_character'
Delimiter(char),
/// NULL 'null_string'
Null(String),
/// HEADER \[ boolean \]
Header(bool),
/// QUOTE 'quote_character'
Quote(char),
/// ESCAPE 'escape_character'
Escape(char),
/// FORCE_QUOTE { ( column_name [, ...] ) | * }
ForceQuote(Vec<Ident>),
/// FORCE_NOT_NULL ( column_name [, ...] )
ForceNotNull(Vec<Ident>),
/// FORCE_NULL ( column_name [, ...] )
ForceNull(Vec<Ident>),
/// ENCODING 'encoding_name'
Encoding(String),
}
impl fmt::Display for CopyOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CopyOption::*;
match self {
Format(name) => write!(f, "FORMAT {name}"),
Freeze(true) => write!(f, "FREEZE"),
Freeze(false) => write!(f, "FREEZE FALSE"),
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
Header(true) => write!(f, "HEADER"),
Header(false) => write!(f, "HEADER FALSE"),
Quote(char) => write!(f, "QUOTE '{char}'"),
Escape(char) => write!(f, "ESCAPE '{char}'"),
ForceQuote(columns) => write!(f, "FORCE_QUOTE ({})", display_comma_separated(columns)),
ForceNotNull(columns) => {
write!(f, "FORCE_NOT_NULL ({})", display_comma_separated(columns))
}
ForceNull(columns) => write!(f, "FORCE_NULL ({})", display_comma_separated(columns)),
Encoding(name) => write!(f, "ENCODING '{}'", value::escape_single_quote_string(name)),
}
}
}
/// An option in `COPY` statement before PostgreSQL version 9.0.
///
/// [PostgreSQL](https://www.postgresql.org/docs/8.4/sql-copy.html)
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_COPY-alphabetical-parm-list.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyLegacyOption {
/// ACCEPTANYDATE
AcceptAnyDate,
/// ACCEPTINVCHARS
AcceptInvChars(Option<String>),
/// ADDQUOTES
AddQuotes,
/// ALLOWOVERWRITE
AllowOverwrite,
/// BINARY
Binary,
/// BLANKSASNULL
BlankAsNull,
/// BZIP2
Bzip2,
/// CLEANPATH
CleanPath,
/// COMPUPDATE [ PRESET | { ON | TRUE } | { OFF | FALSE } ]
CompUpdate {
/// Whether the COMPUPDATE PRESET option was used.
preset: bool,
/// Optional enabled flag for COMPUPDATE.
enabled: Option<bool>,
},
/// CSV ...
Csv(Vec<CopyLegacyCsvOption>),
/// DATEFORMAT \[ AS \] {'dateformat_string' | 'auto' }
DateFormat(Option<String>),
/// DELIMITER \[ AS \] 'delimiter_character'
Delimiter(char),
/// EMPTYASNULL
EmptyAsNull,
/// `ENCRYPTED \[ AUTO \]`
Encrypted {
/// Whether `AUTO` was specified for encryption.
auto: bool,
},
/// ESCAPE
Escape,
/// EXTENSION 'extension-name'
Extension(String),
/// FIXEDWIDTH \[ AS \] 'fixedwidth-spec'
FixedWidth(String),
/// GZIP
Gzip,
/// HEADER
Header,
/// IAM_ROLE { DEFAULT | 'arn:aws:iam::123456789:role/role1' }
IamRole(IamRoleKind),
/// IGNOREHEADER \[ AS \] number_rows
IgnoreHeader(u64),
/// JSON \[ AS \] 'json_option'
Json(Option<String>),
/// MANIFEST \[ VERBOSE \]
Manifest {
/// Whether the MANIFEST is verbose.
verbose: bool,
},
/// MAXFILESIZE \[ AS \] max-size \[ MB | GB \]
MaxFileSize(FileSize),
/// `NULL \[ AS \] 'null_string'`
Null(String),
/// `PARALLEL [ { ON | TRUE } | { OFF | FALSE } ]`
Parallel(Option<bool>),
/// PARQUET
Parquet,
/// PARTITION BY ( column_name [, ... ] ) \[ INCLUDE \]
PartitionBy(UnloadPartitionBy),
/// REGION \[ AS \] 'aws-region' }
Region(String),
/// REMOVEQUOTES
RemoveQuotes,
/// ROWGROUPSIZE \[ AS \] size \[ MB | GB \]
RowGroupSize(FileSize),
/// STATUPDATE [ { ON | TRUE } | { OFF | FALSE } ]
StatUpdate(Option<bool>),
/// TIMEFORMAT \[ AS \] {'timeformat_string' | 'auto' | 'epochsecs' | 'epochmillisecs' }
TimeFormat(Option<String>),
/// TRUNCATECOLUMNS
TruncateColumns,
/// ZSTD
Zstd,
/// Redshift `CREDENTIALS 'auth-args'`
/// <https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html>
Credentials(String),
}
impl fmt::Display for CopyLegacyOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CopyLegacyOption::*;
match self {
AcceptAnyDate => write!(f, "ACCEPTANYDATE"),
AcceptInvChars(ch) => {
write!(f, "ACCEPTINVCHARS")?;
if let Some(ch) = ch {
write!(f, " '{}'", value::escape_single_quote_string(ch))?;
}
Ok(())
}
AddQuotes => write!(f, "ADDQUOTES"),
AllowOverwrite => write!(f, "ALLOWOVERWRITE"),
Binary => write!(f, "BINARY"),
BlankAsNull => write!(f, "BLANKSASNULL"),
Bzip2 => write!(f, "BZIP2"),
CleanPath => write!(f, "CLEANPATH"),
CompUpdate { preset, enabled } => {
write!(f, "COMPUPDATE")?;
if *preset {
write!(f, " PRESET")?;
} else if let Some(enabled) = enabled {
write!(
f,
"{}",
match enabled {
true => " TRUE",
false => " FALSE",
}
)?;
}
Ok(())
}
Csv(opts) => {
write!(f, "CSV")?;
if !opts.is_empty() {
write!(f, " {}", display_separated(opts, " "))?;
}
Ok(())
}
DateFormat(fmt) => {
write!(f, "DATEFORMAT")?;
if let Some(fmt) = fmt {
write!(f, " '{}'", value::escape_single_quote_string(fmt))?;
}
Ok(())
}
Delimiter(char) => write!(f, "DELIMITER '{char}'"),
EmptyAsNull => write!(f, "EMPTYASNULL"),
Encrypted { auto } => write!(f, "ENCRYPTED{}", if *auto { " AUTO" } else { "" }),
Escape => write!(f, "ESCAPE"),
Extension(ext) => write!(f, "EXTENSION '{}'", value::escape_single_quote_string(ext)),
FixedWidth(spec) => write!(
f,
"FIXEDWIDTH '{}'",
value::escape_single_quote_string(spec)
),
Gzip => write!(f, "GZIP"),
Header => write!(f, "HEADER"),
IamRole(role) => write!(f, "IAM_ROLE {role}"),
IgnoreHeader(num_rows) => write!(f, "IGNOREHEADER {num_rows}"),
Json(opt) => {
write!(f, "JSON")?;
if let Some(opt) = opt {
write!(f, " AS '{}'", value::escape_single_quote_string(opt))?;
}
Ok(())
}
Manifest { verbose } => write!(f, "MANIFEST{}", if *verbose { " VERBOSE" } else { "" }),
MaxFileSize(file_size) => write!(f, "MAXFILESIZE {file_size}"),
Null(string) => write!(f, "NULL '{}'", value::escape_single_quote_string(string)),
Parallel(enabled) => {
write!(
f,
"PARALLEL{}",
match enabled {
Some(true) => " TRUE",
Some(false) => " FALSE",
_ => "",
}
)
}
Parquet => write!(f, "PARQUET"),
PartitionBy(p) => write!(f, "{p}"),
Region(region) => write!(f, "REGION '{}'", value::escape_single_quote_string(region)),
RemoveQuotes => write!(f, "REMOVEQUOTES"),
RowGroupSize(file_size) => write!(f, "ROWGROUPSIZE {file_size}"),
StatUpdate(enabled) => {
write!(
f,
"STATUPDATE{}",
match enabled {
Some(true) => " TRUE",
Some(false) => " FALSE",
_ => "",
}
)
}
TimeFormat(fmt) => {
write!(f, "TIMEFORMAT")?;
if let Some(fmt) = fmt {
write!(f, " '{}'", value::escape_single_quote_string(fmt))?;
}
Ok(())
}
TruncateColumns => write!(f, "TRUNCATECOLUMNS"),
Zstd => write!(f, "ZSTD"),
Credentials(s) => write!(f, "CREDENTIALS '{}'", value::escape_single_quote_string(s)),
}
}
}
/// ```sql
/// SIZE \[ MB | GB \]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FileSize {
/// Numeric size value.
pub size: ValueWithSpan,
/// Optional unit for the size (MB or GB).
pub unit: Option<FileSizeUnit>,
}
impl fmt::Display for FileSize {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.size)?;
if let Some(unit) = &self.unit {
write!(f, " {unit}")?;
}
Ok(())
}
}
/// Units for `FileSize` (MB or GB).
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FileSizeUnit {
/// Megabytes.
MB,
/// Gigabytes.
GB,
}
impl fmt::Display for FileSizeUnit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FileSizeUnit::MB => write!(f, "MB"),
FileSizeUnit::GB => write!(f, "GB"),
}
}
}
/// Specifies the partition keys for the unload operation
///
/// ```sql
/// PARTITION BY ( column_name [, ... ] ) [ INCLUDE ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UnloadPartitionBy {
/// Columns used to partition the unload output.
pub columns: Vec<Ident>,
/// Whether to include the partition in the output.
pub include: bool,
}
impl fmt::Display for UnloadPartitionBy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"PARTITION BY ({}){}",
display_comma_separated(&self.columns),
if self.include { " INCLUDE" } else { "" }
)
}
}
/// An `IAM_ROLE` option in the AWS ecosystem
///
/// [Redshift COPY](https://docs.aws.amazon.com/redshift/latest/dg/copy-parameters-authorization.html#copy-iam-role)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IamRoleKind {
/// Default role
Default,
/// Specific role ARN, for example: `arn:aws:iam::123456789:role/role1`
Arn(String),
}
impl fmt::Display for IamRoleKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
IamRoleKind::Default => write!(f, "DEFAULT"),
IamRoleKind::Arn(arn) => write!(f, "'{arn}'"),
}
}
}
/// A `CSV` option in `COPY` statement before PostgreSQL version 9.0.
///
/// <https://www.postgresql.org/docs/8.4/sql-copy.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyLegacyCsvOption {
/// HEADER
Header,
/// QUOTE \[ AS \] 'quote_character'
Quote(char),
/// ESCAPE \[ AS \] 'escape_character'
Escape(char),
/// FORCE QUOTE { column_name [, ...] | * }
ForceQuote(Vec<Ident>),
/// FORCE NOT NULL column_name [, ...]
ForceNotNull(Vec<Ident>),
}
impl fmt::Display for CopyLegacyCsvOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CopyLegacyCsvOption::*;
match self {
Header => write!(f, "HEADER"),
Quote(char) => write!(f, "QUOTE '{char}'"),
Escape(char) => write!(f, "ESCAPE '{char}'"),
ForceQuote(columns) => write!(f, "FORCE QUOTE {}", display_comma_separated(columns)),
ForceNotNull(columns) => {
write!(f, "FORCE NOT NULL {}", display_comma_separated(columns))
}
}
}
}
/// Objects that can be discarded with `DISCARD`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DiscardObject {
/// Discard all session state.
ALL,
/// Discard cached plans.
PLANS,
/// Discard sequence values.
SEQUENCES,
/// Discard temporary objects.
TEMP,
}
impl fmt::Display for DiscardObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DiscardObject::ALL => f.write_str("ALL"),
DiscardObject::PLANS => f.write_str("PLANS"),
DiscardObject::SEQUENCES => f.write_str("SEQUENCES"),
DiscardObject::TEMP => f.write_str("TEMP"),
}
}
}
/// Types of flush operations supported by `FLUSH`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushType {
/// Flush binary logs.
BinaryLogs,
/// Flush engine logs.
EngineLogs,
/// Flush error logs.
ErrorLogs,
/// Flush general logs.
GeneralLogs,
/// Flush hosts information.
Hosts,
/// Flush logs.
Logs,
/// Flush privileges.
Privileges,
/// Flush optimizer costs.
OptimizerCosts,
/// Flush relay logs.
RelayLogs,
/// Flush slow logs.
SlowLogs,
/// Flush status.
Status,
/// Flush user resources.
UserResources,
/// Flush table data.
Tables,
}
impl fmt::Display for FlushType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FlushType::BinaryLogs => f.write_str("BINARY LOGS"),
FlushType::EngineLogs => f.write_str("ENGINE LOGS"),
FlushType::ErrorLogs => f.write_str("ERROR LOGS"),
FlushType::GeneralLogs => f.write_str("GENERAL LOGS"),
FlushType::Hosts => f.write_str("HOSTS"),
FlushType::Logs => f.write_str("LOGS"),
FlushType::Privileges => f.write_str("PRIVILEGES"),
FlushType::OptimizerCosts => f.write_str("OPTIMIZER_COSTS"),
FlushType::RelayLogs => f.write_str("RELAY LOGS"),
FlushType::SlowLogs => f.write_str("SLOW LOGS"),
FlushType::Status => f.write_str("STATUS"),
FlushType::UserResources => f.write_str("USER_RESOURCES"),
FlushType::Tables => f.write_str("TABLES"),
}
}
}
/// Location modifier for flush commands.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushLocation {
/// Do not write changes to the binary log.
NoWriteToBinlog,
/// Apply flush locally.
Local,
}
impl fmt::Display for FlushLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FlushLocation::NoWriteToBinlog => f.write_str("NO_WRITE_TO_BINLOG"),
FlushLocation::Local => f.write_str("LOCAL"),
}
}
}
/// Optional context modifier for statements that can be or `LOCAL`, `GLOBAL`, or `SESSION`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ContextModifier {
/// `LOCAL` identifier, usually related to transactional states.
Local,
/// `SESSION` identifier
Session,
/// `GLOBAL` identifier
Global,
}
impl fmt::Display for ContextModifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Local => {
write!(f, "LOCAL ")
}
Self::Session => {
write!(f, "SESSION ")
}
Self::Global => {
write!(f, "GLOBAL ")
}
}
}
}
/// Function describe in DROP FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DropFunctionOption {
/// `RESTRICT` option for DROP FUNCTION.
Restrict,
/// `CASCADE` option for DROP FUNCTION.
Cascade,
}
impl fmt::Display for DropFunctionOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DropFunctionOption::Restrict => write!(f, "RESTRICT "),
DropFunctionOption::Cascade => write!(f, "CASCADE "),
}
}
}
/// Generic function description for DROP FUNCTION and CREATE TRIGGER.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionDesc {
/// The function name.
pub name: ObjectName,
/// Optional list of function arguments.
pub args: Option<Vec<OperateFunctionArg>>,
}
impl fmt::Display for FunctionDesc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(args) = &self.args {
write!(f, "({})", display_comma_separated(args))?;
}
Ok(())
}
}
/// Function argument in CREATE OR DROP FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OperateFunctionArg {
/// Optional argument mode (`IN`, `OUT`, `INOUT`).
pub mode: Option<ArgMode>,
/// Optional argument identifier/name.
pub name: Option<Ident>,
/// The data type of the argument.
pub data_type: DataType,
/// Optional default expression for the argument.
pub default_expr: Option<Expr>,
}
impl OperateFunctionArg {
/// Returns an unnamed argument.
pub fn unnamed(data_type: DataType) -> Self {
Self {
mode: None,
name: None,
data_type,
default_expr: None,
}
}
/// Returns an argument with name.
pub fn with_name(name: &str, data_type: DataType) -> Self {
Self {
mode: None,
name: Some(name.into()),
data_type,
default_expr: None,
}
}
}
impl fmt::Display for OperateFunctionArg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(mode) = &self.mode {
write!(f, "{mode} ")?;
}
if let Some(name) = &self.name {
write!(f, "{name} ")?;
}
write!(f, "{}", self.data_type)?;
if let Some(default_expr) = &self.default_expr {
write!(f, " = {default_expr}")?;
}
Ok(())
}
}
/// The mode of an argument in CREATE FUNCTION.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ArgMode {
/// `IN` mode.
In,
/// `OUT` mode.
Out,
/// `INOUT` mode.
InOut,
/// `VARIADIC` mode.
Variadic,
}
impl fmt::Display for ArgMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ArgMode::In => write!(f, "IN"),
ArgMode::Out => write!(f, "OUT"),
ArgMode::InOut => write!(f, "INOUT"),
ArgMode::Variadic => write!(f, "VARIADIC"),
}
}
}
/// These attributes inform the query optimizer about the behavior of the function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionBehavior {
/// Function is immutable.
Immutable,
/// Function is stable.
Stable,
/// Function is volatile.
Volatile,
}
impl fmt::Display for FunctionBehavior {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionBehavior::Immutable => write!(f, "IMMUTABLE"),
FunctionBehavior::Stable => write!(f, "STABLE"),
FunctionBehavior::Volatile => write!(f, "VOLATILE"),
}
}
}
/// Security attribute for functions: SECURITY DEFINER or SECURITY INVOKER.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionSecurity {
/// Execute the function with the privileges of the user who defined it.
Definer,
/// Execute the function with the privileges of the user who invokes it.
Invoker,
}
impl fmt::Display for FunctionSecurity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionSecurity::Definer => write!(f, "SECURITY DEFINER"),
FunctionSecurity::Invoker => write!(f, "SECURITY INVOKER"),
}
}
}
/// Value for a SET configuration parameter in a CREATE FUNCTION statement.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionSetValue {
/// SET param = DEFAULT / SET param TO DEFAULT
Default,
/// SET param = value1, value2, ...
Values(Vec<Expr>),
/// SET param FROM CURRENT
FromCurrent,
}
/// A SET configuration_parameter clause in a CREATE FUNCTION statement.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-createfunction.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionDefinitionSetParam {
/// The name of the configuration parameter.
pub name: ObjectName,
/// The value to set for the parameter.
pub value: FunctionSetValue,
}
impl fmt::Display for FunctionDefinitionSetParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SET {} ", self.name)?;
match &self.value {
FunctionSetValue::Default => write!(f, "= DEFAULT"),
FunctionSetValue::Values(values) => {
write!(f, "= {}", display_comma_separated(values))
}
FunctionSetValue::FromCurrent => write!(f, "FROM CURRENT"),
}
}
}
/// These attributes describe the behavior of the function when called with a null argument.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionCalledOnNull {
/// Function is called even when inputs are null.
CalledOnNullInput,
/// Function returns null when any input is null.
ReturnsNullOnNullInput,
/// Function is strict about null inputs.
Strict,
}
impl fmt::Display for FunctionCalledOnNull {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionCalledOnNull::CalledOnNullInput => write!(f, "CALLED ON NULL INPUT"),
FunctionCalledOnNull::ReturnsNullOnNullInput => write!(f, "RETURNS NULL ON NULL INPUT"),
FunctionCalledOnNull::Strict => write!(f, "STRICT"),
}
}
}
/// If it is safe for PostgreSQL to call the function from multiple threads at once
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionParallel {
/// The function is not safe to run in parallel.
Unsafe,
/// The function is restricted for parallel execution.
Restricted,
/// The function is safe to run in parallel.
Safe,
}
impl fmt::Display for FunctionParallel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionParallel::Unsafe => write!(f, "PARALLEL UNSAFE"),
FunctionParallel::Restricted => write!(f, "PARALLEL RESTRICTED"),
FunctionParallel::Safe => write!(f, "PARALLEL SAFE"),
}
}
}
/// [BigQuery] Determinism specifier used in a UDF definition.
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionDeterminismSpecifier {
/// Function is deterministic.
Deterministic,
/// Function is not deterministic.
NotDeterministic,
}
impl fmt::Display for FunctionDeterminismSpecifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FunctionDeterminismSpecifier::Deterministic => {
write!(f, "DETERMINISTIC")
}
FunctionDeterminismSpecifier::NotDeterministic => {
write!(f, "NOT DETERMINISTIC")
}
}
}
}
/// Represent the expression body of a `CREATE FUNCTION` statement as well as
/// where within the statement, the body shows up.
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
/// [PostgreSQL]: https://www.postgresql.org/docs/15/sql-createfunction.html
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateFunctionBody {
/// A function body expression using the 'AS' keyword and shows up
/// before any `OPTIONS` clause.
///
/// Example:
/// ```sql
/// CREATE FUNCTION myfunc(x FLOAT64, y FLOAT64) RETURNS FLOAT64
/// AS (x * y)
/// OPTIONS(description="desc");
/// ```
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
AsBeforeOptions {
/// The primary expression.
body: Expr,
/// Link symbol if the primary expression contains the name of shared library file.
///
/// Example:
/// ```sql
/// CREATE FUNCTION cas_in(input cstring) RETURNS cas
/// AS 'MODULE_PATHNAME', 'cas_in_wrapper'
/// ```
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
link_symbol: Option<Expr>,
},
/// A function body expression using the 'AS' keyword and shows up
/// after any `OPTIONS` clause.
///
/// Example:
/// ```sql
/// CREATE FUNCTION myfunc(x FLOAT64, y FLOAT64) RETURNS FLOAT64
/// OPTIONS(description="desc")
/// AS (x * y);
/// ```
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_11
AsAfterOptions(Expr),
/// Function body with statements before the `RETURN` keyword.
///
/// Example:
/// ```sql
/// CREATE FUNCTION my_scalar_udf(a INT, b INT)
/// RETURNS INT
/// AS
/// BEGIN
/// DECLARE c INT;
/// SET c = a + b;
/// RETURN c;
/// END
/// ```
///
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
AsBeginEnd(BeginEndStatements),
/// Function body expression using the 'RETURN' keyword.
///
/// Example:
/// ```sql
/// CREATE FUNCTION myfunc(a INTEGER, IN b INTEGER = 1) RETURNS INTEGER
/// LANGUAGE SQL
/// RETURN a + b;
/// ```
///
/// [PostgreSQL]: https://www.postgresql.org/docs/current/sql-createfunction.html
Return(Expr),
/// Function body expression using the 'AS RETURN' keywords
///
/// Example:
/// ```sql
/// CREATE FUNCTION myfunc(a INT, b INT)
/// RETURNS TABLE
/// AS RETURN (SELECT a + b AS sum);
/// ```
///
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql
AsReturnExpr(Expr),
/// Function body expression using the 'AS RETURN' keywords, with an un-parenthesized SELECT query
///
/// Example:
/// ```sql
/// CREATE FUNCTION myfunc(a INT, b INT)
/// RETURNS TABLE
/// AS RETURN SELECT a + b AS sum;
/// ```
///
/// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-ver16#select_stmt
AsReturnSelect(Select),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `USING` clause options for `CREATE FUNCTION` (e.g., JAR, FILE, ARCHIVE).
pub enum CreateFunctionUsing {
/// Use a JAR file located at the given URI.
Jar(String),
/// Use a file located at the given URI.
File(String),
/// Use an archive located at the given URI.
Archive(String),
}
impl fmt::Display for CreateFunctionUsing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "USING ")?;
match self {
CreateFunctionUsing::Jar(uri) => write!(f, "JAR '{uri}'"),
CreateFunctionUsing::File(uri) => write!(f, "FILE '{uri}'"),
CreateFunctionUsing::Archive(uri) => write!(f, "ARCHIVE '{uri}'"),
}
}
}
/// `NAME = <EXPR>` arguments for DuckDB macros
///
/// See [Create Macro - DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
/// for more details
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MacroArg {
/// The argument name.
pub name: Ident,
/// Optional default expression for the argument.
pub default_expr: Option<Expr>,
}
impl MacroArg {
/// Returns an argument with name.
pub fn new(name: &str) -> Self {
Self {
name: name.into(),
default_expr: None,
}
}
}
impl fmt::Display for MacroArg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(default_expr) = &self.default_expr {
write!(f, " := {default_expr}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Definition for a DuckDB macro: either an expression or a table-producing query.
pub enum MacroDefinition {
/// The macro is defined as an expression.
Expr(Expr),
/// The macro is defined as a table (query).
Table(Box<Query>),
}
impl fmt::Display for MacroDefinition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MacroDefinition::Expr(expr) => write!(f, "{expr}")?,
MacroDefinition::Table(query) => write!(f, "{query}")?,
}
Ok(())
}
}
/// Schema possible naming variants ([1]).
///
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#schema-definition
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SchemaName {
/// Only schema name specified: `<schema name>`.
Simple(ObjectName),
/// Only authorization identifier specified: `AUTHORIZATION <schema authorization identifier>`.
UnnamedAuthorization(Ident),
/// Both schema name and authorization identifier specified: `<schema name> AUTHORIZATION <schema authorization identifier>`.
NamedAuthorization(ObjectName, Ident),
}
impl fmt::Display for SchemaName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SchemaName::Simple(name) => {
write!(f, "{name}")
}
SchemaName::UnnamedAuthorization(authorization) => {
write!(f, "AUTHORIZATION {authorization}")
}
SchemaName::NamedAuthorization(name, authorization) => {
write!(f, "{name} AUTHORIZATION {authorization}")
}
}
}
}
/// Fulltext search modifiers ([1]).
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html#function_match
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SearchModifier {
/// `IN NATURAL LANGUAGE MODE`.
InNaturalLanguageMode,
/// `IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION`.
InNaturalLanguageModeWithQueryExpansion,
///`IN BOOLEAN MODE`.
InBooleanMode,
///`WITH QUERY EXPANSION`.
WithQueryExpansion,
}
impl fmt::Display for SearchModifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InNaturalLanguageMode => {
write!(f, "IN NATURAL LANGUAGE MODE")?;
}
Self::InNaturalLanguageModeWithQueryExpansion => {
write!(f, "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION")?;
}
Self::InBooleanMode => {
write!(f, "IN BOOLEAN MODE")?;
}
Self::WithQueryExpansion => {
write!(f, "WITH QUERY EXPANSION")?;
}
}
Ok(())
}
}
/// Represents a `LOCK TABLE` clause with optional alias and lock type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct LockTable {
/// The table identifier to lock.
pub table: Ident,
/// Optional alias for the table.
pub alias: Option<Ident>,
/// The type of lock to apply to the table.
pub lock_type: LockTableType,
}
impl fmt::Display for LockTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
table: tbl_name,
alias,
lock_type,
} = self;
write!(f, "{tbl_name} ")?;
if let Some(alias) = alias {
write!(f, "AS {alias} ")?;
}
write!(f, "{lock_type}")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// The type of lock used in `LOCK TABLE` statements.
pub enum LockTableType {
/// Shared/read lock. If `local` is true, it's a local read lock.
Read {
/// Whether the read lock is local.
local: bool,
},
/// Exclusive/write lock. If `low_priority` is true, the write is low priority.
Write {
/// Whether the write lock is low priority.
low_priority: bool,
},
}
impl fmt::Display for LockTableType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Read { local } => {
write!(f, "READ")?;
if *local {
write!(f, " LOCAL")?;
}
}
Self::Write { low_priority } => {
if *low_priority {
write!(f, "LOW_PRIORITY ")?;
}
write!(f, "WRITE")?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Hive-specific `SET LOCATION` helper used in some `LOAD DATA` statements.
pub struct HiveSetLocation {
/// Whether the `SET` keyword was present.
pub has_set: bool,
/// The location identifier.
pub location: Ident,
}
impl fmt::Display for HiveSetLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.has_set {
write!(f, "SET ")?;
}
write!(f, "LOCATION {}", self.location)
}
}
/// MySQL `ALTER TABLE` only [FIRST | AFTER column_name]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `ALTER TABLE` column position specifier: `FIRST` or `AFTER <column>`.
pub enum MySQLColumnPosition {
/// Place the column first in the table.
First,
/// Place the column after the specified identifier.
After(Ident),
}
impl Display for MySQLColumnPosition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MySQLColumnPosition::First => write!(f, "FIRST"),
MySQLColumnPosition::After(ident) => {
let column_name = &ident.value;
write!(f, "AFTER {column_name}")
}
}
}
}
/// MySQL `CREATE VIEW` algorithm parameter: [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `CREATE VIEW` algorithm options.
pub enum CreateViewAlgorithm {
/// `UNDEFINED` algorithm.
Undefined,
/// `MERGE` algorithm.
Merge,
/// `TEMPTABLE` algorithm.
TempTable,
}
impl Display for CreateViewAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateViewAlgorithm::Undefined => write!(f, "UNDEFINED"),
CreateViewAlgorithm::Merge => write!(f, "MERGE"),
CreateViewAlgorithm::TempTable => write!(f, "TEMPTABLE"),
}
}
}
/// MySQL `CREATE VIEW` security parameter: [SQL SECURITY { DEFINER | INVOKER }]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// MySQL `CREATE VIEW` SQL SECURITY options.
pub enum CreateViewSecurity {
/// The view runs with the privileges of the definer.
Definer,
/// The view runs with the privileges of the invoker.
Invoker,
}
impl Display for CreateViewSecurity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateViewSecurity::Definer => write!(f, "DEFINER"),
CreateViewSecurity::Invoker => write!(f, "INVOKER"),
}
}
}
/// [MySQL] `CREATE VIEW` additional parameters
///
/// [MySQL]: https://dev.mysql.com/doc/refman/9.1/en/create-view.html
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateViewParams {
/// Optional view algorithm (e.g., MERGE, TEMPTABLE).
pub algorithm: Option<CreateViewAlgorithm>,
/// Optional definer (the security principal that will own the view).
pub definer: Option<GranteeName>,
/// Optional SQL SECURITY setting for the view.
pub security: Option<CreateViewSecurity>,
}
impl Display for CreateViewParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let CreateViewParams {
algorithm,
definer,
security,
} = self;
if let Some(algorithm) = algorithm {
write!(f, "ALGORITHM = {algorithm} ")?;
}
if let Some(definers) = definer {
write!(f, "DEFINER = {definers} ")?;
}
if let Some(security) = security {
write!(f, "SQL SECURITY {security} ")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Key/Value, where the value is a (optionally named) list of identifiers
///
/// ```sql
/// UNION = (tbl_name[,tbl_name]...)
/// ENGINE = ReplicatedMergeTree('/table_name','{replica}', ver)
/// ENGINE = SummingMergeTree([columns])
/// ```
pub struct NamedParenthesizedList {
/// The option key (identifier) for this named list.
pub key: Ident,
/// Optional secondary name associated with the key.
pub name: Option<Ident>,
/// The list of identifier values for the key.
pub values: Vec<Ident>,
}
/// Snowflake `WITH ROW ACCESS POLICY policy_name ON (identifier, ...)`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
/// <https://docs.snowflake.com/en/user-guide/security-row-intro>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RowAccessPolicy {
/// The fully-qualified policy object name.
pub policy: ObjectName,
/// Identifiers for the columns or objects the policy applies to.
pub on: Vec<Ident>,
}
impl RowAccessPolicy {
/// Create a new `RowAccessPolicy` for the given `policy` and `on` identifiers.
pub fn new(policy: ObjectName, on: Vec<Ident>) -> Self {
Self { policy, on }
}
}
impl Display for RowAccessPolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WITH ROW ACCESS POLICY {} ON ({})",
self.policy,
display_comma_separated(self.on.as_slice())
)
}
}
/// Snowflake `[ WITH ] STORAGE LIFECYCLE POLICY <policy_name> ON ( <col_name> [ , ... ] )`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct StorageLifecyclePolicy {
/// The fully-qualified policy object name.
pub policy: ObjectName,
/// Column names the policy applies to.
pub on: Vec<Ident>,
}
impl Display for StorageLifecyclePolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WITH STORAGE LIFECYCLE POLICY {} ON ({})",
self.policy,
display_comma_separated(self.on.as_slice())
)
}
}
/// Snowflake `WITH TAG ( tag_name = '<tag_value>', ...)`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Tag {
/// The tag key (can be qualified).
pub key: ObjectName,
/// The tag value as a string.
pub value: String,
}
impl Tag {
/// Create a new `Tag` with the given key and value.
pub fn new(key: ObjectName, value: String) -> Self {
Self { key, value }
}
}
impl Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}='{}'", self.key, self.value)
}
}
/// Snowflake `WITH CONTACT ( purpose = contact [ , purpose = contact ...] )`
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ContactEntry {
/// The purpose label for the contact entry.
pub purpose: String,
/// The contact information associated with the purpose.
pub contact: String,
}
impl Display for ContactEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.purpose, self.contact)
}
}
/// Helper to indicate if a comment includes the `=` in the display form
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CommentDef {
/// Includes `=` when printing the comment, as `COMMENT = 'comment'`
/// Does not include `=` when printing the comment, as `COMMENT 'comment'`
WithEq(String),
/// Comment variant that omits the `=` when displayed.
WithoutEq(String),
}
impl Display for CommentDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CommentDef::WithEq(comment) | CommentDef::WithoutEq(comment) => write!(f, "{comment}"),
}
}
}
/// Helper to indicate if a collection should be wrapped by a symbol in the display form
///
/// [`Display`] is implemented for every [`Vec<T>`] where `T: Display`.
/// The string output is a comma separated list for the vec items
///
/// # Examples
/// ```
/// # use sqlparser::ast::WrappedCollection;
/// let items = WrappedCollection::Parentheses(vec!["one", "two", "three"]);
/// assert_eq!("(one, two, three)", items.to_string());
///
/// let items = WrappedCollection::NoWrapping(vec!["one", "two", "three"]);
/// assert_eq!("one, two, three", items.to_string());
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WrappedCollection<T> {
/// Print the collection without wrapping symbols, as `item, item, item`
NoWrapping(T),
/// Wraps the collection in Parentheses, as `(item, item, item)`
Parentheses(T),
}
impl<T> Display for WrappedCollection<Vec<T>>
where
T: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WrappedCollection::NoWrapping(inner) => {
write!(f, "{}", display_comma_separated(inner.as_slice()))
}
WrappedCollection::Parentheses(inner) => {
write!(f, "({})", display_comma_separated(inner.as_slice()))
}
}
}
}
/// Represents a single PostgreSQL utility option.
///
/// A utility option is a key-value pair where the key is an identifier (IDENT) and the value
/// can be one of the following:
/// - A number with an optional sign (`+` or `-`). Example: `+10`, `-10.2`, `3`
/// - A non-keyword string. Example: `option1`, `'option2'`, `"option3"`
/// - keyword: `TRUE`, `FALSE`, `ON` (`off` is also accept).
/// - Empty. Example: `ANALYZE` (identifier only)
///
/// Utility options are used in various PostgreSQL DDL statements, including statements such as
/// `CLUSTER`, `EXPLAIN`, `VACUUM`, and `REINDEX`. These statements format options as `( option [, ...] )`.
///
/// [CLUSTER](https://www.postgresql.org/docs/current/sql-cluster.html)
/// [EXPLAIN](https://www.postgresql.org/docs/current/sql-explain.html)
/// [VACUUM](https://www.postgresql.org/docs/current/sql-vacuum.html)
/// [REINDEX](https://www.postgresql.org/docs/current/sql-reindex.html)
///
/// For example, the `EXPLAIN` AND `VACUUM` statements with options might look like this:
/// ```sql
/// EXPLAIN (ANALYZE, VERBOSE TRUE, FORMAT TEXT) SELECT * FROM my_table;
///
/// VACUUM (VERBOSE, ANALYZE ON, PARALLEL 10) my_table;
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UtilityOption {
/// The option name (identifier).
pub name: Ident,
/// Optional argument for the option (number, string, keyword, etc.).
pub arg: Option<Expr>,
}
impl Display for UtilityOption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref arg) = self.arg {
write!(f, "{} {}", self.name, arg)
} else {
write!(f, "{}", self.name)
}
}
}
/// Represents the different options available for `SHOW`
/// statements to filter the results. Example from Snowflake:
/// <https://docs.snowflake.com/en/sql-reference/sql/show-tables>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowStatementOptions {
/// Optional scope to show in (for example: TABLE, SCHEMA).
pub show_in: Option<ShowStatementIn>,
/// Optional `STARTS WITH` filter value.
pub starts_with: Option<ValueWithSpan>,
/// Optional `LIMIT` expression.
pub limit: Option<Expr>,
/// Optional `FROM` value used with `LIMIT`.
pub limit_from: Option<ValueWithSpan>,
/// Optional filter position (infix or suffix) for `LIKE`/`FILTER`.
pub filter_position: Option<ShowStatementFilterPosition>,
}
impl Display for ShowStatementOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (like_in_infix, like_in_suffix) = match &self.filter_position {
Some(ShowStatementFilterPosition::Infix(filter)) => {
(format!(" {filter}"), "".to_string())
}
Some(ShowStatementFilterPosition::Suffix(filter)) => {
("".to_string(), format!(" {filter}"))
}
None => ("".to_string(), "".to_string()),
};
write!(
f,
"{like_in_infix}{show_in}{starts_with}{limit}{from}{like_in_suffix}",
show_in = match &self.show_in {
Some(i) => format!(" {i}"),
None => String::new(),
},
starts_with = match &self.starts_with {
Some(s) => format!(" STARTS WITH {s}"),
None => String::new(),
},
limit = match &self.limit {
Some(l) => format!(" LIMIT {l}"),
None => String::new(),
},
from = match &self.limit_from {
Some(f) => format!(" FROM {f}"),
None => String::new(),
}
)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Where a `SHOW` filter appears relative to the main clause.
pub enum ShowStatementFilterPosition {
/// Put the filter in an infix position (e.g. `SHOW COLUMNS LIKE '%name%' IN TABLE tbl`).
Infix(ShowStatementFilter), // For example: SHOW COLUMNS LIKE '%name%' IN TABLE tbl
/// Put the filter in a suffix position (e.g. `SHOW COLUMNS IN tbl LIKE '%name%'`).
Suffix(ShowStatementFilter), // For example: SHOW COLUMNS IN tbl LIKE '%name%'
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Parent object types usable with `SHOW ... IN <parent>` clauses.
pub enum ShowStatementInParentType {
/// ACCOUNT parent type for SHOW statements.
Account,
/// DATABASE parent type for SHOW statements.
Database,
/// SCHEMA parent type for SHOW statements.
Schema,
/// TABLE parent type for SHOW statements.
Table,
/// VIEW parent type for SHOW statements.
View,
}
impl fmt::Display for ShowStatementInParentType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ShowStatementInParentType::Account => write!(f, "ACCOUNT"),
ShowStatementInParentType::Database => write!(f, "DATABASE"),
ShowStatementInParentType::Schema => write!(f, "SCHEMA"),
ShowStatementInParentType::Table => write!(f, "TABLE"),
ShowStatementInParentType::View => write!(f, "VIEW"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents a `SHOW ... IN` clause with optional parent qualifier and name.
pub struct ShowStatementIn {
/// The clause that specifies what to show (e.g. COLUMNS, TABLES).
pub clause: ShowStatementInClause,
/// Optional parent type qualifier (ACCOUNT/DATABASE/...).
pub parent_type: Option<ShowStatementInParentType>,
/// Optional parent object name for the SHOW clause.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub parent_name: Option<ObjectName>,
}
impl fmt::Display for ShowStatementIn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.clause)?;
if let Some(parent_type) = &self.parent_type {
write!(f, " {parent_type}")?;
}
if let Some(parent_name) = &self.parent_name {
write!(f, " {parent_name}")?;
}
Ok(())
}
}
/// A Show Charset statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowCharset {
/// The statement can be written as `SHOW CHARSET` or `SHOW CHARACTER SET`
/// true means CHARSET was used and false means CHARACTER SET was used
pub is_shorthand: bool,
/// Optional `LIKE`/`WHERE`-style filter for the statement.
pub filter: Option<ShowStatementFilter>,
}
impl fmt::Display for ShowCharset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SHOW")?;
if self.is_shorthand {
write!(f, " CHARSET")?;
} else {
write!(f, " CHARACTER SET")?;
}
if let Some(filter) = &self.filter {
write!(f, " {filter}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Options for a `SHOW OBJECTS` statement.
pub struct ShowObjects {
/// Whether to show terse output.
pub terse: bool,
/// Additional options controlling the SHOW output.
pub show_options: ShowStatementOptions,
}
/// MSSQL's json null clause
///
/// ```plaintext
/// <json_null_clause> ::=
/// NULL ON NULL
/// | ABSENT ON NULL
/// ```
///
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16#json_null_clause>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonNullClause {
/// `NULL ON NULL` behavior for JSON functions.
NullOnNull,
/// `ABSENT ON NULL` behavior for JSON functions.
AbsentOnNull,
}
impl Display for JsonNullClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JsonNullClause::NullOnNull => write!(f, "NULL ON NULL"),
JsonNullClause::AbsentOnNull => write!(f, "ABSENT ON NULL"),
}
}
}
/// PostgreSQL JSON function RETURNING clause
///
/// Example:
/// ```sql
/// JSON_OBJECT('a': 1 RETURNING jsonb)
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct JsonReturningClause {
/// The data type to return from the JSON function (e.g. JSON/JSONB).
pub data_type: DataType,
}
impl Display for JsonReturningClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RETURNING {}", self.data_type)
}
}
/// rename object definition
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RenameTable {
/// The current name of the object to rename.
pub old_name: ObjectName,
/// The new name for the object.
pub new_name: ObjectName,
}
impl fmt::Display for RenameTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} TO {}", self.old_name, self.new_name)?;
Ok(())
}
}
/// Represents the referenced table in an `INSERT INTO` statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TableObject {
/// Table specified by name.
/// Example:
/// ```sql
/// INSERT INTO my_table
/// ```
TableName(#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] ObjectName),
/// Table specified as a function.
/// Example:
/// ```sql
/// INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table)
/// ```
/// [Clickhouse](https://clickhouse.com/docs/en/sql-reference/table-functions)
TableFunction(Function),
/// Table specified through a sub-query
/// Example:
/// ```sql
/// INSERT INTO
/// (SELECT employee_id, last_name, email, hire_date, job_id, salary, commission_pct FROM employees)
/// VALUES (207, 'Gregory', 'pgregory@example.com', sysdate, 'PU_CLERK', 1.2E3, NULL);
/// ```
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/INSERT.html#GUID-903F8043-0254-4EE9-ACC1-CB8AC0AF3423__I2126242)
TableQuery(Box<Query>),
}
impl fmt::Display for TableObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TableName(table_name) => write!(f, "{table_name}"),
Self::TableFunction(func) => write!(f, "FUNCTION {func}"),
Self::TableQuery(table_query) => write!(f, "({table_query})"),
}
}
}
/// Represents a SET SESSION AUTHORIZATION statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct SetSessionAuthorizationParam {
/// The scope for the `SET SESSION AUTHORIZATION` (e.g., GLOBAL/SESSION).
pub scope: ContextModifier,
/// The specific authorization parameter kind.
pub kind: SetSessionAuthorizationParamKind,
}
impl fmt::Display for SetSessionAuthorizationParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
/// Represents the parameter kind for SET SESSION AUTHORIZATION
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum SetSessionAuthorizationParamKind {
/// Default authorization
Default,
/// User name
User(Ident),
}
impl fmt::Display for SetSessionAuthorizationParamKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SetSessionAuthorizationParamKind::Default => write!(f, "DEFAULT"),
SetSessionAuthorizationParamKind::User(name) => write!(f, "{}", name),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Kind of session parameter being set by `SET SESSION`.
pub enum SetSessionParamKind {
/// Generic session parameter (name/value pair).
Generic(SetSessionParamGeneric),
/// Identity insert related parameter.
IdentityInsert(SetSessionParamIdentityInsert),
/// Offsets-related parameter.
Offsets(SetSessionParamOffsets),
/// Statistics-related parameter.
Statistics(SetSessionParamStatistics),
}
impl fmt::Display for SetSessionParamKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SetSessionParamKind::Generic(x) => write!(f, "{x}"),
SetSessionParamKind::IdentityInsert(x) => write!(f, "{x}"),
SetSessionParamKind::Offsets(x) => write!(f, "{x}"),
SetSessionParamKind::Statistics(x) => write!(f, "{x}"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Generic `SET SESSION` parameter represented as name(s) and value.
pub struct SetSessionParamGeneric {
/// Names of the session parameters being set.
pub names: Vec<String>,
/// The value to assign to the parameter(s).
pub value: String,
}
impl fmt::Display for SetSessionParamGeneric {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", display_comma_separated(&self.names), self.value)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `IDENTITY_INSERT` session parameter for a specific object.
pub struct SetSessionParamIdentityInsert {
/// Object name targeted by `IDENTITY_INSERT`.
pub obj: ObjectName,
/// Value (ON/OFF) for the identity insert setting.
pub value: SessionParamValue,
}
impl fmt::Display for SetSessionParamIdentityInsert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IDENTITY_INSERT {} {}", self.obj, self.value)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Offsets-related session parameter with keywords and a value.
pub struct SetSessionParamOffsets {
/// Keywords specifying which offsets to modify.
pub keywords: Vec<String>,
/// Value (ON/OFF) for the offsets setting.
pub value: SessionParamValue,
}
impl fmt::Display for SetSessionParamOffsets {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"OFFSETS {} {}",
display_comma_separated(&self.keywords),
self.value
)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Statistics-related session parameter specifying topic and value.
pub struct SetSessionParamStatistics {
/// Statistics topic to set (IO/PROFILE/TIME/XML).
pub topic: SessionParamStatsTopic,
/// Value (ON/OFF) for the statistics topic.
pub value: SessionParamValue,
}
impl fmt::Display for SetSessionParamStatistics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "STATISTICS {} {}", self.topic, self.value)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Topics available for session statistics configuration.
pub enum SessionParamStatsTopic {
/// Input/output statistics.
IO,
/// Profile statistics.
Profile,
/// Time statistics.
Time,
/// XML-related statistics.
Xml,
}
impl fmt::Display for SessionParamStatsTopic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SessionParamStatsTopic::IO => write!(f, "IO"),
SessionParamStatsTopic::Profile => write!(f, "PROFILE"),
SessionParamStatsTopic::Time => write!(f, "TIME"),
SessionParamStatsTopic::Xml => write!(f, "XML"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Value for a session boolean-like parameter (ON/OFF).
pub enum SessionParamValue {
/// Session parameter enabled.
On,
/// Session parameter disabled.
Off,
}
impl fmt::Display for SessionParamValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SessionParamValue::On => write!(f, "ON"),
SessionParamValue::Off => write!(f, "OFF"),
}
}
}
/// Snowflake StorageSerializationPolicy for Iceberg Tables
/// ```sql
/// [ STORAGE_SERIALIZATION_POLICY = { COMPATIBLE | OPTIMIZED } ]
/// ```
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum StorageSerializationPolicy {
/// Use compatible serialization mode.
Compatible,
/// Use optimized serialization mode.
Optimized,
}
impl Display for StorageSerializationPolicy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageSerializationPolicy::Compatible => write!(f, "COMPATIBLE"),
StorageSerializationPolicy::Optimized => write!(f, "OPTIMIZED"),
}
}
}
/// Snowflake CatalogSyncNamespaceMode
/// ```sql
/// [ CATALOG_SYNC_NAMESPACE_MODE = { NEST | FLATTEN } ]
/// ```
///
/// <https://docs.snowflake.com/en/sql-reference/sql/create-database>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CatalogSyncNamespaceMode {
/// Nest namespaces when syncing catalog.
Nest,
/// Flatten namespaces when syncing catalog.
Flatten,
}
impl Display for CatalogSyncNamespaceMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CatalogSyncNamespaceMode::Nest => write!(f, "NEST"),
CatalogSyncNamespaceMode::Flatten => write!(f, "FLATTEN"),
}
}
}
/// Variants of the Snowflake `COPY INTO` statement
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CopyIntoSnowflakeKind {
/// Loads data from files to a table
/// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-table>
Table,
/// Unloads data from a table or query to external files
/// See: <https://docs.snowflake.com/en/sql-reference/sql/copy-into-location>
Location,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// `PRINT` statement for producing debug/output messages.
pub struct PrintStatement {
/// The expression producing the message to print.
pub message: Box<Expr>,
}
impl fmt::Display for PrintStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PRINT {}", self.message)
}
}
/// The type of `WAITFOR` statement (MSSQL).
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum WaitForType {
/// `WAITFOR DELAY 'time_to_pass'`
Delay,
/// `WAITFOR TIME 'time_to_execute'`
Time,
}
impl fmt::Display for WaitForType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WaitForType::Delay => write!(f, "DELAY"),
WaitForType::Time => write!(f, "TIME"),
}
}
}
/// MSSQL `WAITFOR` statement.
///
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WaitForStatement {
/// `DELAY` or `TIME`.
pub wait_type: WaitForType,
/// The time expression.
pub expr: Expr,
}
impl fmt::Display for WaitForStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WAITFOR {} {}", self.wait_type, self.expr)
}
}
/// Represents a `Return` statement.
///
/// [MsSql triggers](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql)
/// [MsSql functions](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ReturnStatement {
/// Optional return value expression.
pub value: Option<ReturnStatementValue>,
}
impl fmt::Display for ReturnStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.value {
Some(ReturnStatementValue::Expr(expr)) => write!(f, "RETURN {expr}"),
None => write!(f, "RETURN"),
}
}
}
/// Variants of a `RETURN` statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ReturnStatementValue {
/// Return an expression from a function or trigger.
Expr(Expr),
}
/// Represents an `OPEN` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OpenStatement {
/// Cursor name
pub cursor_name: Ident,
}
impl fmt::Display for OpenStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OPEN {}", self.cursor_name)
}
}
/// Specifies Include / Exclude NULL within UNPIVOT command.
/// For example
/// `UNPIVOT (column1 FOR new_column IN (col3, col4, col5, col6))`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum NullInclusion {
/// Include NULL values in the UNPIVOT output.
IncludeNulls,
/// Exclude NULL values from the UNPIVOT output.
ExcludeNulls,
}
impl fmt::Display for NullInclusion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
NullInclusion::IncludeNulls => write!(f, "INCLUDE NULLS"),
NullInclusion::ExcludeNulls => write!(f, "EXCLUDE NULLS"),
}
}
}
/// Checks membership of a value in a JSON array
///
/// Syntax:
/// ```sql
/// <value> MEMBER OF(<array>)
/// ```
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#operator_member-of)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MemberOf {
/// The value to check for membership.
pub value: Box<Expr>,
/// The JSON array expression to check against.
pub array: Box<Expr>,
}
impl fmt::Display for MemberOf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} MEMBER OF({})", self.value, self.array)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents an `EXPORT DATA` statement.
pub struct ExportData {
/// Options for the export operation.
pub options: Vec<SqlOption>,
/// The query producing the data to export.
pub query: Box<Query>,
/// Optional named connection to use for export.
pub connection: Option<ObjectName>,
}
impl fmt::Display for ExportData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(connection) = &self.connection {
write!(
f,
"EXPORT DATA WITH CONNECTION {connection} OPTIONS({}) AS {}",
display_comma_separated(&self.options),
self.query
)
} else {
write!(
f,
"EXPORT DATA OPTIONS({}) AS {}",
display_comma_separated(&self.options),
self.query
)
}
}
}
/// Creates a user
///
/// Syntax:
/// ```sql
/// CREATE [OR REPLACE] USER [IF NOT EXISTS] <name> [OPTIONS]
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateUser {
/// Replace existing user if present.
pub or_replace: bool,
/// Only create the user if it does not already exist.
pub if_not_exists: bool,
/// The name of the user to create.
pub name: Ident,
/// Key/value options for user creation.
pub options: KeyValueOptions,
/// Whether tags are specified using `WITH TAG`.
pub with_tags: bool,
/// Tags for the user.
pub tags: KeyValueOptions,
}
impl fmt::Display for CreateUser {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CREATE")?;
if self.or_replace {
write!(f, " OR REPLACE")?;
}
write!(f, " USER")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?;
}
write!(f, " {}", self.name)?;
if !self.options.options.is_empty() {
write!(f, " {}", self.options)?;
}
if !self.tags.options.is_empty() {
if self.with_tags {
write!(f, " WITH")?;
}
write!(f, " TAG ({})", self.tags)?;
}
Ok(())
}
}
/// Modifies the properties of a user
///
/// [Snowflake Syntax:](https://docs.snowflake.com/en/sql-reference/sql/alter-user)
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] [ OPTIONS ]
/// ```
///
/// [PostgreSQL Syntax:](https://www.postgresql.org/docs/current/sql-alteruser.html)
/// ```sql
/// ALTER USER <role_specification> [ WITH ] option [ ... ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUser {
/// Whether to only alter the user if it exists.
pub if_exists: bool,
/// The name of the user to alter.
pub name: Ident,
/// Optional new name for the user (Snowflake-specific).
/// See: <https://docs.snowflake.com/en/sql-reference/sql/alter-user#syntax>
pub rename_to: Option<Ident>,
/// Reset the user's password.
pub reset_password: bool,
/// Abort all running queries for the user.
pub abort_all_queries: bool,
/// Optionally add a delegated role authorization.
pub add_role_delegation: Option<AlterUserAddRoleDelegation>,
/// Optionally remove a delegated role authorization.
pub remove_role_delegation: Option<AlterUserRemoveRoleDelegation>,
/// Enroll the user in MFA.
pub enroll_mfa: bool,
/// Set the default MFA method for the user.
pub set_default_mfa_method: Option<MfaMethodKind>,
/// Remove the user's default MFA method.
pub remove_mfa_method: Option<MfaMethodKind>,
/// Modify an MFA method for the user.
pub modify_mfa_method: Option<AlterUserModifyMfaMethod>,
/// Add an MFA OTP method with optional count.
pub add_mfa_method_otp: Option<AlterUserAddMfaMethodOtp>,
/// Set a user policy.
pub set_policy: Option<AlterUserSetPolicy>,
/// Unset a user policy.
pub unset_policy: Option<UserPolicyKind>,
/// Key/value tag options to set on the user.
pub set_tag: KeyValueOptions,
/// Tags to unset on the user.
pub unset_tag: Vec<String>,
/// Key/value properties to set on the user.
pub set_props: KeyValueOptions,
/// Properties to unset on the user.
pub unset_props: Vec<String>,
/// The following options are PostgreSQL-specific: <https://www.postgresql.org/docs/current/sql-alteruser.html>
pub password: Option<AlterUserPassword>,
}
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] ADD DELEGATED AUTHORIZATION OF ROLE <role_name> TO SECURITY INTEGRATION <integration_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserAddRoleDelegation {
/// Role name to delegate.
pub role: Ident,
/// Security integration receiving the delegation.
pub integration: Ident,
}
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] REMOVE DELEGATED { AUTHORIZATION OF ROLE <role_name> | AUTHORIZATIONS } FROM SECURITY INTEGRATION <integration_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserRemoveRoleDelegation {
/// Optional role name to remove delegation for.
pub role: Option<Ident>,
/// Security integration from which to remove delegation.
pub integration: Ident,
}
/// ```sql
/// ADD MFA METHOD OTP [ COUNT = number ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserAddMfaMethodOtp {
/// Optional OTP count parameter.
pub count: Option<ValueWithSpan>,
}
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] MODIFY MFA METHOD <mfa_method> SET COMMENT = '<string>'
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserModifyMfaMethod {
/// The MFA method being modified.
pub method: MfaMethodKind,
/// The new comment for the MFA method.
pub comment: String,
}
/// Types of MFA methods
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MfaMethodKind {
/// PassKey (hardware or platform passkey) MFA method.
PassKey,
/// Time-based One-Time Password (TOTP) MFA method.
Totp,
/// Duo Security MFA method.
Duo,
}
impl fmt::Display for MfaMethodKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MfaMethodKind::PassKey => write!(f, "PASSKEY"),
MfaMethodKind::Totp => write!(f, "TOTP"),
MfaMethodKind::Duo => write!(f, "DUO"),
}
}
}
/// ```sql
/// ALTER USER [ IF EXISTS ] [ <name> ] SET { AUTHENTICATION | PASSWORD | SESSION } POLICY <policy_name>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserSetPolicy {
/// The kind of user policy being set (authentication/password/session).
pub policy_kind: UserPolicyKind,
/// The identifier of the policy to apply.
pub policy: Ident,
}
/// Types of user-based policies
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum UserPolicyKind {
/// Authentication policy.
Authentication,
/// Password policy.
Password,
/// Session policy.
Session,
}
impl fmt::Display for UserPolicyKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UserPolicyKind::Authentication => write!(f, "AUTHENTICATION"),
UserPolicyKind::Password => write!(f, "PASSWORD"),
UserPolicyKind::Session => write!(f, "SESSION"),
}
}
}
impl fmt::Display for AlterUser {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ALTER")?;
write!(f, " USER")?;
if self.if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", self.name)?;
if let Some(new_name) = &self.rename_to {
write!(f, " RENAME TO {new_name}")?;
}
if self.reset_password {
write!(f, " RESET PASSWORD")?;
}
if self.abort_all_queries {
write!(f, " ABORT ALL QUERIES")?;
}
if let Some(role_delegation) = &self.add_role_delegation {
let role = &role_delegation.role;
let integration = &role_delegation.integration;
write!(
f,
" ADD DELEGATED AUTHORIZATION OF ROLE {role} TO SECURITY INTEGRATION {integration}"
)?;
}
if let Some(role_delegation) = &self.remove_role_delegation {
write!(f, " REMOVE DELEGATED")?;
match &role_delegation.role {
Some(role) => write!(f, " AUTHORIZATION OF ROLE {role}")?,
None => write!(f, " AUTHORIZATIONS")?,
}
let integration = &role_delegation.integration;
write!(f, " FROM SECURITY INTEGRATION {integration}")?;
}
if self.enroll_mfa {
write!(f, " ENROLL MFA")?;
}
if let Some(method) = &self.set_default_mfa_method {
write!(f, " SET DEFAULT_MFA_METHOD {method}")?
}
if let Some(method) = &self.remove_mfa_method {
write!(f, " REMOVE MFA METHOD {method}")?;
}
if let Some(modify) = &self.modify_mfa_method {
let method = &modify.method;
let comment = &modify.comment;
write!(
f,
" MODIFY MFA METHOD {method} SET COMMENT '{}'",
value::escape_single_quote_string(comment)
)?;
}
if let Some(add_mfa_method_otp) = &self.add_mfa_method_otp {
write!(f, " ADD MFA METHOD OTP")?;
if let Some(count) = &add_mfa_method_otp.count {
write!(f, " COUNT = {count}")?;
}
}
if let Some(policy) = &self.set_policy {
let policy_kind = &policy.policy_kind;
let name = &policy.policy;
write!(f, " SET {policy_kind} POLICY {name}")?;
}
if let Some(policy_kind) = &self.unset_policy {
write!(f, " UNSET {policy_kind} POLICY")?;
}
if !self.set_tag.options.is_empty() {
write!(f, " SET TAG {}", self.set_tag)?;
}
if !self.unset_tag.is_empty() {
write!(f, " UNSET TAG {}", display_comma_separated(&self.unset_tag))?;
}
let has_props = !self.set_props.options.is_empty();
if has_props {
write!(f, " SET")?;
write!(f, " {}", &self.set_props)?;
}
if !self.unset_props.is_empty() {
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
}
if let Some(password) = &self.password {
write!(f, " {}", password)?;
}
Ok(())
}
}
/// ```sql
/// ALTER USER <role_specification> [ WITH ] PASSWORD { 'password' | NULL }``
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterUserPassword {
/// Whether the password is encrypted.
pub encrypted: bool,
/// The password string, or `None` for `NULL`.
pub password: Option<String>,
}
impl Display for AlterUserPassword {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.encrypted {
write!(f, "ENCRYPTED ")?;
}
write!(f, "PASSWORD")?;
match &self.password {
None => write!(f, " NULL")?,
Some(password) => write!(f, " '{}'", value::escape_single_quote_string(password))?,
}
Ok(())
}
}
/// Specifies how to create a new table based on an existing table's schema.
/// '''sql
/// CREATE TABLE new LIKE old ...
/// '''
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateTableLikeKind {
/// '''sql
/// CREATE TABLE new (LIKE old ...)
/// '''
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html)
Parenthesized(CreateTableLike),
/// '''sql
/// CREATE TABLE new LIKE old ...
/// '''
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-table#label-create-table-like)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_like)
Plain(CreateTableLike),
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Controls whether defaults are included when creating a table FROM/LILE another.
pub enum CreateTableLikeDefaults {
/// Include default values from the source table.
Including,
/// Exclude default values from the source table.
Excluding,
}
impl fmt::Display for CreateTableLikeDefaults {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateTableLikeDefaults::Including => write!(f, "INCLUDING DEFAULTS"),
CreateTableLikeDefaults::Excluding => write!(f, "EXCLUDING DEFAULTS"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// Represents the `LIKE` clause of a `CREATE TABLE` statement.
pub struct CreateTableLike {
/// The source table name to copy the schema from.
pub name: ObjectName,
/// Optional behavior controlling whether defaults are copied.
pub defaults: Option<CreateTableLikeDefaults>,
}
impl fmt::Display for CreateTableLike {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LIKE {}", self.name)?;
if let Some(defaults) = &self.defaults {
write!(f, " {defaults}")?;
}
Ok(())
}
}
/// Specifies the refresh mode for the dynamic table.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum RefreshModeKind {
/// Automatic refresh mode (`AUTO`).
Auto,
/// Full refresh mode (`FULL`).
Full,
/// Incremental refresh mode (`INCREMENTAL`).
Incremental,
}
impl fmt::Display for RefreshModeKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RefreshModeKind::Auto => write!(f, "AUTO"),
RefreshModeKind::Full => write!(f, "FULL"),
RefreshModeKind::Incremental => write!(f, "INCREMENTAL"),
}
}
}
/// Specifies the behavior of the initial refresh of the dynamic table.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table)
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum InitializeKind {
/// Initialize on creation (`ON CREATE`).
OnCreate,
/// Initialize on schedule (`ON SCHEDULE`).
OnSchedule,
}
impl fmt::Display for InitializeKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InitializeKind::OnCreate => write!(f, "ON_CREATE"),
InitializeKind::OnSchedule => write!(f, "ON_SCHEDULE"),
}
}
}
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
///
/// '''sql
/// VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ] [ \[ table_name \] [ TO threshold PERCENT ] \[ BOOST \] ]
/// '''
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct VacuumStatement {
/// Whether `FULL` was specified.
pub full: bool,
/// Whether `SORT ONLY` was specified.
pub sort_only: bool,
/// Whether `DELETE ONLY` was specified.
pub delete_only: bool,
/// Whether `REINDEX` was specified.
pub reindex: bool,
/// Whether `RECLUSTER` was specified.
pub recluster: bool,
/// Optional table to run `VACUUM` on.
pub table_name: Option<ObjectName>,
/// Optional threshold value (percent) for `TO threshold PERCENT`.
pub threshold: Option<ValueWithSpan>,
/// Whether `BOOST` was specified.
pub boost: bool,
}
impl fmt::Display for VacuumStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"VACUUM{}{}{}{}{}",
if self.full { " FULL" } else { "" },
if self.sort_only { " SORT ONLY" } else { "" },
if self.delete_only { " DELETE ONLY" } else { "" },
if self.reindex { " REINDEX" } else { "" },
if self.recluster { " RECLUSTER" } else { "" },
)?;
if let Some(table_name) = &self.table_name {
write!(f, " {table_name}")?;
}
if let Some(threshold) = &self.threshold {
write!(f, " TO {threshold} PERCENT")?;
}
if self.boost {
write!(f, " BOOST")?;
}
Ok(())
}
}
/// Variants of the RESET statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum Reset {
/// Resets all session parameters to their default values.
ALL,
/// Resets a specific session parameter to its default value.
ConfigurationParameter(ObjectName),
}
/// Resets a session parameter to its default value.
/// ```sql
/// RESET { ALL | <configuration_parameter> }
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ResetStatement {
/// The reset action to perform (either `ALL` or a specific configuration parameter).
pub reset: Reset,
}
/// Query optimizer hints are optionally supported comments after the
/// `SELECT`, `INSERT`, `UPDATE`, `REPLACE`, `MERGE`, and `DELETE` keywords in
/// the corresponding statements.
///
/// See [Select::optimizer_hints]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OptimizerHint {
/// An optional prefix between the comment marker and `+`.
///
/// Standard optimizer hints like `/*+ ... */` have an empty prefix,
/// while system-specific hints like `/*abc+ ... */` have `prefix = "abc"`.
/// The prefix is any sequence of ASCII alphanumeric characters
/// immediately before the `+` marker.
pub prefix: String,
/// the raw text of the optimizer hint without its markers
pub text: String,
/// the style of the comment which `text` was extracted from,
/// e.g. `/*+...*/` or `--+...`
///
/// Not all dialects support all styles, though.
pub style: OptimizerHintStyle,
}
/// The commentary style of an [optimizer hint](OptimizerHint)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OptimizerHintStyle {
/// A hint corresponding to a single line comment,
/// e.g. `--+ LEADING(v.e v.d t)`
SingleLine {
/// the comment prefix, e.g. `--`
prefix: String,
},
/// A hint corresponding to a multi line comment,
/// e.g. `/*+ LEADING(v.e v.d t) */`
MultiLine,
}
impl fmt::Display for OptimizerHint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.style {
OptimizerHintStyle::SingleLine { prefix } => {
f.write_str(prefix)?;
f.write_str(&self.prefix)?;
f.write_str("+")?;
f.write_str(&self.text)
}
OptimizerHintStyle::MultiLine => {
f.write_str("/*")?;
f.write_str(&self.prefix)?;
f.write_str("+")?;
f.write_str(&self.text)?;
f.write_str("*/")
}
}
}
}
impl fmt::Display for ResetStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.reset {
Reset::ALL => write!(f, "RESET ALL"),
Reset::ConfigurationParameter(param) => write!(f, "RESET {}", param),
}
}
}
impl From<Set> for Statement {
fn from(s: Set) -> Self {
Self::Set(s)
}
}
impl From<Query> for Statement {
fn from(q: Query) -> Self {
Box::new(q).into()
}
}
impl From<Box<Query>> for Statement {
fn from(q: Box<Query>) -> Self {
Self::Query(q)
}
}
impl From<Insert> for Statement {
fn from(i: Insert) -> Self {
Self::Insert(i)
}
}
impl From<Update> for Statement {
fn from(u: Update) -> Self {
Self::Update(u)
}
}
impl From<CreateView> for Statement {
fn from(cv: CreateView) -> Self {
Self::CreateView(cv)
}
}
impl From<CreateRole> for Statement {
fn from(cr: CreateRole) -> Self {
Self::CreateRole(cr)
}
}
impl From<AlterTable> for Statement {
fn from(at: AlterTable) -> Self {
Self::AlterTable(at)
}
}
impl From<DropFunction> for Statement {
fn from(df: DropFunction) -> Self {
Self::DropFunction(df)
}
}
impl From<CreateExtension> for Statement {
fn from(ce: CreateExtension) -> Self {
Self::CreateExtension(ce)
}
}
impl From<DropExtension> for Statement {
fn from(de: DropExtension) -> Self {
Self::DropExtension(de)
}
}
impl From<CaseStatement> for Statement {
fn from(c: CaseStatement) -> Self {
Self::Case(c)
}
}
impl From<IfStatement> for Statement {
fn from(i: IfStatement) -> Self {
Self::If(i)
}
}
impl From<WhileStatement> for Statement {
fn from(w: WhileStatement) -> Self {
Self::While(w)
}
}
impl From<RaiseStatement> for Statement {
fn from(r: RaiseStatement) -> Self {
Self::Raise(r)
}
}
impl From<ThrowStatement> for Statement {
fn from(t: ThrowStatement) -> Self {
Self::Throw(t)
}
}
impl From<Function> for Statement {
fn from(f: Function) -> Self {
Self::Call(f)
}
}
impl From<OpenStatement> for Statement {
fn from(o: OpenStatement) -> Self {
Self::Open(o)
}
}
impl From<Delete> for Statement {
fn from(d: Delete) -> Self {
Self::Delete(d)
}
}
impl From<CreateTable> for Statement {
fn from(c: CreateTable) -> Self {
Self::CreateTable(c)
}
}
impl From<CreateIndex> for Statement {
fn from(c: CreateIndex) -> Self {
Self::CreateIndex(c)
}
}
impl From<CreateServerStatement> for Statement {
fn from(c: CreateServerStatement) -> Self {
Self::CreateServer(c)
}
}
impl From<CreateConnector> for Statement {
fn from(c: CreateConnector) -> Self {
Self::CreateConnector(c)
}
}
impl From<CreateOperator> for Statement {
fn from(c: CreateOperator) -> Self {
Self::CreateOperator(c)
}
}
impl From<CreateOperatorFamily> for Statement {
fn from(c: CreateOperatorFamily) -> Self {
Self::CreateOperatorFamily(c)
}
}
impl From<CreateOperatorClass> for Statement {
fn from(c: CreateOperatorClass) -> Self {
Self::CreateOperatorClass(c)
}
}
impl From<AlterSchema> for Statement {
fn from(a: AlterSchema) -> Self {
Self::AlterSchema(a)
}
}
impl From<AlterFunction> for Statement {
fn from(a: AlterFunction) -> Self {
Self::AlterFunction(a)
}
}
impl From<AlterType> for Statement {
fn from(a: AlterType) -> Self {
Self::AlterType(a)
}
}
impl From<AlterOperator> for Statement {
fn from(a: AlterOperator) -> Self {
Self::AlterOperator(a)
}
}
impl From<AlterOperatorFamily> for Statement {
fn from(a: AlterOperatorFamily) -> Self {
Self::AlterOperatorFamily(a)
}
}
impl From<AlterOperatorClass> for Statement {
fn from(a: AlterOperatorClass) -> Self {
Self::AlterOperatorClass(a)
}
}
impl From<Merge> for Statement {
fn from(m: Merge) -> Self {
Self::Merge(m)
}
}
impl From<AlterUser> for Statement {
fn from(a: AlterUser) -> Self {
Self::AlterUser(a)
}
}
impl From<DropDomain> for Statement {
fn from(d: DropDomain) -> Self {
Self::DropDomain(d)
}
}
impl From<ShowCharset> for Statement {
fn from(s: ShowCharset) -> Self {
Self::ShowCharset(s)
}
}
impl From<ShowObjects> for Statement {
fn from(s: ShowObjects) -> Self {
Self::ShowObjects(s)
}
}
impl From<Use> for Statement {
fn from(u: Use) -> Self {
Self::Use(u)
}
}
impl From<CreateFunction> for Statement {
fn from(c: CreateFunction) -> Self {
Self::CreateFunction(c)
}
}
impl From<CreateTrigger> for Statement {
fn from(c: CreateTrigger) -> Self {
Self::CreateTrigger(c)
}
}
impl From<DropTrigger> for Statement {
fn from(d: DropTrigger) -> Self {
Self::DropTrigger(d)
}
}
impl From<DropOperator> for Statement {
fn from(d: DropOperator) -> Self {
Self::DropOperator(d)
}
}
impl From<DropOperatorFamily> for Statement {
fn from(d: DropOperatorFamily) -> Self {
Self::DropOperatorFamily(d)
}
}
impl From<DropOperatorClass> for Statement {
fn from(d: DropOperatorClass) -> Self {
Self::DropOperatorClass(d)
}
}
impl From<DenyStatement> for Statement {
fn from(d: DenyStatement) -> Self {
Self::Deny(d)
}
}
impl From<CreateDomain> for Statement {
fn from(c: CreateDomain) -> Self {
Self::CreateDomain(c)
}
}
impl From<RenameTable> for Statement {
fn from(r: RenameTable) -> Self {
vec![r].into()
}
}
impl From<Vec<RenameTable>> for Statement {
fn from(r: Vec<RenameTable>) -> Self {
Self::RenameTable(r)
}
}
impl From<PrintStatement> for Statement {
fn from(p: PrintStatement) -> Self {
Self::Print(p)
}
}
impl From<ReturnStatement> for Statement {
fn from(r: ReturnStatement) -> Self {
Self::Return(r)
}
}
impl From<ExportData> for Statement {
fn from(e: ExportData) -> Self {
Self::ExportData(e)
}
}
impl From<CreateUser> for Statement {
fn from(c: CreateUser) -> Self {
Self::CreateUser(c)
}
}
impl From<VacuumStatement> for Statement {
fn from(v: VacuumStatement) -> Self {
Self::Vacuum(v)
}
}
impl From<ResetStatement> for Statement {
fn from(r: ResetStatement) -> Self {
Self::Reset(r)
}
}
#[cfg(test)]
mod tests {
use crate::tokenizer::Location;
use super::*;
#[test]
fn test_window_frame_default() {
let window_frame = WindowFrame::default();
assert_eq!(WindowFrameBound::Preceding(None), window_frame.start_bound);
}
#[test]
fn test_grouping_sets_display() {
// a and b in different group
let grouping_sets = Expr::GroupingSets(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
]);
assert_eq!("GROUPING SETS ((a), (b))", format!("{grouping_sets}"));
// a and b in the same group
let grouping_sets = Expr::GroupingSets(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
]]);
assert_eq!("GROUPING SETS ((a, b))", format!("{grouping_sets}"));
// (a, b) and (c, d) in different group
let grouping_sets = Expr::GroupingSets(vec![
vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
],
vec![
Expr::Identifier(Ident::new("c")),
Expr::Identifier(Ident::new("d")),
],
]);
assert_eq!("GROUPING SETS ((a, b), (c, d))", format!("{grouping_sets}"));
}
#[test]
fn test_rollup_display() {
let rollup = Expr::Rollup(vec![vec![Expr::Identifier(Ident::new("a"))]]);
assert_eq!("ROLLUP (a)", format!("{rollup}"));
let rollup = Expr::Rollup(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
]]);
assert_eq!("ROLLUP ((a, b))", format!("{rollup}"));
let rollup = Expr::Rollup(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
]);
assert_eq!("ROLLUP (a, b)", format!("{rollup}"));
let rollup = Expr::Rollup(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![
Expr::Identifier(Ident::new("b")),
Expr::Identifier(Ident::new("c")),
],
vec![Expr::Identifier(Ident::new("d"))],
]);
assert_eq!("ROLLUP (a, (b, c), d)", format!("{rollup}"));
}
#[test]
fn test_cube_display() {
let cube = Expr::Cube(vec![vec![Expr::Identifier(Ident::new("a"))]]);
assert_eq!("CUBE (a)", format!("{cube}"));
let cube = Expr::Cube(vec![vec![
Expr::Identifier(Ident::new("a")),
Expr::Identifier(Ident::new("b")),
]]);
assert_eq!("CUBE ((a, b))", format!("{cube}"));
let cube = Expr::Cube(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![Expr::Identifier(Ident::new("b"))],
]);
assert_eq!("CUBE (a, b)", format!("{cube}"));
let cube = Expr::Cube(vec![
vec![Expr::Identifier(Ident::new("a"))],
vec![
Expr::Identifier(Ident::new("b")),
Expr::Identifier(Ident::new("c")),
],
vec![Expr::Identifier(Ident::new("d"))],
]);
assert_eq!("CUBE (a, (b, c), d)", format!("{cube}"));
}
#[test]
fn test_interval_display() {
let interval = Expr::Interval(Interval {
value: Box::new(Expr::Value(
Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(),
)),
leading_field: Some(DateTimeField::Minute),
leading_precision: Some(10),
last_field: Some(DateTimeField::Second),
fractional_seconds_precision: Some(9),
});
assert_eq!(
"INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)",
format!("{interval}"),
);
let interval = Expr::Interval(Interval {
value: Box::new(Expr::Value(
Value::SingleQuotedString(String::from("5")).with_empty_span(),
)),
leading_field: Some(DateTimeField::Second),
leading_precision: Some(1),
last_field: None,
fractional_seconds_precision: Some(3),
});
assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}"));
}
#[test]
fn test_one_or_many_with_parens_deref() {
use core::ops::Index;
let one = OneOrManyWithParens::One("a");
assert_eq!(one.deref(), &["a"]);
assert_eq!(<OneOrManyWithParens<_> as Deref>::deref(&one), &["a"]);
assert_eq!(one[0], "a");
assert_eq!(one.index(0), &"a");
assert_eq!(
<<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&one, 0),
&"a"
);
assert_eq!(one.len(), 1);
assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&one), 1);
let many1 = OneOrManyWithParens::Many(vec!["b"]);
assert_eq!(many1.deref(), &["b"]);
assert_eq!(<OneOrManyWithParens<_> as Deref>::deref(&many1), &["b"]);
assert_eq!(many1[0], "b");
assert_eq!(many1.index(0), &"b");
assert_eq!(
<<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many1, 0),
&"b"
);
assert_eq!(many1.len(), 1);
assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&many1), 1);
let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);
assert_eq!(many2.deref(), &["c", "d"]);
assert_eq!(
<OneOrManyWithParens<_> as Deref>::deref(&many2),
&["c", "d"]
);
assert_eq!(many2[0], "c");
assert_eq!(many2.index(0), &"c");
assert_eq!(
<<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many2, 0),
&"c"
);
assert_eq!(many2[1], "d");
assert_eq!(many2.index(1), &"d");
assert_eq!(
<<OneOrManyWithParens<_> as Deref>::Target as Index<usize>>::index(&many2, 1),
&"d"
);
assert_eq!(many2.len(), 2);
assert_eq!(<OneOrManyWithParens<_> as Deref>::Target::len(&many2), 2);
}
#[test]
fn test_one_or_many_with_parens_as_ref() {
let one = OneOrManyWithParens::One("a");
assert_eq!(one.as_ref(), &["a"]);
assert_eq!(<OneOrManyWithParens<_> as AsRef<_>>::as_ref(&one), &["a"]);
let many1 = OneOrManyWithParens::Many(vec!["b"]);
assert_eq!(many1.as_ref(), &["b"]);
assert_eq!(<OneOrManyWithParens<_> as AsRef<_>>::as_ref(&many1), &["b"]);
let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);
assert_eq!(many2.as_ref(), &["c", "d"]);
assert_eq!(
<OneOrManyWithParens<_> as AsRef<_>>::as_ref(&many2),
&["c", "d"]
);
}
#[test]
fn test_one_or_many_with_parens_ref_into_iter() {
let one = OneOrManyWithParens::One("a");
assert_eq!(Vec::from_iter(&one), vec![&"a"]);
let many1 = OneOrManyWithParens::Many(vec!["b"]);
assert_eq!(Vec::from_iter(&many1), vec![&"b"]);
let many2 = OneOrManyWithParens::Many(vec!["c", "d"]);
assert_eq!(Vec::from_iter(&many2), vec![&"c", &"d"]);
}
#[test]
fn test_one_or_many_with_parens_value_into_iter() {
use core::iter::once;
//tests that our iterator implemented methods behaves exactly as it's inner iterator, at every step up to n calls to next/next_back
fn test_steps<I>(ours: OneOrManyWithParens<usize>, inner: I, n: usize)
where
I: IntoIterator<Item = usize, IntoIter: DoubleEndedIterator + Clone> + Clone,
{
fn checks<I>(ours: OneOrManyWithParensIntoIter<usize>, inner: I)
where
I: Iterator<Item = usize> + Clone + DoubleEndedIterator,
{
assert_eq!(ours.size_hint(), inner.size_hint());
assert_eq!(ours.clone().count(), inner.clone().count());
assert_eq!(
ours.clone().fold(1, |a, v| a + v),
inner.clone().fold(1, |a, v| a + v)
);
assert_eq!(Vec::from_iter(ours.clone()), Vec::from_iter(inner.clone()));
assert_eq!(
Vec::from_iter(ours.clone().rev()),
Vec::from_iter(inner.clone().rev())
);
}
let mut ours_next = ours.clone().into_iter();
let mut inner_next = inner.clone().into_iter();
for _ in 0..n {
checks(ours_next.clone(), inner_next.clone());
assert_eq!(ours_next.next(), inner_next.next());
}
let mut ours_next_back = ours.clone().into_iter();
let mut inner_next_back = inner.clone().into_iter();
for _ in 0..n {
checks(ours_next_back.clone(), inner_next_back.clone());
assert_eq!(ours_next_back.next_back(), inner_next_back.next_back());
}
let mut ours_mixed = ours.clone().into_iter();
let mut inner_mixed = inner.clone().into_iter();
for i in 0..n {
checks(ours_mixed.clone(), inner_mixed.clone());
if i % 2 == 0 {
assert_eq!(ours_mixed.next_back(), inner_mixed.next_back());
} else {
assert_eq!(ours_mixed.next(), inner_mixed.next());
}
}
let mut ours_mixed2 = ours.into_iter();
let mut inner_mixed2 = inner.into_iter();
for i in 0..n {
checks(ours_mixed2.clone(), inner_mixed2.clone());
if i % 2 == 0 {
assert_eq!(ours_mixed2.next(), inner_mixed2.next());
} else {
assert_eq!(ours_mixed2.next_back(), inner_mixed2.next_back());
}
}
}
test_steps(OneOrManyWithParens::One(1), once(1), 3);
test_steps(OneOrManyWithParens::Many(vec![2]), vec![2], 3);
test_steps(OneOrManyWithParens::Many(vec![3, 4]), vec![3, 4], 4);
}
// Tests that the position in the code of an `Ident` does not affect its
// ordering.
#[test]
fn test_ident_ord() {
let mut a = Ident::with_span(Span::new(Location::new(1, 1), Location::new(1, 1)), "a");
let mut b = Ident::with_span(Span::new(Location::new(2, 2), Location::new(2, 2)), "b");
assert!(a < b);
std::mem::swap(&mut a.span, &mut b.span);
assert!(a < b);
}
}