diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index b622c1da3..a8993e685 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -365,6 +365,16 @@ pub trait Dialect: Debug + Any { self.supports_trailing_commas() } + /// Returns true if the dialect supports double dot notation for object names + /// + /// Example + /// ```sql + /// SELECT * FROM db_name..table_name + /// ``` + fn supports_object_name_double_dot_notation(&self) -> bool { + false + } + /// Dialect-specific infix parser override /// /// This method is called to parse the next infix expression. diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 56919fb31..77d2ccff1 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -54,6 +54,14 @@ impl Dialect for SnowflakeDialect { true } + // Snowflake supports double-dot notation when the schema name is not specified + // In this case the default PUBLIC schema is used + // + // see https://docs.snowflake.com/en/sql-reference/name-resolution#resolution-when-schema-omitted-double-dot-notation + fn supports_object_name_double_dot_notation(&self) -> bool { + true + } + fn is_identifier_part(&self, ch: char) -> bool { ch.is_ascii_lowercase() || ch.is_ascii_uppercase() diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 7148ae48a..16362ebba 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8457,6 +8457,13 @@ impl<'a> Parser<'a> { pub fn parse_object_name(&mut self, in_table_clause: bool) -> Result { let mut idents = vec![]; loop { + if self.dialect.supports_object_name_double_dot_notation() + && idents.len() == 1 + && self.consume_token(&Token::Period) + { + // Empty string here means default schema + idents.push(Ident::new("")); + } idents.push(self.parse_identifier(in_table_clause)?); if !self.consume_token(&Token::Period) { break; diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 08792380d..e31811c2b 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2866,3 +2866,35 @@ fn test_projection_with_nested_trailing_commas() { let sql = "SELECT a, b, FROM c, (SELECT d, e, FROM f, LATERAL FLATTEN(input => events))"; let _ = snowflake().parse_sql_statements(sql).unwrap(); } + +#[test] +fn test_sf_double_dot_notation() { + snowflake().verified_stmt("SELECT * FROM db_name..table_name"); + snowflake().verified_stmt("SELECT * FROM x, y..z JOIN a..b AS b ON x.id = b.id"); + + assert_eq!( + snowflake() + .parse_sql_statements("SELECT * FROM X.Y..") + .unwrap_err() + .to_string(), + "sql parser error: Expected: identifier, found: ." + ); + assert_eq!( + snowflake() + .parse_sql_statements("SELECT * FROM X..Y..Z") + .unwrap_err() + .to_string(), + "sql parser error: Expected: identifier, found: ." + ); + assert_eq!( + // Ensure we don't parse leading token + snowflake() + .parse_sql_statements("SELECT * FROM .X.Y") + .unwrap_err() + .to_string(), + "sql parser error: Expected: identifier, found: ." + ); +} + +#[test] +fn test_parse_double_dot_notation_wrong_position() {}