diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 28125768c..acc294c09 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -374,6 +374,8 @@ pub enum Expr { Identifier(Ident), /// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col` CompoundIdentifier(Vec), + /// A reference to a Sigma scalar value, e.g. `@sigma.my_parameter`. + SigmaParameter(Ident), /// JSON access (postgres) eg: data->'tags' JsonAccess { left: Box, @@ -752,6 +754,7 @@ impl fmt::Display for Expr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Expr::Identifier(s) => write!(f, "{s}"), + Expr::SigmaParameter(s) => write!(f, "@sigma.{s}"), Expr::MapAccess { column, keys } => { write!(f, "{column}")?; for k in keys { diff --git a/src/ast/query.rs b/src/ast/query.rs index d7037ce4c..04309d9b7 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -761,6 +761,11 @@ pub enum TableFactor { /// [Partition selection](https://dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html), supported by MySQL. partitions: Vec, }, + /// A reference to an element in a Sigma workbook. + SigmaElement { + element: Ident, + alias: Option, + }, Derived { lateral: bool, subquery: Box, @@ -887,6 +892,13 @@ impl fmt::Display for TableFactor { } Ok(()) } + TableFactor::SigmaElement { element, alias } => { + write!(f, "@sigma.{element}")?; + if let Some(alias) = alias { + write!(f, " AS {alias}")?; + } + Ok(()) + } TableFactor::Derived { lateral, subquery, diff --git a/src/keywords.rs b/src/keywords.rs index 3621961bc..b3208ffeb 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -612,6 +612,7 @@ define_keywords!( SETS, SHARE, SHOW, + SIGMA, SIMILAR, SKIP, SLOW, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f9ca62f8d..def6637d7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1050,6 +1050,10 @@ impl<'a> Parser<'a> { }, // End of Token::Word // array `[1, 2, 3]` Token::LBracket => self.parse_array_expr(false), + Token::AtSign if self.parse_keyword(Keyword::SIGMA) => { + self.expect_token(&Token::Period)?; + Ok(Expr::SigmaParameter(self.parse_identifier(false)?)) + } tok @ Token::Minus | tok @ Token::Plus => { let op = if tok == Token::Plus { UnaryOperator::Plus @@ -7985,6 +7989,7 @@ impl<'a> Parser<'a> { match &mut table_and_joins.relation { TableFactor::Derived { alias, .. } | TableFactor::Table { alias, .. } + | TableFactor::SigmaElement { alias, .. } | TableFactor::Function { alias, .. } | TableFactor::UNNEST { alias, .. } | TableFactor::JsonTable { alias, .. } @@ -8062,6 +8067,11 @@ impl<'a> Parser<'a> { columns, alias, }) + } else if self.parse_sigma_directive() { + self.expect_token(&Token::Period)?; + let element = self.parse_identifier(true)?; + let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; + Ok(TableFactor::SigmaElement { element, alias }) } else { let name = self.parse_object_name(true)?; @@ -8118,6 +8128,15 @@ impl<'a> Parser<'a> { } } + pub fn parse_sigma_directive(&mut self) -> bool { + self.maybe_parse(|p| { + p.expect_token(&Token::AtSign)?; + p.expect_keyword(Keyword::SIGMA)?; + Ok(()) + }) + .is_some() + } + /// Parse a given table version specifier. /// /// For now it only supports timestamp versioning for BigQuery and MSSQL dialects. diff --git a/tests/sqlparser_sigma.rs b/tests/sqlparser_sigma.rs new file mode 100644 index 000000000..0a28b331a --- /dev/null +++ b/tests/sqlparser_sigma.rs @@ -0,0 +1,38 @@ +#![warn(clippy::all)] + +use sqlparser::ast::*; +use sqlparser::dialect::SnowflakeDialect; +use test_utils::*; + +#[macro_use] +mod test_utils; + +fn snowflake() -> TestedDialects { + TestedDialects { + dialects: vec![Box::new(SnowflakeDialect {})], + options: None, + } +} +#[test] +fn parse_sigma() { + let sql = "SELECT my_column FROM @sigma.my_element WHERE my_column <> @sigma.param_filter"; + let select = snowflake().verified_only_select(sql); + assert_eq!( + select.from, + vec![TableWithJoins { + relation: TableFactor::SigmaElement { + element: Ident::new("my_element"), + alias: None + }, + joins: vec![] + }] + ); + assert_eq!( + select.selection, + Some(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("my_column"))), + op: BinaryOperator::NotEq, + right: Box::new(Expr::SigmaParameter(Ident::new("param_filter"))), + }) + ) +}