Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# GitHub's language statistics count the code SQLQuack ships. Test suites, development scripts
# and development-only tools are marked as documentation so the statistics leave them out.
/tests/** linguist-documentation
/scripts/** linguist-documentation
/crates/**/tests/** linguist-documentation
/crates/**/tests.rs linguist-documentation
/tools/** linguist-documentation
/fuzz/** linguist-documentation
/recorder/** linguist-documentation
/website/scripts/** linguist-documentation
2,207 changes: 1,568 additions & 639 deletions coverage/syntax/baseline.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions coverage/syntax/corpus-verdicts.json
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,12 @@
1144,
1145,
1186,
1187,
1188,
1189,
1190,
1191,
1192,
1193,
1194,
1195,
Expand Down Expand Up @@ -5540,6 +5546,7 @@
566,
614,
615,
616,
624,
625,
626,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1075,7 +1075,11 @@ impl Parser {
}
}

fn parse_create_view_query(&mut self, query_parenthesized: bool) -> Result<Expression> {
/// The view's query after AS, in parentheses when `query_parenthesized`.
pub(crate) fn parse_create_view_query(
&mut self,
query_parenthesized: bool,
) -> Result<Expression> {
Ok(if self.check(TokenType::With) {
self.parse_statement()?
} else if query_parenthesized {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ pub(crate) mod snowflake_routines;
pub(crate) mod snowflake_semantic_view;
pub(crate) mod snowflake_show;
pub(crate) mod snowflake_statements;
pub(crate) mod snowflake_streams;
pub(crate) mod snowflake_views;
pub(crate) mod table_options;
pub(crate) mod transactions;
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const OMITTED_NAME_ACTIONS: &[(&str, &[&str])] = &[
("CATALOG INTEGRATION", &["SET"]),
("EXTERNAL ACCESS INTEGRATION", &["SET", "UNSET"]),
("SECURITY INTEGRATION", &["SET"]),
("VIEW", &["SET"]),
("MATERIALIZED VIEW", &["SET"]),
];

impl Parser {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ fn resource_actions(kind: &str) -> &'static [&'static [&'static str]] {
&["DROP", "CLUSTERING", "KEY"],
&["SUSPEND", "RECLUSTER"],
&["RESUME", "RECLUSTER"],
&["RECLUSTER"],
&["SUSPEND"],
&["RESUME"],
&["RENAME", "TO"],
Expand Down Expand Up @@ -290,7 +291,7 @@ impl Parser {
}

/// Parse the operands and properties of `action` on `kind`, extending `action` with its later keywords.
fn parse_resource_action(
pub(super) fn parse_resource_action(
&mut self,
kind: &str,
action: String,
Expand All @@ -303,6 +304,13 @@ impl Parser {
properties.extend(self.parse_resource_properties()?);
(vec![], properties)
}
("VIEW", "SET" | "UNSET") if self.check_identifier("SECURE") => {
self.skip();
if !self.is_statement_end_at(0) {
return Err(self.parse_error("A view's SECURE is set or unset on its own"));
}
(vec![], vec![property("SECURE", None, None)])
}
("DATASET", "DROP VERSION") | (_, "REMOVE STORAGE_LOCATION") => {
(vec![self.parse_string_value()?], vec![])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Parser {
} else if self.match_token(TokenType::Sequence) {
self.parse_alter_kind_extension("SEQUENCE", &[&["RENAME", "TO"], &["UNSET"]])
} else if self.match_token(TokenType::View) {
self.parse_alter_kind_extension("VIEW", &[&["RENAME", "TO"]])
self.parse_alter_view_extension()
} else if self.match_identifier("SHARE") {
return self.parse_alter_share().map(Some);
} else {
Expand Down Expand Up @@ -311,6 +311,41 @@ impl Parser {
}))
}

/// Parse the head and a RENAME TO, property SET or property UNSET of an ALTER VIEW, or None for another action.
fn parse_alter_view_extension(&mut self) -> Result<Option<Expression>> {
let head = self.parse_alter_head_or_omitted("VIEW")?;
if self.is_statement_end_at(0) {
return Err(self.parse_error("Expected an ALTER VIEW action"));
}
let sets_property = self.check(TokenType::Set)
&& (self.check_next_identifier("SECURE")
|| self
.tokens
.get(self.current + 2)
.is_some_and(|token| token.token_type == TokenType::Eq));
let unsets_property =
self.check_text_seq(&["UNSET"]) && !self.check_next_identifier("TBLPROPERTIES");
let action = if self.match_text_seq(&["RENAME", "TO"]) {
return Ok(Some(alter_object(
"VIEW",
head,
"RENAME TO".to_owned(),
(vec![self.parse_tag_operand()?], vec![]),
)));
} else if sets_property || unsets_property {
self.skip();
if sets_property {
"SET"
} else {
"UNSET"
}
} else {
return Ok(None);
};
let (action, parts) = self.parse_resource_action("VIEW", action.to_owned())?;
Ok(Some(alter_object("VIEW", head, action, parts)))
}

/// Parse the head and one of `actions` of an ALTER `kind`, or None for another action.
fn parse_alter_kind_extension(
&mut self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@ pub(crate) enum CloneOptions {
impl Parser {
/// A clone's `{ AT | BEFORE } ( { TIMESTAMP | OFFSET | STATEMENT } => <expr> )`, if present.
pub(crate) fn parse_clone_time_travel(&mut self) -> Result<Option<Expression>> {
self.parse_time_travel(&[&["TIMESTAMP"], &["OFFSET"], &["STATEMENT"]])
}

/// `{ AT | BEFORE } ( <kind> => <expr> )` for one of `kinds`, if present.
pub(crate) fn parse_time_travel(&mut self, kinds: &[&[&str]]) -> Result<Option<Expression>> {
if !self.check_identifier("AT") && !self.check(TokenType::Before) {
return Ok(None);
}
let this = self.advance_text()?.to_ascii_uppercase();
self.expect_token(TokenType::LParen)?;
let kind = self.expect_alter_words(&[&["TIMESTAMP"], &["OFFSET"], &["STATEMENT"]])?;
let kind = self.expect_alter_words(kinds)?;
self.expect_token(TokenType::FArrow)?;
let expression = self.parse_expression()?;
self.expect_token(TokenType::RParen)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ impl Parser {
if let Some(table) = self.parse_snowflake_create_event_table(or_replace, or_alter)? {
return Ok(Some(table));
}
if self.check_text_seq(&["SEMANTIC", "VIEW"]) {
self.check_snowflake_semantic_view()?;
if let Some(stream) = self.parse_snowflake_create_stream(or_replace, or_alter)? {
return Ok(Some(stream));
}
if let Some(view) = self.parse_snowflake_create_semantic_view(or_replace, or_alter)? {
return Ok(Some(view));
}
if self.check(TokenType::Alter) || self.check(TokenType::String) {
return Err(self.parse_error("Expected an object kind after CREATE"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,13 @@ impl Parser {
}

/// `( <tag> = <value> [ , ... ] )`, one TAG property per tag.
fn parse_external_tags(&mut self) -> Result<Vec<ObjectProperty>> {
pub(super) fn parse_external_tags(&mut self) -> Result<Vec<ObjectProperty>> {
self.expect_token(TokenType::LParen)?;
let mut tags: Vec<ObjectProperty> = Vec::new();
loop {
let tag = self.parse_object_name()?;
self.expect_token(TokenType::Eq)?;
let value = match self.check(TokenType::String) {
let value = match self.check(TokenType::String) || self.check(TokenType::Number) {
true => self.parse_primary()?,
false => self.parse_external_word()?,
};
Expand Down Expand Up @@ -390,7 +390,8 @@ impl Parser {
Ok(Expression::Tuple(Box::new(Tuple { expressions })))
}

fn parse_external_scalar(&mut self) -> Result<Expression> {
/// A string, number or boolean literal, or a bare word.
pub(super) fn parse_external_scalar(&mut self) -> Result<Expression> {
let literal = matches!(
self.peek().token_type,
TokenType::String | TokenType::Number | TokenType::True | TokenType::False
Expand Down
Loading
Loading