Parse MySQL ALTER TABLE ALGORITHM option (#1745)

This commit is contained in:
Michael Victor Zink 2025-02-25 22:03:38 -08:00 committed by GitHub
parent de4dbc5b1d
commit 3adc746b11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 119 additions and 13 deletions

View file

@ -65,7 +65,6 @@ pub enum AlterTableOperation {
name: Ident,
select: ProjectionSelect,
},
/// `DROP PROJECTION [IF EXISTS] name`
///
/// Note: this is a ClickHouse-specific operation.
@ -74,7 +73,6 @@ pub enum AlterTableOperation {
if_exists: bool,
name: Ident,
},
/// `MATERIALIZE PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
///
/// Note: this is a ClickHouse-specific operation.
@ -84,7 +82,6 @@ pub enum AlterTableOperation {
name: Ident,
partition: Option<Ident>,
},
/// `CLEAR PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
///
/// Note: this is a ClickHouse-specific operation.
@ -94,7 +91,6 @@ pub enum AlterTableOperation {
name: Ident,
partition: Option<Ident>,
},
/// `DISABLE ROW LEVEL SECURITY`
///
/// Note: this is a PostgreSQL-specific operation.
@ -272,6 +268,15 @@ pub enum AlterTableOperation {
DropClusteringKey,
SuspendRecluster,
ResumeRecluster,
/// `ALGORITHM [=] { DEFAULT | INSTANT | INPLACE | COPY }`
///
/// [MySQL]-specific table alter algorithm.
///
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
Algorithm {
equals: bool,
algorithm: AlterTableAlgorithm,
},
}
/// An `ALTER Policy` (`Statement::AlterPolicy`) operation
@ -317,6 +322,30 @@ impl fmt::Display for AlterPolicyOperation {
}
}
/// [MySQL] `ALTER TABLE` algorithm.
///
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.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 AlterTableAlgorithm {
Default,
Instant,
Inplace,
Copy,
}
impl fmt::Display for AlterTableAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Self::Default => "DEFAULT",
Self::Instant => "INSTANT",
Self::Inplace => "INPLACE",
Self::Copy => "COPY",
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@ -407,6 +436,14 @@ impl fmt::Display for AlterTableOperation {
}
write!(f, " {} ({})", name, query)
}
AlterTableOperation::Algorithm { equals, algorithm } => {
write!(
f,
"ALGORITHM {}{}",
if *equals { "= " } else { "" },
algorithm
)
}
AlterTableOperation::DropProjection { if_exists, name } => {
write!(f, "DROP PROJECTION")?;
if *if_exists {

View file

@ -48,15 +48,15 @@ pub use self::dcl::{
};
pub use self::ddl::{
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics,
CreateConnector, CreateFunction, Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay,
NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction, TableConstraint,
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
ViewColumnDef,
AlterTableAlgorithm, AlterTableOperation, AlterType, AlterTypeAddValue,
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateConnector, CreateFunction, Deduplicate, DeferrableInitial,
DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexOption,
IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, ProcedureParam,
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeRepresentation, ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
pub use self::operator::{BinaryOperator, UnaryOperator};

View file

@ -1062,6 +1062,7 @@ impl Spanned for AlterTableOperation {
AlterTableOperation::DropClusteringKey => Span::empty(),
AlterTableOperation::SuspendRecluster => Span::empty(),
AlterTableOperation::ResumeRecluster => Span::empty(),
AlterTableOperation::Algorithm { .. } => Span::empty(),
}
}
}

View file

@ -427,11 +427,13 @@ define_keywords!(
INNER,
INOUT,
INPATH,
INPLACE,
INPUT,
INPUTFORMAT,
INSENSITIVE,
INSERT,
INSTALL,
INSTANT,
INSTEAD,
INT,
INT128,

View file

@ -8163,6 +8163,24 @@ impl<'a> Parser<'a> {
AlterTableOperation::SuspendRecluster
} else if self.parse_keywords(&[Keyword::RESUME, Keyword::RECLUSTER]) {
AlterTableOperation::ResumeRecluster
} else if self.parse_keyword(Keyword::ALGORITHM) {
let equals = self.consume_token(&Token::Eq);
let algorithm = match self.parse_one_of_keywords(&[
Keyword::DEFAULT,
Keyword::INSTANT,
Keyword::INPLACE,
Keyword::COPY,
]) {
Some(Keyword::DEFAULT) => AlterTableAlgorithm::Default,
Some(Keyword::INSTANT) => AlterTableAlgorithm::Instant,
Some(Keyword::INPLACE) => AlterTableAlgorithm::Inplace,
Some(Keyword::COPY) => AlterTableAlgorithm::Copy,
_ => self.expected(
"DEFAULT, INSTANT, INPLACE, or COPY after ALGORITHM [=]",
self.peek_token(),
)?,
};
AlterTableOperation::Algorithm { equals, algorithm }
} else {
let options: Vec<SqlOption> =
self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?;

View file

@ -2422,6 +2422,54 @@ fn parse_alter_table_modify_column() {
assert_eq!(expected_operation, operation);
}
#[test]
fn parse_alter_table_with_algorithm() {
let sql = "ALTER TABLE tab ALGORITHM = COPY";
let expected_operation = AlterTableOperation::Algorithm {
equals: true,
algorithm: AlterTableAlgorithm::Copy,
};
let operation = alter_table_op(mysql_and_generic().verified_stmt(sql));
assert_eq!(expected_operation, operation);
// Check order doesn't matter
let sql =
"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, .. } => {
assert_eq!(
operations,
vec![
AlterTableOperation::DropColumn {
column_name: Ident::new("password_digest"),
if_exists: false,
drop_behavior: None,
},
AlterTableOperation::Algorithm {
equals: true,
algorithm: AlterTableAlgorithm::Copy,
},
AlterTableOperation::RenameColumn {
old_column_name: Ident::new("name"),
new_column_name: Ident::new("username")
},
]
)
}
_ => panic!("Unexpected statement {stmt}"),
}
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM DEFAULT");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM INSTANT");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM INPLACE");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM COPY");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM = DEFAULT");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM = INSTANT");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM = INPLACE");
mysql_and_generic().verified_stmt("ALTER TABLE `users` ALGORITHM = COPY");
}
#[test]
fn parse_alter_table_modify_column_with_column_position() {
let expected_name = ObjectName::from(vec![Ident::new("orders")]);