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
This commit is contained in:
fys
2025-05-29 20:00:36 +08:00
committed by GitHub
parent 4ae6df607b
commit b4d00fb499
9 changed files with 165 additions and 1 deletions

View File

@@ -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]

View File

@@ -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) => {

View File

@@ -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)

View File

@@ -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<Output> {
crate::error::UnsupportedTriggerSnafu.fail()
}
#[tracing::instrument(skip_all)]
pub async fn show_create_flow(
&self,

View File

@@ -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<Statement> {
#[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,

View File

@@ -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<Statement> {
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());
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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),