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]