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
1,512 changes: 872 additions & 640 deletions coverage/syntax/baseline.json

Large diffs are not rendered by default.

375 changes: 0 additions & 375 deletions coverage/syntax/receipt-disagreements.toml

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions coverage/syntax/reviews/classes.toml
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,23 @@ note = "Read in full against the documentation page, the obligations, the cases

[review.definitions]
"sf.classes.classification-profile.commands.show-classification-profile" = "830810c2c45743599371412ea8822b0acd56c7a181b9981987c0c03b6794d05b"

[[review]]
id = "classes-007"
family = "classes"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.classes.anomaly-insights.methods.add-notification-integration",
"sf.classes.anomaly-insights.methods.remove-notification-integration",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.classes.anomaly-insights.methods.add-notification-integration" = "1504e9b2d02ca63baee0f30b539cdcf87938f8fc7df3e01b49619670a7dde124"
"sf.classes.anomaly-insights.methods.remove-notification-integration" = "9e636e4481124e64c94e2e3f4eb55c362b36583f50af1ac766258b02c14f87d4"

[review.definitions]
"sf.classes.anomaly-insights.methods.add-notification-integration" = "a0a6c4362f4fc4f32e7e739a1109d029f8a650f5c4d97c4b0d9cd0ddb15d3fff"
"sf.classes.anomaly-insights.methods.remove-notification-integration" = "1b2a0c4bab93d1ac1e265ab3b5890743294de3a147f18bbfc064aa40951e0997"
20 changes: 20 additions & 0 deletions coverage/syntax/reviews/dml.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,23 @@ note = "Re-read after the Snowflake parser batch at 552b9c1b6. The definition is

[review.definitions]
"sf.dml.insert" = "bad9b5d87ae130a845de65c5f83951140610f12c8fc401de7ce02655598fe694"

[[review]]
id = "dml-005"
family = "dml"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.dml.insert-multi-table.all-intoclause",
"sf.dml.insert-multi-table.first-all-when-then",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.dml.insert-multi-table.all-intoclause" = "c30ccb81b6f9db31485a71b380ae10df3fdaeef4dce04c4ffd33cd554fec175f"
"sf.dml.insert-multi-table.first-all-when-then" = "2201d8b1488f032d36070359d83be30b63dd900a2479986a2cef952d00399dd2"

[review.definitions]
"sf.dml.insert-multi-table.all-intoclause" = "ae75822dff04ebf57b8c2cc0bf22d0f4a2aa340b9862c5f9f019fa054516db5d"
"sf.dml.insert-multi-table.first-all-when-then" = "a079e76802663f30c07c025634cc3c8ed9d82342c2055289117dcddf33ecc865"
41 changes: 41 additions & 0 deletions coverage/syntax/reviews/functions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4440,3 +4440,44 @@ note = "Read in full against the documentation page, the obligations, the cases

[review.definitions]
"sf.functions.trunc2" = "68b34fd700dad6b111c5dca54696c7c6142a83229072bcbe20e47ac55511b3f7"

[[review]]
id = "functions-021"
family = "functions"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.functions.finetune-snowflake-cortex",
"sf.functions.object-construct",
"sf.functions.system-generate-saml-csr",
"sf.functions.system-pipe-force-resume",
"sf.functions.system-schedule-async-replication-group-refresh",
"sf.functions.system-wait",
"sf.functions.system-wait-for-services",
"sf.functions.to-timestamp",
"sf.functions.try-to-timestamp",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.functions.finetune-snowflake-cortex" = "72df257452d6771ea32069bd7942a4a21f4a178a75c7b7f3457326f1bc11e0cc"
"sf.functions.object-construct" = "bf295a4705f08a73ca6bbb71737c984be5a7bf766086b35a119661427f7c197b"
"sf.functions.system-generate-saml-csr" = "ed6bf933405bd9cece4086e71b0dbd0d1100a146b49c6651c78e8d75d69f807b"
"sf.functions.system-pipe-force-resume" = "d0e298b3a7d4caac0a1b5ee61b8f1f79742108a34f59dc63cafa3e733143f4aa"
"sf.functions.system-schedule-async-replication-group-refresh" = "a54ed0802d7df59fdc92eddfc93d3b6bdf7f81919fc20eeff3274111f58e4736"
"sf.functions.system-wait" = "7b6eec1df842944f3c322366f6e9bcea1a1f3b4ae4a6a22ef734c4b6a3b2cf28"
"sf.functions.system-wait-for-services" = "4ccddf998b86e38b2cfae04fd7f7135bed3afda08554b9b688e80412bf83750a"
"sf.functions.to-timestamp" = "aa81cc568acfc94f6cf8fcf5502017b1d070d81b121f367bbaa8d2f304de42b8"
"sf.functions.try-to-timestamp" = "c08a4c1905427b4d762609116ea940093714f1564e857b9570794e45035465d2"

[review.definitions]
"sf.functions.finetune-snowflake-cortex" = "8c288a7f4d64c3b9b020f0b67fc36fc4f4c4cbbf1dee9f8cd41556c855d6a753"
"sf.functions.object-construct" = "139e8fdbb1cceb34cddc53897021710490b29fd456dee903d29e40767129d511"
"sf.functions.system-generate-saml-csr" = "9aafbe6f40607c87032362cb51adb16afed35623d804255eadc24a6e220422b5"
"sf.functions.system-pipe-force-resume" = "1725d303019042ead2fa157135b0a8a33bffef59deb5921e3340a65d3ed45ed4"
"sf.functions.system-schedule-async-replication-group-refresh" = "f9e03050a1d81b56f67dd2a785186dd46cba52a4eb1569c6772c11a87a32ba32"
"sf.functions.system-wait" = "6a9bb964557a0667cbb44491cff2ff6049872fa7db557f8484191ae5fb117cb7"
"sf.functions.system-wait-for-services" = "958c9c6d758321c89e94508adbb8d17c255df86440518d0d3e2ba4f1e3213ebe"
"sf.functions.to-timestamp" = "0dde869a76996ffb7971a6d3acf2b7ea6f00423dd4fd73f118c2af3ff6245604"
"sf.functions.try-to-timestamp" = "48da47f57371860d581749d54cfb1f17e2a9be8889d7ea7015561898f568cd86"
26 changes: 26 additions & 0 deletions coverage/syntax/reviews/query.toml
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,29 @@ note = "Read in full against the documentation page, the obligations, the cases
"sf.query.pivot" = "0143158f5f51016a4e932fbde42fa1ab0786b9dbb4de7872980cb89c743174eb"
"sf.query.with.select" = "19d0a58ffbccd906ccbd2e8249af5f8a8888210aaa8e4533a9eb663926af24d8"
"sf.query.with.select.syntax" = "89bdc6f1c45a069f52f2d9b37fb295ba7a410535033e355d84472682cdb3583a"

[[review]]
id = "query-011"
family = "query"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.query.asof-join",
"sf.query.resample",
"sf.query.sample",
"sf.query.values",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.query.asof-join" = "3da7f3dc4a87984c7cc70529c02f741926fb825d915d1bcb49f6c64a8b59cf90"
"sf.query.resample" = "dd197f421969ad0be8e11648cf273a8e1ad6acf351ea0ba71feca37e1b2b1795"
"sf.query.sample" = "93239785bb2ea18c4548bc9ca2a9666a014688d8b808bae1d3dfd9e105042779"
"sf.query.values" = "c1e86fe0cd90ebffa092948ec6fae8c9a227274158de08ce48ab4ab9ff1bd8dc"

[review.definitions]
"sf.query.asof-join" = "b5c6df5815fbb31ebcf5f0c70639505c6e04119465cde56273f2011ee7dfdb33"
"sf.query.resample" = "df67c94a39e7e80bb01ec0b97d6ba7de7fe46aed271a5d877e04bcf3a05126e3"
"sf.query.sample" = "f6803dda7d63c241c930b0734b519a0a6155ff989d3f59c55348b21a7999871b"
"sf.query.values" = "5b43f2350f2ae8f0e5a95418a1dc9d86929cc1e37862a18e6d8170fd7c377ebe"
17 changes: 17 additions & 0 deletions coverage/syntax/reviews/transaction-session.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,20 @@ note = "Read in full against the documentation page, the obligations, the cases

[review.definitions]
"sf.transaction_session.desc-result" = "2128a9b1b7fab1f24e8eebb4c77e28baff1adf4ef6ac1d4699caf8577edf8c3c"

[[review]]
id = "transaction-session-010"
family = "transaction_session"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.transaction_session.alter-session.set-sessionparams",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.transaction_session.alter-session.set-sessionparams" = "7dc3a535a1ba4d04c819f709f14680cb142d3ac0772faea89359e52bda78a106"

[review.definitions]
"sf.transaction_session.alter-session.set-sessionparams" = "4c007d28e1a6c817f01c37def65476e052fbbd6bbe64708e61a3ea93793e72b7"
29 changes: 29 additions & 0 deletions coverage/syntax/reviews/utility.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,32 @@ note = "Read in full against the documentation page, the obligations, the cases
"sf.utility.execute-code-bundle" = "c16d88e8ce02a60b39760b47133af578be1993fe286d20de7791dee02a2c45bc"
"sf.utility.execute-notebook" = "efda46598201cea03a4d64af6e20a5865ef4cd68049ba5b3ae44b1047bf76126"
"sf.utility.execute-task-graph.graph-retry-last" = "5d3899caa702d4ec1b99cca9d3b4bd764b601fa2c7b151d18262de5d6fa7a99c"

[[review]]
id = "utility-005"
family = "utility"
parser_tree = "582bdba8f75e43db2dfbf7d7ba7b39411f31a8fb"
reviewed_utc = "2026-11-12T13:29:00Z"
verdict = "approved"
forms = [
"sf.utility.call",
"sf.utility.call-with.table-language-sql-as",
"sf.utility.execute-dcm-project.preview-using-configuration-from",
"sf.utility.execute-notebook-project",
"sf.utility.execute-task-graph.graph-retry-graph-run",
]
note = "Read in full against the documentation page, the obligations, the cases and the parse trees of the parser at feat/syntax-class-commands 5da5226c7299; every scored case agrees with its Snowflake receipt."

[review.fingerprints]
"sf.utility.call" = "065909479dafe1e14b5b1760161da5ccd4d3dceb848fbe7ed024aa9ed3aff98d"
"sf.utility.call-with.table-language-sql-as" = "1ddaed420d422a18de09d1746f34a6c3f97b6e4889008b092b4296cba70976d8"
"sf.utility.execute-dcm-project.preview-using-configuration-from" = "1779a55d8ede51d159ace5a838e56bbf1c5628102437e67a9d2ef47e179d5768"
"sf.utility.execute-notebook-project" = "ea838e3e718089c566e3dc566c38f42f4376c49b353af122a883111e5caf6330"
"sf.utility.execute-task-graph.graph-retry-graph-run" = "3e19b2d5a6a4254ef085c90c8786747fc577b2c32ca39e29740a1e965e58b1d3"

[review.definitions]
"sf.utility.call" = "1258ecf9f03552c802c61265466aea01f9bbc8b9cfa7bf98490ecc0b1d385968"
"sf.utility.call-with.table-language-sql-as" = "4adcc148271445fb21450fd8c6b593879b9511bc23fa3d9eb863f84b48af3aa8"
"sf.utility.execute-dcm-project.preview-using-configuration-from" = "2fa9e31deafc49e3d0f3c19d67c57c0be982fc22946d1b0cc66e282020557a2a"
"sf.utility.execute-notebook-project" = "73f0ce13afa2c2f0fb0483c4f54ef235f7753056de9acab7ddd05627fc03fb06"
"sf.utility.execute-task-graph.graph-retry-graph-run" = "e6fc055460eac539f7a5e0529c1bc4ba57fe688cfa28be2a7b2c2b6d4b432413"
Original file line number Diff line number Diff line change
Expand Up @@ -1400,8 +1400,8 @@ impl Parser {
self.config.dialect,
Some(sqlquack_syntax::tokens::models::DialectType::Snowflake)
);
if snowflake && self.match_text_seq(&["DATABASE", "ROLE"]) {
return self.parse_drop_snowflake_object("DATABASE ROLE".to_owned());
if let Some(kind) = self.parse_snowflake_drop_kind(snowflake)? {
return self.parse_drop_snowflake_object(kind);
}
match self.peek().token_type {
TokenType::Table => self.parse_drop_table_with_iceberg(leading_comments, false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub(crate) mod snowflake_alter_objects;
pub(crate) mod snowflake_alter_resources;
pub(crate) mod snowflake_alter_tables;
pub(crate) mod snowflake_alter_user;
pub(crate) mod snowflake_classes;
pub(crate) mod snowflake_commands;
pub(crate) mod snowflake_containers;
pub(crate) mod snowflake_copy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const OMITTED_NAME_ACTIONS: &[(&str, &[&str])] = &[
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_object(&mut self) -> Result<Option<Expression>> {
if let Some(kind) = self.parse_snowflake_class_kind()? {
return self.parse_alter_snowflake_class(&kind).map(Some);
}
if let Some(expr) = self.parse_alter_snowflake_objects()? {
return Ok(Some(expr));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! Parser ddl: CREATE, ALTER and DROP of a Snowflake class instance, such as `SNOWFLAKE.ML.FORECAST`, checked against its grammar.

use crate::parser::_helpers::ddl::snowflake_alter::{alter_object, property};
use crate::parser::models::Parser;
use sqlquack_syntax::expressions::schema_objects::models::{CreateObject, ObjectProperty};
use sqlquack_syntax::expressions::tree::models::Expression;
use sqlquack_syntax::tokens::models::TokenType;
use sqlquack_syntax::tokens::types::Result;

/// The classes whose instances CREATE, ALTER and DROP name, as the last part of the class.
const SNOWFLAKE_CLASSES: [&str; 7] = [
"ANOMALY_DETECTION",
"BUDGET",
"CLASSIFICATION",
"CLASSIFICATION_PROFILE",
"CUSTOM_CLASSIFIER",
"FORECAST",
"TOP_INSIGHTS",
];

impl Parser {
/// The class after CREATE, ALTER or DROP as `SNOWFLAKE.<schema>.<class>`, where an empty schema stays empty; None when the next words name no class, and outside the faithful Snowflake modes.
pub(crate) fn parse_snowflake_class_kind(&mut self) -> Result<Option<String>> {
if !self.applies_snowflake_context_rules() {
return Ok(None);
}
if self.check(TokenType::Dot) {
return Err(self.parse_error("Expected SNOWFLAKE before a class's schema"));
}
if !self.check_identifier("SNOWFLAKE") || !self.check_next(TokenType::Dot) {
return Ok(None);
}
self.current += 2;
let schema = match self.check(TokenType::Dot) {
true => String::new(),
false => self.parse_simple_name()?.name.to_ascii_uppercase(),
};
self.expect_token(TokenType::Dot)?;
let class = self.peek_text().to_ascii_uppercase();
if self.check(TokenType::QuotedIdentifier) || !SNOWFLAKE_CLASSES.contains(&class.as_str()) {
return Err(self.parse_error("Expected a Snowflake class"));
}
self.skip();
Ok(Some(format!("SNOWFLAKE.{schema}.{class}")))
}

/// The kind a Snowflake DROP names before the other kinds are tried: a class, or DATABASE ROLE; None otherwise.
pub(crate) fn parse_snowflake_drop_kind(&mut self, snowflake: bool) -> Result<Option<String>> {
if let Some(class) = self.parse_snowflake_class_kind()? {
return Ok(Some(class));
}
let database_role = snowflake && self.match_text_seq(&["DATABASE", "ROLE"]);
Ok(database_role.then(|| "DATABASE ROLE".to_owned()))
}

/// A CREATE [ OR REPLACE ] of a class instance: `<class> [ IF NOT EXISTS ] <name> ( [ <argument> , ... ] ) [ [ WITH ] TAG ( ... ) ] [ [ WITH ] COMMENT = <value> ]`; None for any other CREATE.
pub(crate) fn parse_snowflake_create_class(
&mut self,
or_replace: bool,
or_alter: bool,
) -> Result<Option<Expression>> {
let Some(kind) = self.parse_snowflake_class_kind()? else {
return Ok(None);
};
let if_not_exists = self.check(TokenType::If);
if if_not_exists
&& !self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists])
{
return Err(self.parse_error("Expected IF NOT EXISTS"));
}
let name = self.parse_object_name()?;
self.expect_token(TokenType::LParen)?;
let operands = match self.match_token(TokenType::RParen) {
true => Vec::new(),
false => {
let arguments = self.parse_function_arguments()?;
self.expect_token(TokenType::RParen)?;
arguments
}
};
let mut properties: Vec<ObjectProperty> = Vec::new();
if self.match_text_seq(&["WITH", "TAG"]) || self.match_text_seq(&["TAG"]) {
properties.extend(self.parse_external_tags()?);
}
if self.match_text_seq(&["WITH", "COMMENT"]) || self.match_text_seq(&["COMMENT"]) {
self.expect_token(TokenType::Eq)?;
properties.push(property("COMMENT", None, Some(self.parse_object_value()?)));
}
Ok(Some(Expression::CreateObject(Box::new(CreateObject {
kind,
modifier: None,
or_replace,
or_alter,
if_not_exists,
name,
source: String::new(),
operands,
properties,
}))))
}

/// An ALTER of a class instance after its class: `[ IF EXISTS ] <name>`, then RENAME TO, or SET or UNSET of tags and properties.
pub(super) fn parse_alter_snowflake_class(&mut self, kind: &str) -> Result<Expression> {
let head = self.parse_alter_object_head()?;
let action = self.expect_alter_words(&[&["RENAME", "TO"], &["SET"], &["UNSET"]])?;
let (operands, properties) = match action.as_str() {
"RENAME TO" => (
vec![Expression::Table(Box::new(self.parse_object_name()?))],
Vec::new(),
),
"SET" => (Vec::new(), self.parse_alter_object_properties(true, false)?),
_ => (
Vec::new(),
self.parse_alter_object_properties(false, false)?,
),
};
Ok(alter_object(kind, head, action, (operands, properties)))
}
}
Loading
Loading