diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 09496feeb..e1262a975 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4628,7 +4628,7 @@ pub struct HiveFormat { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct SqlOption { - pub name: WithSpan, + pub name: ObjectName, pub value: Expr, } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d452dbf58..b55a04157 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5275,7 +5275,7 @@ impl<'a> Parser<'a> { } pub fn parse_sql_option(&mut self) -> Result { - let name = self.parse_identifier(false)?; + let name = self.parse_object_name(false)?; self.expect_token(&Token::Eq)?; let value = self.parse_expr()?; Ok(SqlOption { name, value }) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index dce81c23e..5e01405ae 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3171,11 +3171,11 @@ fn parse_create_table_with_options() { assert_eq!( vec![ SqlOption { - name: Ident::new("foo").empty_span(), + name: ObjectName(vec![Ident::new("foo")]), value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { - name: Ident::new("a").empty_span(), + name: ObjectName(vec![Ident::new("a")]), value: Expr::Value(number("123")), }, ], @@ -3456,11 +3456,11 @@ fn parse_alter_view_with_options() { assert_eq!( vec![ SqlOption { - name: Ident::new("foo").empty_span(), + name: ObjectName(vec![Ident::new("foo")]), value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { - name: Ident::new("a").empty_span(), + name: ObjectName(vec![Ident::new("a")]), value: Expr::Value(number("123")), }, ], @@ -5948,11 +5948,11 @@ fn parse_create_view_with_options() { assert_eq!( vec![ SqlOption { - name: Ident::new("foo").empty_span(), + name: ObjectName(vec![Ident::new("foo")]), value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { - name: Ident::new("a").empty_span(), + name: ObjectName(vec![Ident::new("a")]), value: Expr::Value(number("123")), }, ], @@ -7854,11 +7854,11 @@ fn parse_cache_table() { has_as: false, options: vec![ SqlOption { - name: Ident::with_quote('\'', "K1").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K1")]), value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { - name: Ident::with_quote('\'', "K2").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K2")]), value: Expr::Value(number("0.88")), }, ], @@ -7879,11 +7879,11 @@ fn parse_cache_table() { has_as: false, options: vec![ SqlOption { - name: Ident::with_quote('\'', "K1").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K1")]), value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { - name: Ident::with_quote('\'', "K2").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K2")]), value: Expr::Value(number("0.88")), }, ], @@ -7904,11 +7904,11 @@ fn parse_cache_table() { has_as: true, options: vec![ SqlOption { - name: Ident::with_quote('\'', "K1").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K1")]), value: Expr::Value(Value::SingleQuotedString("V1".into())), }, SqlOption { - name: Ident::with_quote('\'', "K2").empty_span(), + name: ObjectName(vec![Ident::with_quote('\'', "K2")]), value: Expr::Value(number("0.88")), }, ], @@ -8631,11 +8631,10 @@ fn parse_unload() { } .empty_span(), with: vec![SqlOption { - name: Ident { + name: ObjectName(vec![Ident { value: "format".to_string(), quote_style: None - } - .empty_span(), + }]), value: Expr::Value(Value::SingleQuotedString("AVRO".to_string())) }] } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index e01bcd62b..a27c2706c 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -508,15 +508,15 @@ fn parse_create_table_with_defaults() { with_options, vec![ SqlOption { - name: Ident::new("fillfactor").empty_span(), + name: ObjectName(vec![Ident::new("fillfactor")]), value: Expr::Value(number("20")), }, SqlOption { - name: Ident::new("user_catalog_table").empty_span(), + name: ObjectName(vec![Ident::new("user_catalog_table")]), value: Expr::Value(Value::Boolean(true)), }, SqlOption { - name: Ident::new("autovacuum_vacuum_threshold").empty_span(), + name: ObjectName(vec![Ident::new("autovacuum_vacuum_threshold")]), value: Expr::Value(number("100")), }, ] @@ -3758,11 +3758,11 @@ fn parse_create_table_with_options() { assert_eq!( vec![ SqlOption { - name: Ident::new("foo").empty_span(), + name: ObjectName(vec![Ident::new("foo")]), value: Expr::Value(Value::SingleQuotedString("bar".into())), }, SqlOption { - name: Ident::new("a").empty_span(), + name: ObjectName(vec![Ident::new("a")]), value: Expr::Value(number("123")), }, ], diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 19126f9cb..ab557eb70 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1414,7 +1414,35 @@ fn test_column_with_masking() { #[test] fn test_table_with_tag() { - snowflake().one_statement_parses_to("CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216)) WITH TAG (UNKNOWN_TAG='#UNKNOWN_VALUE')", "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216))"); + // Simple tag name + snowflake().one_statement_parses_to( + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216)) WITH TAG (UNKNOWN_TAG='#UNKNOWN_VALUE')", + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216))" + ); + + // Schema-qualified tag name + snowflake().one_statement_parses_to( + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216)) WITH TAG (TAG_SCHEMA.DOMAIN_MAPPING='marketing')", + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216))" + ); + + // Fully-qualified tag name (database.schema.tag) + snowflake().one_statement_parses_to( + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216)) WITH TAG (PROD.TAG_SCHEMA.DOMAIN_MAPPING='marketing')", + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216))" + ); + + // Multiple tags with different qualification levels + snowflake().one_statement_parses_to( + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216)) WITH TAG (SIMPLE_TAG='value1', SCHEMA.TAG_NAME='value2', DB.SCHEMA.TAG_NAME='value3')", + "CREATE OR REPLACE TABLE TBL (ID VARCHAR(16777216))" + ); + + // Real-world example from the issue (anonymized) + snowflake().one_statement_parses_to( + "CREATE OR REPLACE TABLE SCHEMA.DERIVED_TABLE (USER_ID VARCHAR(36), REPORTING_DATE TIMESTAMP_NTZ(9)) WITH TAG (STAGE.TAG_SCHEMA.DOMAIN_MAPPING='analytics')", + "CREATE OR REPLACE TABLE SCHEMA.DERIVED_TABLE (USER_ID VARCHAR(36), REPORTING_DATE TIMESTAMP_NTZ(9))" + ); } #[test]