Skip to content

Commit eda99cd

Browse files
authored
sigma: support for parsing @sigma table factors and expressions (#19)
2 parents 4d6ff63 + 0037d8b commit eda99cd

5 files changed

Lines changed: 73 additions & 0 deletions

File tree

src/ast/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ pub enum Expr {
374374
Identifier(Ident),
375375
/// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col`
376376
CompoundIdentifier(Vec<Ident>),
377+
/// A reference to a Sigma scalar value, e.g. `@sigma.my_parameter`.
378+
SigmaParameter(Ident),
377379
/// JSON access (postgres) eg: data->'tags'
378380
JsonAccess {
379381
left: Box<Expr>,
@@ -752,6 +754,7 @@ impl fmt::Display for Expr {
752754
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
753755
match self {
754756
Expr::Identifier(s) => write!(f, "{s}"),
757+
Expr::SigmaParameter(s) => write!(f, "@sigma.{s}"),
755758
Expr::MapAccess { column, keys } => {
756759
write!(f, "{column}")?;
757760
for k in keys {

src/ast/query.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,11 @@ pub enum TableFactor {
761761
/// [Partition selection](https://dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html), supported by MySQL.
762762
partitions: Vec<Ident>,
763763
},
764+
/// A reference to an element in a Sigma workbook.
765+
SigmaElement {
766+
element: Ident,
767+
alias: Option<TableAlias>,
768+
},
764769
Derived {
765770
lateral: bool,
766771
subquery: Box<Query>,
@@ -887,6 +892,13 @@ impl fmt::Display for TableFactor {
887892
}
888893
Ok(())
889894
}
895+
TableFactor::SigmaElement { element, alias } => {
896+
write!(f, "@sigma.{element}")?;
897+
if let Some(alias) = alias {
898+
write!(f, " AS {alias}")?;
899+
}
900+
Ok(())
901+
}
890902
TableFactor::Derived {
891903
lateral,
892904
subquery,

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ define_keywords!(
612612
SETS,
613613
SHARE,
614614
SHOW,
615+
SIGMA,
615616
SIMILAR,
616617
SKIP,
617618
SLOW,

src/parser/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,10 @@ impl<'a> Parser<'a> {
10501050
}, // End of Token::Word
10511051
// array `[1, 2, 3]`
10521052
Token::LBracket => self.parse_array_expr(false),
1053+
Token::AtSign if self.parse_keyword(Keyword::SIGMA) => {
1054+
self.expect_token(&Token::Period)?;
1055+
Ok(Expr::SigmaParameter(self.parse_identifier(false)?))
1056+
}
10531057
tok @ Token::Minus | tok @ Token::Plus => {
10541058
let op = if tok == Token::Plus {
10551059
UnaryOperator::Plus
@@ -7985,6 +7989,7 @@ impl<'a> Parser<'a> {
79857989
match &mut table_and_joins.relation {
79867990
TableFactor::Derived { alias, .. }
79877991
| TableFactor::Table { alias, .. }
7992+
| TableFactor::SigmaElement { alias, .. }
79887993
| TableFactor::Function { alias, .. }
79897994
| TableFactor::UNNEST { alias, .. }
79907995
| TableFactor::JsonTable { alias, .. }
@@ -8062,6 +8067,11 @@ impl<'a> Parser<'a> {
80628067
columns,
80638068
alias,
80648069
})
8070+
} else if self.parse_sigma_directive() {
8071+
self.expect_token(&Token::Period)?;
8072+
let element = self.parse_identifier(true)?;
8073+
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
8074+
Ok(TableFactor::SigmaElement { element, alias })
80658075
} else {
80668076
let name = self.parse_object_name(true)?;
80678077

@@ -8118,6 +8128,15 @@ impl<'a> Parser<'a> {
81188128
}
81198129
}
81208130

8131+
pub fn parse_sigma_directive(&mut self) -> bool {
8132+
self.maybe_parse(|p| {
8133+
p.expect_token(&Token::AtSign)?;
8134+
p.expect_keyword(Keyword::SIGMA)?;
8135+
Ok(())
8136+
})
8137+
.is_some()
8138+
}
8139+
81218140
/// Parse a given table version specifier.
81228141
///
81238142
/// For now it only supports timestamp versioning for BigQuery and MSSQL dialects.

tests/sqlparser_sigma.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#![warn(clippy::all)]
2+
3+
use sqlparser::ast::*;
4+
use sqlparser::dialect::SnowflakeDialect;
5+
use test_utils::*;
6+
7+
#[macro_use]
8+
mod test_utils;
9+
10+
fn snowflake() -> TestedDialects {
11+
TestedDialects {
12+
dialects: vec![Box::new(SnowflakeDialect {})],
13+
options: None,
14+
}
15+
}
16+
#[test]
17+
fn parse_sigma() {
18+
let sql = "SELECT my_column FROM @sigma.my_element WHERE my_column <> @sigma.param_filter";
19+
let select = snowflake().verified_only_select(sql);
20+
assert_eq!(
21+
select.from,
22+
vec![TableWithJoins {
23+
relation: TableFactor::SigmaElement {
24+
element: Ident::new("my_element"),
25+
alias: None
26+
},
27+
joins: vec![]
28+
}]
29+
);
30+
assert_eq!(
31+
select.selection,
32+
Some(Expr::BinaryOp {
33+
left: Box::new(Expr::Identifier(Ident::new("my_column"))),
34+
op: BinaryOperator::NotEq,
35+
right: Box::new(Expr::SigmaParameter(Ident::new("param_filter"))),
36+
})
37+
)
38+
}

0 commit comments

Comments
 (0)