mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-11 08:26:18 +00:00
Modifier support for Custom Datatype (#680)
* feat: add support for custom types with argument * refactor: add support for number and string as type arguments * fix: ignore CustomWithArgs when parsing TypedString * refactor: merge CustomWithArgs into Custom * refactor: rename arguments to modifiers
This commit is contained in:
parent
2c266a437c
commit
914810d366
2 changed files with 69 additions and 4 deletions
|
@ -129,7 +129,7 @@ pub enum DataType {
|
||||||
/// Bytea
|
/// Bytea
|
||||||
Bytea,
|
Bytea,
|
||||||
/// Custom type such as enums
|
/// Custom type such as enums
|
||||||
Custom(ObjectName),
|
Custom(ObjectName, Vec<String>),
|
||||||
/// Arrays
|
/// Arrays
|
||||||
Array(Box<DataType>),
|
Array(Box<DataType>),
|
||||||
/// Enums
|
/// Enums
|
||||||
|
@ -217,7 +217,13 @@ impl fmt::Display for DataType {
|
||||||
DataType::String => write!(f, "STRING"),
|
DataType::String => write!(f, "STRING"),
|
||||||
DataType::Bytea => write!(f, "BYTEA"),
|
DataType::Bytea => write!(f, "BYTEA"),
|
||||||
DataType::Array(ty) => write!(f, "{}[]", ty),
|
DataType::Array(ty) => write!(f, "{}[]", ty),
|
||||||
DataType::Custom(ty) => write!(f, "{}", ty),
|
DataType::Custom(ty, modifiers) => {
|
||||||
|
if modifiers.is_empty() {
|
||||||
|
write!(f, "{}", ty)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}({})", ty, modifiers.join(", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
DataType::Enum(vals) => {
|
DataType::Enum(vals) => {
|
||||||
write!(f, "ENUM(")?;
|
write!(f, "ENUM(")?;
|
||||||
for (i, v) in vals.iter().enumerate() {
|
for (i, v) in vals.iter().enumerate() {
|
||||||
|
|
|
@ -3661,7 +3661,11 @@ impl<'a> Parser<'a> {
|
||||||
_ => {
|
_ => {
|
||||||
self.prev_token();
|
self.prev_token();
|
||||||
let type_name = self.parse_object_name()?;
|
let type_name = self.parse_object_name()?;
|
||||||
Ok(DataType::Custom(type_name))
|
if let Some(modifiers) = self.parse_optional_type_modifiers()? {
|
||||||
|
Ok(DataType::Custom(type_name, modifiers))
|
||||||
|
} else {
|
||||||
|
Ok(DataType::Custom(type_name, vec![]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unexpected => self.expected("a data type name", unexpected),
|
unexpected => self.expected("a data type name", unexpected),
|
||||||
|
@ -3907,6 +3911,31 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_optional_type_modifiers(&mut self) -> Result<Option<Vec<String>>, ParserError> {
|
||||||
|
if self.consume_token(&Token::LParen) {
|
||||||
|
let mut modifiers = Vec::new();
|
||||||
|
loop {
|
||||||
|
match self.next_token() {
|
||||||
|
Token::Word(w) => modifiers.push(w.to_string()),
|
||||||
|
Token::Number(n, _) => modifiers.push(n),
|
||||||
|
Token::SingleQuotedString(s) => modifiers.push(s),
|
||||||
|
|
||||||
|
Token::Comma => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Token::RParen => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unexpected => self.expected("type modifiers", unexpected)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(modifiers))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
|
||||||
self.expect_keyword(Keyword::FROM)?;
|
self.expect_keyword(Keyword::FROM)?;
|
||||||
let table_name = self.parse_table_factor()?;
|
let table_name = self.parse_table_factor()?;
|
||||||
|
@ -5540,7 +5569,7 @@ mod tests {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_parse_data_type {
|
mod test_parse_data_type {
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, TimezoneInfo,
|
CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo,
|
||||||
};
|
};
|
||||||
use crate::dialect::{AnsiDialect, GenericDialect};
|
use crate::dialect::{AnsiDialect, GenericDialect};
|
||||||
use crate::test_utils::TestedDialects;
|
use crate::test_utils::TestedDialects;
|
||||||
|
@ -5717,6 +5746,36 @@ mod tests {
|
||||||
test_parse_data_type!(dialect, "CLOB(20)", DataType::Clob(Some(20)));
|
test_parse_data_type!(dialect, "CLOB(20)", DataType::Clob(Some(20)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_custom_types() {
|
||||||
|
let dialect = TestedDialects {
|
||||||
|
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
|
||||||
|
};
|
||||||
|
test_parse_data_type!(
|
||||||
|
dialect,
|
||||||
|
"GEOMETRY",
|
||||||
|
DataType::Custom(ObjectName(vec!["GEOMETRY".into()]), vec![])
|
||||||
|
);
|
||||||
|
|
||||||
|
test_parse_data_type!(
|
||||||
|
dialect,
|
||||||
|
"GEOMETRY(POINT)",
|
||||||
|
DataType::Custom(
|
||||||
|
ObjectName(vec!["GEOMETRY".into()]),
|
||||||
|
vec!["POINT".to_string()]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
test_parse_data_type!(
|
||||||
|
dialect,
|
||||||
|
"GEOMETRY(POINT, 4326)",
|
||||||
|
DataType::Custom(
|
||||||
|
ObjectName(vec!["GEOMETRY".into()]),
|
||||||
|
vec!["POINT".to_string(), "4326".to_string()]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ansii_exact_numeric_types() {
|
fn test_ansii_exact_numeric_types() {
|
||||||
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
|
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue