Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3794,6 +3794,63 @@ impl fmt::Display for CreateConnector {
}
}

/// A native Snowflake `ALTER DATABASE` property operation.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterDatabaseOperation {
/// Set primitive database properties without an `OPTIONS (...)` wrapper.
SetOptions {
/// Ordered assignments, including duplicates; values are not semantically validated.
options: Vec<SqlOption>,
},
/// Reset database properties to their defaults.
UnsetOptions {
/// Ordered property names, including duplicates.
options: Vec<Ident>,
},
}

impl fmt::Display for AlterDatabaseOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::SetOptions { options } => write!(f, "SET {}", display_separated(options, " ")),
Self::UnsetOptions { options } => {
write!(f, "UNSET {}", display_comma_separated(options))
}
}
}
}

/// A Snowflake `ALTER DATABASE` (`Statement::AlterDatabase`) statement.
///
/// See [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-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 AlterDatabase {
/// The account-level database name, not a table relation.
pub name: ObjectName,
/// Whether `IF EXISTS` was specified.
pub if_exists: bool,
/// The property operations to perform on the database.
pub operations: Vec<AlterDatabaseOperation>,
}

impl fmt::Display for AlterDatabase {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ALTER DATABASE ")?;
if self.if_exists {
write!(f, "IF EXISTS ")?;
}
write!(f, "{}", self.name)?;
for operation in &self.operations {
write!(f, " {operation}")?;
}
Ok(())
}
}

/// An `ALTER SCHEMA` (`Statement::AlterSchema`) operation.
///
/// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#alter_schema_collate_statement)
Expand Down
55 changes: 34 additions & 21 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,28 @@ pub use self::dcl::{
};
pub use self::ddl::{
Alignment, AlterCollation, AlterCollationOperation, AlterColumnOperation, AlterConnectorOwner,
AlterFunction, AlterFunctionAction, AlterFunctionKind, AlterFunctionOperation,
AlterIndexOperation, AlterOperator, AlterOperatorClass, AlterOperatorClassOperation,
AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicy,
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateCollation, CreateCollationDefinition,
CreateConnector, CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial, DistStyle,
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropPolicy, DropTrigger, ForValues, FunctionReturnType, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, OperatorClassItem,
OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition,
PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
TagsColumnOption, TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
AlterDatabase, AlterDatabaseOperation, AlterFunction, AlterFunctionAction, AlterFunctionKind,
AlterFunctionOperation, AlterIndexOperation, AlterOperator, AlterOperatorClass,
AlterOperatorClassOperation, AlterOperatorFamily, AlterOperatorFamilyOperation,
AlterOperatorOperation, AlterPolicy, AlterPolicyOperation, AlterSchema, AlterSchemaOperation,
AlterTable, AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterTableType,
AlterType, AlterTypeAddValue, AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename,
AlterTypeRenameValue, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions,
ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, CreateCollation,
CreateCollationDefinition, CreateConnector, CreateDomain, CreateExtension, CreateFunction,
CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreatePolicy,
CreatePolicyCommand, CreatePolicyType, CreateTable, CreateTrigger, CreateView, Deduplicate,
DeferrableInitial, DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator,
DropOperatorClass, DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger,
ForValues, FunctionReturnType, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption,
OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem,
OperatorOption, OperatorPurpose, Owner, Partition, PartitionBoundValue, ProcedureParam,
ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind,
Truncate, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption,
UserDefinedTypeStorage, ViewColumnDef,
};
pub use self::dml::{
Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
Expand Down Expand Up @@ -3777,6 +3778,11 @@ pub enum Statement {
/// See [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#alter_schema_collate_statement)
AlterSchema(AlterSchema),
/// ```sql
/// ALTER DATABASE
/// ```
/// See [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-database)
AlterDatabase(AlterDatabase),
/// ```sql
/// ALTER INDEX
/// ```
AlterIndex {
Expand Down Expand Up @@ -6767,6 +6773,7 @@ impl fmt::Display for Statement {
Statement::DescribeWarehouse(s) => write!(f, "{s}"),
Statement::DropWarehouse(s) => write!(f, "{s}"),
Statement::AlterSchema(s) => write!(f, "{s}"),
Statement::AlterDatabase(s) => write!(f, "{s}"),
Statement::Vacuum(s) => write!(f, "{s}"),
Statement::AlterUser(s) => write!(f, "{s}"),
Statement::Reset(s) => write!(f, "{s}"),
Expand Down Expand Up @@ -12761,6 +12768,12 @@ impl From<AlterSchema> for Statement {
}
}

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

impl From<AlterFunction> for Statement {
fn from(a: AlterFunction) -> Self {
Self::AlterFunction(a)
Expand Down
24 changes: 21 additions & 3 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@

use crate::{
ast::{
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateView,
ExportData, Owner, TypedString,
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterDatabase,
AlterDatabaseOperation, AlterSchemaOperation, AlterTable, ColumnOptions, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreateView, ExportData, Owner, TypedString,
},
tokenizer::TokenWithSpan,
};
Expand Down Expand Up @@ -544,6 +544,7 @@ impl Spanned for Statement {
),
Statement::CreateUser(..) => Span::empty(),
Statement::AlterSchema(s) => s.span(),
Statement::AlterDatabase(s) => s.span(),
Statement::Vacuum(..) => Span::empty(),
Statement::AlterUser(..) => Span::empty(),
Statement::Reset(..) => Span::empty(),
Expand Down Expand Up @@ -2474,6 +2475,23 @@ impl Spanned for OpenStatement {
}
}

impl Spanned for AlterDatabaseOperation {
fn span(&self) -> Span {
match self {
Self::SetOptions { options } => union_spans(options.iter().map(|i| i.span())),
Self::UnsetOptions { options } => union_spans(options.iter().map(|i| i.span)),
}
}
}

impl Spanned for AlterDatabase {
fn span(&self) -> Span {
union_spans(
core::iter::once(self.name.span()).chain(self.operations.iter().map(|i| i.span())),
)
}
}

impl Spanned for AlterSchemaOperation {
fn span(&self) -> Span {
match self {
Expand Down
9 changes: 9 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,15 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if `ALTER DATABASE` supports native `SET key = <literal>`
/// assignments separated by whitespace or commas, and `UNSET key, ...`.
/// This does not enable structured properties or other database operations.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/alter-database)
fn supports_alter_database_options(&self) -> bool {
false
}

/// Returns true if the dialect supports the `USING` clause in an `ALTER COLUMN` statement.
/// Example:
/// ```sql
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,10 @@ impl Dialect for SnowflakeDialect {
true
}

fn supports_alter_database_options(&self) -> bool {
true
}

fn supports_comma_separated_drop_column_list(&self) -> bool {
true
}
Expand Down
83 changes: 67 additions & 16 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10770,6 +10770,10 @@ impl<'a> Parser<'a> {

/// Parse an `ALTER <object>` statement and dispatch to the appropriate alter handler.
pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
if self.peek_keyword(Keyword::DATABASE) && self.dialect.supports_alter_database_options() {
self.prev_token();
return self.parse_alter_database();
}
let object_type = self.expect_one_of_keywords(&[
Keyword::VIEW,
Keyword::TYPE,
Expand Down Expand Up @@ -11543,6 +11547,34 @@ impl<'a> Parser<'a> {
})
}

/// Parse a native `ALTER DATABASE` primitive property statement.
pub fn parse_alter_database(&mut self) -> Result<Statement, ParserError> {
if !self.dialect.supports_alter_database_options() {
return self.expected_ref(
"dialect supporting ALTER DATABASE properties",
self.peek_token_ref(),
);
}
self.expect_keywords(&[Keyword::ALTER, Keyword::DATABASE])?;
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let name = self.parse_object_name(false)?;
let operation = if self.parse_keyword(Keyword::SET) {
let options = self.parse_alter_namespace_set_options("database")?;
AlterDatabaseOperation::SetOptions { options }
} else if self.parse_keyword(Keyword::UNSET) {
let options = self.parse_alter_namespace_unset_options("database")?;
AlterDatabaseOperation::UnsetOptions { options }
} else {
return self.expected_ref("ALTER DATABASE property operation", self.peek_token_ref());
};
Ok(AlterDatabase {
name,
if_exists,
operations: vec![operation],
}
.into())
}

/// Parse an `ALTER SCHEMA` statement.
///
/// Supports operations such as setting options, renaming, adding/dropping replicas, and changing owner.
Expand Down Expand Up @@ -11575,18 +11607,11 @@ impl<'a> Parser<'a> {
let owner = self.parse_owner()?;
AlterSchemaOperation::OwnerTo { owner }
} else if self.dialect.supports_alter_schema_options() && self.parse_keyword(Keyword::SET) {
let options = self.parse_alter_schema_set_options()?;
let options = self.parse_alter_namespace_set_options("schema")?;
AlterSchemaOperation::SetOptions { options }
} else if self.dialect.supports_alter_schema_options() && self.parse_keyword(Keyword::UNSET)
{
let options = self.parse_comma_separated_with_trailing_commas(
Self::parse_alter_schema_option_name,
false,
Self::is_reserved_for_column_alias,
)?;
if !matches!(self.peek_token_ref().token, Token::EOF | Token::SemiColon) {
return self.expected_ref("comma or statement end", self.peek_token_ref());
}
let options = self.parse_alter_namespace_unset_options("schema")?;
AlterSchemaOperation::UnsetOptions { options }
} else {
return self.expected_ref("ALTER SCHEMA operation", self.peek_token_ref());
Expand All @@ -11598,21 +11623,45 @@ impl<'a> Parser<'a> {
}))
}

fn parse_alter_schema_option_name(&mut self) -> Result<Ident, ParserError> {
fn parse_alter_namespace_option_name(
&mut self,
object_type: &str,
) -> Result<Ident, ParserError> {
if !matches!(self.peek_token_ref().token, Token::Word(_))
|| self
.peek_one_of_keywords(&[Keyword::TAG, Keyword::CONTACT])
.is_some()
{
return self.expected_ref("primitive schema property name", self.peek_token_ref());
return self.expected_ref(
&format!("primitive {object_type} property name"),
self.peek_token_ref(),
);
}
self.parse_identifier()
}

fn parse_alter_schema_set_options(&mut self) -> Result<Vec<SqlOption>, ParserError> {
fn parse_alter_namespace_unset_options(
&mut self,
object_type: &str,
) -> Result<Vec<Ident>, ParserError> {
let options = self.parse_comma_separated_with_trailing_commas(
|parser| parser.parse_alter_namespace_option_name(object_type),
false,
Self::is_reserved_for_column_alias,
)?;
if !matches!(self.peek_token_ref().token, Token::EOF | Token::SemiColon) {
return self.expected_ref("comma or statement end", self.peek_token_ref());
}
Ok(options)
}

fn parse_alter_namespace_set_options(
&mut self,
object_type: &str,
) -> Result<Vec<SqlOption>, ParserError> {
let mut options = Vec::new();
loop {
let key = self.parse_alter_schema_option_name()?;
let key = self.parse_alter_namespace_option_name(object_type)?;
self.expect_token(&Token::Eq)?;
let value = match self.peek_token_ref().token {
Token::Plus | Token::Minus
Expand All @@ -11626,8 +11675,10 @@ impl<'a> Parser<'a> {
| Token::Placeholder(_)
| Token::Colon
| Token::AtSign => {
return self
.expected_ref("primitive schema property literal", self.peek_token_ref());
return self.expected_ref(
&format!("primitive {object_type} property literal"),
self.peek_token_ref(),
);
}
_ => Expr::Value(self.parse_value()?),
};
Expand All @@ -11645,7 +11696,7 @@ impl<'a> Parser<'a> {
Token::Word(_) if space_separated => {}
_ => {
return self.expected_ref(
"schema property separator or statement end",
&format!("{object_type} property separator or statement end"),
self.peek_token_ref(),
)
}
Expand Down
Loading
Loading