From b4d00fb49957030c0577a84cffbf8a7d0b1366ac Mon Sep 17 00:00:00 2001 From: fys <40801205+fengys1996@users.noreply.github.com> Date: Thu, 29 May 2025 20:00:36 +0800 Subject: [PATCH] feat: support SQL parsing for trigger show (#6217) * feat: support SQL parsing for trigger show * add excludes in licenserc * refine comment * fix: typo * fix: add show/trigger.rs to excludes in licenserc --- licenserc.toml | 2 + src/frontend/src/instance.rs | 5 ++ src/operator/src/statement.rs | 3 + src/operator/src/statement/show.rs | 10 ++++ src/sql/src/parsers/show_parser.rs | 9 ++- src/sql/src/parsers/show_parser/trigger.rs | 70 ++++++++++++++++++++++ src/sql/src/statements/show.rs | 3 + src/sql/src/statements/show/trigger.rs | 59 ++++++++++++++++++ src/sql/src/statements/statement.rs | 5 ++ 9 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/sql/src/parsers/show_parser/trigger.rs create mode 100644 src/sql/src/statements/show/trigger.rs diff --git a/licenserc.toml b/licenserc.toml index 6783bc98fe..4fc00026c3 100644 --- a/licenserc.toml +++ b/licenserc.toml @@ -28,7 +28,9 @@ excludes = [ "src/servers/src/http/test_helpers.rs", # enterprise "src/sql/src/statements/create/trigger.rs", + "src/sql/src/statements/show/trigger.rs", "src/sql/src/parsers/create_parser/trigger.rs", + "src/sql/src/parsers/show_parser/trigger.rs", ] [properties] diff --git a/src/frontend/src/instance.rs b/src/frontend/src/instance.rs index 3887379cad..4d85169099 100644 --- a/src/frontend/src/instance.rs +++ b/src/frontend/src/instance.rs @@ -550,6 +550,11 @@ pub fn check_permission( Statement::ShowFlows(stmt) => { validate_db_permission!(stmt, query_ctx); } + #[cfg(feature = "enterprise")] + Statement::ShowTriggers(_stmt) => { + // The trigger is organized based on the catalog dimension, so there + // is no need to check the permission of the database(schema). + } Statement::ShowStatus(_stmt) => {} Statement::ShowSearchPath(_stmt) => {} Statement::DescribeTable(stmt) => { diff --git a/src/operator/src/statement.rs b/src/operator/src/statement.rs index d4df416e32..ad104a5ae9 100644 --- a/src/operator/src/statement.rs +++ b/src/operator/src/statement.rs @@ -170,6 +170,9 @@ impl StatementExecutor { Statement::ShowFlows(stmt) => self.show_flows(stmt, query_ctx).await, + #[cfg(feature = "enterprise")] + Statement::ShowTriggers(stmt) => self.show_triggers(stmt, query_ctx).await, + Statement::Copy(sql::statements::copy::Copy::CopyQueryTo(stmt)) => { let query_output = self .plan_exec(QueryStatement::Sql(*stmt.query), query_ctx) diff --git a/src/operator/src/statement/show.rs b/src/operator/src/statement/show.rs index cc4ad3ff86..bf6069d49d 100644 --- a/src/operator/src/statement/show.rs +++ b/src/operator/src/statement/show.rs @@ -226,6 +226,16 @@ impl StatementExecutor { .context(ExecuteStatementSnafu) } + #[cfg(feature = "enterprise")] + #[tracing::instrument(skip_all)] + pub(super) async fn show_triggers( + &self, + _stmt: sql::statements::show::trigger::ShowTriggers, + _query_ctx: QueryContextRef, + ) -> Result { + crate::error::UnsupportedTriggerSnafu.fail() + } + #[tracing::instrument(skip_all)] pub async fn show_create_flow( &self, diff --git a/src/sql/src/parsers/show_parser.rs b/src/sql/src/parsers/show_parser.rs index 511c539f33..f7a32385f4 100644 --- a/src/sql/src/parsers/show_parser.rs +++ b/src/sql/src/parsers/show_parser.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(feature = "enterprise")] +pub mod trigger; + use snafu::{ensure, ResultExt}; use sqlparser::keywords::Keyword; use sqlparser::tokenizer::Token; @@ -32,6 +35,10 @@ impl ParserContext<'_> { /// Parses SHOW statements /// todo(hl) support `show settings`/`show create`/`show users` etc. pub(crate) fn parse_show(&mut self) -> Result { + #[cfg(feature = "enterprise")] + if self.consume_token("TRIGGERS") { + return self.parse_show_triggers(); + } if self.consume_token("DATABASES") || self.consume_token("SCHEMAS") { self.parse_show_databases(false) } else if self.matches_keyword(Keyword::TABLES) { @@ -530,7 +537,7 @@ impl ParserContext<'_> { })); } - // SHOW FLOWS [in | FROM] [DATABASE] + // SHOW VIEWS [in | FROM] [DATABASE] Token::Word(w) => match w.keyword { Keyword::IN | Keyword::FROM => self.parse_db_name()?, _ => None, diff --git a/src/sql/src/parsers/show_parser/trigger.rs b/src/sql/src/parsers/show_parser/trigger.rs new file mode 100644 index 0000000000..e6fa13e40c --- /dev/null +++ b/src/sql/src/parsers/show_parser/trigger.rs @@ -0,0 +1,70 @@ +use crate::error::Result; +use crate::parser::ParserContext; +use crate::statements::show::trigger::ShowTriggers; +use crate::statements::statement::Statement; + +impl ParserContext<'_> { + pub(super) fn parse_show_triggers(&mut self) -> Result { + let kind = self.parse_show_kind()?; + let show_triggers = ShowTriggers { kind }; + Ok(Statement::ShowTriggers(show_triggers)) + } +} + +#[cfg(test)] +mod tests { + use crate::dialect::GreptimeDbDialect; + use crate::parser::ParserContext; + use crate::statements::show::ShowKind; + use crate::statements::statement::Statement; + + #[test] + fn test_parse_show_triggers() { + // Valid, sql: `SHOW TRIGGERS`. + let sql = ""; + let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap(); + let statement = ctx.parse_show_triggers().unwrap(); + let Statement::ShowTriggers(show_triggers) = statement else { + panic!("Expected ShowTriggers statement"); + }; + assert_eq!(show_triggers.kind, ShowKind::All); + + // Valid, sql: `SHOW TRIGGERS;`. + let sql = ";"; + let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap(); + let statement = ctx.parse_show_triggers().unwrap(); + let Statement::ShowTriggers(show_triggers) = statement else { + panic!("Expected ShowTriggers statement"); + }; + assert_eq!(show_triggers.kind, ShowKind::All); + + // Valid, sql: `SHOW TRIGGERS LIKE 'test_trigger'`. + let sql = "LIKE 'test_trigger'"; + let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap(); + let statement = ctx.parse_show_triggers().unwrap(); + let Statement::ShowTriggers(show_triggers) = statement else { + panic!("Expected ShowTriggers statement"); + }; + let ShowKind::Like(like) = show_triggers.kind else { + panic!("Expected ShowKind::Like"); + }; + assert_eq!(like.value, "test_trigger"); + + // Valid, sql: `SHOW TRIGGERS WHERE name = 'test_trigger'`. + let sql = "WHERE name = 'test_trigger'"; + let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap(); + let statement = ctx.parse_show_triggers().unwrap(); + let Statement::ShowTriggers(show_triggers) = statement else { + panic!("Expected ShowTriggers statement"); + }; + let ShowKind::Where(expr) = show_triggers.kind else { + panic!("Expected ShowKind::Where"); + }; + assert_eq!(expr.to_string(), "name = 'test_trigger'"); + + // Invalid, since incorrect keyword `LI`. + let sql = "LI 'test_trigger'"; + let mut ctx = ParserContext::new(&GreptimeDbDialect {}, sql).unwrap(); + assert!(ctx.parse_show_triggers().is_err()); + } +} diff --git a/src/sql/src/statements/show.rs b/src/sql/src/statements/show.rs index 73ef8fe28e..2fbf9db728 100644 --- a/src/sql/src/statements/show.rs +++ b/src/sql/src/statements/show.rs @@ -46,6 +46,9 @@ macro_rules! format_kind { }; } +#[cfg(feature = "enterprise")] +pub mod trigger; + /// SQL structure for `SHOW DATABASES`. #[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)] pub struct ShowDatabases { diff --git a/src/sql/src/statements/show/trigger.rs b/src/sql/src/statements/show/trigger.rs new file mode 100644 index 0000000000..bc6de33a3b --- /dev/null +++ b/src/sql/src/statements/show/trigger.rs @@ -0,0 +1,59 @@ +use std::fmt::{self, Display}; + +use serde::Serialize; +use sqlparser_derive::{Visit, VisitMut}; + +use crate::statements::show::ShowKind; + +/// SQL structure for `SHOW TRIGGERS`. +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)] +pub struct ShowTriggers { + pub kind: ShowKind, +} + +impl Display for ShowTriggers { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SHOW TRIGGERS")?; + format_kind!(self, f); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::dialect::GreptimeDbDialect; + use crate::parser::{ParseOptions, ParserContext}; + use crate::statements::show::trigger::ShowTriggers; + use crate::statements::show::ShowKind; + use crate::statements::statement::Statement; + + #[test] + fn test_show_triggers_display() { + let show_triggers = ShowTriggers { + kind: ShowKind::All, + }; + assert_eq!(show_triggers.to_string(), "SHOW TRIGGERS"); + + let sql = "SHOW TRIGGERS LIKE 'test_trigger'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()) + .unwrap(); + assert_eq!(1, result.len()); + let Statement::ShowTriggers(show_triggers) = &result[0] else { + panic!("Expected ShowTriggers statement"); + }; + let expected = "SHOW TRIGGERS LIKE 'test_trigger'"; + assert_eq!(show_triggers.to_string(), expected); + + let sql = "SHOW TRIGGERS WHERE name = 'test_trigger'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()) + .unwrap(); + assert_eq!(1, result.len()); + let Statement::ShowTriggers(show_triggers) = &result[0] else { + panic!("Expected ShowTriggers statement"); + }; + let expected = "SHOW TRIGGERS WHERE name = 'test_trigger'"; + assert_eq!(show_triggers.to_string(), expected); + } +} diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index bc2491db67..4ab10d52ae 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -103,6 +103,9 @@ pub enum Statement { ShowCreateFlow(ShowCreateFlow), /// SHOW FLOWS ShowFlows(ShowFlows), + // SHOW TRIGGERS + #[cfg(feature = "enterprise")] + ShowTriggers(crate::statements::show::trigger::ShowTriggers), // SHOW CREATE VIEW ShowCreateView(ShowCreateView), // SHOW STATUS @@ -165,6 +168,8 @@ impl Display for Statement { Statement::ShowCreateTable(s) => s.fmt(f), Statement::ShowCreateFlow(s) => s.fmt(f), Statement::ShowFlows(s) => s.fmt(f), + #[cfg(feature = "enterprise")] + Statement::ShowTriggers(s) => s.fmt(f), Statement::ShowCreateDatabase(s) => s.fmt(f), Statement::ShowCreateView(s) => s.fmt(f), Statement::ShowViews(s) => s.fmt(f),