From 36572d42bcdc42f55ff7ccfb6fdacc84aa828af4 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Mon, 6 Apr 2026 20:25:05 +0200 Subject: [PATCH 1/3] Handle Snowflake TEXT cast modifiers --- src/parser/mod.rs | 15 ++++++++++- tests/sqlparser_snowflake.rs | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6282ed3d7..85e1f43ca 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12170,7 +12170,20 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; Ok(DataType::FixedString(character_length)) } - Keyword::TEXT => Ok(DataType::Text), + Keyword::TEXT => { + if dialect_is!(dialect is SnowflakeDialect) { + if let Some(modifiers) = self.parse_optional_type_modifiers()? { + Ok(DataType::Custom( + ObjectName::from(vec![Ident::new("TEXT")]), + modifiers, + )) + } else { + Ok(DataType::Text) + } + } else { + Ok(DataType::Text) + } + } Keyword::TINYTEXT => Ok(DataType::TinyText), Keyword::MEDIUMTEXT => Ok(DataType::MediumText), Keyword::LONGTEXT => Ok(DataType::LongText), diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 790bf1515..96f61cd64 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -5010,3 +5010,52 @@ fn test_select_dollar_column_from_stage() { // With table function args, without alias snowflake().verified_stmt("SELECT $1, $2 FROM @mystage1(file_format => 'myformat')"); } + +#[test] +fn test_parse_pg_style_cast_to_text_with_length() { + let select = snowflake().verified_only_select( + "SELECT _ID::TEXT(16777216) AS _ID FROM INCARE_ANALYTICS.USER_DETAILS", + ); + match only(&select.projection) { + SelectItem::ExprWithAlias { + expr: + Expr::Cast { + kind: CastKind::DoubleColon, + data_type, + .. + }, + alias, + } => { + assert_eq!(alias.value, "_ID"); + assert_eq!( + *data_type, + DataType::Custom( + ObjectName::from(vec![Ident::new("TEXT")]), + vec!["16777216".to_string()], + ) + ); + } + _ => unreachable!(), + } +} + +#[test] +fn test_parse_cast_function_to_text_with_length() { + let select = snowflake().verified_only_select("SELECT CAST(_ID AS TEXT(42)) FROM USER_DETAILS"); + match expr_from_projection(only(&select.projection)) { + Expr::Cast { + kind: CastKind::Cast, + data_type, + .. + } => { + assert_eq!( + *data_type, + DataType::Custom( + ObjectName::from(vec![Ident::new("TEXT")]), + vec!["42".to_string()], + ) + ); + } + _ => unreachable!(), + } +} From 90a4ac6efd021f279cf4fcad8f102977f4cd8165 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Mon, 6 Apr 2026 20:35:18 +0200 Subject: [PATCH 2/3] Handle TEXT type modifiers across dialects --- src/parser/mod.rs | 41 +++++++++++++++++++++++------------- tests/sqlparser_snowflake.rs | 23 +------------------- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 85e1f43ca..65ac56b04 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12170,20 +12170,13 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; Ok(DataType::FixedString(character_length)) } - Keyword::TEXT => { - if dialect_is!(dialect is SnowflakeDialect) { - if let Some(modifiers) = self.parse_optional_type_modifiers()? { - Ok(DataType::Custom( - ObjectName::from(vec![Ident::new("TEXT")]), - modifiers, - )) - } else { - Ok(DataType::Text) - } - } else { - Ok(DataType::Text) - } - } + Keyword::TEXT => match self.parse_optional_type_modifiers()? { + Some(modifiers) => Ok(DataType::Custom( + ObjectName::from(vec![Ident::new("TEXT")]), + modifiers, + )), + None => Ok(DataType::Text), + }, Keyword::TINYTEXT => Ok(DataType::TinyText), Keyword::MEDIUMTEXT => Ok(DataType::MediumText), Keyword::LONGTEXT => Ok(DataType::LongText), @@ -20213,7 +20206,7 @@ mod tests { use crate::ast::{ CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo, }; - use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect}; + use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect, SnowflakeDialect}; use crate::test_utils::TestedDialects; macro_rules! test_parse_data_type { @@ -20416,6 +20409,24 @@ mod tests { ); } + #[test] + fn test_parse_text_with_length_as_custom_type() { + let dialect = TestedDialects::new(vec![ + Box::new(GenericDialect {}), + Box::new(PostgreSqlDialect {}), + Box::new(SnowflakeDialect {}), + ]); + + test_parse_data_type!( + dialect, + "TEXT(16777216)", + DataType::Custom( + ObjectName::from(vec!["TEXT".into()]), + vec!["16777216".to_string()] + ) + ); + } + #[test] fn test_ansii_exact_numeric_types() { // Exact numeric types: diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 96f61cd64..f6464fea2 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -5012,7 +5012,7 @@ fn test_select_dollar_column_from_stage() { } #[test] -fn test_parse_pg_style_cast_to_text_with_length() { +fn test_parse_cast_to_text_with_length() { let select = snowflake().verified_only_select( "SELECT _ID::TEXT(16777216) AS _ID FROM INCARE_ANALYTICS.USER_DETAILS", ); @@ -5038,24 +5038,3 @@ fn test_parse_pg_style_cast_to_text_with_length() { _ => unreachable!(), } } - -#[test] -fn test_parse_cast_function_to_text_with_length() { - let select = snowflake().verified_only_select("SELECT CAST(_ID AS TEXT(42)) FROM USER_DETAILS"); - match expr_from_projection(only(&select.projection)) { - Expr::Cast { - kind: CastKind::Cast, - data_type, - .. - } => { - assert_eq!( - *data_type, - DataType::Custom( - ObjectName::from(vec![Ident::new("TEXT")]), - vec!["42".to_string()], - ) - ); - } - _ => unreachable!(), - } -} From c6b2605e1b2e5335ec4fe3dcfcf8082e16421eb6 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Mon, 6 Apr 2026 20:42:31 +0200 Subject: [PATCH 3/3] Remove Snowflake TEXT cast regression test --- tests/sqlparser_snowflake.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index f6464fea2..790bf1515 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -5010,31 +5010,3 @@ fn test_select_dollar_column_from_stage() { // With table function args, without alias snowflake().verified_stmt("SELECT $1, $2 FROM @mystage1(file_format => 'myformat')"); } - -#[test] -fn test_parse_cast_to_text_with_length() { - let select = snowflake().verified_only_select( - "SELECT _ID::TEXT(16777216) AS _ID FROM INCARE_ANALYTICS.USER_DETAILS", - ); - match only(&select.projection) { - SelectItem::ExprWithAlias { - expr: - Expr::Cast { - kind: CastKind::DoubleColon, - data_type, - .. - }, - alias, - } => { - assert_eq!(alias.value, "_ID"); - assert_eq!( - *data_type, - DataType::Custom( - ObjectName::from(vec![Ident::new("TEXT")]), - vec!["16777216".to_string()], - ) - ); - } - _ => unreachable!(), - } -}