Skip to content

Commit 655b87b

Browse files
IndexSeekiffyio
authored andcommitted
Postgres: enhance NUMERIC/DECIMAL parsing to support negative scale (apache#1990)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent 88888e9 commit 655b87b

2 files changed

Lines changed: 76 additions & 5 deletions

File tree

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
@@ -11287,7 +11287,7 @@ impl<'a> Parser<'a> {
1128711287
if self.consume_token(&Token::LParen) {
1128811288
let precision = self.parse_literal_uint()?;
1128911289
let scale = if self.consume_token(&Token::Comma) {
11290-
Some(self.parse_literal_uint()?)
11290+
Some(self.parse_signed_integer()?)
1129111291
} else {
1129211292
None
1129311293
};
@@ -11303,6 +11303,27 @@ impl<'a> Parser<'a> {
1130311303
}
1130411304
}
1130511305

11306+
/// Parse an optionally signed integer literal.
11307+
fn parse_signed_integer(&mut self) -> Result<i64, ParserError> {
11308+
let is_negative = self.consume_token(&Token::Minus);
11309+
11310+
if !is_negative {
11311+
let _ = self.consume_token(&Token::Plus);
11312+
}
11313+
11314+
let current_token = self.peek_token_ref();
11315+
match &current_token.token {
11316+
Token::Number(s, _) => {
11317+
let s = s.clone();
11318+
let span_start = current_token.span.start;
11319+
self.advance_token();
11320+
let value = Self::parse::<i64>(s, span_start)?;
11321+
Ok(if is_negative { -value } else { value })
11322+
}
11323+
_ => self.expected_ref("number", current_token),
11324+
}
11325+
}
11326+
1130611327
pub fn parse_optional_type_modifiers(&mut self) -> Result<Option<Vec<String>>, ParserError> {
1130711328
if self.consume_token(&Token::LParen) {
1130811329
let mut modifiers = Vec::new();
@@ -17150,7 +17171,7 @@ mod tests {
1715017171
use crate::ast::{
1715117172
CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo,
1715217173
};
17153-
use crate::dialect::{AnsiDialect, GenericDialect};
17174+
use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect};
1715417175
use crate::test_utils::TestedDialects;
1715517176

1715617177
macro_rules! test_parse_data_type {
@@ -17356,8 +17377,11 @@ mod tests {
1735617377
#[test]
1735717378
fn test_ansii_exact_numeric_types() {
1735817379
// Exact numeric types: <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#exact-numeric-type>
17359-
let dialect =
17360-
TestedDialects::new(vec![Box::new(GenericDialect {}), Box::new(AnsiDialect {})]);
17380+
let dialect = TestedDialects::new(vec![
17381+
Box::new(GenericDialect {}),
17382+
Box::new(AnsiDialect {}),
17383+
Box::new(PostgreSqlDialect {}),
17384+
]);
1736117385

1736217386
test_parse_data_type!(dialect, "NUMERIC", DataType::Numeric(ExactNumberInfo::None));
1736317387

@@ -17400,6 +17424,53 @@ mod tests {
1740017424
"DEC(2,10)",
1740117425
DataType::Dec(ExactNumberInfo::PrecisionAndScale(2, 10))
1740217426
);
17427+
17428+
// Test negative scale values.
17429+
test_parse_data_type!(
17430+
dialect,
17431+
"NUMERIC(10,-2)",
17432+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -2))
17433+
);
17434+
17435+
test_parse_data_type!(
17436+
dialect,
17437+
"DECIMAL(1000,-10)",
17438+
DataType::Decimal(ExactNumberInfo::PrecisionAndScale(1000, -10))
17439+
);
17440+
17441+
test_parse_data_type!(
17442+
dialect,
17443+
"DEC(5,-1000)",
17444+
DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -1000))
17445+
);
17446+
17447+
test_parse_data_type!(
17448+
dialect,
17449+
"NUMERIC(10,-5)",
17450+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -5))
17451+
);
17452+
17453+
test_parse_data_type!(
17454+
dialect,
17455+
"DECIMAL(20,-10)",
17456+
DataType::Decimal(ExactNumberInfo::PrecisionAndScale(20, -10))
17457+
);
17458+
17459+
test_parse_data_type!(
17460+
dialect,
17461+
"DEC(5,-2)",
17462+
DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -2))
17463+
);
17464+
17465+
dialect.run_parser_method("NUMERIC(10,+5)", |parser| {
17466+
let data_type = parser.parse_data_type().unwrap();
17467+
assert_eq!(
17468+
DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, 5)),
17469+
data_type
17470+
);
17471+
// Note: Explicit '+' sign is not preserved in output, which is correct
17472+
assert_eq!("NUMERIC(10,5)", data_type.to_string());
17473+
});
1740317474
}
1740417475

1740517476
#[test]

0 commit comments

Comments
 (0)