This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new a5bbb5e8 Add support for MS Varbinary(MAX) (#1714) (#1715)
a5bbb5e8 is described below

commit a5bbb5e8ac52c98fe0f3e85e3b25c24048deade9
Author: Tyler Brinks <[email protected]>
AuthorDate: Thu Feb 13 03:40:35 2025 -0700

    Add support for MS Varbinary(MAX) (#1714) (#1715)
---
 src/ast/data_type.rs      | 44 +++++++++++++++++++++++++++++++++----
 src/ast/mod.rs            |  4 ++--
 src/parser/mod.rs         | 20 ++++++++++++++++-
 tests/sqlparser_common.rs |  2 +-
 tests/sqlparser_mssql.rs  | 55 ++++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 116 insertions(+), 9 deletions(-)

diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs
index 4f1f21e0..bd46f8bd 100644
--- a/src/ast/data_type.rs
+++ b/src/ast/data_type.rs
@@ -85,7 +85,7 @@ pub enum DataType {
     ///
     /// [standard]: 
https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#binary-string-type
     /// [MS SQL Server]: 
https://learn.microsoft.com/pt-br/sql/t-sql/data-types/binary-and-varbinary-transact-sql?view=sql-server-ver16
-    Varbinary(Option<u64>),
+    Varbinary(Option<BinaryLength>),
     /// Large binary object with optional length e.g. BLOB, BLOB(1000), 
[standard], [Oracle]
     ///
     /// [standard]: 
https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#binary-large-object-string-type
@@ -408,9 +408,7 @@ impl fmt::Display for DataType {
             }
             DataType::Clob(size) => format_type_with_optional_length(f, 
"CLOB", size, false),
             DataType::Binary(size) => format_type_with_optional_length(f, 
"BINARY", size, false),
-            DataType::Varbinary(size) => {
-                format_type_with_optional_length(f, "VARBINARY", size, false)
-            }
+            DataType::Varbinary(size) => format_varbinary_type(f, "VARBINARY", 
size),
             DataType::Blob(size) => format_type_with_optional_length(f, 
"BLOB", size, false),
             DataType::TinyBlob => write!(f, "TINYBLOB"),
             DataType::MediumBlob => write!(f, "MEDIUMBLOB"),
@@ -673,6 +671,18 @@ fn format_character_string_type(
     Ok(())
 }
 
+fn format_varbinary_type(
+    f: &mut fmt::Formatter,
+    sql_type: &str,
+    size: &Option<BinaryLength>,
+) -> fmt::Result {
+    write!(f, "{sql_type}")?;
+    if let Some(size) = size {
+        write!(f, "({size})")?;
+    }
+    Ok(())
+}
+
 fn format_datetime_precision_and_tz(
     f: &mut fmt::Formatter,
     sql_type: &'static str,
@@ -862,6 +872,32 @@ impl fmt::Display for CharLengthUnits {
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum BinaryLength {
+    IntegerLength {
+        /// Default (if VARYING)
+        length: u64,
+    },
+    /// VARBINARY(MAX) used in T-SQL (Microsoft SQL Server)
+    Max,
+}
+
+impl fmt::Display for BinaryLength {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            BinaryLength::IntegerLength { length } => {
+                write!(f, "{}", length)?;
+            }
+            BinaryLength::Max => {
+                write!(f, "MAX")?;
+            }
+        }
+        Ok(())
+    }
+}
+
 /// Represents the data type of the elements in an array (if any) as well as
 /// the syntax used to declare the array.
 ///
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index f693ab7d..5835447d 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -40,8 +40,8 @@ use sqlparser_derive::{Visit, VisitMut};
 use crate::tokenizer::Span;
 
 pub use self::data_type::{
-    ArrayElemTypeDef, CharLengthUnits, CharacterLength, DataType, EnumMember, 
ExactNumberInfo,
-    StructBracketKind, TimezoneInfo,
+    ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, 
DataType, EnumMember,
+    ExactNumberInfo, StructBracketKind, TimezoneInfo,
 };
 pub use self::dcl::{
     AlterRoleOperation, ResetConfig, RoleOption, SecondaryRoles, 
SetConfigValue, Use,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index c4f65a1a..6d3290d2 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -8766,7 +8766,7 @@ impl<'a> Parser<'a> {
                 }
                 Keyword::CLOB => 
Ok(DataType::Clob(self.parse_optional_precision()?)),
                 Keyword::BINARY => 
Ok(DataType::Binary(self.parse_optional_precision()?)),
-                Keyword::VARBINARY => 
Ok(DataType::Varbinary(self.parse_optional_precision()?)),
+                Keyword::VARBINARY => 
Ok(DataType::Varbinary(self.parse_optional_binary_length()?)),
                 Keyword::BLOB => 
Ok(DataType::Blob(self.parse_optional_precision()?)),
                 Keyword::TINYBLOB => Ok(DataType::TinyBlob),
                 Keyword::MEDIUMBLOB => Ok(DataType::MediumBlob),
@@ -9650,6 +9650,16 @@ impl<'a> Parser<'a> {
         }
     }
 
+    pub fn parse_optional_binary_length(&mut self) -> 
Result<Option<BinaryLength>, ParserError> {
+        if self.consume_token(&Token::LParen) {
+            let binary_length = self.parse_binary_length()?;
+            self.expect_token(&Token::RParen)?;
+            Ok(Some(binary_length))
+        } else {
+            Ok(None)
+        }
+    }
+
     pub fn parse_character_length(&mut self) -> Result<CharacterLength, 
ParserError> {
         if self.parse_keyword(Keyword::MAX) {
             return Ok(CharacterLength::Max);
@@ -9665,6 +9675,14 @@ impl<'a> Parser<'a> {
         Ok(CharacterLength::IntegerLength { length, unit })
     }
 
+    pub fn parse_binary_length(&mut self) -> Result<BinaryLength, ParserError> 
{
+        if self.parse_keyword(Keyword::MAX) {
+            return Ok(BinaryLength::Max);
+        }
+        let length = self.parse_literal_uint()?;
+        Ok(BinaryLength::IntegerLength { length })
+    }
+
     pub fn parse_optional_precision_scale(
         &mut self,
     ) -> Result<(Option<u64>, Option<u64>), ParserError> {
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index f8c0a26b..aef4d0d7 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -2633,7 +2633,7 @@ fn parse_cast() {
         &Expr::Cast {
             kind: CastKind::Cast,
             expr: Box::new(Expr::Identifier(Ident::new("id"))),
-            data_type: DataType::Varbinary(Some(50)),
+            data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { 
length: 50 })),
             format: None,
         },
         expr_from_projection(only(&select.projection))
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index e6a12b9c..6865bdd4 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -26,7 +26,7 @@ use helpers::attached_token::AttachedToken;
 use sqlparser::tokenizer::Span;
 use test_utils::*;
 
-use sqlparser::ast::DataType::{Int, Text};
+use sqlparser::ast::DataType::{Int, Text, Varbinary};
 use sqlparser::ast::DeclareAssignment::MsSqlAssignment;
 use sqlparser::ast::Value::SingleQuotedString;
 use sqlparser::ast::*;
@@ -1796,6 +1796,59 @@ fn parse_mssql_set_session_value() {
     ms().verified_stmt("SET ANSI_NULLS, ANSI_PADDING ON");
 }
 
+#[test]
+fn parse_mssql_varbinary_max_length() {
+    let sql = "CREATE TABLE example (var_binary_col VARBINARY(MAX))";
+
+    match ms_and_generic().verified_stmt(sql) {
+        Statement::CreateTable(CreateTable { name, columns, .. }) => {
+            assert_eq!(
+                name,
+                ObjectName::from(vec![Ident {
+                    value: "example".to_string(),
+                    quote_style: None,
+                    span: Span::empty(),
+                }])
+            );
+            assert_eq!(
+                columns,
+                vec![ColumnDef {
+                    name: Ident::new("var_binary_col"),
+                    data_type: Varbinary(Some(BinaryLength::Max)),
+                    collation: None,
+                    options: vec![]
+                },],
+            );
+        }
+        _ => unreachable!(),
+    }
+
+    let sql = "CREATE TABLE example (var_binary_col VARBINARY(50))";
+
+    match ms_and_generic().verified_stmt(sql) {
+        Statement::CreateTable(CreateTable { name, columns, .. }) => {
+            assert_eq!(
+                name,
+                ObjectName::from(vec![Ident {
+                    value: "example".to_string(),
+                    quote_style: None,
+                    span: Span::empty(),
+                }])
+            );
+            assert_eq!(
+                columns,
+                vec![ColumnDef {
+                    name: Ident::new("var_binary_col"),
+                    data_type: Varbinary(Some(BinaryLength::IntegerLength { 
length: 50 })),
+                    collation: None,
+                    options: vec![]
+                },],
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
 fn ms() -> TestedDialects {
     TestedDialects::new(vec![Box::new(MsSqlDialect {})])
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to