Moving several struct variants out of Statement enum to allow for trait impls for specific sub-variants (#2057)
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run

This commit is contained in:
Luca Cappelletti 2025-10-10 16:08:20 +02:00 committed by GitHub
parent e7d42f3d1a
commit cc595cfd84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1032 additions and 962 deletions

View file

@ -28,8 +28,9 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
use super::{display_comma_separated, Expr, Ident, Password};
use super::{display_comma_separated, Expr, Ident, Password, Spanned};
use crate::ast::{display_separated, ObjectName};
use crate::tokenizer::Span;
/// An option in `ROLE` statement.
///
@ -252,3 +253,113 @@ impl fmt::Display for SecondaryRoles {
}
}
}
/// CREATE ROLE statement
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.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 CreateRole {
pub names: Vec<ObjectName>,
pub if_not_exists: bool,
// Postgres
pub login: Option<bool>,
pub inherit: Option<bool>,
pub bypassrls: Option<bool>,
pub password: Option<Password>,
pub superuser: Option<bool>,
pub create_db: Option<bool>,
pub create_role: Option<bool>,
pub replication: Option<bool>,
pub connection_limit: Option<Expr>,
pub valid_until: Option<Expr>,
pub in_role: Vec<Ident>,
pub in_group: Vec<Ident>,
pub role: Vec<Ident>,
pub user: Vec<Ident>,
pub admin: Vec<Ident>,
// MSSQL
pub authorization_owner: Option<ObjectName>,
}
impl fmt::Display for CreateRole {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}",
if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" },
names = display_separated(&self.names, ", "),
superuser = match self.superuser {
Some(true) => " SUPERUSER",
Some(false) => " NOSUPERUSER",
None => ""
},
create_db = match self.create_db {
Some(true) => " CREATEDB",
Some(false) => " NOCREATEDB",
None => ""
},
create_role = match self.create_role {
Some(true) => " CREATEROLE",
Some(false) => " NOCREATEROLE",
None => ""
},
inherit = match self.inherit {
Some(true) => " INHERIT",
Some(false) => " NOINHERIT",
None => ""
},
login = match self.login {
Some(true) => " LOGIN",
Some(false) => " NOLOGIN",
None => ""
},
replication = match self.replication {
Some(true) => " REPLICATION",
Some(false) => " NOREPLICATION",
None => ""
},
bypassrls = match self.bypassrls {
Some(true) => " BYPASSRLS",
Some(false) => " NOBYPASSRLS",
None => ""
}
)?;
if let Some(limit) = &self.connection_limit {
write!(f, " CONNECTION LIMIT {limit}")?;
}
match &self.password {
Some(Password::Password(pass)) => write!(f, " PASSWORD {pass}")?,
Some(Password::NullPassword) => write!(f, " PASSWORD NULL")?,
None => {}
};
if let Some(until) = &self.valid_until {
write!(f, " VALID UNTIL {until}")?;
}
if !self.in_role.is_empty() {
write!(f, " IN ROLE {}", display_comma_separated(&self.in_role))?;
}
if !self.in_group.is_empty() {
write!(f, " IN GROUP {}", display_comma_separated(&self.in_group))?;
}
if !self.role.is_empty() {
write!(f, " ROLE {}", display_comma_separated(&self.role))?;
}
if !self.user.is_empty() {
write!(f, " USER {}", display_comma_separated(&self.user))?;
}
if !self.admin.is_empty() {
write!(f, " ADMIN {}", display_comma_separated(&self.admin))?;
}
if let Some(owner) = &self.authorization_owner {
write!(f, " AUTHORIZATION {owner}")?;
}
Ok(())
}
}
impl Spanned for CreateRole {
fn span(&self) -> Span {
Span::empty()
}
}

View file

@ -19,7 +19,7 @@
//! (commonly referred to as Data Definition Language, or DDL)
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String, vec::Vec};
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
use core::fmt::{self, Display, Write};
#[cfg(feature = "serde")]
@ -30,15 +30,16 @@ use sqlparser_derive::{Visit, VisitMut};
use crate::ast::value::escape_single_quote_string;
use crate::ast::{
display_comma_separated, display_separated, table_constraints::TableConstraint, ArgMode,
CommentDef, ConditionalStatements, CreateFunctionBody, CreateFunctionUsing,
CreateTableLikeKind, CreateTableOptions, DataType, Expr, FileFormat, FunctionBehavior,
FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel, HiveDistributionStyle,
HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InitializeKind, MySQLColumnPosition,
ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg, OrderByExpr, ProjectionSelect,
Query, RefreshModeKind, RowAccessPolicy, SequenceOptions, Spanned, SqlOption,
StorageSerializationPolicy, TableVersion, Tag, TriggerEvent, TriggerExecBody, TriggerObject,
TriggerPeriod, TriggerReferencing, Value, ValueWithSpan, WrappedCollection,
display_comma_separated, display_separated, ArgMode, AttachedToken, CommentDef,
ConditionalStatements, CreateFunctionBody, CreateFunctionUsing, CreateTableLikeKind,
CreateTableOptions, CreateViewParams, DataType, Expr, FileFormat, FunctionBehavior,
FunctionCalledOnNull, FunctionDesc, FunctionDeterminismSpecifier, FunctionParallel,
HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, HiveSetLocation, Ident,
InitializeKind, MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens,
OperateFunctionArg, OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy,
SequenceOptions, Spanned, SqlOption, StorageSerializationPolicy, TableConstraint, TableVersion,
Tag, TriggerEvent, TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value,
ValueWithSpan, WrappedCollection,
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
@ -3138,3 +3139,394 @@ impl fmt::Display for DropTrigger {
Ok(())
}
}
/// A `TRUNCATE` statement.
///
/// ```sql
/// TRUNCATE TABLE table_names [PARTITION (partitions)] [RESTART IDENTITY | CONTINUE IDENTITY] [CASCADE | RESTRICT] [ON CLUSTER cluster_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 Truncate {
/// Table names to truncate
pub table_names: Vec<super::TruncateTableTarget>,
/// Optional partition specification
pub partitions: Option<Vec<Expr>>,
/// TABLE - optional keyword
pub table: bool,
/// Postgres-specific option: [ RESTART IDENTITY | CONTINUE IDENTITY ]
pub identity: Option<super::TruncateIdentityOption>,
/// Postgres-specific option: [ CASCADE | RESTRICT ]
pub cascade: Option<super::CascadeOption>,
/// ClickHouse-specific option: [ ON CLUSTER cluster_name ]
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/truncate/)
pub on_cluster: Option<Ident>,
}
impl fmt::Display for Truncate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let table = if self.table { "TABLE " } else { "" };
write!(
f,
"TRUNCATE {table}{table_names}",
table_names = display_comma_separated(&self.table_names)
)?;
if let Some(identity) = &self.identity {
match identity {
super::TruncateIdentityOption::Restart => write!(f, " RESTART IDENTITY")?,
super::TruncateIdentityOption::Continue => write!(f, " CONTINUE IDENTITY")?,
}
}
if let Some(cascade) = &self.cascade {
match cascade {
super::CascadeOption::Cascade => write!(f, " CASCADE")?,
super::CascadeOption::Restrict => write!(f, " RESTRICT")?,
}
}
if let Some(ref parts) = &self.partitions {
if !parts.is_empty() {
write!(f, " PARTITION ({})", display_comma_separated(parts))?;
}
}
if let Some(on_cluster) = &self.on_cluster {
write!(f, " ON CLUSTER {on_cluster}")?;
}
Ok(())
}
}
impl Spanned for Truncate {
fn span(&self) -> Span {
Span::union_iter(
self.table_names.iter().map(|i| i.name.span()).chain(
self.partitions
.iter()
.flat_map(|i| i.iter().map(|k| k.span())),
),
)
}
}
/// An `MSCK` statement.
///
/// ```sql
/// MSCK [REPAIR] TABLE table_name [ADD|DROP|SYNC PARTITIONS]
/// ```
/// MSCK (Hive) - MetaStore Check command
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Msck {
/// Table name to check
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
/// Whether to repair the table
pub repair: bool,
/// Partition action (ADD, DROP, or SYNC)
pub partition_action: Option<super::AddDropSync>,
}
impl fmt::Display for Msck {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"MSCK {repair}TABLE {table}",
repair = if self.repair { "REPAIR " } else { "" },
table = self.table_name
)?;
if let Some(pa) = &self.partition_action {
write!(f, " {pa}")?;
}
Ok(())
}
}
impl Spanned for Msck {
fn span(&self) -> Span {
self.table_name.span()
}
}
/// CREATE VIEW 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 CreateView {
/// True if this is a `CREATE OR ALTER VIEW` statement
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql)
pub or_alter: bool,
pub or_replace: bool,
pub materialized: bool,
/// Snowflake: SECURE view modifier
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
pub secure: bool,
/// View name
pub name: ObjectName,
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
/// Example:
/// ```sql
/// CREATE VIEW myview IF NOT EXISTS AS SELECT 1`
/// ```
/// Otherwise, the flag is set to false if the view name comes after the clause
/// Example:
/// ```sql
/// CREATE VIEW IF NOT EXISTS myview AS SELECT 1`
/// ```
pub name_before_not_exists: bool,
pub columns: Vec<ViewColumnDef>,
pub query: Box<Query>,
pub options: CreateTableOptions,
pub cluster_by: Vec<Ident>,
/// Snowflake: Views can have comments in Snowflake.
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
pub comment: Option<String>,
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
pub with_no_schema_binding: bool,
/// if true, has SQLite `IF NOT EXISTS` clause <https://www.sqlite.org/lang_createview.html>
pub if_not_exists: bool,
/// if true, has SQLite `TEMP` or `TEMPORARY` clause <https://www.sqlite.org/lang_createview.html>
pub temporary: bool,
/// if not None, has Clickhouse `TO` clause, specify the table into which to insert results
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/view#materialized-view>
pub to: Option<ObjectName>,
/// MySQL: Optional parameters for the view algorithm, definer, and security context
pub params: Option<CreateViewParams>,
}
impl fmt::Display for CreateView {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE {or_alter}{or_replace}",
or_alter = if self.or_alter { "OR ALTER " } else { "" },
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
)?;
if let Some(ref params) = self.params {
params.fmt(f)?;
}
write!(
f,
"{secure}{materialized}{temporary}VIEW {if_not_and_name}{to}",
if_not_and_name = if self.if_not_exists {
if self.name_before_not_exists {
format!("{} IF NOT EXISTS", self.name)
} else {
format!("IF NOT EXISTS {}", self.name)
}
} else {
format!("{}", self.name)
},
secure = if self.secure { "SECURE " } else { "" },
materialized = if self.materialized {
"MATERIALIZED "
} else {
""
},
temporary = if self.temporary { "TEMPORARY " } else { "" },
to = self
.to
.as_ref()
.map(|to| format!(" TO {to}"))
.unwrap_or_default()
)?;
if !self.columns.is_empty() {
write!(f, " ({})", display_comma_separated(&self.columns))?;
}
if matches!(self.options, CreateTableOptions::With(_)) {
write!(f, " {}", self.options)?;
}
if let Some(ref comment) = self.comment {
write!(f, " COMMENT = '{}'", escape_single_quote_string(comment))?;
}
if !self.cluster_by.is_empty() {
write!(
f,
" CLUSTER BY ({})",
display_comma_separated(&self.cluster_by)
)?;
}
if matches!(self.options, CreateTableOptions::Options(_)) {
write!(f, " {}", self.options)?;
}
f.write_str(" AS")?;
SpaceOrNewline.fmt(f)?;
self.query.fmt(f)?;
if self.with_no_schema_binding {
write!(f, " WITH NO SCHEMA BINDING")?;
}
Ok(())
}
}
/// CREATE EXTENSION statement
/// Note: this is a PostgreSQL-specific 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 CreateExtension {
pub name: Ident,
pub if_not_exists: bool,
pub cascade: bool,
pub schema: Option<Ident>,
pub version: Option<Ident>,
}
impl fmt::Display for CreateExtension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE EXTENSION {if_not_exists}{name}",
if_not_exists = if self.if_not_exists {
"IF NOT EXISTS "
} else {
""
},
name = self.name
)?;
if self.cascade || self.schema.is_some() || self.version.is_some() {
write!(f, " WITH")?;
if let Some(name) = &self.schema {
write!(f, " SCHEMA {name}")?;
}
if let Some(version) = &self.version {
write!(f, " VERSION {version}")?;
}
if self.cascade {
write!(f, " CASCADE")?;
}
}
Ok(())
}
}
impl Spanned for CreateExtension {
fn span(&self) -> Span {
Span::empty()
}
}
/// DROP EXTENSION statement
/// Note: this is a PostgreSQL-specific statement
///
/// # References
///
/// PostgreSQL Documentation:
/// <https://www.postgresql.org/docs/current/sql-dropextension.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 DropExtension {
pub names: Vec<Ident>,
pub if_exists: bool,
/// `CASCADE` or `RESTRICT`
pub cascade_or_restrict: Option<ReferentialAction>,
}
impl fmt::Display for DropExtension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DROP EXTENSION")?;
if self.if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", display_comma_separated(&self.names))?;
if let Some(cascade_or_restrict) = &self.cascade_or_restrict {
write!(f, " {cascade_or_restrict}")?;
}
Ok(())
}
}
impl Spanned for DropExtension {
fn span(&self) -> Span {
Span::empty()
}
}
/// ALTER TABLE 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 AlterTable {
/// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub name: ObjectName,
pub if_exists: bool,
pub only: bool,
pub operations: Vec<AlterTableOperation>,
pub location: Option<HiveSetLocation>,
/// ClickHouse dialect supports `ON CLUSTER` clause for ALTER TABLE
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/update)
pub on_cluster: Option<Ident>,
/// Snowflake "ICEBERG" clause for Iceberg tables
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
pub iceberg: bool,
/// Token that represents the end of the statement (semicolon or EOF)
pub end_token: AttachedToken,
}
impl fmt::Display for AlterTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.iceberg {
write!(f, "ALTER ICEBERG TABLE ")?;
} else {
write!(f, "ALTER TABLE ")?;
}
if self.if_exists {
write!(f, "IF EXISTS ")?;
}
if self.only {
write!(f, "ONLY ")?;
}
write!(f, "{} ", &self.name)?;
if let Some(cluster) = &self.on_cluster {
write!(f, "ON CLUSTER {cluster} ")?;
}
write!(f, "{}", display_comma_separated(&self.operations))?;
if let Some(loc) = &self.location {
write!(f, " {loc}")?
}
Ok(())
}
}
/// DROP FUNCTION 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 DropFunction {
pub if_exists: bool,
/// One or more functions to drop
pub func_desc: Vec<FunctionDesc>,
/// `CASCADE` or `RESTRICT`
pub drop_behavior: Option<DropBehavior>,
}
impl fmt::Display for DropFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DROP FUNCTION{} {}",
if self.if_exists { " IF EXISTS" } else { "" },
display_comma_separated(&self.func_desc),
)?;
if let Some(op) = &self.drop_behavior {
write!(f, " {op}")?;
}
Ok(())
}
}
impl Spanned for DropFunction {
fn span(&self) -> Span {
Span::empty()
}
}

View file

@ -29,7 +29,7 @@ use crate::display_utils::{indented_list, Indent, SpaceOrNewline};
use super::{
display_comma_separated, query::InputFormatClause, Assignment, Expr, FromTable, Ident,
InsertAliases, MysqlInsertPriority, ObjectName, OnInsert, OrderByExpr, Query, SelectItem,
Setting, SqliteOnConflict, TableObject, TableWithJoins,
Setting, SqliteOnConflict, TableObject, TableWithJoins, UpdateTableFromKind,
};
/// INSERT statement.
@ -240,3 +240,66 @@ impl Display for Delete {
Ok(())
}
}
/// UPDATE 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 Update {
/// TABLE
pub table: TableWithJoins,
/// Column assignments
pub assignments: Vec<Assignment>,
/// Table which provide value to be set
pub from: Option<UpdateTableFromKind>,
/// WHERE
pub selection: Option<Expr>,
/// RETURNING
pub returning: Option<Vec<SelectItem>>,
/// SQLite-specific conflict resolution clause
pub or: Option<SqliteOnConflict>,
/// LIMIT
pub limit: Option<Expr>,
}
impl Display for Update {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("UPDATE ")?;
if let Some(or) = &self.or {
or.fmt(f)?;
f.write_str(" ")?;
}
self.table.fmt(f)?;
if let Some(UpdateTableFromKind::BeforeSet(from)) = &self.from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if !self.assignments.is_empty() {
SpaceOrNewline.fmt(f)?;
f.write_str("SET")?;
indented_list(f, &self.assignments)?;
}
if let Some(UpdateTableFromKind::AfterSet(from)) = &self.from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if let Some(selection) = &self.selection {
SpaceOrNewline.fmt(f)?;
f.write_str("WHERE")?;
SpaceOrNewline.fmt(f)?;
Indent(selection).fmt(f)?;
}
if let Some(returning) = &self.returning {
SpaceOrNewline.fmt(f)?;
f.write_str("RETURNING")?;
indented_list(f, returning)?;
}
if let Some(limit) = &self.limit {
SpaceOrNewline.fmt(f)?;
write!(f, "LIMIT {limit}")?;
}
Ok(())
}
}

View file

@ -43,7 +43,7 @@ use serde::{Deserialize, Serialize};
use sqlparser_derive::{Visit, VisitMut};
use crate::{
display_utils::{indented_list, SpaceOrNewline},
display_utils::SpaceOrNewline,
tokenizer::{Span, Token},
};
use crate::{
@ -56,22 +56,24 @@ pub use self::data_type::{
ExactNumberInfo, IntervalFields, StructBracketKind, TimezoneInfo,
};
pub use self::dcl::{
AlterRoleOperation, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,
AlterRoleOperation, CreateRole, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,
};
pub use self::ddl::{
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
AlterSchema, AlterSchemaOperation, AlterTableAlgorithm, AlterTableLock, AlterTableOperation,
AlterType, AlterTypeAddValue, AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename,
AlterTypeRenameValue, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions,
ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
CreateFunction, CreateIndex, CreateTable, CreateTrigger, Deduplicate, DeferrableInitial,
DropBehavior, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm, AlterTableLock,
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction,
CreateIndex, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DropBehavior, DropExtension, DropFunction, DropTrigger, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction, RenameTableNameKind,
ReplicaIdentity, TagsColumnOption, Truncate, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeRepresentation, ViewColumnDef,
};
pub use self::dml::{Delete, Insert};
pub use self::dml::{Delete, Insert, Update};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
@ -3064,6 +3066,59 @@ impl Display for ExceptionWhen {
}
}
/// ANALYZE TABLE statement (Hive-specific)
#[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"))]
pub table_name: ObjectName,
pub partitions: Option<Vec<Expr>>,
pub for_columns: bool,
pub columns: Vec<Ident>,
pub cache_metadata: bool,
pub noscan: bool,
pub compute_statistics: bool,
pub has_table_keyword: bool,
}
impl fmt::Display for Analyze {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ANALYZE{}{table_name}",
if self.has_table_keyword {
" TABLE "
} else {
" "
},
table_name = self.table_name
)?;
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)]
@ -3078,49 +3133,18 @@ pub enum Statement {
/// ANALYZE
/// ```
/// Analyze (Hive)
Analyze {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
table_name: ObjectName,
partitions: Option<Vec<Expr>>,
for_columns: bool,
columns: Vec<Ident>,
cache_metadata: bool,
noscan: bool,
compute_statistics: bool,
has_table_keyword: bool,
},
Analyze(Analyze),
Set(Set),
/// ```sql
/// TRUNCATE
/// ```
/// Truncate (Hive)
Truncate {
table_names: Vec<TruncateTableTarget>,
partitions: Option<Vec<Expr>>,
/// TABLE - optional keyword;
table: bool,
/// Postgres-specific option
/// [ RESTART IDENTITY | CONTINUE IDENTITY ]
identity: Option<TruncateIdentityOption>,
/// Postgres-specific option
/// [ CASCADE | RESTRICT ]
cascade: Option<CascadeOption>,
/// ClickHouse-specific option
/// [ ON CLUSTER cluster_name ]
///
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/truncate/)
on_cluster: Option<Ident>,
},
Truncate(Truncate),
/// ```sql
/// MSCK
/// ```
/// Msck (Hive)
Msck {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
table_name: ObjectName,
repair: bool,
partition_action: Option<AddDropSync>,
},
Msck(Msck),
/// ```sql
/// SELECT
/// ```
@ -3223,22 +3247,7 @@ pub enum Statement {
/// ```sql
/// UPDATE
/// ```
Update {
/// TABLE
table: TableWithJoins,
/// Column assignments
assignments: Vec<Assignment>,
/// Table which provide value to be set
from: Option<UpdateTableFromKind>,
/// WHERE
selection: Option<Expr>,
/// RETURNING
returning: Option<Vec<SelectItem>>,
/// SQLite-specific conflict resolution clause
or: Option<SqliteOnConflict>,
/// LIMIT
limit: Option<Expr>,
},
Update(Update),
/// ```sql
/// DELETE
/// ```
@ -3246,48 +3255,7 @@ pub enum Statement {
/// ```sql
/// CREATE VIEW
/// ```
CreateView {
/// True if this is a `CREATE OR ALTER VIEW` statement
///
/// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql)
or_alter: bool,
or_replace: bool,
materialized: bool,
/// Snowflake: SECURE view modifier
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
secure: bool,
/// View name
name: ObjectName,
/// If `if_not_exists` is true, this flag is set to true if the view name comes before the `IF NOT EXISTS` clause.
/// Example:
/// ```sql
/// CREATE VIEW myview IF NOT EXISTS AS SELECT 1`
/// ```
/// Otherwise, the flag is set to false if the view name comes after the clause
/// Example:
/// ```sql
/// CREATE VIEW IF NOT EXISTS myview AS SELECT 1`
/// ```
name_before_not_exists: bool,
columns: Vec<ViewColumnDef>,
query: Box<Query>,
options: CreateTableOptions,
cluster_by: Vec<Ident>,
/// Snowflake: Views can have comments in Snowflake.
/// <https://docs.snowflake.com/en/sql-reference/sql/create-view#syntax>
comment: Option<String>,
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
with_no_schema_binding: bool,
/// if true, has SQLite `IF NOT EXISTS` clause <https://www.sqlite.org/lang_createview.html>
if_not_exists: bool,
/// if true, has SQLite `TEMP` or `TEMPORARY` clause <https://www.sqlite.org/lang_createview.html>
temporary: bool,
/// if not None, has Clickhouse `TO` clause, specify the table into which to insert results
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/view#materialized-view>
to: Option<ObjectName>,
/// MySQL: Optional parameters for the view algorithm, definer, and security context
params: Option<CreateViewParams>,
},
CreateView(CreateView),
/// ```sql
/// CREATE TABLE
/// ```
@ -3311,28 +3279,7 @@ pub enum Statement {
/// CREATE ROLE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.html)
CreateRole {
names: Vec<ObjectName>,
if_not_exists: bool,
// Postgres
login: Option<bool>,
inherit: Option<bool>,
bypassrls: Option<bool>,
password: Option<Password>,
superuser: Option<bool>,
create_db: Option<bool>,
create_role: Option<bool>,
replication: Option<bool>,
connection_limit: Option<Expr>,
valid_until: Option<Expr>,
in_role: Vec<Ident>,
in_group: Vec<Ident>,
role: Vec<Ident>,
user: Vec<Ident>,
admin: Vec<Ident>,
// MSSQL
authorization_owner: Option<ObjectName>,
},
CreateRole(CreateRole),
/// ```sql
/// CREATE SECRET
/// ```
@ -3370,24 +3317,7 @@ pub enum Statement {
/// ```sql
/// ALTER TABLE
/// ```
AlterTable {
/// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
name: ObjectName,
if_exists: bool,
only: bool,
operations: Vec<AlterTableOperation>,
location: Option<HiveSetLocation>,
/// ClickHouse dialect supports `ON CLUSTER` clause for ALTER TABLE
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/update)
on_cluster: Option<Ident>,
/// Snowflake "ICEBERG" clause for Iceberg tables
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-iceberg-table>
iceberg: bool,
/// Token that represents the end of the statement (semicolon or EOF)
end_token: AttachedToken,
},
AlterTable(AlterTable),
/// ```sql
/// ALTER SCHEMA
/// ```
@ -3523,13 +3453,7 @@ pub enum Statement {
/// ```sql
/// DROP FUNCTION
/// ```
DropFunction {
if_exists: bool,
/// One or more function to drop
func_desc: Vec<FunctionDesc>,
/// `CASCADE` or `RESTRICT`
drop_behavior: Option<DropBehavior>,
},
DropFunction(DropFunction),
/// ```sql
/// DROP DOMAIN
/// ```
@ -3593,25 +3517,13 @@ pub enum Statement {
/// ```
///
/// Note: this is a PostgreSQL-specific statement,
CreateExtension {
name: Ident,
if_not_exists: bool,
cascade: bool,
schema: Option<Ident>,
version: Option<Ident>,
},
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 {
names: Vec<Ident>,
if_exists: bool,
/// `CASCADE` or `RESTRICT`
cascade_or_restrict: Option<ReferentialAction>,
},
/// Note: this is a PostgreSQL-specific statement.
/// <https://www.postgresql.org/docs/current/sql-dropextension.html>
DropExtension(DropExtension),
/// ```sql
/// FETCH
/// ```
@ -4328,6 +4240,24 @@ pub enum Statement {
Vacuum(VacuumStatement),
}
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<ddl::Msck> for Statement {
fn from(msck: ddl::Msck) -> Self {
Statement::Msck(msck)
}
}
/// ```sql
/// {COPY | REVOKE} CURRENT GRANTS
/// ```
@ -4528,61 +4458,8 @@ impl fmt::Display for Statement {
}
write!(f, " {source}")
}
Statement::Msck {
table_name,
repair,
partition_action,
} => {
write!(
f,
"MSCK {repair}TABLE {table}",
repair = if *repair { "REPAIR " } else { "" },
table = table_name
)?;
if let Some(pa) = partition_action {
write!(f, " {pa}")?;
}
Ok(())
}
Statement::Truncate {
table_names,
partitions,
table,
identity,
cascade,
on_cluster,
} => {
let table = if *table { "TABLE " } else { "" };
write!(
f,
"TRUNCATE {table}{table_names}",
table_names = display_comma_separated(table_names)
)?;
if let Some(identity) = identity {
match identity {
TruncateIdentityOption::Restart => write!(f, " RESTART IDENTITY")?,
TruncateIdentityOption::Continue => write!(f, " CONTINUE IDENTITY")?,
}
}
if let Some(cascade) = cascade {
match cascade {
CascadeOption::Cascade => write!(f, " CASCADE")?,
CascadeOption::Restrict => write!(f, " RESTRICT")?,
}
}
if let Some(ref parts) = partitions {
if !parts.is_empty() {
write!(f, " PARTITION ({})", display_comma_separated(parts))?;
}
}
if let Some(on_cluster) = on_cluster {
write!(f, " ON CLUSTER {on_cluster}")?;
}
Ok(())
}
Statement::Msck(msck) => msck.fmt(f),
Statement::Truncate(truncate) => truncate.fmt(f),
Statement::Case(stmt) => {
write!(f, "{stmt}")
}
@ -4637,44 +4514,7 @@ impl fmt::Display for Statement {
)?;
Ok(())
}
Statement::Analyze {
table_name,
partitions,
for_columns,
columns,
cache_metadata,
noscan,
compute_statistics,
has_table_keyword,
} => {
write!(
f,
"ANALYZE{}{table_name}",
if *has_table_keyword { " TABLE " } else { " " }
)?;
if let Some(ref parts) = partitions {
if !parts.is_empty() {
write!(f, " PARTITION ({})", display_comma_separated(parts))?;
}
}
if *compute_statistics {
write!(f, " COMPUTE STATISTICS")?;
}
if *noscan {
write!(f, " NOSCAN")?;
}
if *cache_metadata {
write!(f, " CACHE METADATA")?;
}
if *for_columns {
write!(f, " FOR COLUMNS")?;
if !columns.is_empty() {
write!(f, " {}", display_comma_separated(columns))?;
}
}
Ok(())
}
Statement::Analyze(analyze) => analyze.fmt(f),
Statement::Insert(insert) => insert.fmt(f),
Statement::Install {
extension_name: name,
@ -4730,53 +4570,7 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::Update {
table,
assignments,
from,
selection,
returning,
or,
limit,
} => {
f.write_str("UPDATE ")?;
if let Some(or) = or {
or.fmt(f)?;
f.write_str(" ")?;
}
table.fmt(f)?;
if let Some(UpdateTableFromKind::BeforeSet(from)) = from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if !assignments.is_empty() {
SpaceOrNewline.fmt(f)?;
f.write_str("SET")?;
indented_list(f, assignments)?;
}
if let Some(UpdateTableFromKind::AfterSet(from)) = from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if let Some(selection) = selection {
SpaceOrNewline.fmt(f)?;
f.write_str("WHERE")?;
SpaceOrNewline.fmt(f)?;
Indent(selection).fmt(f)?;
}
if let Some(returning) = returning {
SpaceOrNewline.fmt(f)?;
f.write_str("RETURNING")?;
indented_list(f, returning)?;
}
if let Some(limit) = limit {
SpaceOrNewline.fmt(f)?;
write!(f, "LIMIT {limit}")?;
}
Ok(())
}
Statement::Update(update) => update.fmt(f),
Statement::Delete(delete) => delete.fmt(f),
Statement::Open(open) => open.fmt(f),
Statement::Close { cursor } => {
@ -4932,80 +4726,7 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::CreateView {
or_alter,
name,
or_replace,
columns,
query,
materialized,
secure,
options,
cluster_by,
comment,
with_no_schema_binding,
if_not_exists,
temporary,
to,
params,
name_before_not_exists,
} => {
write!(
f,
"CREATE {or_alter}{or_replace}",
or_alter = if *or_alter { "OR ALTER " } else { "" },
or_replace = if *or_replace { "OR REPLACE " } else { "" },
)?;
if let Some(params) = params {
params.fmt(f)?;
}
write!(
f,
"{secure}{materialized}{temporary}VIEW {if_not_and_name}{to}",
if_not_and_name = if *if_not_exists {
if *name_before_not_exists {
format!("{name} IF NOT EXISTS")
} else {
format!("IF NOT EXISTS {name}")
}
} else {
format!("{name}")
},
secure = if *secure { "SECURE " } else { "" },
materialized = if *materialized { "MATERIALIZED " } else { "" },
temporary = if *temporary { "TEMPORARY " } else { "" },
to = to
.as_ref()
.map(|to| format!(" TO {to}"))
.unwrap_or_default()
)?;
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
if matches!(options, CreateTableOptions::With(_)) {
write!(f, " {options}")?;
}
if let Some(comment) = comment {
write!(
f,
" COMMENT = '{}'",
value::escape_single_quote_string(comment)
)?;
}
if !cluster_by.is_empty() {
write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?;
}
if matches!(options, CreateTableOptions::Options(_)) {
write!(f, " {options}")?;
}
f.write_str(" AS")?;
SpaceOrNewline.fmt(f)?;
query.fmt(f)?;
if *with_no_schema_binding {
write!(f, " WITH NO SCHEMA BINDING")?;
}
Ok(())
}
Statement::CreateView(create_view) => create_view.fmt(f),
Statement::CreateTable(create_table) => create_table.fmt(f),
Statement::LoadData {
local,
@ -5056,141 +4777,9 @@ impl fmt::Display for Statement {
Ok(())
}
Statement::CreateIndex(create_index) => create_index.fmt(f),
Statement::CreateExtension {
name,
if_not_exists,
cascade,
schema,
version,
} => {
write!(
f,
"CREATE EXTENSION {if_not_exists}{name}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }
)?;
if *cascade || schema.is_some() || version.is_some() {
write!(f, " WITH")?;
if let Some(name) = schema {
write!(f, " SCHEMA {name}")?;
}
if let Some(version) = version {
write!(f, " VERSION {version}")?;
}
if *cascade {
write!(f, " CASCADE")?;
}
}
Ok(())
}
Statement::DropExtension {
names,
if_exists,
cascade_or_restrict,
} => {
write!(f, "DROP EXTENSION")?;
if *if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", display_comma_separated(names))?;
if let Some(cascade_or_restrict) = cascade_or_restrict {
write!(f, " {cascade_or_restrict}")?;
}
Ok(())
}
Statement::CreateRole {
names,
if_not_exists,
inherit,
login,
bypassrls,
password,
create_db,
create_role,
superuser,
replication,
connection_limit,
valid_until,
in_role,
in_group,
role,
user,
admin,
authorization_owner,
} => {
write!(
f,
"CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
names = display_separated(names, ", "),
superuser = match *superuser {
Some(true) => " SUPERUSER",
Some(false) => " NOSUPERUSER",
None => ""
},
create_db = match *create_db {
Some(true) => " CREATEDB",
Some(false) => " NOCREATEDB",
None => ""
},
create_role = match *create_role {
Some(true) => " CREATEROLE",
Some(false) => " NOCREATEROLE",
None => ""
},
inherit = match *inherit {
Some(true) => " INHERIT",
Some(false) => " NOINHERIT",
None => ""
},
login = match *login {
Some(true) => " LOGIN",
Some(false) => " NOLOGIN",
None => ""
},
replication = match *replication {
Some(true) => " REPLICATION",
Some(false) => " NOREPLICATION",
None => ""
},
bypassrls = match *bypassrls {
Some(true) => " BYPASSRLS",
Some(false) => " NOBYPASSRLS",
None => ""
}
)?;
if let Some(limit) = connection_limit {
write!(f, " CONNECTION LIMIT {limit}")?;
}
match password {
Some(Password::Password(pass)) => write!(f, " PASSWORD {pass}"),
Some(Password::NullPassword) => write!(f, " PASSWORD NULL"),
None => Ok(()),
}?;
if let Some(until) = valid_until {
write!(f, " VALID UNTIL {until}")?;
}
if !in_role.is_empty() {
write!(f, " IN ROLE {}", display_comma_separated(in_role))?;
}
if !in_group.is_empty() {
write!(f, " IN GROUP {}", display_comma_separated(in_group))?;
}
if !role.is_empty() {
write!(f, " ROLE {}", display_comma_separated(role))?;
}
if !user.is_empty() {
write!(f, " USER {}", display_comma_separated(user))?;
}
if !admin.is_empty() {
write!(f, " ADMIN {}", display_comma_separated(admin))?;
}
if let Some(owner) = authorization_owner {
write!(f, " AUTHORIZATION {owner}")?;
}
Ok(())
}
Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
Statement::CreateSecret {
or_replace,
temporary,
@ -5272,42 +4861,7 @@ impl fmt::Display for Statement {
Ok(())
}
Statement::CreateConnector(create_connector) => create_connector.fmt(f),
Statement::AlterTable {
name,
if_exists,
only,
operations,
location,
on_cluster,
iceberg,
end_token: _,
} => {
if *iceberg {
write!(f, "ALTER ICEBERG TABLE ")?;
} else {
write!(f, "ALTER TABLE ")?;
}
if *if_exists {
write!(f, "IF EXISTS ")?;
}
if *only {
write!(f, "ONLY ")?;
}
write!(f, "{name} ")?;
if let Some(cluster) = on_cluster {
write!(f, "ON CLUSTER {cluster} ")?;
}
write!(
f,
"{operations}",
operations = display_comma_separated(operations)
)?;
if let Some(loc) = location {
write!(f, " {loc}")?
}
Ok(())
}
Statement::AlterTable(alter_table) => write!(f, "{alter_table}"),
Statement::AlterIndex { name, operation } => {
write!(f, "ALTER INDEX {name} {operation}")
}
@ -5410,22 +4964,7 @@ impl fmt::Display for Statement {
};
Ok(())
}
Statement::DropFunction {
if_exists,
func_desc,
drop_behavior,
} => {
write!(
f,
"DROP FUNCTION{} {}",
if *if_exists { " IF EXISTS" } else { "" },
display_comma_separated(func_desc),
)?;
if let Some(op) = drop_behavior {
write!(f, " {op}")?;
}
Ok(())
}
Statement::DropFunction(drop_function) => write!(f, "{drop_function}"),
Statement::DropDomain(DropDomain {
if_exists,
name,
@ -10945,6 +10484,48 @@ impl From<Insert> for Statement {
}
}
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)

View file

@ -16,8 +16,8 @@
// under the License.
use crate::ast::{
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, ColumnOptions,
ExportData, Owner, TypedString,
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
ColumnOptions, CreateView, ExportData, Owner, TypedString,
};
use core::iter;
@ -25,23 +25,23 @@ use crate::tokenizer::Span;
use super::{
dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation,
AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, AttachedToken,
BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption,
ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectBy,
ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte,
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert,
Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem,
LateralView, LimitClause, MatchRecognizePattern, Measure, NamedParenthesizedList,
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource,
ProjectionSelect, Query, RaiseStatement, RaiseStatementValue, ReferentialAction,
RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem,
SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef,
TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins,
UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement,
WildcardAdditionalOptions, With, WithFill,
AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget,
AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef,
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr,
FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound,
IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join,
JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
MatchRecognizePattern, Measure, NamedParenthesizedList, NamedWindowDefinition, ObjectName,
ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy,
OrderByExpr, OrderByKind, Partition, PivotValueSource, ProjectionSelect, Query, RaiseStatement,
RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Value, Values,
ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill,
};
/// Given an iterator of spans, return the [Span::union] of all spans.
@ -298,38 +298,9 @@ impl Spanned for Values {
impl Spanned for Statement {
fn span(&self) -> Span {
match self {
Statement::Analyze {
table_name,
partitions,
for_columns: _,
columns,
cache_metadata: _,
noscan: _,
compute_statistics: _,
has_table_keyword: _,
} => union_spans(
core::iter::once(table_name.span())
.chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span())))
.chain(columns.iter().map(|i| i.span)),
),
Statement::Truncate {
table_names,
partitions,
table: _,
identity: _,
cascade: _,
on_cluster: _,
} => union_spans(
table_names
.iter()
.map(|i| i.name.span())
.chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span()))),
),
Statement::Msck {
table_name,
repair: _,
partition_action: _,
} => table_name.span(),
Statement::Analyze(analyze) => analyze.span(),
Statement::Truncate(truncate) => truncate.span(),
Statement::Msck(msck) => msck.span(),
Statement::Query(query) => query.span(),
Statement::Insert(insert) => insert.span(),
Statement::Install { extension_name } => extension_name.span,
@ -375,47 +346,9 @@ impl Spanned for Statement {
CloseCursor::All => Span::empty(),
CloseCursor::Specific { name } => name.span,
},
Statement::Update {
table,
assignments,
from,
selection,
returning,
or: _,
limit: _,
} => union_spans(
core::iter::once(table.span())
.chain(assignments.iter().map(|i| i.span()))
.chain(from.iter().map(|i| i.span()))
.chain(selection.iter().map(|i| i.span()))
.chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))),
),
Statement::Update(update) => update.span(),
Statement::Delete(delete) => delete.span(),
Statement::CreateView {
or_alter: _,
or_replace: _,
materialized: _,
secure: _,
name,
columns,
query,
options,
cluster_by,
comment: _,
with_no_schema_binding: _,
if_not_exists: _,
temporary: _,
to,
name_before_not_exists: _,
params: _,
} => union_spans(
core::iter::once(name.span())
.chain(columns.iter().map(|i| i.span()))
.chain(core::iter::once(query.span()))
.chain(core::iter::once(options.span()))
.chain(cluster_by.iter().map(|i| i.span))
.chain(to.iter().map(|i| i.span())),
),
Statement::CreateView(create_view) => create_view.span(),
Statement::CreateTable(create_table) => create_table.span(),
Statement::CreateVirtualTable {
name,
@ -428,25 +361,13 @@ impl Spanned for Statement {
.chain(module_args.iter().map(|i| i.span)),
),
Statement::CreateIndex(create_index) => create_index.span(),
Statement::CreateRole { .. } => Span::empty(),
Statement::CreateRole(create_role) => create_role.span(),
Statement::CreateExtension(create_extension) => create_extension.span(),
Statement::DropExtension(drop_extension) => drop_extension.span(),
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateConnector { .. } => Span::empty(),
Statement::AlterTable {
name,
if_exists: _,
only: _,
operations,
location: _,
on_cluster,
iceberg: _,
end_token,
} => union_spans(
core::iter::once(name.span())
.chain(operations.iter().map(|i| i.span()))
.chain(on_cluster.iter().map(|i| i.span))
.chain(core::iter::once(end_token.0.span)),
),
Statement::AlterTable(alter_table) => alter_table.span(),
Statement::AlterIndex { name, operation } => name.span().union(&operation.span()),
Statement::AlterView {
name,
@ -467,13 +388,11 @@ impl Spanned for Statement {
Statement::AttachDuckDBDatabase { .. } => Span::empty(),
Statement::DetachDuckDBDatabase { .. } => Span::empty(),
Statement::Drop { .. } => Span::empty(),
Statement::DropFunction { .. } => Span::empty(),
Statement::DropFunction(drop_function) => drop_function.span(),
Statement::DropDomain { .. } => Span::empty(),
Statement::DropProcedure { .. } => Span::empty(),
Statement::DropSecret { .. } => Span::empty(),
Statement::Declare { .. } => Span::empty(),
Statement::CreateExtension { .. } => Span::empty(),
Statement::DropExtension { .. } => Span::empty(),
Statement::Fetch { .. } => Span::empty(),
Statement::Flush { .. } => Span::empty(),
Statement::Discard { .. } => Span::empty(),
@ -873,6 +792,20 @@ impl Spanned for ConstraintCharacteristics {
}
}
impl Spanned for Analyze {
fn span(&self) -> Span {
union_spans(
core::iter::once(self.table_name.span())
.chain(
self.partitions
.iter()
.flat_map(|i| i.iter().map(|k| k.span())),
)
.chain(self.columns.iter().map(|i| i.span)),
)
}
}
/// # partial span
///
/// Missing spans:
@ -941,6 +874,29 @@ impl Spanned for Delete {
}
}
impl Spanned for Update {
fn span(&self) -> Span {
let Update {
table,
assignments,
from,
selection,
returning,
or: _,
limit,
} = self;
union_spans(
core::iter::once(table.span())
.chain(assignments.iter().map(|i| i.span()))
.chain(from.iter().map(|i| i.span()))
.chain(selection.iter().map(|i| i.span()))
.chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span())))
.chain(limit.iter().map(|i| i.span())),
)
}
}
impl Spanned for FromTable {
fn span(&self) -> Span {
match self {
@ -2375,6 +2331,30 @@ impl Spanned for AlterSchema {
}
}
impl Spanned for CreateView {
fn span(&self) -> Span {
union_spans(
core::iter::once(self.name.span())
.chain(self.columns.iter().map(|i| i.span()))
.chain(core::iter::once(self.query.span()))
.chain(core::iter::once(self.options.span()))
.chain(self.cluster_by.iter().map(|i| i.span))
.chain(self.to.iter().map(|i| i.span())),
)
}
}
impl Spanned for AlterTable {
fn span(&self) -> Span {
union_spans(
core::iter::once(self.name.span())
.chain(self.operations.iter().map(|i| i.span()))
.chain(self.on_cluster.iter().map(|i| i.span))
.chain(core::iter::once(self.end_token.0.span)),
)
}
}
#[cfg(test)]
pub mod tests {
use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect};

View file

@ -982,11 +982,12 @@ impl<'a> Parser<'a> {
Ok(pa)
})?
.unwrap_or_default();
Ok(Statement::Msck {
Ok(Msck {
repair,
table_name,
partition_action,
})
}
.into())
}
pub fn parse_truncate(&mut self) -> Result<Statement, ParserError> {
@ -1024,14 +1025,15 @@ impl<'a> Parser<'a> {
let on_cluster = self.parse_optional_on_cluster()?;
Ok(Statement::Truncate {
Ok(Truncate {
table_names,
partitions,
table,
identity,
cascade,
on_cluster,
})
}
.into())
}
fn parse_cascade_option(&mut self) -> Option<CascadeOption> {
@ -1167,7 +1169,7 @@ impl<'a> Parser<'a> {
}
}
Ok(Statement::Analyze {
Ok(Analyze {
has_table_keyword,
table_name,
for_columns,
@ -1176,7 +1178,8 @@ impl<'a> Parser<'a> {
cache_metadata,
noscan,
compute_statistics,
})
}
.into())
}
/// Parse a new expression including wildcard & qualified wildcard.
@ -5926,7 +5929,7 @@ impl<'a> Parser<'a> {
Keyword::BINDING,
]);
Ok(Statement::CreateView {
Ok(CreateView {
or_alter,
name,
columns,
@ -5943,7 +5946,8 @@ impl<'a> Parser<'a> {
to,
params: create_view_params,
name_before_not_exists,
})
}
.into())
}
/// Parse optional parameters for the `CREATE VIEW` statement supported by [MySQL].
@ -6206,7 +6210,7 @@ impl<'a> Parser<'a> {
}?
}
Ok(Statement::CreateRole {
Ok(CreateRole {
names,
if_not_exists,
login,
@ -6225,7 +6229,8 @@ impl<'a> Parser<'a> {
user,
admin,
authorization_owner,
})
}
.into())
}
pub fn parse_owner(&mut self) -> Result<Owner, ParserError> {
@ -6503,11 +6508,11 @@ impl<'a> Parser<'a> {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let func_desc = self.parse_comma_separated(Parser::parse_function_desc)?;
let drop_behavior = self.parse_optional_drop_behavior();
Ok(Statement::DropFunction {
Ok(Statement::DropFunction(DropFunction {
if_exists,
func_desc,
drop_behavior,
})
}))
}
/// ```sql
@ -7175,13 +7180,14 @@ impl<'a> Parser<'a> {
(None, None, false)
};
Ok(Statement::CreateExtension {
Ok(CreateExtension {
name,
if_not_exists,
schema,
version,
cascade,
})
}
.into())
}
/// Parse a PostgreSQL-specific [Statement::DropExtension] statement.
@ -7190,7 +7196,7 @@ impl<'a> Parser<'a> {
let names = self.parse_comma_separated(|p| p.parse_identifier())?;
let cascade_or_restrict =
self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]);
Ok(Statement::DropExtension {
Ok(Statement::DropExtension(DropExtension {
names,
if_exists,
cascade_or_restrict: cascade_or_restrict
@ -7200,7 +7206,7 @@ impl<'a> Parser<'a> {
_ => self.expected("CASCADE or RESTRICT", self.peek_token()),
})
.transpose()?,
})
}))
}
//TODO: Implement parsing for Skewed
@ -9378,7 +9384,7 @@ impl<'a> Parser<'a> {
self.get_current_token().clone()
};
Ok(Statement::AlterTable {
Ok(AlterTable {
name: table_name,
if_exists,
only,
@ -9387,7 +9393,8 @@ impl<'a> Parser<'a> {
on_cluster,
iceberg,
end_token: AttachedToken(end_token),
})
}
.into())
}
pub fn parse_alter_view(&mut self) -> Result<Statement, ParserError> {
@ -15662,7 +15669,7 @@ impl<'a> Parser<'a> {
} else {
None
};
Ok(Statement::Update {
Ok(Update {
table,
assignments,
from,
@ -15670,7 +15677,8 @@ impl<'a> Parser<'a> {
returning,
or,
limit,
})
}
.into())
}
/// Parse a `var = expr` assignment, used in an UPDATE statement

View file

@ -343,21 +343,12 @@ pub fn expr_from_projection(item: &SelectItem) -> &Expr {
pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTableOperation {
match stmt {
Statement::AlterTable {
name,
if_exists,
only: is_only,
operations,
on_cluster: _,
location: _,
iceberg,
end_token: _,
} => {
assert_eq!(name.to_string(), expected_name);
assert!(!if_exists);
assert!(!is_only);
assert!(!iceberg);
only(operations)
Statement::AlterTable(alter_table) => {
assert_eq!(alter_table.name.to_string(), expected_name);
assert!(!alter_table.if_exists);
assert!(!alter_table.only);
assert!(!alter_table.iceberg);
only(alter_table.operations)
}
_ => panic!("Expected ALTER TABLE statement"),
}
@ -484,7 +475,7 @@ pub fn index_column(stmt: Statement) -> Expr {
_ => panic!("Expected an index, unique, primary, full text, or spatial constraint (foreign key does not support general key part expressions)"),
}
}
Statement::AlterTable { operations, .. } => match operations.first().unwrap() {
Statement::AlterTable(alter_table) => match alter_table.operations.first().unwrap() {
AlterTableOperation::AddConstraint { constraint, .. } => {
match constraint {
TableConstraint::Index(constraint) => {

View file

@ -332,13 +332,13 @@ fn parse_create_view_with_options() {
"AS SELECT column_1, column_2, column_3 FROM myproject.mydataset.mytable",
);
match bigquery().verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
name,
query,
options,
columns,
..
} => {
}) => {
assert_eq!(
name,
ObjectName::from(vec![
@ -401,7 +401,7 @@ fn parse_create_view_with_options() {
fn parse_create_view_if_not_exists() {
let sql = "CREATE VIEW IF NOT EXISTS mydataset.newview AS SELECT foo FROM bar";
match bigquery().verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
name,
columns,
query,
@ -414,7 +414,7 @@ fn parse_create_view_if_not_exists() {
if_not_exists,
temporary,
..
} => {
}) => {
assert_eq!("mydataset.newview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
assert_eq!("SELECT foo FROM bar", query.to_string());
@ -435,12 +435,12 @@ fn parse_create_view_if_not_exists() {
fn parse_create_view_with_unquoted_hyphen() {
let sql = "CREATE VIEW IF NOT EXISTS my-pro-ject.mydataset.myview AS SELECT 1";
match bigquery().verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
name,
query,
if_not_exists,
..
} => {
}) => {
assert_eq!("my-pro-ject.mydataset.myview", name.to_string());
assert_eq!("SELECT 1", query.to_string());
assert!(if_not_exists);

View file

@ -243,12 +243,10 @@ fn parse_alter_table_attach_and_detach_partition() {
match clickhouse_and_generic()
.verified_stmt(format!("ALTER TABLE t0 {operation} PARTITION part").as_str())
{
Statement::AlterTable {
name, operations, ..
} => {
pretty_assertions::assert_eq!("t0", name.to_string());
Statement::AlterTable(alter_table) => {
pretty_assertions::assert_eq!("t0", alter_table.name.to_string());
pretty_assertions::assert_eq!(
operations[0],
alter_table.operations[0],
if operation == &"ATTACH" {
AlterTableOperation::AttachPartition {
partition: Partition::Expr(Identifier(Ident::new("part"))),
@ -266,9 +264,9 @@ fn parse_alter_table_attach_and_detach_partition() {
match clickhouse_and_generic()
.verified_stmt(format!("ALTER TABLE t1 {operation} PART part").as_str())
{
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, operations, ..
} => {
}) => {
pretty_assertions::assert_eq!("t1", name.to_string());
pretty_assertions::assert_eq!(
operations[0],
@ -308,9 +306,9 @@ fn parse_alter_table_add_projection() {
"ALTER TABLE t0 ADD PROJECTION IF NOT EXISTS my_name",
" (SELECT a, b GROUP BY a ORDER BY b)",
)) {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, operations, ..
} => {
}) => {
assert_eq!(name, ObjectName::from(vec!["t0".into()]));
assert_eq!(1, operations.len());
assert_eq!(
@ -380,9 +378,9 @@ fn parse_alter_table_add_projection() {
fn parse_alter_table_drop_projection() {
match clickhouse_and_generic().verified_stmt("ALTER TABLE t0 DROP PROJECTION IF EXISTS my_name")
{
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, operations, ..
} => {
}) => {
assert_eq!(name, ObjectName::from(vec!["t0".into()]));
assert_eq!(1, operations.len());
assert_eq!(
@ -413,9 +411,9 @@ fn parse_alter_table_clear_and_materialize_projection() {
format!("ALTER TABLE t0 {keyword} PROJECTION IF EXISTS my_name IN PARTITION p0",)
.as_str(),
) {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, operations, ..
} => {
}) => {
assert_eq!(name, ObjectName::from(vec!["t0".into()]));
assert_eq!(1, operations.len());
assert_eq!(
@ -904,7 +902,7 @@ fn parse_create_table_with_variant_default_expressions() {
#[test]
fn parse_create_view_with_fields_data_types() {
match clickhouse().verified_stmt(r#"CREATE VIEW v (i "int", f "String") AS SELECT * FROM t"#) {
Statement::CreateView { name, columns, .. } => {
Statement::CreateView(CreateView { name, columns, .. }) => {
assert_eq!(name, ObjectName::from(vec!["v".into()]));
assert_eq!(
columns,
@ -1518,7 +1516,7 @@ fn parse_freeze_and_unfreeze_partition() {
Value::SingleQuotedString("2024-08-14".to_string()).with_empty_span(),
));
match clickhouse_and_generic().verified_stmt(&sql) {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(operations.len(), 1);
let expected_operation = if operation_name == &"FREEZE" {
AlterTableOperation::FreezePartition {
@ -1542,7 +1540,7 @@ fn parse_freeze_and_unfreeze_partition() {
let sql =
format!("ALTER TABLE t {operation_name} PARTITION '2024-08-14' WITH NAME 'hello'");
match clickhouse_and_generic().verified_stmt(&sql) {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(operations.len(), 1);
let expected_partition = Partition::Expr(Expr::Value(
Value::SingleQuotedString("2024-08-14".to_string()).with_empty_span(),

View file

@ -377,12 +377,12 @@ fn parse_insert_sqlite() {
fn parse_update() {
let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d";
match verified_stmt(sql) {
Statement::Update {
Statement::Update(Update {
table,
assignments,
selection,
..
} => {
}) => {
assert_eq!(table.to_string(), "t".to_string());
assert_eq!(
assignments,
@ -439,7 +439,7 @@ fn parse_update_set_from() {
let stmt = dialects.verified_stmt(sql);
assert_eq!(
stmt,
Statement::Update {
Statement::Update(Update {
table: TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident::new("t1")])),
joins: vec![],
@ -516,7 +516,7 @@ fn parse_update_set_from() {
returning: None,
or: None,
limit: None
}
})
);
let sql = "UPDATE T SET a = b FROM U, (SELECT foo FROM V) AS W WHERE 1 = 1";
@ -527,7 +527,7 @@ fn parse_update_set_from() {
fn parse_update_with_table_alias() {
let sql = "UPDATE users AS u SET u.username = 'new_user' WHERE u.username = 'old_user'";
match verified_stmt(sql) {
Statement::Update {
Statement::Update(Update {
table,
assignments,
from: _from,
@ -535,7 +535,7 @@ fn parse_update_with_table_alias() {
returning,
or: None,
limit: None,
} => {
}) => {
assert_eq!(
TableWithJoins {
relation: TableFactor::Table {
@ -591,7 +591,7 @@ fn parse_update_with_table_alias() {
#[test]
fn parse_update_or() {
let expect_or_clause = |sql: &str, expected_action: SqliteOnConflict| match verified_stmt(sql) {
Statement::Update { or, .. } => assert_eq!(or, Some(expected_action)),
Statement::Update(Update { or, .. }) => assert_eq!(or, Some(expected_action)),
other => unreachable!("Expected update with or, got {:?}", other),
};
expect_or_clause(
@ -4846,9 +4846,9 @@ fn test_alter_table_with_on_cluster() {
match all_dialects()
.verified_stmt("ALTER TABLE t ON CLUSTER 'cluster' ADD CONSTRAINT bar PRIMARY KEY (baz)")
{
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, on_cluster, ..
} => {
}) => {
assert_eq!(name.to_string(), "t");
assert_eq!(on_cluster, Some(Ident::with_quote('\'', "cluster")));
}
@ -4858,9 +4858,9 @@ fn test_alter_table_with_on_cluster() {
match all_dialects()
.verified_stmt("ALTER TABLE t ON CLUSTER cluster_name ADD CONSTRAINT bar PRIMARY KEY (baz)")
{
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name, on_cluster, ..
} => {
}) => {
assert_eq!(name.to_string(), "t");
assert_eq!(on_cluster, Some(Ident::new("cluster_name")));
}
@ -7615,7 +7615,7 @@ fn parse_ctes() {
// CTE in a view
let sql = &format!("CREATE VIEW v AS {with}");
match verified_stmt(sql) {
Statement::CreateView { query, .. } => assert_ctes_in_select(&cte_sqls, &query),
Statement::CreateView(create_view) => assert_ctes_in_select(&cte_sqls, &create_view.query),
_ => panic!("Expected: CREATE VIEW"),
}
// CTE in a CTE...
@ -8103,7 +8103,7 @@ fn parse_drop_database_if_exists() {
fn parse_create_view() {
let sql = "CREATE VIEW myschema.myview AS SELECT foo FROM bar";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
columns,
@ -8120,7 +8120,7 @@ fn parse_create_view() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@ -8146,7 +8146,7 @@ fn parse_create_view() {
fn parse_create_view_with_options() {
let sql = "CREATE VIEW v WITH (foo = 'bar', a = 123) AS SELECT 1";
match verified_stmt(sql) {
Statement::CreateView { options, .. } => {
Statement::CreateView(create_view) => {
assert_eq!(
CreateTableOptions::With(vec![
SqlOption::KeyValue {
@ -8160,7 +8160,7 @@ fn parse_create_view_with_options() {
value: Expr::value(number("123")),
},
]),
options
create_view.options
);
}
_ => unreachable!(),
@ -8173,24 +8173,21 @@ fn parse_create_view_with_columns() {
// TODO: why does this fail for ClickHouseDialect? (#1449)
// match all_dialects().verified_stmt(sql) {
match all_dialects_except(|d| d.is::<ClickHouseDialect>()).verified_stmt(sql) {
Statement::CreateView {
or_alter,
name,
columns,
or_replace,
options,
query,
materialized,
cluster_by,
comment,
with_no_schema_binding: late_binding,
if_not_exists,
temporary,
to,
params,
name_before_not_exists: _,
secure: _,
} => {
Statement::CreateView(create_view) => {
let or_alter = create_view.or_alter;
let name = create_view.name;
let columns = create_view.columns;
let or_replace = create_view.or_replace;
let options = create_view.options;
let query = create_view.query;
let materialized = create_view.materialized;
let cluster_by = create_view.cluster_by;
let comment = create_view.comment;
let late_binding = create_view.with_no_schema_binding;
let if_not_exists = create_view.if_not_exists;
let temporary = create_view.temporary;
let to = create_view.to;
let params = create_view.params;
assert_eq!(or_alter, false);
assert_eq!("v", name.to_string());
assert_eq!(
@ -8224,7 +8221,7 @@ fn parse_create_view_with_columns() {
fn parse_create_view_temporary() {
let sql = "CREATE TEMPORARY VIEW myschema.myview AS SELECT foo FROM bar";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
columns,
@ -8241,7 +8238,7 @@ fn parse_create_view_temporary() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@ -8265,7 +8262,7 @@ fn parse_create_view_temporary() {
fn parse_create_or_replace_view() {
let sql = "CREATE OR REPLACE VIEW v AS SELECT 1";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
columns,
@ -8282,7 +8279,7 @@ fn parse_create_or_replace_view() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("v", name.to_string());
assert_eq!(columns, vec![]);
@ -8310,7 +8307,7 @@ fn parse_create_or_replace_materialized_view() {
// https://docs.snowflake.com/en/sql-reference/sql/create-materialized-view.html
let sql = "CREATE OR REPLACE MATERIALIZED VIEW v AS SELECT 1";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
columns,
@ -8327,7 +8324,7 @@ fn parse_create_or_replace_materialized_view() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("v", name.to_string());
assert_eq!(columns, vec![]);
@ -8351,7 +8348,7 @@ fn parse_create_or_replace_materialized_view() {
fn parse_create_materialized_view() {
let sql = "CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
or_replace,
@ -8368,7 +8365,7 @@ fn parse_create_materialized_view() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@ -8392,7 +8389,7 @@ fn parse_create_materialized_view() {
fn parse_create_materialized_view_with_cluster_by() {
let sql = "CREATE MATERIALIZED VIEW myschema.myview CLUSTER BY (foo) AS SELECT foo FROM bar";
match verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
or_alter,
name,
or_replace,
@ -8409,7 +8406,7 @@ fn parse_create_materialized_view_with_cluster_by() {
params,
name_before_not_exists: _,
secure: _,
} => {
}) => {
assert_eq!(or_alter, false);
assert_eq!("myschema.myview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@ -9417,21 +9414,17 @@ fn parse_drop_index() {
fn parse_create_role() {
let sql = "CREATE ROLE consultant";
match verified_stmt(sql) {
Statement::CreateRole { names, .. } => {
assert_eq_vec(&["consultant"], &names);
Statement::CreateRole(create_role) => {
assert_eq_vec(&["consultant"], &create_role.names);
}
_ => unreachable!(),
}
let sql = "CREATE ROLE IF NOT EXISTS mysql_a, mysql_b";
match verified_stmt(sql) {
Statement::CreateRole {
names,
if_not_exists,
..
} => {
assert_eq_vec(&["mysql_a", "mysql_b"], &names);
assert!(if_not_exists);
Statement::CreateRole(create_role) => {
assert_eq_vec(&["mysql_a", "mysql_b"], &create_role.names);
assert!(create_role.if_not_exists);
}
_ => unreachable!(),
}
@ -13351,8 +13344,8 @@ fn test_extract_seconds_single_quote_err() {
fn test_truncate_table_with_on_cluster() {
let sql = "TRUNCATE TABLE t ON CLUSTER cluster_name";
match all_dialects().verified_stmt(sql) {
Statement::Truncate { on_cluster, .. } => {
assert_eq!(on_cluster, Some(Ident::new("cluster_name")));
Statement::Truncate(truncate) => {
assert_eq!(truncate.on_cluster, Some(Ident::new("cluster_name")));
}
_ => panic!("Expected: TRUNCATE TABLE statement"),
}
@ -16407,14 +16400,14 @@ fn parse_truncate_only() {
];
assert_eq!(
Statement::Truncate {
Statement::Truncate(Truncate {
table_names,
partitions: None,
table: true,
identity: None,
cascade: None,
on_cluster: None,
},
}),
truncate
);
}
@ -17288,9 +17281,9 @@ fn parse_invisible_column() {
let sql = r#"ALTER TABLE t ADD COLUMN bar INT INVISIBLE"#;
let stmt = verified_stmt(sql);
match stmt {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(alter_table) => {
assert_eq!(
operations,
alter_table.operations,
vec![AlterTableOperation::AddColumn {
column_keyword: true,
if_not_exists: false,

View file

@ -778,14 +778,10 @@ fn parse_mssql_bin_literal() {
fn parse_mssql_create_role() {
let sql = "CREATE ROLE mssql AUTHORIZATION helena";
match ms().verified_stmt(sql) {
Statement::CreateRole {
names,
authorization_owner,
..
} => {
assert_eq_vec(&["mssql"], &names);
Statement::CreateRole(create_role) => {
assert_eq_vec(&["mssql"], &create_role.names);
assert_eq!(
authorization_owner,
create_role.authorization_owner,
Some(ObjectName::from(vec![Ident {
value: "helena".into(),
quote_style: None,

View file

@ -2603,7 +2603,7 @@ fn parse_insert_with_numeric_prefix_column_name() {
fn parse_update_with_joins() {
let sql = "UPDATE orders AS o JOIN customers AS c ON o.customer_id = c.id SET o.completed = true WHERE c.firstname = 'Peter'";
match mysql().verified_stmt(sql) {
Statement::Update {
Statement::Update(Update {
table,
assignments,
from: _from,
@ -2611,7 +2611,7 @@ fn parse_update_with_joins() {
returning,
or: None,
limit: None,
} => {
}) => {
assert_eq!(
TableWithJoins {
relation: TableFactor::Table {
@ -2729,7 +2729,7 @@ fn parse_delete_with_limit() {
#[test]
fn parse_alter_table_add_column() {
match mysql().verified_stmt("ALTER TABLE tab ADD COLUMN b INT FIRST") {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name,
if_exists,
only,
@ -2738,7 +2738,7 @@ fn parse_alter_table_add_column() {
location: _,
on_cluster: _,
end_token: _,
} => {
}) => {
assert_eq!(name.to_string(), "tab");
assert!(!if_exists);
assert!(!iceberg);
@ -2761,13 +2761,13 @@ fn parse_alter_table_add_column() {
}
match mysql().verified_stmt("ALTER TABLE tab ADD COLUMN b INT AFTER foo") {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name,
if_exists,
only,
operations,
..
} => {
}) => {
assert_eq!(name.to_string(), "tab");
assert!(!if_exists);
assert!(!only);
@ -2798,13 +2798,13 @@ fn parse_alter_table_add_columns() {
match mysql()
.verified_stmt("ALTER TABLE tab ADD COLUMN a TEXT FIRST, ADD COLUMN b INT AFTER foo")
{
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name,
if_exists,
only,
operations,
..
} => {
}) => {
assert_eq!(name.to_string(), "tab");
assert!(!if_exists);
assert!(!only);
@ -3026,7 +3026,7 @@ fn parse_alter_table_with_algorithm() {
"ALTER TABLE users DROP COLUMN password_digest, ALGORITHM = COPY, RENAME COLUMN name TO username";
let stmt = mysql_and_generic().verified_stmt(sql);
match stmt {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![
@ -3074,7 +3074,7 @@ fn parse_alter_table_with_lock() {
"ALTER TABLE users DROP COLUMN password_digest, LOCK = EXCLUSIVE, RENAME COLUMN name TO username";
let stmt = mysql_and_generic().verified_stmt(sql);
match stmt {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![
@ -3857,7 +3857,7 @@ fn parse_revoke() {
fn parse_create_view_algorithm_param() {
let sql = "CREATE ALGORITHM = MERGE VIEW foo AS SELECT 1";
let stmt = mysql().verified_stmt(sql);
if let Statement::CreateView {
if let Statement::CreateView(CreateView {
params:
Some(CreateViewParams {
algorithm,
@ -3865,7 +3865,7 @@ fn parse_create_view_algorithm_param() {
security,
}),
..
} = stmt
}) = stmt
{
assert_eq!(algorithm, Some(CreateViewAlgorithm::Merge));
assert!(definer.is_none());
@ -3881,7 +3881,7 @@ fn parse_create_view_algorithm_param() {
fn parse_create_view_definer_param() {
let sql = "CREATE DEFINER = 'jeffrey'@'localhost' VIEW foo AS SELECT 1";
let stmt = mysql().verified_stmt(sql);
if let Statement::CreateView {
if let Statement::CreateView(CreateView {
params:
Some(CreateViewParams {
algorithm,
@ -3889,7 +3889,7 @@ fn parse_create_view_definer_param() {
security,
}),
..
} = stmt
}) = stmt
{
assert!(algorithm.is_none());
if let Some(GranteeName::UserHost { user, host }) = definer {
@ -3910,7 +3910,7 @@ fn parse_create_view_definer_param() {
fn parse_create_view_security_param() {
let sql = "CREATE SQL SECURITY DEFINER VIEW foo AS SELECT 1";
let stmt = mysql().verified_stmt(sql);
if let Statement::CreateView {
if let Statement::CreateView(CreateView {
params:
Some(CreateViewParams {
algorithm,
@ -3918,7 +3918,7 @@ fn parse_create_view_security_param() {
security,
}),
..
} = stmt
}) = stmt
{
assert!(algorithm.is_none());
assert!(definer.is_none());
@ -3933,7 +3933,7 @@ fn parse_create_view_security_param() {
fn parse_create_view_multiple_params() {
let sql = "CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW foo AS SELECT 1";
let stmt = mysql().verified_stmt(sql);
if let Statement::CreateView {
if let Statement::CreateView(CreateView {
params:
Some(CreateViewParams {
algorithm,
@ -3941,7 +3941,7 @@ fn parse_create_view_multiple_params() {
security,
}),
..
} = stmt
}) = stmt
{
assert_eq!(algorithm, Some(CreateViewAlgorithm::Undefined));
if let Some(GranteeName::UserHost { user, host }) = definer {
@ -4179,7 +4179,7 @@ fn test_variable_assignment_using_colon_equal() {
let stmt = mysql().verified_stmt(sql_update);
match stmt {
Statement::Update { assignments, .. } => {
Statement::Update(Update { assignments, .. }) => {
assert_eq!(
assignments,
vec![Assignment {

View file

@ -605,7 +605,7 @@ fn parse_alter_table_constraints_unique_nulls_distinct() {
match pg_and_generic()
.verified_stmt("ALTER TABLE t ADD CONSTRAINT b UNIQUE NULLS NOT DISTINCT (c)")
{
Statement::AlterTable { operations, .. } => match &operations[0] {
Statement::AlterTable(alter_table) => match &alter_table.operations[0] {
AlterTableOperation::AddConstraint {
constraint: TableConstraint::Unique(constraint),
..
@ -674,93 +674,93 @@ fn parse_create_extension() {
fn parse_drop_extension() {
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION extension_name"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: false,
cascade_or_restrict: None,
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION extension_name CASCADE"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: false,
cascade_or_restrict: Some(ReferentialAction::Cascade),
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION extension_name RESTRICT"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: false,
cascade_or_restrict: Some(ReferentialAction::Restrict),
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION extension_name, extension_name2 CASCADE"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into(), "extension_name2".into()],
if_exists: false,
cascade_or_restrict: Some(ReferentialAction::Cascade),
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION extension_name, extension_name2 RESTRICT"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into(), "extension_name2".into()],
if_exists: false,
cascade_or_restrict: Some(ReferentialAction::Restrict),
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: true,
cascade_or_restrict: None,
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name CASCADE"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: true,
cascade_or_restrict: Some(ReferentialAction::Cascade),
}
})
);
assert_eq!(
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name RESTRICT"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name".into()],
if_exists: true,
cascade_or_restrict: Some(ReferentialAction::Restrict),
}
})
);
assert_eq!(
pg_and_generic()
.verified_stmt("DROP EXTENSION IF EXISTS extension_name1, extension_name2 CASCADE"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name1".into(), "extension_name2".into()],
if_exists: true,
cascade_or_restrict: Some(ReferentialAction::Cascade),
}
})
);
assert_eq!(
pg_and_generic()
.verified_stmt("DROP EXTENSION IF EXISTS extension_name1, extension_name2 RESTRICT"),
Statement::DropExtension {
Statement::DropExtension(DropExtension {
names: vec!["extension_name1".into(), "extension_name2".into()],
if_exists: true,
cascade_or_restrict: Some(ReferentialAction::Restrict),
}
})
);
}
@ -829,13 +829,13 @@ fn parse_alter_table_alter_column_add_generated() {
#[test]
fn parse_alter_table_add_columns() {
match pg().verified_stmt("ALTER TABLE IF EXISTS ONLY tab ADD COLUMN a TEXT, ADD COLUMN b INT") {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name,
if_exists,
only,
operations,
..
} => {
}) => {
assert_eq!(name.to_string(), "tab");
assert!(if_exists);
assert!(only);
@ -909,13 +909,13 @@ fn parse_alter_table_owner_to() {
for case in test_cases {
match pg_and_generic().verified_stmt(case.sql) {
Statement::AlterTable {
Statement::AlterTable(AlterTable {
name,
if_exists: _,
only: _,
operations,
..
} => {
}) => {
assert_eq!(name.to_string(), "tab");
assert_eq!(
operations,
@ -2009,7 +2009,7 @@ fn parse_pg_returning() {
RETURNING temp_lo AS lo, temp_hi AS hi, prcp",
);
match stmt {
Statement::Update { returning, .. } => {
Statement::Update(Update { returning, .. }) => {
assert_eq!(
Some(vec![
SelectItem::ExprWithAlias {
@ -3833,47 +3833,29 @@ fn parse_custom_operator() {
fn parse_create_role() {
let sql = "CREATE ROLE IF NOT EXISTS mysql_a, mysql_b";
match pg().verified_stmt(sql) {
Statement::CreateRole {
names,
if_not_exists,
..
} => {
assert_eq_vec(&["mysql_a", "mysql_b"], &names);
assert!(if_not_exists);
Statement::CreateRole(create_role) => {
assert_eq_vec(&["mysql_a", "mysql_b"], &create_role.names);
assert!(create_role.if_not_exists);
}
_ => unreachable!(),
}
let sql = "CREATE ROLE abc LOGIN PASSWORD NULL";
match pg().parse_sql_statements(sql).as_deref() {
Ok(
[Statement::CreateRole {
names,
login,
password,
..
}],
) => {
assert_eq_vec(&["abc"], names);
assert_eq!(*login, Some(true));
assert_eq!(*password, Some(Password::NullPassword));
Ok([Statement::CreateRole(create_role)]) => {
assert_eq_vec(&["abc"], &create_role.names);
assert_eq!(create_role.login, Some(true));
assert_eq!(create_role.password, Some(Password::NullPassword));
}
err => panic!("Failed to parse CREATE ROLE test case: {err:?}"),
}
let sql = "CREATE ROLE abc WITH LOGIN PASSWORD NULL";
match pg().parse_sql_statements(sql).as_deref() {
Ok(
[Statement::CreateRole {
names,
login,
password,
..
}],
) => {
assert_eq_vec(&["abc"], names);
assert_eq!(*login, Some(true));
assert_eq!(*password, Some(Password::NullPassword));
Ok([Statement::CreateRole(create_role)]) => {
assert_eq_vec(&["abc"], &create_role.names);
assert_eq!(create_role.login, Some(true));
assert_eq!(create_role.password, Some(Password::NullPassword));
}
err => panic!("Failed to parse CREATE ROLE test case: {err:?}"),
}
@ -3881,69 +3863,44 @@ fn parse_create_role() {
let sql = "CREATE ROLE magician WITH SUPERUSER CREATEROLE NOCREATEDB BYPASSRLS INHERIT PASSWORD 'abcdef' LOGIN VALID UNTIL '2025-01-01' IN ROLE role1, role2 ROLE role3 ADMIN role4, role5 REPLICATION";
// Roundtrip order of optional parameters is not preserved
match pg().parse_sql_statements(sql).as_deref() {
Ok(
[Statement::CreateRole {
names,
if_not_exists,
bypassrls,
login,
inherit,
password,
superuser,
create_db,
create_role,
replication,
connection_limit,
valid_until,
in_role,
in_group,
role,
user: _,
admin,
authorization_owner,
}],
) => {
assert_eq_vec(&["magician"], names);
assert!(!*if_not_exists);
assert_eq!(*login, Some(true));
assert_eq!(*inherit, Some(true));
assert_eq!(*bypassrls, Some(true));
Ok([Statement::CreateRole(create_role)]) => {
assert_eq_vec(&["magician"], &create_role.names);
assert!(!create_role.if_not_exists);
assert_eq!(create_role.login, Some(true));
assert_eq!(create_role.inherit, Some(true));
assert_eq!(create_role.bypassrls, Some(true));
assert_eq!(
*password,
create_role.password,
Some(Password::Password(Expr::Value(
(Value::SingleQuotedString("abcdef".into())).with_empty_span()
)))
);
assert_eq!(*superuser, Some(true));
assert_eq!(*create_db, Some(false));
assert_eq!(*create_role, Some(true));
assert_eq!(*replication, Some(true));
assert_eq!(*connection_limit, None);
assert_eq!(create_role.superuser, Some(true));
assert_eq!(create_role.create_db, Some(false));
assert_eq!(create_role.create_role, Some(true));
assert_eq!(create_role.replication, Some(true));
assert_eq!(create_role.connection_limit, None);
assert_eq!(
*valid_until,
create_role.valid_until,
Some(Expr::Value(
(Value::SingleQuotedString("2025-01-01".into())).with_empty_span()
))
);
assert_eq_vec(&["role1", "role2"], in_role);
assert!(in_group.is_empty());
assert_eq_vec(&["role3"], role);
assert_eq_vec(&["role4", "role5"], admin);
assert_eq!(*authorization_owner, None);
assert_eq_vec(&["role1", "role2"], &create_role.in_role);
assert!(create_role.in_group.is_empty());
assert_eq_vec(&["role3"], &create_role.role);
assert_eq_vec(&["role4", "role5"], &create_role.admin);
assert_eq!(create_role.authorization_owner, None);
}
err => panic!("Failed to parse CREATE ROLE test case: {err:?}"),
}
let sql = "CREATE ROLE abc WITH USER foo, bar ROLE baz ";
match pg().parse_sql_statements(sql).as_deref() {
Ok(
[Statement::CreateRole {
names, user, role, ..
}],
) => {
assert_eq_vec(&["abc"], names);
assert_eq_vec(&["foo", "bar"], user);
assert_eq_vec(&["baz"], role);
Ok([Statement::CreateRole(create_role)]) => {
assert_eq_vec(&["abc"], &create_role.names);
assert_eq_vec(&["foo", "bar"], &create_role.user);
assert_eq_vec(&["baz"], &create_role.role);
}
err => panic!("Failed to parse CREATE ROLE test case: {err:?}"),
}
@ -4532,7 +4489,7 @@ fn parse_drop_function() {
let sql = "DROP FUNCTION IF EXISTS test_func";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropFunction {
Statement::DropFunction(DropFunction {
if_exists: true,
func_desc: vec![FunctionDesc {
name: ObjectName::from(vec![Ident {
@ -4543,13 +4500,13 @@ fn parse_drop_function() {
args: None
}],
drop_behavior: None
}
})
);
let sql = "DROP FUNCTION IF EXISTS test_func(a INTEGER, IN b INTEGER = 1)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropFunction {
Statement::DropFunction(DropFunction {
if_exists: true,
func_desc: vec![FunctionDesc {
name: ObjectName::from(vec![Ident {
@ -4570,13 +4527,13 @@ fn parse_drop_function() {
]),
}],
drop_behavior: None
}
})
);
let sql = "DROP FUNCTION IF EXISTS test_func1(a INTEGER, IN b INTEGER = 1), test_func2(a VARCHAR, IN b INTEGER = 1)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropFunction {
Statement::DropFunction(DropFunction {
if_exists: true,
func_desc: vec![
FunctionDesc {
@ -4617,7 +4574,7 @@ fn parse_drop_function() {
}
],
drop_behavior: None
}
})
);
}
@ -4957,14 +4914,14 @@ fn parse_truncate() {
only: false,
}];
assert_eq!(
Statement::Truncate {
Statement::Truncate(Truncate {
table_names,
partitions: None,
table: false,
identity: None,
cascade: None,
on_cluster: None,
},
}),
truncate
);
}
@ -4981,14 +4938,14 @@ fn parse_truncate_with_options() {
}];
assert_eq!(
Statement::Truncate {
Statement::Truncate(Truncate {
table_names,
partitions: None,
table: true,
identity: Some(TruncateIdentityOption::Restart),
cascade: Some(CascadeOption::Cascade),
on_cluster: None,
},
}),
truncate
);
}
@ -5014,14 +4971,14 @@ fn parse_truncate_with_table_list() {
];
assert_eq!(
Statement::Truncate {
Statement::Truncate(Truncate {
table_names,
partitions: None,
table: true,
identity: Some(TruncateIdentityOption::Restart),
cascade: Some(CascadeOption::Cascade),
on_cluster: None,
},
}),
truncate
);
}
@ -6409,7 +6366,7 @@ fn parse_varbit_datatype() {
#[test]
fn parse_alter_table_replica_identity() {
match pg_and_generic().verified_stmt("ALTER TABLE foo REPLICA IDENTITY FULL") {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![AlterTableOperation::ReplicaIdentity {
@ -6421,7 +6378,7 @@ fn parse_alter_table_replica_identity() {
}
match pg_and_generic().verified_stmt("ALTER TABLE foo REPLICA IDENTITY USING INDEX foo_idx") {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![AlterTableOperation::ReplicaIdentity {
@ -6469,7 +6426,7 @@ fn parse_alter_table_constraint_not_valid() {
match pg_and_generic().verified_stmt(
"ALTER TABLE foo ADD CONSTRAINT bar FOREIGN KEY (baz) REFERENCES other(ref) NOT VALID",
) {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![AlterTableOperation::AddConstraint {
@ -6495,7 +6452,7 @@ fn parse_alter_table_constraint_not_valid() {
#[test]
fn parse_alter_table_validate_constraint() {
match pg_and_generic().verified_stmt("ALTER TABLE foo VALIDATE CONSTRAINT bar") {
Statement::AlterTable { operations, .. } => {
Statement::AlterTable(AlterTable { operations, .. }) => {
assert_eq!(
operations,
vec![AlterTableOperation::ValidateConstraint { name: "bar".into() }]

View file

@ -53,11 +53,11 @@ fn parse_sf_create_secure_view_and_materialized_view() {
"CREATE OR REPLACE SECURE MATERIALIZED VIEW v AS SELECT 1",
] {
match snowflake().verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
secure,
materialized,
..
} => {
}) => {
assert!(secure);
if sql.contains("MATERIALIZED") {
assert!(materialized);
@ -1047,7 +1047,7 @@ fn parse_sf_create_or_replace_with_comment_for_snowflake() {
test_utils::TestedDialects::new(vec![Box::new(SnowflakeDialect {}) as Box<dyn Dialect>]);
match dialect.verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
name,
columns,
or_replace,
@ -1060,7 +1060,7 @@ fn parse_sf_create_or_replace_with_comment_for_snowflake() {
if_not_exists,
temporary,
..
} => {
}) => {
assert_eq!("v", name.to_string());
assert_eq!(columns, vec![]);
assert_eq!(options, CreateTableOptions::None);
@ -3281,7 +3281,7 @@ fn parse_view_column_descriptions() {
let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment', b) AS SELECT a, b FROM table1";
match snowflake().verified_stmt(sql) {
Statement::CreateView { name, columns, .. } => {
Statement::CreateView(CreateView { name, columns, .. }) => {
assert_eq!(name.to_string(), "v");
assert_eq!(
columns,

View file

@ -166,7 +166,7 @@ fn parse_create_virtual_table() {
fn parse_create_view_temporary_if_not_exists() {
let sql = "CREATE TEMPORARY VIEW IF NOT EXISTS myschema.myview AS SELECT foo FROM bar";
match sqlite_and_generic().verified_stmt(sql) {
Statement::CreateView {
Statement::CreateView(CreateView {
name,
columns,
query,
@ -179,7 +179,7 @@ fn parse_create_view_temporary_if_not_exists() {
if_not_exists,
temporary,
..
} => {
}) => {
assert_eq!("myschema.myview", name.to_string());
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
assert_eq!("SELECT foo FROM bar", query.to_string());
@ -467,7 +467,7 @@ fn parse_update_tuple_row_values() {
// See https://github.com/sqlparser-rs/sqlparser-rs/issues/1311
assert_eq!(
sqlite().verified_stmt("UPDATE x SET (a, b) = (1, 2)"),
Statement::Update {
Statement::Update(Update {
or: None,
assignments: vec![Assignment {
target: AssignmentTarget::Tuple(vec![
@ -487,7 +487,7 @@ fn parse_update_tuple_row_values() {
from: None,
returning: None,
limit: None
}
})
);
}
@ -596,7 +596,7 @@ fn test_regexp_operator() {
#[test]
fn test_update_delete_limit() {
match sqlite().verified_stmt("UPDATE foo SET bar = 1 LIMIT 99") {
Statement::Update { limit, .. } => {
Statement::Update(Update { limit, .. }) => {
assert_eq!(limit, Some(Expr::value(number("99"))));
}
_ => unreachable!(),