Skip to content

Commit c8d35a3

Browse files
TylerBrinksayman-sigma
authored andcommitted
Add support for MS Varbinary(MAX) (apache#1714) (apache#1715)
1 parent e011dff commit c8d35a3

File tree

5 files changed

+116
-9
lines changed

5 files changed

+116
-9
lines changed

src/ast/data_type.rs

+40-4
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub enum DataType {
8585
///
8686
/// [standard]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#binary-string-type
8787
/// [MS SQL Server]: https://learn.microsoft.com/pt-br/sql/t-sql/data-types/binary-and-varbinary-transact-sql?view=sql-server-ver16
88-
Varbinary(Option<u64>),
88+
Varbinary(Option<BinaryLength>),
8989
/// Large binary object with optional length e.g. BLOB, BLOB(1000), [standard], [Oracle]
9090
///
9191
/// [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 {
408408
}
409409
DataType::Clob(size) => format_type_with_optional_length(f, "CLOB", size, false),
410410
DataType::Binary(size) => format_type_with_optional_length(f, "BINARY", size, false),
411-
DataType::Varbinary(size) => {
412-
format_type_with_optional_length(f, "VARBINARY", size, false)
413-
}
411+
DataType::Varbinary(size) => format_varbinary_type(f, "VARBINARY", size),
414412
DataType::Blob(size) => format_type_with_optional_length(f, "BLOB", size, false),
415413
DataType::TinyBlob => write!(f, "TINYBLOB"),
416414
DataType::MediumBlob => write!(f, "MEDIUMBLOB"),
@@ -673,6 +671,18 @@ fn format_character_string_type(
673671
Ok(())
674672
}
675673

674+
fn format_varbinary_type(
675+
f: &mut fmt::Formatter,
676+
sql_type: &str,
677+
size: &Option<BinaryLength>,
678+
) -> fmt::Result {
679+
write!(f, "{sql_type}")?;
680+
if let Some(size) = size {
681+
write!(f, "({size})")?;
682+
}
683+
Ok(())
684+
}
685+
676686
fn format_datetime_precision_and_tz(
677687
f: &mut fmt::Formatter,
678688
sql_type: &'static str,
@@ -862,6 +872,32 @@ impl fmt::Display for CharLengthUnits {
862872
}
863873
}
864874

875+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
876+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
877+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
878+
pub enum BinaryLength {
879+
IntegerLength {
880+
/// Default (if VARYING)
881+
length: u64,
882+
},
883+
/// VARBINARY(MAX) used in T-SQL (Microsoft SQL Server)
884+
Max,
885+
}
886+
887+
impl fmt::Display for BinaryLength {
888+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
889+
match self {
890+
BinaryLength::IntegerLength { length } => {
891+
write!(f, "{}", length)?;
892+
}
893+
BinaryLength::Max => {
894+
write!(f, "MAX")?;
895+
}
896+
}
897+
Ok(())
898+
}
899+
}
900+
865901
/// Represents the data type of the elements in an array (if any) as well as
866902
/// the syntax used to declare the array.
867903
///

src/ast/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ use sqlparser_derive::{Visit, VisitMut};
4040
use crate::tokenizer::Span;
4141

4242
pub use self::data_type::{
43-
ArrayElemTypeDef, CharLengthUnits, CharacterLength, DataType, EnumMember, ExactNumberInfo,
44-
StructBracketKind, TimezoneInfo,
43+
ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember,
44+
ExactNumberInfo, StructBracketKind, TimezoneInfo,
4545
};
4646
pub use self::dcl::{
4747
AlterRoleOperation, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,

src/parser/mod.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -8776,7 +8776,7 @@ impl<'a> Parser<'a> {
87768776
}
87778777
Keyword::CLOB => Ok(DataType::Clob(self.parse_optional_precision()?)),
87788778
Keyword::BINARY => Ok(DataType::Binary(self.parse_optional_precision()?)),
8779-
Keyword::VARBINARY => Ok(DataType::Varbinary(self.parse_optional_precision()?)),
8779+
Keyword::VARBINARY => Ok(DataType::Varbinary(self.parse_optional_binary_length()?)),
87808780
Keyword::BLOB => Ok(DataType::Blob(self.parse_optional_precision()?)),
87818781
Keyword::TINYBLOB => Ok(DataType::TinyBlob),
87828782
Keyword::MEDIUMBLOB => Ok(DataType::MediumBlob),
@@ -9660,6 +9660,16 @@ impl<'a> Parser<'a> {
96609660
}
96619661
}
96629662

9663+
pub fn parse_optional_binary_length(&mut self) -> Result<Option<BinaryLength>, ParserError> {
9664+
if self.consume_token(&Token::LParen) {
9665+
let binary_length = self.parse_binary_length()?;
9666+
self.expect_token(&Token::RParen)?;
9667+
Ok(Some(binary_length))
9668+
} else {
9669+
Ok(None)
9670+
}
9671+
}
9672+
96639673
pub fn parse_character_length(&mut self) -> Result<CharacterLength, ParserError> {
96649674
if self.parse_keyword(Keyword::MAX) {
96659675
return Ok(CharacterLength::Max);
@@ -9675,6 +9685,14 @@ impl<'a> Parser<'a> {
96759685
Ok(CharacterLength::IntegerLength { length, unit })
96769686
}
96779687

9688+
pub fn parse_binary_length(&mut self) -> Result<BinaryLength, ParserError> {
9689+
if self.parse_keyword(Keyword::MAX) {
9690+
return Ok(BinaryLength::Max);
9691+
}
9692+
let length = self.parse_literal_uint()?;
9693+
Ok(BinaryLength::IntegerLength { length })
9694+
}
9695+
96789696
pub fn parse_optional_precision_scale(
96799697
&mut self,
96809698
) -> Result<(Option<u64>, Option<u64>), ParserError> {

tests/sqlparser_common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2634,7 +2634,7 @@ fn parse_cast() {
26342634
&Expr::Cast {
26352635
kind: CastKind::Cast,
26362636
expr: Box::new(Expr::Identifier(Ident::new("id"))),
2637-
data_type: DataType::Varbinary(Some(50)),
2637+
data_type: DataType::Varbinary(Some(BinaryLength::IntegerLength { length: 50 })),
26382638
format: None,
26392639
},
26402640
expr_from_projection(only(&select.projection))

tests/sqlparser_mssql.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use helpers::attached_token::AttachedToken;
2626
use sqlparser::tokenizer::Span;
2727
use test_utils::*;
2828

29-
use sqlparser::ast::DataType::{Int, Text};
29+
use sqlparser::ast::DataType::{Int, Text, Varbinary};
3030
use sqlparser::ast::DeclareAssignment::MsSqlAssignment;
3131
use sqlparser::ast::Value::SingleQuotedString;
3232
use sqlparser::ast::*;
@@ -1796,6 +1796,59 @@ fn parse_mssql_set_session_value() {
17961796
ms().verified_stmt("SET ANSI_NULLS, ANSI_PADDING ON");
17971797
}
17981798

1799+
#[test]
1800+
fn parse_mssql_varbinary_max_length() {
1801+
let sql = "CREATE TABLE example (var_binary_col VARBINARY(MAX))";
1802+
1803+
match ms_and_generic().verified_stmt(sql) {
1804+
Statement::CreateTable(CreateTable { name, columns, .. }) => {
1805+
assert_eq!(
1806+
name,
1807+
ObjectName::from(vec![Ident {
1808+
value: "example".to_string(),
1809+
quote_style: None,
1810+
span: Span::empty(),
1811+
}])
1812+
);
1813+
assert_eq!(
1814+
columns,
1815+
vec![ColumnDef {
1816+
name: Ident::new("var_binary_col"),
1817+
data_type: Varbinary(Some(BinaryLength::Max)),
1818+
collation: None,
1819+
options: vec![]
1820+
},],
1821+
);
1822+
}
1823+
_ => unreachable!(),
1824+
}
1825+
1826+
let sql = "CREATE TABLE example (var_binary_col VARBINARY(50))";
1827+
1828+
match ms_and_generic().verified_stmt(sql) {
1829+
Statement::CreateTable(CreateTable { name, columns, .. }) => {
1830+
assert_eq!(
1831+
name,
1832+
ObjectName::from(vec![Ident {
1833+
value: "example".to_string(),
1834+
quote_style: None,
1835+
span: Span::empty(),
1836+
}])
1837+
);
1838+
assert_eq!(
1839+
columns,
1840+
vec![ColumnDef {
1841+
name: Ident::new("var_binary_col"),
1842+
data_type: Varbinary(Some(BinaryLength::IntegerLength { length: 50 })),
1843+
collation: None,
1844+
options: vec![]
1845+
},],
1846+
);
1847+
}
1848+
_ => unreachable!(),
1849+
}
1850+
}
1851+
17991852
fn ms() -> TestedDialects {
18001853
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
18011854
}

0 commit comments

Comments
 (0)