Skip to content

Commit fb1f194

Browse files
committed
Add support for MySQL ALTER TABLE options
Implemented parsing for MySQL-specific ALTER TABLE options: - CHECKSUM [=] {0 | 1} - DELAY_KEY_WRITE [=] {0 | 1} - FORCE - ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT} - STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1} Added new keywords in alphabetical order: - COMPACT, COMPRESSED, FIXED, REDUNDANT Added AST types: - AlterTableRowFormat enum - AlterTableStatsAutoRecalc enum - AlterTableOperation variants for each option Includes tests for individual options and combined usage.
1 parent 982f766 commit fb1f194

File tree

6 files changed

+299
-1
lines changed

6 files changed

+299
-1
lines changed

src/ast/ddl.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,48 @@ pub enum AlterTableOperation {
411411
equals: bool,
412412
value: ValueWithSpan,
413413
},
414+
/// `CHECKSUM [=] {0 | 1}`
415+
///
416+
/// [MySQL]-specific table option for checksum.
417+
///
418+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
419+
Checksum {
420+
equals: bool,
421+
value: u8,
422+
},
423+
/// `DELAY_KEY_WRITE [=] {0 | 1}`
424+
///
425+
/// [MySQL]-specific table option for delay key write.
426+
///
427+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
428+
DelayKeyWrite {
429+
equals: bool,
430+
value: u8,
431+
},
432+
/// `FORCE`
433+
///
434+
/// [MySQL]-specific table option to force table rebuild.
435+
///
436+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
437+
Force,
438+
/// `ROW_FORMAT [=] <format>`
439+
///
440+
/// [MySQL]-specific table option for row format.
441+
///
442+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
443+
RowFormat {
444+
equals: bool,
445+
format: AlterTableRowFormat,
446+
},
447+
/// `STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}`
448+
///
449+
/// [MySQL]-specific table option for stats auto recalc.
450+
///
451+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
452+
StatsAutoRecalc {
453+
equals: bool,
454+
value: AlterTableStatsAutoRecalc,
455+
},
414456
/// `VALIDATE CONSTRAINT <name>`
415457
ValidateConstraint {
416458
name: Ident,
@@ -518,6 +560,56 @@ impl fmt::Display for AlterTableLock {
518560
}
519561
}
520562

563+
/// [MySQL] `ALTER TABLE` row format.
564+
///
565+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
566+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
567+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
568+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
569+
pub enum AlterTableRowFormat {
570+
Default,
571+
Dynamic,
572+
Fixed,
573+
Compressed,
574+
Redundant,
575+
Compact,
576+
}
577+
578+
impl fmt::Display for AlterTableRowFormat {
579+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
580+
f.write_str(match self {
581+
Self::Default => "DEFAULT",
582+
Self::Dynamic => "DYNAMIC",
583+
Self::Fixed => "FIXED",
584+
Self::Compressed => "COMPRESSED",
585+
Self::Redundant => "REDUNDANT",
586+
Self::Compact => "COMPACT",
587+
})
588+
}
589+
}
590+
591+
/// [MySQL] `ALTER TABLE` stats auto recalc.
592+
///
593+
/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html
594+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
595+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
596+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
597+
pub enum AlterTableStatsAutoRecalc {
598+
Default,
599+
Zero,
600+
One,
601+
}
602+
603+
impl fmt::Display for AlterTableStatsAutoRecalc {
604+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605+
f.write_str(match self {
606+
Self::Default => "DEFAULT",
607+
Self::Zero => "0",
608+
Self::One => "1",
609+
})
610+
}
611+
}
612+
521613
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
522614
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
523615
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -880,6 +972,21 @@ impl fmt::Display for AlterTableOperation {
880972
value
881973
)
882974
}
975+
AlterTableOperation::Checksum { equals, value } => {
976+
write!(f, "CHECKSUM {}{}", if *equals { "= " } else { "" }, value)
977+
}
978+
AlterTableOperation::DelayKeyWrite { equals, value } => {
979+
write!(f, "DELAY_KEY_WRITE {}{}", if *equals { "= " } else { "" }, value)
980+
}
981+
AlterTableOperation::Force => {
982+
write!(f, "FORCE")
983+
}
984+
AlterTableOperation::RowFormat { equals, format } => {
985+
write!(f, "ROW_FORMAT {}{}", if *equals { "= " } else { "" }, format)
986+
}
987+
AlterTableOperation::StatsAutoRecalc { equals, value } => {
988+
write!(f, "STATS_AUTO_RECALC {}{}", if *equals { "= " } else { "" }, value)
989+
}
883990
AlterTableOperation::Lock { equals, lock } => {
884991
write!(f, "LOCK {}{}", if *equals { "= " } else { "" }, lock)
885992
}

src/ast/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ pub use self::dcl::{
6161
pub use self::ddl::{
6262
Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation,
6363
AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, AlterTableAlgorithm,
64-
AlterTableLock, AlterTableOperation, AlterTableType, AlterType, AlterTypeAddValue,
64+
AlterTableLock, AlterTableOperation, AlterTableRowFormat, AlterTableStatsAutoRecalc,
65+
AlterTableType, AlterType, AlterTypeAddValue,
6566
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
6667
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
6768
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,

src/ast/spans.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,11 @@ impl Spanned for AlterTableOperation {
11491149
AlterTableOperation::Resume => Span::empty(),
11501150
AlterTableOperation::Algorithm { .. } => Span::empty(),
11511151
AlterTableOperation::AutoIncrement { value, .. } => value.span(),
1152+
AlterTableOperation::Checksum { .. } => Span::empty(),
1153+
AlterTableOperation::DelayKeyWrite { .. } => Span::empty(),
1154+
AlterTableOperation::Force => Span::empty(),
1155+
AlterTableOperation::RowFormat { .. } => Span::empty(),
1156+
AlterTableOperation::StatsAutoRecalc { .. } => Span::empty(),
11521157
AlterTableOperation::Lock { .. } => Span::empty(),
11531158
AlterTableOperation::ReplicaIdentity { .. } => Span::empty(),
11541159
AlterTableOperation::ValidateConstraint { name } => name.span,

src/keywords.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,9 @@ define_keywords!(
219219
COMMIT,
220220
COMMITTED,
221221
COMMUTATOR,
222+
COMPACT,
222223
COMPATIBLE,
224+
COMPRESSED,
223225
COMPRESSION,
224226
COMPUPDATE,
225227
COMPUTE,
@@ -398,6 +400,7 @@ define_keywords!(
398400
FINAL,
399401
FIRST,
400402
FIRST_VALUE,
403+
FIXED,
401404
FIXEDSTRING,
402405
FIXEDWIDTH,
403406
FLATTEN,
@@ -800,6 +803,7 @@ define_keywords!(
800803
RECEIVE,
801804
RECLUSTER,
802805
RECURSIVE,
806+
REDUNDANT,
803807
REF,
804808
REFERENCES,
805809
REFERENCING,

src/parser/mod.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9681,6 +9681,103 @@ impl<'a> Parser<'a> {
96819681
let equals = self.consume_token(&Token::Eq);
96829682
let value = self.parse_number_value()?;
96839683
AlterTableOperation::AutoIncrement { equals, value }
9684+
} else if self.parse_keyword(Keyword::CHECKSUM) {
9685+
let equals = self.consume_token(&Token::Eq);
9686+
let num = self.parse_number_value()?;
9687+
let value_u8 = match &num.value {
9688+
#[cfg(not(feature = "bigdecimal"))]
9689+
Value::Number(s, _) => match s.as_str() {
9690+
"0" => 0,
9691+
"1" => 1,
9692+
_ => return self.expected("0 or 1 after CHECKSUM [=]", self.peek_token()),
9693+
},
9694+
#[cfg(feature = "bigdecimal")]
9695+
Value::Number(n, _) => {
9696+
if n == &0.into() {
9697+
0
9698+
} else if n == &1.into() {
9699+
1
9700+
} else {
9701+
return self.expected("0 or 1 after CHECKSUM [=]", self.peek_token())
9702+
}
9703+
}
9704+
_ => return self.expected("0 or 1 after CHECKSUM [=]", self.peek_token()),
9705+
};
9706+
AlterTableOperation::Checksum { equals, value: value_u8 }
9707+
} else if self.parse_keyword(Keyword::DELAY_KEY_WRITE) {
9708+
let equals = self.consume_token(&Token::Eq);
9709+
let num = self.parse_number_value()?;
9710+
let value_u8 = match &num.value {
9711+
#[cfg(not(feature = "bigdecimal"))]
9712+
Value::Number(s, _) => match s.as_str() {
9713+
"0" => 0,
9714+
"1" => 1,
9715+
_ => return self.expected("0 or 1 after DELAY_KEY_WRITE [=]", self.peek_token()),
9716+
},
9717+
#[cfg(feature = "bigdecimal")]
9718+
Value::Number(n, _) => {
9719+
if n == &0.into() {
9720+
0
9721+
} else if n == &1.into() {
9722+
1
9723+
} else {
9724+
return self.expected("0 or 1 after DELAY_KEY_WRITE [=]", self.peek_token())
9725+
}
9726+
}
9727+
_ => return self.expected("0 or 1 after DELAY_KEY_WRITE [=]", self.peek_token()),
9728+
};
9729+
AlterTableOperation::DelayKeyWrite { equals, value: value_u8 }
9730+
} else if self.parse_keyword(Keyword::FORCE) {
9731+
AlterTableOperation::Force
9732+
} else if self.parse_keyword(Keyword::ROW_FORMAT) {
9733+
let equals = self.consume_token(&Token::Eq);
9734+
let format = match self.parse_one_of_keywords(&[
9735+
Keyword::DEFAULT,
9736+
Keyword::DYNAMIC,
9737+
Keyword::FIXED,
9738+
Keyword::COMPRESSED,
9739+
Keyword::REDUNDANT,
9740+
Keyword::COMPACT,
9741+
]) {
9742+
Some(Keyword::DEFAULT) => AlterTableRowFormat::Default,
9743+
Some(Keyword::DYNAMIC) => AlterTableRowFormat::Dynamic,
9744+
Some(Keyword::FIXED) => AlterTableRowFormat::Fixed,
9745+
Some(Keyword::COMPRESSED) => AlterTableRowFormat::Compressed,
9746+
Some(Keyword::REDUNDANT) => AlterTableRowFormat::Redundant,
9747+
Some(Keyword::COMPACT) => AlterTableRowFormat::Compact,
9748+
_ => self.expected(
9749+
"DEFAULT, DYNAMIC, FIXED, COMPRESSED, REDUNDANT, or COMPACT after ROW_FORMAT [=]",
9750+
self.peek_token(),
9751+
)?,
9752+
};
9753+
AlterTableOperation::RowFormat { equals, format }
9754+
} else if self.parse_keyword(Keyword::STATS_AUTO_RECALC) {
9755+
let equals = self.consume_token(&Token::Eq);
9756+
let value = if self.parse_keyword(Keyword::DEFAULT) {
9757+
AlterTableStatsAutoRecalc::Default
9758+
} else {
9759+
let num = self.parse_number_value()?;
9760+
match &num.value {
9761+
#[cfg(not(feature = "bigdecimal"))]
9762+
Value::Number(s, _) => match s.as_str() {
9763+
"0" => AlterTableStatsAutoRecalc::Zero,
9764+
"1" => AlterTableStatsAutoRecalc::One,
9765+
_ => return self.expected("DEFAULT, 0, or 1 after STATS_AUTO_RECALC [=]", self.peek_token()),
9766+
},
9767+
#[cfg(feature = "bigdecimal")]
9768+
Value::Number(n, _) => {
9769+
if n == &0.into() {
9770+
AlterTableStatsAutoRecalc::Zero
9771+
} else if n == &1.into() {
9772+
AlterTableStatsAutoRecalc::One
9773+
} else {
9774+
return self.expected("DEFAULT, 0, or 1 after STATS_AUTO_RECALC [=]", self.peek_token())
9775+
}
9776+
}
9777+
_ => return self.expected("DEFAULT, 0, or 1 after STATS_AUTO_RECALC [=]", self.peek_token()),
9778+
}
9779+
};
9780+
AlterTableOperation::StatsAutoRecalc { equals, value }
96849781
} else if self.parse_keywords(&[Keyword::REPLICA, Keyword::IDENTITY]) {
96859782
let identity = if self.parse_keyword(Keyword::NONE) {
96869783
ReplicaIdentity::None

tests/sqlparser_mysql.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,90 @@ fn parse_alter_table_auto_increment() {
31363136
mysql_and_generic().verified_stmt("ALTER TABLE `users` AUTO_INCREMENT 42");
31373137
}
31383138

3139+
#[test]
3140+
fn parse_alter_table_checksum() {
3141+
let sql = "ALTER TABLE tab CHECKSUM = 1";
3142+
let expected_operation = AlterTableOperation::Checksum {
3143+
equals: true,
3144+
value: 1,
3145+
};
3146+
let operation = alter_table_op(mysql().verified_stmt(sql));
3147+
assert_eq!(expected_operation, operation);
3148+
3149+
mysql_and_generic().verified_stmt("ALTER TABLE `users` CHECKSUM 0");
3150+
mysql_and_generic().verified_stmt("ALTER TABLE `users` CHECKSUM = 1");
3151+
}
3152+
3153+
#[test]
3154+
fn parse_alter_table_delay_key_write() {
3155+
let sql = "ALTER TABLE tab DELAY_KEY_WRITE = 0";
3156+
let expected_operation = AlterTableOperation::DelayKeyWrite {
3157+
equals: true,
3158+
value: 0,
3159+
};
3160+
let operation = alter_table_op(mysql().verified_stmt(sql));
3161+
assert_eq!(expected_operation, operation);
3162+
3163+
mysql_and_generic().verified_stmt("ALTER TABLE `users` DELAY_KEY_WRITE 1");
3164+
mysql_and_generic().verified_stmt("ALTER TABLE `users` DELAY_KEY_WRITE = 0");
3165+
}
3166+
3167+
#[test]
3168+
fn parse_alter_table_force() {
3169+
let sql = "ALTER TABLE tab FORCE";
3170+
let expected_operation = AlterTableOperation::Force;
3171+
let operation = alter_table_op(mysql().verified_stmt(sql));
3172+
assert_eq!(expected_operation, operation);
3173+
}
3174+
3175+
#[test]
3176+
fn parse_alter_table_row_format() {
3177+
let sql = "ALTER TABLE tab ROW_FORMAT = COMPACT";
3178+
let expected_operation = AlterTableOperation::RowFormat {
3179+
equals: true,
3180+
format: AlterTableRowFormat::Compact,
3181+
};
3182+
let operation = alter_table_op(mysql().verified_stmt(sql));
3183+
assert_eq!(expected_operation, operation);
3184+
3185+
mysql_and_generic().verified_stmt("ALTER TABLE `users` ROW_FORMAT DEFAULT");
3186+
mysql_and_generic().verified_stmt("ALTER TABLE `users` ROW_FORMAT = DYNAMIC");
3187+
mysql_and_generic().verified_stmt("ALTER TABLE `users` ROW_FORMAT = FIXED");
3188+
mysql_and_generic().verified_stmt("ALTER TABLE `users` ROW_FORMAT = COMPRESSED");
3189+
mysql_and_generic().verified_stmt("ALTER TABLE `users` ROW_FORMAT = REDUNDANT");
3190+
}
3191+
3192+
#[test]
3193+
fn parse_alter_table_stats_auto_recalc() {
3194+
let sql = "ALTER TABLE tab STATS_AUTO_RECALC = 1";
3195+
let expected_operation = AlterTableOperation::StatsAutoRecalc {
3196+
equals: true,
3197+
value: AlterTableStatsAutoRecalc::One,
3198+
};
3199+
let operation = alter_table_op(mysql().verified_stmt(sql));
3200+
assert_eq!(expected_operation, operation);
3201+
3202+
mysql_and_generic().verified_stmt("ALTER TABLE `users` STATS_AUTO_RECALC 0");
3203+
mysql_and_generic().verified_stmt("ALTER TABLE `users` STATS_AUTO_RECALC = DEFAULT");
3204+
}
3205+
3206+
#[test]
3207+
fn parse_alter_table_multiple_table_options() {
3208+
let sql = "ALTER TABLE t0 CHECKSUM 1, ALGORITHM COPY, DELAY_KEY_WRITE 0, FORCE, STATS_AUTO_RECALC 1, ROW_FORMAT COMPACT";
3209+
match mysql().verified_stmt(sql) {
3210+
Statement::AlterTable(AlterTable { operations, .. }) => {
3211+
assert_eq!(operations.len(), 6);
3212+
assert_eq!(operations[0], AlterTableOperation::Checksum { equals: false, value: 1 });
3213+
assert_eq!(operations[1], AlterTableOperation::Algorithm { equals: false, algorithm: AlterTableAlgorithm::Copy });
3214+
assert_eq!(operations[2], AlterTableOperation::DelayKeyWrite { equals: false, value: 0 });
3215+
assert_eq!(operations[3], AlterTableOperation::Force);
3216+
assert_eq!(operations[4], AlterTableOperation::StatsAutoRecalc { equals: false, value: AlterTableStatsAutoRecalc::One });
3217+
assert_eq!(operations[5], AlterTableOperation::RowFormat { equals: false, format: AlterTableRowFormat::Compact });
3218+
}
3219+
_ => unreachable!(),
3220+
}
3221+
}
3222+
31393223
#[test]
31403224
fn parse_alter_table_modify_column_with_column_position() {
31413225
let expected_name = ObjectName::from(vec![Ident::new("orders")]);

0 commit comments

Comments
 (0)