Skip to content
Open
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
74 changes: 74 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,43 @@ impl fmt::Display for AlterPolicyOperation {
}
}

/// An `ALTER TRIGGER` (`Statement::AlterTrigger`) operation
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-altertrigger.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterTriggerOperation {
/// `RENAME TO new_name`
Rename {
/// The new identifier for the trigger.
new_name: Ident,
},
/// `[ NO ] DEPENDS ON EXTENSION extension_name`
DependsOnExtension {
/// `true` when `NO DEPENDS ON EXTENSION`.
no: bool,
/// Extension name.
extension_name: ObjectName,
},
}

impl fmt::Display for AlterTriggerOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterTriggerOperation::Rename { new_name } => {
write!(f, "RENAME TO {new_name}")
}
AlterTriggerOperation::DependsOnExtension { no, extension_name } => {
if *no {
write!(f, "NO ")?;
}
write!(f, "DEPENDS ON EXTENSION {extension_name}")
}
}
}
}

/// [MySQL] `ALTER TABLE` algorithm.
///
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
Expand Down Expand Up @@ -5994,3 +6031,40 @@ impl From<AlterPolicy> for crate::ast::Statement {
crate::ast::Statement::AlterPolicy(v)
}
}

/// ALTER TRIGGER statement.
///
/// ```sql
/// ALTER TRIGGER <NAME> ON <TABLE NAME> <OPERATION>
/// ```
/// (Postgresql-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 AlterTrigger {
/// Trigger name to alter.
pub name: Ident,
/// Target table name the trigger is defined on.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
/// Operation specific to the trigger alteration.
pub operation: AlterTriggerOperation,
}

impl fmt::Display for AlterTrigger {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ALTER TRIGGER {name} ON {table_name} {operation}",
name = self.name,
table_name = self.table_name,
operation = self.operation
)
}
}

impl From<AlterTrigger> for crate::ast::Statement {
fn from(v: AlterTrigger) -> Self {
crate::ast::Statement::AlterTrigger(v)
}
}
16 changes: 11 additions & 5 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ pub use self::ddl::{
AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation, AlterPolicy,
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
AlterTableLock, AlterTableOperation, AlterTableType, AlterTextSearch, AlterTextSearchOperation,
AlterTextSearchOption, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateCollation, CreateCollationDefinition, CreateConnector,
CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator,
AlterTextSearchOption, AlterTrigger, AlterTriggerOperation, 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, CreateTextSearch, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
Expand Down Expand Up @@ -3881,6 +3881,11 @@ pub enum Statement {
/// (Postgresql-specific)
AlterPolicy(AlterPolicy),
/// ```sql
/// ALTER TRIGGER <NAME> ON <TABLE NAME> <OPERATION>
/// ```
/// (Postgresql-specific)
AlterTrigger(AlterTrigger),
/// ```sql
/// ALTER CONNECTOR connector_name SET DCPROPERTIES(property_name=property_value, ...);
/// or
/// ALTER CONNECTOR connector_name SET URL new_url;
Expand Down Expand Up @@ -5673,6 +5678,7 @@ impl fmt::Display for Statement {
write!(f, "ALTER ROLE {name} {operation}")
}
Statement::AlterPolicy(alter_policy) => write!(f, "{alter_policy}"),
Statement::AlterTrigger(alter_trigger) => write!(f, "{alter_trigger}"),
Statement::AlterConnector {
name,
properties,
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ impl Spanned for Statement {
Statement::OptimizeTable { .. } => Span::empty(),
Statement::CreatePolicy { .. } => Span::empty(),
Statement::AlterPolicy { .. } => Span::empty(),
Statement::AlterTrigger { .. } => Span::empty(),
Statement::AlterConnector { .. } => Span::empty(),
Statement::DropPolicy { .. } => Span::empty(),
Statement::DropConnector { .. } => Span::empty(),
Expand Down
53 changes: 49 additions & 4 deletions src/parser/alter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ use super::{Parser, ParserError};
use crate::{
ast::{
helpers::key_value_options::{KeyValueOptions, KeyValueOptionsDelimiter},
AlterConnectorOwner, AlterPolicy, AlterPolicyOperation, AlterRoleOperation, AlterUser,
AlterUserAddMfaMethodOtp, AlterUserAddRoleDelegation, AlterUserModifyMfaMethod,
AlterUserPassword, AlterUserRemoveRoleDelegation, AlterUserSetPolicy, Expr, MfaMethodKind,
Password, ResetConfig, RoleOption, SetConfigValue, Statement, UserPolicyKind,
AlterConnectorOwner, AlterPolicy, AlterPolicyOperation, AlterRoleOperation, AlterTrigger,
AlterTriggerOperation, AlterUser, AlterUserAddMfaMethodOtp, AlterUserAddRoleDelegation,
AlterUserModifyMfaMethod, AlterUserPassword, AlterUserRemoveRoleDelegation,
AlterUserSetPolicy, Expr, MfaMethodKind, Password, ResetConfig, RoleOption, SetConfigValue,
Statement, UserPolicyKind,
},
dialect::{MsSqlDialect, PostgreSqlDialect},
keywords::Keyword,
Expand Down Expand Up @@ -103,6 +104,50 @@ impl Parser<'_> {
}
}

/// Parse ALTER TRIGGER statement
/// ```sql
/// ALTER TRIGGER trigger_name ON table_name RENAME TO new_name
/// or
/// ALTER TRIGGER trigger_name ON table_name [ NO ] DEPENDS ON EXTENSION extension_name
/// ```
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertrigger.html)
pub fn parse_alter_trigger(&mut self) -> Result<AlterTrigger, ParserError> {
let name = self.parse_identifier()?;
self.expect_keyword_is(Keyword::ON)?;
let table_name = self.parse_object_name(false)?;

let operation = if self.parse_keyword(Keyword::RENAME) {
self.expect_keyword_is(Keyword::TO)?;
AlterTriggerOperation::Rename {
new_name: self.parse_identifier()?,
}
} else {
let no = self.parse_keyword(Keyword::NO);
if !self.parse_keyword(Keyword::DEPENDS) {
return self.expected_ref(
if no {
"DEPENDS after NO"
} else {
"RENAME, DEPENDS or NO DEPENDS after ALTER TRIGGER"
},
self.peek_token_ref(),
);
}
self.expect_keywords(&[Keyword::ON, Keyword::EXTENSION])?;
AlterTriggerOperation::DependsOnExtension {
no,
extension_name: self.parse_object_name(false)?,
}
};

Ok(AlterTrigger {
name,
table_name,
operation,
})
}

/// Parse an `ALTER CONNECTOR` statement
/// ```sql
/// ALTER CONNECTOR connector_name SET DCPROPERTIES(property_name=property_value, ...);
Expand Down
4 changes: 3 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11154,6 +11154,7 @@ impl<'a> Parser<'a> {
Keyword::AGGREGATE,
Keyword::ROLE,
Keyword::POLICY,
Keyword::TRIGGER,
Keyword::CONNECTOR,
Keyword::ICEBERG,
Keyword::SCHEMA,
Expand Down Expand Up @@ -11205,14 +11206,15 @@ impl<'a> Parser<'a> {
}
Keyword::ROLE => self.parse_alter_role(),
Keyword::POLICY => self.parse_alter_policy().map(Into::into),
Keyword::TRIGGER => self.parse_alter_trigger().map(Into::into),
Keyword::CONNECTOR => self.parse_alter_connector(),
Keyword::USER if self.dialect.supports_alter_user_as_alter_role() => {
self.parse_alter_role()
}
Keyword::USER => self.parse_alter_user().map(Into::into),
// unreachable because expect_one_of_keywords used above
unexpected_keyword => Err(ParserError::ParserError(
format!("Internal parser error: expected any of {{TEXT SEARCH, VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"),
format!("Internal parser error: expected any of {{TEXT SEARCH, VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, TRIGGER, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR}}, got {unexpected_keyword:?}"),
)),
}
}
Expand Down
87 changes: 87 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14761,6 +14761,93 @@ fn test_alter_policy() {
);
}

#[test]
fn test_alter_trigger() {
match verified_stmt("ALTER TRIGGER old_trigger ON my_table RENAME TO new_trigger") {
Statement::AlterTrigger(AlterTrigger {
name,
table_name,
operation,
}) => {
assert_eq!(name.to_string(), "old_trigger");
assert_eq!(table_name.to_string(), "my_table");
assert_eq!(
operation,
AlterTriggerOperation::Rename {
new_name: Ident::new("new_trigger")
}
);
}
_ => unreachable!(),
}

match verified_stmt("ALTER TRIGGER my_trigger ON my_table DEPENDS ON EXTENSION my_extension") {
Statement::AlterTrigger(AlterTrigger { operation, .. }) => {
assert_eq!(
operation,
AlterTriggerOperation::DependsOnExtension {
no: false,
extension_name: ObjectName::from(Ident::new("my_extension"))
}
);
}
_ => unreachable!(),
}

match verified_stmt("ALTER TRIGGER my_trigger ON my_table NO DEPENDS ON EXTENSION my_extension")
{
Statement::AlterTrigger(AlterTrigger { operation, .. }) => {
assert_eq!(
operation,
AlterTriggerOperation::DependsOnExtension {
no: true,
extension_name: ObjectName::from(Ident::new("my_extension"))
}
);
}
_ => unreachable!(),
}

// a qualified table name is preserved
verified_stmt("ALTER TRIGGER my_trigger ON my_schema.my_table RENAME TO new_trigger");

// an operation is required
assert_eq!(
parse_sql_statements("ALTER TRIGGER my_trigger ON my_table")
.unwrap_err()
.to_string(),
"sql parser error: Expected: RENAME, DEPENDS or NO DEPENDS after ALTER TRIGGER, found: EOF"
);
// missing TO in RENAME TO
assert_eq!(
parse_sql_statements("ALTER TRIGGER my_trigger ON my_table RENAME")
.unwrap_err()
.to_string(),
"sql parser error: Expected: TO, found: EOF"
);
// missing new name in RENAME TO
assert_eq!(
parse_sql_statements("ALTER TRIGGER my_trigger ON my_table RENAME TO")
.unwrap_err()
.to_string(),
"sql parser error: Expected: identifier, found: EOF"
);
// NO must be followed by DEPENDS
assert_eq!(
parse_sql_statements("ALTER TRIGGER my_trigger ON my_table NO EXTENSION my_extension")
.unwrap_err()
.to_string(),
"sql parser error: Expected: DEPENDS after NO, found: EXTENSION"
);
// missing the extension name
assert_eq!(
parse_sql_statements("ALTER TRIGGER my_trigger ON my_table DEPENDS ON EXTENSION")
.unwrap_err()
.to_string(),
"sql parser error: Expected: identifier, found: EOF"
);
}

#[test]
fn test_create_connector() {
let sql = "CREATE CONNECTOR my_connector \
Expand Down
Loading