Enum to handle exact number precisios (#654)

This commit is contained in:
AugustoFKL 2022-10-08 06:59:11 -03:00 committed by GitHub
parent a3194ddd52
commit a9939b0a4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 12 deletions

View file

@ -60,7 +60,7 @@ pub enum DataType {
/// [Oracle]: https://docs.oracle.com/javadb/10.8.3.0/ref/rrefblob.html
Blob(Option<u64>),
/// Decimal type with optional precision and scale e.g. DECIMAL(10,2)
Decimal(Option<u64>, Option<u64>),
Decimal(ExactNumberInfo),
/// Floating point with optional precision e.g. FLOAT(8)
Float(Option<u64>),
/// Tiny integer with optional display width e.g. TINYINT or TINYINT(3)
@ -154,12 +154,8 @@ impl fmt::Display for DataType {
format_type_with_optional_length(f, "VARBINARY", size, false)
}
DataType::Blob(size) => format_type_with_optional_length(f, "BLOB", size, false),
DataType::Decimal(precision, scale) => {
if let Some(scale) = scale {
write!(f, "NUMERIC({},{})", precision.unwrap(), scale)
} else {
format_type_with_optional_length(f, "NUMERIC", precision, false)
}
DataType::Decimal(info) => {
write!(f, "NUMERIC{}", info)
}
DataType::Float(size) => format_type_with_optional_length(f, "FLOAT", size, false),
DataType::TinyInt(zerofill) => {
@ -297,3 +293,34 @@ impl fmt::Display for TimezoneInfo {
}
}
}
/// Additional information for `NUMERIC`, `DECIMAL`, and `DEC` data types
/// following the 2016 [standard].
///
/// [standard]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ExactNumberInfo {
/// No additional information e.g. `DECIMAL`
None,
/// Only precision information e.g. `DECIMAL(10)`
Precision(u64),
/// Precision and scale information e.g. `DECIMAL(10,2)`
PrecisionAndScale(u64, u64),
}
impl fmt::Display for ExactNumberInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExactNumberInfo::None => {
write!(f, "")
}
ExactNumberInfo::Precision(p) => {
write!(f, "({p})")
}
ExactNumberInfo::PrecisionAndScale(p, s) => {
write!(f, "({p},{s})")
}
}
}
}

View file

@ -30,6 +30,7 @@ use core::fmt;
use serde::{Deserialize, Serialize};
pub use self::data_type::DataType;
pub use self::data_type::ExactNumberInfo;
pub use self::data_type::TimezoneInfo;
pub use self::ddl::{
AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef,

View file

@ -3481,10 +3481,9 @@ impl<'a> Parser<'a> {
Keyword::STRING => Ok(DataType::String),
Keyword::TEXT => Ok(DataType::Text),
Keyword::BYTEA => Ok(DataType::Bytea),
Keyword::NUMERIC | Keyword::DECIMAL | Keyword::DEC => {
let (precision, scale) = self.parse_optional_precision_scale()?;
Ok(DataType::Decimal(precision, scale))
}
Keyword::NUMERIC | Keyword::DECIMAL | Keyword::DEC => Ok(DataType::Decimal(
self.parse_exact_number_optional_precision_scale()?,
)),
Keyword::ENUM => Ok(DataType::Enum(self.parse_string_values()?)),
Keyword::SET => Ok(DataType::Set(self.parse_string_values()?)),
Keyword::ARRAY => {
@ -3698,6 +3697,28 @@ impl<'a> Parser<'a> {
}
}
pub fn parse_exact_number_optional_precision_scale(
&mut self,
) -> Result<ExactNumberInfo, ParserError> {
if self.consume_token(&Token::LParen) {
let precision = self.parse_literal_uint()?;
let scale = if self.consume_token(&Token::Comma) {
Some(self.parse_literal_uint()?)
} else {
None
};
self.expect_token(&Token::RParen)?;
match scale {
None => Ok(ExactNumberInfo::Precision(precision)),
Some(scale) => Ok(ExactNumberInfo::PrecisionAndScale(precision, scale)),
}
} else {
Ok(ExactNumberInfo::None)
}
}
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword(Keyword::FROM)?;
let table_name = self.parse_table_factor()?;
@ -5311,7 +5332,7 @@ mod tests {
#[cfg(test)]
mod test_parse_data_type {
use crate::ast::{DataType, TimezoneInfo};
use crate::ast::{DataType, ExactNumberInfo, TimezoneInfo};
use crate::dialect::{AnsiDialect, GenericDialect};
use crate::test_utils::TestedDialects;
@ -5351,6 +5372,28 @@ mod tests {
test_parse_data_type!(dialect, "VARCHAR(20)", DataType::Varchar(Some(20)));
}
#[test]
fn test_ansii_exact_numeric_types() {
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
let dialect = TestedDialects {
dialects: vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})],
};
test_parse_data_type!(dialect, "NUMERIC", DataType::Decimal(ExactNumberInfo::None));
test_parse_data_type!(
dialect,
"NUMERIC(2)",
DataType::Decimal(ExactNumberInfo::Precision(2))
);
test_parse_data_type!(
dialect,
"NUMERIC(2,10)",
DataType::Decimal(ExactNumberInfo::PrecisionAndScale(2, 10))
);
}
#[test]
fn test_ansii_datetime_types() {
// Datetime types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#datetime-type>