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
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ type Parts = (Vec<Expression>, Vec<ObjectProperty>);
impl Parser {
/// Parse a Snowflake ALTER of a kind this module owns, or None when the next words name another kind.
pub(crate) fn parse_alter_snowflake_objects(&mut self) -> Result<Option<Expression>> {
for kind in ["APPLICATION", "DATABASE"] {
if self.check_text_seq(&[kind, "ROLE"])
&& !self
.text_at(2)
.is_some_and(|word| matches!(word.as_str(), "RENAME" | "SET" | "UNSET"))
{
self.current += 2;
return self
.parse_alter_scoped_role(&format!("{kind} ROLE"))
.map(Some);
}
}
if self.match_text_seq(&["APPLICATION", "PACKAGE"]) {
return self.parse_alter_application_package().map(Some);
}
Expand Down Expand Up @@ -675,6 +687,18 @@ impl Parser {
Ok(alter_object("BACKUP SET", head, action, parts))
}

/// Parse ALTER APPLICATION ROLE or ALTER DATABASE ROLE after its kind.
fn parse_alter_scoped_role(&mut self, kind: &str) -> Result<Expression> {
let head = self.parse_alter_object_head()?;
let action = self.expect_alter_words(&[&["RENAME"], &["SET"], &["UNSET"]])?;
let parts = match action.as_str() {
"RENAME" => self.parse_alter_target("TO")?,
"SET" => self.parse_alter_set(true)?,
_ => self.parse_alter_set(false)?,
};
Ok(alter_object(kind, head, action, parts))
}

/// Consume the first of `words` that comes next and return it.
fn match_one_of(&mut self, words: &[&str]) -> Option<String> {
let word = words.iter().find(|word| self.check_text_seq(&[word]))?;
Expand Down
36 changes: 36 additions & 0 deletions crates/sqlquack-syntax/tests/parser/test_fixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,42 @@ fn given_a_snowflake_alter_of_an_application_database_or_service_kind_when_parsi
dialect: Dialect::Snowflake,
expected_accepted: false,
},
AcceptanceTestCase {
description: "a database role renames (recorded errno 2003, after parsing)",
sql: "ALTER DATABASE ROLE v1 RENAME TO v1 ;",
dialect: Dialect::Snowflake,
expected_accepted: true,
},
AcceptanceTestCase {
description: "a database role unsets a tag (recorded errno 2003, after parsing)",
sql: "ALTER DATABASE ROLE \"SELECT\" UNSET TAG v1 ;",
dialect: Dialect::Snowflake,
expected_accepted: true,
},
AcceptanceTestCase {
description: "an application role sets a comment (recorded accepted)",
sql: "ALTER APPLICATION ROLE IF EXISTS v1 SET COMMENT = 'm' ;",
dialect: Dialect::Snowflake,
expected_accepted: true,
},
AcceptanceTestCase {
description: "with no role name, ROLE names the database (recorded errno 2003)",
sql: "ALTER DATABASE ROLE RENAME TO v1 ;",
dialect: Dialect::Snowflake,
expected_accepted: true,
},
AcceptanceTestCase {
description: "an application role needs a property to unset (recorded errno 1003)",
sql: "ALTER APPLICATION ROLE v1 UNSET ;",
dialect: Dialect::Snowflake,
expected_accepted: false,
},
AcceptanceTestCase {
description: "a database role name is no string (recorded errno 1003)",
sql: "ALTER DATABASE ROLE 'v1' RENAME TO v1 ;",
dialect: Dialect::Snowflake,
expected_accepted: false,
},
];

for test_case in test_cases {
Expand Down