Skip to content

Commit

Permalink
Support MySQL FLUSH statement (#1076)
Browse files Browse the repository at this point in the history
  • Loading branch information
emin100 authored Dec 31, 2023
1 parent c62ecb1 commit 593c090
Show file tree
Hide file tree
Showing 4 changed files with 370 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,20 @@ pub enum Statement {
into: Option<ObjectName>,
},
/// ```sql
/// FLUSH [NO_WRITE_TO_BINLOG | LOCAL] flush_option [, flush_option] ... | tables_option
/// ```
///
/// Note: this is a Mysql-specific statement,
/// but may also compatible with other SQL.
Flush {
object_type: FlushType,
location: Option<FlushLocation>,
channel: Option<String>,
read_lock: bool,
export: bool,
tables: Vec<ObjectName>,
},
/// ```sql
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
/// ```
///
Expand Down Expand Up @@ -2199,6 +2213,36 @@ impl fmt::Display for Statement {
#[allow(clippy::cognitive_complexity)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Statement::Flush {
object_type,
location,
channel,
read_lock,
export,
tables,
} => {
write!(f, "FLUSH")?;
if let Some(location) = location {
write!(f, " {location}")?;
}
write!(f, " {object_type}")?;

if let Some(channel) = channel {
write!(f, " FOR CHANNEL {channel}")?;
}

write!(
f,
"{tables}{read}{export}",
tables = if !tables.is_empty() {
" ".to_string() + &display_comma_separated(tables).to_string()
} else {
"".to_string()
},
export = if *export { " FOR EXPORT" } else { "" },
read = if *read_lock { " WITH READ LOCK" } else { "" }
)
}
Statement::Kill { modifier, id } => {
write!(f, "KILL ")?;

Expand Down Expand Up @@ -4818,6 +4862,62 @@ impl fmt::Display for DiscardObject {
}
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushType {
BinaryLogs,
EngineLogs,
ErrorLogs,
GeneralLogs,
Hosts,
Logs,
Privileges,
OptimizerCosts,
RelayLogs,
SlowLogs,
Status,
UserResources,
Tables,
}

impl fmt::Display for FlushType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FlushType::BinaryLogs => f.write_str("BINARY LOGS"),
FlushType::EngineLogs => f.write_str("ENGINE LOGS"),
FlushType::ErrorLogs => f.write_str("ERROR LOGS"),
FlushType::GeneralLogs => f.write_str("GENERAL LOGS"),
FlushType::Hosts => f.write_str("HOSTS"),
FlushType::Logs => f.write_str("LOGS"),
FlushType::Privileges => f.write_str("PRIVILEGES"),
FlushType::OptimizerCosts => f.write_str("OPTIMIZER_COSTS"),
FlushType::RelayLogs => f.write_str("RELAY LOGS"),
FlushType::SlowLogs => f.write_str("SLOW LOGS"),
FlushType::Status => f.write_str("STATUS"),
FlushType::UserResources => f.write_str("USER_RESOURCES"),
FlushType::Tables => f.write_str("TABLES"),
}
}
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FlushLocation {
NoWriteToBinlog,
Local,
}

impl fmt::Display for FlushLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FlushLocation::NoWriteToBinlog => f.write_str("NO_WRITE_TO_BINLOG"),
FlushLocation::Local => f.write_str("LOCAL"),
}
}
}

/// Optional context modifier for statements that can be or `LOCAL`, or `SESSION`.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down
12 changes: 12 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ define_keywords!(
CENTURY,
CHAIN,
CHANGE,
CHANNEL,
CHAR,
CHARACTER,
CHARACTERS,
Expand Down Expand Up @@ -265,6 +266,7 @@ define_keywords!(
EXPANSION,
EXPLAIN,
EXPLICIT,
EXPORT,
EXTENDED,
EXTERNAL,
EXTRACT,
Expand All @@ -283,6 +285,7 @@ define_keywords!(
FLOAT64,
FLOAT8,
FLOOR,
FLUSH,
FOLLOWING,
FOR,
FORCE,
Expand All @@ -302,6 +305,7 @@ define_keywords!(
FUNCTION,
FUNCTIONS,
FUSION,
GENERAL,
GENERATE,
GENERATED,
GEOGRAPHY,
Expand All @@ -320,6 +324,7 @@ define_keywords!(
HISTORY,
HIVEVAR,
HOLD,
HOSTS,
HOUR,
HOURS,
IDENTITY,
Expand Down Expand Up @@ -385,6 +390,7 @@ define_keywords!(
LOCK,
LOCKED,
LOGIN,
LOGS,
LOWER,
LOW_PRIORITY,
MACRO,
Expand Down Expand Up @@ -439,6 +445,7 @@ define_keywords!(
NOT,
NOTHING,
NOWAIT,
NO_WRITE_TO_BINLOG,
NTH_VALUE,
NTILE,
NULL,
Expand All @@ -458,6 +465,7 @@ define_keywords!(
OPEN,
OPERATOR,
OPTIMIZE,
OPTIMIZER_COSTS,
OPTION,
OPTIONS,
OR,
Expand Down Expand Up @@ -531,6 +539,7 @@ define_keywords!(
REGR_SXY,
REGR_SYY,
RELATIVE,
RELAY,
RELEASE,
RENAME,
REORG,
Expand Down Expand Up @@ -581,6 +590,7 @@ define_keywords!(
SHOW,
SIMILAR,
SKIP,
SLOW,
SMALLINT,
SNAPSHOT,
SOME,
Expand All @@ -598,6 +608,7 @@ define_keywords!(
START,
STATIC,
STATISTICS,
STATUS,
STDDEV_POP,
STDDEV_SAMP,
STDIN,
Expand Down Expand Up @@ -673,6 +684,7 @@ define_keywords!(
USAGE,
USE,
USER,
USER_RESOURCES,
USING,
UUID,
VACUUM,
Expand Down
88 changes: 88 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ impl<'a> Parser<'a> {
match &next_token.token {
Token::Word(w) => match w.keyword {
Keyword::KILL => Ok(self.parse_kill()?),
Keyword::FLUSH => Ok(self.parse_flush()?),
Keyword::DESCRIBE => Ok(self.parse_explain(true)?),
Keyword::EXPLAIN => Ok(self.parse_explain(false)?),
Keyword::ANALYZE => Ok(self.parse_analyze()?),
Expand Down Expand Up @@ -534,6 +535,93 @@ impl<'a> Parser<'a> {
}
}

pub fn parse_flush(&mut self) -> Result<Statement, ParserError> {
let mut channel = None;
let mut tables: Vec<ObjectName> = vec![];
let mut read_lock = false;
let mut export = false;

if !dialect_of!(self is MySqlDialect | GenericDialect) {
return parser_err!("Unsupported statement FLUSH", self.peek_token().location);
}

let location = if self.parse_keyword(Keyword::NO_WRITE_TO_BINLOG) {
Some(FlushLocation::NoWriteToBinlog)
} else if self.parse_keyword(Keyword::LOCAL) {
Some(FlushLocation::Local)
} else {
None
};

let object_type = if self.parse_keywords(&[Keyword::BINARY, Keyword::LOGS]) {
FlushType::BinaryLogs
} else if self.parse_keywords(&[Keyword::ENGINE, Keyword::LOGS]) {
FlushType::EngineLogs
} else if self.parse_keywords(&[Keyword::ERROR, Keyword::LOGS]) {
FlushType::ErrorLogs
} else if self.parse_keywords(&[Keyword::GENERAL, Keyword::LOGS]) {
FlushType::GeneralLogs
} else if self.parse_keywords(&[Keyword::HOSTS]) {
FlushType::Hosts
} else if self.parse_keyword(Keyword::PRIVILEGES) {
FlushType::Privileges
} else if self.parse_keyword(Keyword::OPTIMIZER_COSTS) {
FlushType::OptimizerCosts
} else if self.parse_keywords(&[Keyword::RELAY, Keyword::LOGS]) {
if self.parse_keywords(&[Keyword::FOR, Keyword::CHANNEL]) {
channel = Some(self.parse_object_name().unwrap().to_string());
}
FlushType::RelayLogs
} else if self.parse_keywords(&[Keyword::SLOW, Keyword::LOGS]) {
FlushType::SlowLogs
} else if self.parse_keyword(Keyword::STATUS) {
FlushType::Status
} else if self.parse_keyword(Keyword::USER_RESOURCES) {
FlushType::UserResources
} else if self.parse_keywords(&[Keyword::LOGS]) {
FlushType::Logs
} else if self.parse_keywords(&[Keyword::TABLES]) {
loop {
let next_token = self.next_token();
match &next_token.token {
Token::Word(w) => match w.keyword {
Keyword::WITH => {
read_lock = self.parse_keywords(&[Keyword::READ, Keyword::LOCK]);
}
Keyword::FOR => {
export = self.parse_keyword(Keyword::EXPORT);
}
Keyword::NoKeyword => {
self.prev_token();
tables = self.parse_comma_separated(Parser::parse_object_name)?;
}
_ => {}
},
_ => {
break;
}
}
}

FlushType::Tables
} else {
return self.expected(
"BINARY LOGS, ENGINE LOGS, ERROR LOGS, GENERAL LOGS, HOSTS, LOGS, PRIVILEGES, OPTIMIZER_COSTS,\
RELAY LOGS [FOR CHANNEL channel], SLOW LOGS, STATUS, USER_RESOURCES",
self.peek_token(),
);
};

Ok(Statement::Flush {
object_type,
location,
channel,
read_lock,
export,
tables,
})
}

pub fn parse_msck(&mut self) -> Result<Statement, ParserError> {
let repair = self.parse_keyword(Keyword::REPAIR);
self.expect_keyword(Keyword::TABLE)?;
Expand Down
Loading

0 comments on commit 593c090

Please sign in to comment.