Skip to content

Commit b2f9773

Browse files
IndexSeekiffyio
andauthored
Postgres: enhance NUMERIC/DECIMAL parsing to support negative scale (#1990)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 356308b commit b2f9773

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

src/ast/data_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ pub enum ExactNumberInfo {
962962
/// Only precision information, e.g. `DECIMAL(10)`
963963
Precision(u64),
964964
/// Precision and scale information, e.g. `DECIMAL(10,2)`
965-
PrecisionAndScale(u64, u64),
965+
PrecisionAndScale(u64, i64),
966966
}
967967

968968
impl fmt::Display for ExactNumberInfo {

src/parser/mod.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11256,7 +11256,7 @@ impl<'a> Parser<'a> {
1125611256
if self.consume_token(&Token::LParen) {
1125711257
let precision = self.parse_literal_uint()?;
1125811258
let scale = if self.consume_token(&Token::Comma) {
11259-
Some(self.parse_literal_uint()?)
11259+
Some(self.parse_signed_integer()?)
1126011260
} else {
1126111261
None
1126211262
};
@@ -11272,6 +11272,27 @@ impl<'a> Parser<'a> {
1127211272
}
1127311273
}
1127411274

11275+
/// Parse an optionally signed integer literal.
11276+
fn parse_signed_integer(&mut self) -> Result<i64, ParserError> {
11277+
let is_negative = self.consume_token(&Token::Minus);
11278+
11279+
if !is_negative {
11280+
let _ = self.consume_token(&Token::Plus);
11281+
}
11282+
11283+
let current_token = self.peek_token_ref();
11284+
match &current_token.token {
11285+
Token::Number(s, _) => {
11286+
let s = s.clone();
11287+
let span_start = current_token.span.start;
11288+
self.advance_token();
11289+
let value = Self::parse::<i64>(s, span_start)?;
11290+
Ok(if is_negative { -value } else { value })
11291+
}
11292+
_ => self.expected_ref("number", current_token),
11293+
}
11294+
}
11295+
1127511296
pub fn parse_optional_type_modifiers(&mut self) -> Result<Option<Vec<String>>, ParserError> {
1127611297
if self.consume_token(&Token::LParen) {
1127711298
let mut modifiers = Vec::new();
@@ -17118,7 +17139,7 @@ mod tests {
1711817139
use crate::ast::{
1711917140
CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo,
1712017141
};
17121-
use crate::dialect::{AnsiDialect, GenericDialect};
17142+
use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect};
1712217143
use crate::test_utils::TestedDialects;
1712317144

1712417145
macro_rules! test_parse_data_type {
@@ -17324,8 +17345,11 @@ mod tests {
1732417345
#[test]
1732517346
fn test_ansii_exact_numeric_types() {
1732617347
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
17327-
let dialect =
17328-
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]);
17348+
let dialect = TestedDialects::new(vec![
17349+
Box::new(GenericDialect {}),
17350+
Box::new(AnsiDialect {}),
17351+
Box::new(PostgreSqlDialect {}),
17352+
]);
1732917353

1733017354
test_parse_data_type!(dialect, "NUMERIC", DataType::Numeric(ExactNumberInfo::None));
1733117355

@@ -17368,6 +17392,53 @@ mod tests {
1736817392
"DEC(2,10)",
1736917393
DataType::Dec(ExactNumberInfo::PrecisionAndScale(2, 10))
1737017394
);
17395+
17396+
// Test negative scale values.
17397+
test_parse_data_type!(
17398+
dialect,
17399+
"NUMERIC(10,-2)",
17400+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -2))
17401+
);
17402+
17403+
test_parse_data_type!(
17404+
dialect,
17405+
"DECIMAL(1000,-10)",
17406+
DataType::Decimal(ExactNumberInfo::PrecisionAndScale(1000, -10))
17407+
);
17408+
17409+
test_parse_data_type!(
17410+
dialect,
17411+
"DEC(5,-1000)",
17412+
DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -1000))
17413+
);
17414+
17415+
test_parse_data_type!(
17416+
dialect,
17417+
"NUMERIC(10,-5)",
17418+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -5))
17419+
);
17420+
17421+
test_parse_data_type!(
17422+
dialect,
17423+
"DECIMAL(20,-10)",
17424+
DataType::Decimal(ExactNumberInfo::PrecisionAndScale(20, -10))
17425+
);
17426+
17427+
test_parse_data_type!(
17428+
dialect,
17429+
"DEC(5,-2)",
17430+
DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -2))
17431+
);
17432+
17433+
dialect.run_parser_method("NUMERIC(10,+5)", |parser| {
17434+
let data_type = parser.parse_data_type().unwrap();
17435+
assert_eq!(
17436+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, 5)),
17437+
data_type
17438+
);
17439+
// Note: Explicit '+' sign is not preserved in output, which is correct
17440+
assert_eq!("NUMERIC(10,5)", data_type.to_string());
17441+
});
1737117442
}
1737217443

1737317444
#[test]

0 commit comments

Comments
 (0)