Skip to content

Commit accaaa3

Browse files
committed
Snowflake parser: keep SELECT ALL, aggregate DISTINCT, NTZ, transaction names and role lists
From the omission audit after #46 and #48: - SELECT ALL sets select.all; it was consumed and dropped. - An aggregate of several arguments, such as APPROX_COUNT_DISTINCT ( DISTINCT a , b ), keeps distinct; the generic call dropped it. - TIMESTAMP [ ( p ) ] WITHOUT TIME ZONE is TIMESTAMP_NTZ, as its synonyms are, not bare TIMESTAMP (whose meaning follows TIMESTAMP_TYPE_MAPPING). - START TRANSACTION NAME n keeps n as the name, quoting kept, apart from NAME. - USE SECONDARY ROLES lists each role with its quoting in use.roles.
1 parent 6eefa6a commit accaaa3

12 files changed

Lines changed: 111 additions & 10 deletions

File tree

‎crates/sqlquack-core/src/classification/_helpers/clauses.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn select_clauses(select: &Select) -> Vec<String> {
5656
limit_by,
5757
fetch,
5858
distinct,
59+
all: _,
5960
distinct_on,
6061
top,
6162
with,

‎crates/sqlquack-syntax/src/expressions/queries/models.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub struct Select {
4444
pub limit_by: Option<Vec<Expression>>,
4545
pub fetch: Option<Fetch>,
4646
pub distinct: bool,
47+
/// Whether ALL was written where DISTINCT could be: it changes nothing, but was written.
48+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
49+
pub all: bool,
4750
pub distinct_on: Option<Vec<Expression>>,
4851
pub top: Option<Top>,
4952
pub with: Option<With>,
@@ -130,6 +133,7 @@ impl Select {
130133
limit_by: None,
131134
fetch: None,
132135
distinct: false,
136+
all: false,
133137
distinct_on: None,
134138
top: None,
135139
with: None,

‎crates/sqlquack-syntax/src/expressions/statements/models.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,9 @@ pub struct Use {
506506
pub kind: Option<UseKind>,
507507
/// The name of the object
508508
pub this: Identifier,
509+
/// Snowflake's secondary roles one by one, quoting kept; empty for ALL, NONE and other kinds.
510+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
511+
pub roles: Vec<Identifier>,
509512
}
510513

511514
/// Kind of USE statement

‎crates/sqlquack-syntax/src/parser/_helpers/ddl/alter.rs‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1424,8 +1424,10 @@ impl Parser {
14241424
self.skip();
14251425
let snowflake = self.is_snowflake();
14261426
let mut roles: Vec<String> = Vec::new();
1427+
let mut listed: Vec<Identifier> = Vec::new();
14271428
loop {
14281429
if snowflake && self.check(TokenType::QuotedIdentifier) {
1430+
listed.push(Identifier::quoted(self.peek_text()));
14291431
roles.push(self.parse_quoted_role()?);
14301432
if !self.match_token(TokenType::Comma) {
14311433
break;
@@ -1435,6 +1437,7 @@ impl Parser {
14351437
|| self.check(TokenType::Identifier)
14361438
{
14371439
let role = self.advance_text()?;
1440+
listed.push(Identifier::new(role.clone()));
14381441
roles.push(role);
14391442
if !self.match_token(TokenType::Comma) {
14401443
break;
@@ -1451,9 +1454,14 @@ impl Parser {
14511454
} else {
14521455
roles.join(", ")
14531456
};
1457+
let keyword = matches!(roles.as_slice(), [role] if role.eq_ignore_ascii_case("ALL") || role.eq_ignore_ascii_case("NONE"));
14541458
return Ok(Expression::Use(Box::new(Use {
14551459
kind: Some(UseKind::SecondaryRoles),
14561460
this: Identifier::new(name),
1461+
roles: match snowflake && !keyword {
1462+
true => listed,
1463+
false => Vec::new(),
1464+
},
14571465
})));
14581466
}
14591467
}
@@ -1483,7 +1491,11 @@ impl Parser {
14831491
ident.name = format!("{}.{}", ident.name, second_part.name);
14841492
}
14851493

1486-
Ok(Expression::Use(Box::new(Use { kind, this: ident })))
1494+
Ok(Expression::Use(Box::new(Use {
1495+
kind,
1496+
this: ident,
1497+
roles: Vec::new(),
1498+
})))
14871499
}
14881500

14891501
/// Parses BigQuery EXPORT DATA [WITH CONNECTION c] OPTIONS (...) AS SELECT ...

‎crates/sqlquack-syntax/src/parser/_helpers/ddl/transactions.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ impl Parser {
1919

2020
self.expect_token(TokenType::Transaction)?;
2121
self.refuse_snowflake_nameless_transaction()?;
22+
if self.is_snowflake() && self.match_identifier("NAME") {
23+
let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
24+
return Ok(Expression::Transaction(Box::new(Transaction {
25+
this: Some(Box::new(Expression::Identifier(name))),
26+
modes: None,
27+
mark: Some(Box::new(Expression::Identifier(Identifier::new(
28+
"START".to_string(),
29+
)))),
30+
})));
31+
}
2232

2333
let mut mode_parts: Vec<String> = Vec::new();
2434
while self.is_identifier_token()

‎crates/sqlquack-syntax/src/parser/_helpers/functions/typed.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ impl Parser {
851851

852852
name: name.to_string(),
853853
args,
854-
distinct: false,
854+
distinct,
855855
trailing_comments: Vec::new(),
856856
use_bracket_syntax: false,
857857
no_parens: false,

‎crates/sqlquack-syntax/src/parser/_helpers/queries/select.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ impl Parser {
446446
limit_by: None,
447447
fetch: None,
448448
distinct: false,
449+
all: false,
449450
distinct_on: None,
450451
top: None,
451452
with: None,

‎crates/sqlquack-syntax/src/parser/_helpers/statements/entry.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,8 @@ impl Parser {
11801180
let top = self.parse_select_top()?;
11811181

11821182
let is_distinct_token = self.match_token(TokenType::Distinct);
1183+
let all =
1184+
!is_distinct_token && self.check(TokenType::All) && !self.check_next(TokenType::Dot);
11831185
let (distinct, distinct_on) = if is_distinct_token {
11841186
if self.match_token(TokenType::On) {
11851187
self.expect_token(TokenType::LParen)?;
@@ -1311,6 +1313,7 @@ impl Parser {
13111313
hint,
13121314
top,
13131315
distinct,
1316+
all,
13141317
distinct_on,
13151318
kind,
13161319
operation_modifiers,
@@ -1539,6 +1542,7 @@ impl Parser {
15391542
hint,
15401543
top,
15411544
distinct,
1545+
all,
15421546
distinct_on,
15431547
kind,
15441548
operation_modifiers,
@@ -1575,6 +1579,7 @@ impl Parser {
15751579
limit_by,
15761580
fetch,
15771581
distinct,
1582+
all,
15781583
distinct_on,
15791584
top,
15801585
with: None,

‎crates/sqlquack-syntax/src/parser/_helpers/types/cast_base_types.rs‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,7 @@ impl Parser {
346346
}
347347
} else if self.match_keyword("WITHOUT") {
348348
self.match_time_zone_words()?;
349-
DataType::Timestamp {
350-
precision,
351-
timezone: false,
352-
}
349+
self.timestamp_without_time_zone(precision)
353350
} else {
354351
DataType::Timestamp {
355352
precision,
@@ -359,6 +356,22 @@ impl Parser {
359356
})
360357
}
361358

359+
/// `TIMESTAMP WITHOUT TIME ZONE`: Snowflake's TIMESTAMP_NTZ, whatever TIMESTAMP maps to.
360+
pub(crate) fn timestamp_without_time_zone(&self, precision: Option<u32>) -> DataType {
361+
match (self.is_snowflake(), precision) {
362+
(true, Some(precision)) => DataType::Custom {
363+
name: format!("TIMESTAMPNTZ({precision})"),
364+
},
365+
(true, None) => DataType::Custom {
366+
name: "TIMESTAMPNTZ".to_owned(),
367+
},
368+
(false, _) => DataType::Timestamp {
369+
precision,
370+
timezone: false,
371+
},
372+
}
373+
}
374+
362375
pub(crate) fn parse_cast_timestamp_tz_type(&mut self) -> Result<DataType> {
363376
Ok({
364377
let precision = if self.match_token(TokenType::LParen) {

‎crates/sqlquack-syntax/src/parser/_helpers/types/casts.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -784,10 +784,7 @@ impl Parser {
784784
}
785785
} else if self.match_keyword("WITHOUT") {
786786
self.match_time_zone_words()?;
787-
Ok(DataType::Timestamp {
788-
precision,
789-
timezone: false,
790-
})
787+
Ok(self.timestamp_without_time_zone(precision))
791788
} else {
792789
Ok(DataType::Timestamp {
793790
precision,

0 commit comments

Comments
 (0)