@@ -11213,7 +11213,7 @@ impl<'a> Parser<'a> {
1121311213 if self.consume_token(&Token::LParen) {
1121411214 let precision = self.parse_literal_uint()?;
1121511215 let scale = if self.consume_token(&Token::Comma) {
11216- Some(self.parse_literal_uint ()?)
11216+ Some(self.parse_scale_value ()?)
1121711217 } else {
1121811218 None
1121911219 };
@@ -11229,6 +11229,38 @@ impl<'a> Parser<'a> {
1122911229 }
1123011230 }
1123111231
11232+ /// Parse a scale value for NUMERIC/DECIMAL data types.
11233+ ///
11234+ /// Supports positive, negative, and explicitly positive (with `+`) scale values.
11235+ /// Negative scale values are particularly useful for PostgreSQL, where they indicate
11236+ /// rounding to the left of the decimal point. For example:
11237+ /// - `NUMERIC(5, 2)` stores up to 5 digits with 2 decimal places (e.g., 123.45)
11238+ /// - `NUMERIC(5, -2)` stores up to 5 digits rounded to hundreds (e.g., 12300)
11239+ fn parse_scale_value(&mut self) -> Result<i64, ParserError> {
11240+ let next_token = self.next_token();
11241+ match next_token.token {
11242+ Token::Number(s, _) => Self::parse::<i64>(s, next_token.span.start),
11243+ Token::Minus => {
11244+ let next_token = self.next_token();
11245+ match next_token.token {
11246+ Token::Number(s, _) => {
11247+ let positive_value = Self::parse::<i64>(s, next_token.span.start)?;
11248+ Ok(-positive_value)
11249+ }
11250+ _ => self.expected("number after minus", next_token),
11251+ }
11252+ }
11253+ Token::Plus => {
11254+ let next_token = self.next_token();
11255+ match next_token.token {
11256+ Token::Number(s, _) => Self::parse::<i64>(s, next_token.span.start),
11257+ _ => self.expected("number after plus", next_token),
11258+ }
11259+ }
11260+ _ => self.expected("number", next_token),
11261+ }
11262+ }
11263+
1123211264 pub fn parse_optional_type_modifiers(&mut self) -> Result<Option<Vec<String>>, ParserError> {
1123311265 if self.consume_token(&Token::LParen) {
1123411266 let mut modifiers = Vec::new();
@@ -17069,7 +17101,7 @@ mod tests {
1706917101 use crate::ast::{
1707017102 CharLengthUnits, CharacterLength, DataType, ExactNumberInfo, ObjectName, TimezoneInfo,
1707117103 };
17072- use crate::dialect::{AnsiDialect, GenericDialect};
17104+ use crate::dialect::{AnsiDialect, GenericDialect, PostgreSqlDialect };
1707317105 use crate::test_utils::TestedDialects;
1707417106
1707517107 macro_rules! test_parse_data_type {
@@ -17319,6 +17351,75 @@ mod tests {
1731917351 "DEC(2,10)",
1732017352 DataType::Dec(ExactNumberInfo::PrecisionAndScale(2, 10))
1732117353 );
17354+
17355+ // Test negative scale values (PostgreSQL supports scale from -1000 to 1000)
17356+ test_parse_data_type!(
17357+ dialect,
17358+ "NUMERIC(10,-2)",
17359+ DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -2))
17360+ );
17361+
17362+ test_parse_data_type!(
17363+ dialect,
17364+ "DECIMAL(1000,-10)",
17365+ DataType::Decimal(ExactNumberInfo::PrecisionAndScale(1000, -10))
17366+ );
17367+
17368+ test_parse_data_type!(
17369+ dialect,
17370+ "DEC(5,-1000)",
17371+ DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -1000))
17372+ );
17373+
17374+ // Test positive scale with explicit plus sign
17375+ dialect.run_parser_method("NUMERIC(10,+5)", |parser| {
17376+ let data_type = parser.parse_data_type().unwrap();
17377+ assert_eq!(
17378+ DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, 5)),
17379+ data_type
17380+ );
17381+ // Note: Explicit '+' sign is not preserved in output, which is correct
17382+ assert_eq!("NUMERIC(10,5)", data_type.to_string());
17383+ });
17384+ }
17385+
17386+ #[test]
17387+ fn test_numeric_negative_scale() {
17388+ let dialect = TestedDialects::new(vec![
17389+ Box::new(PostgreSqlDialect {}),
17390+ Box::new(GenericDialect {}),
17391+ ]);
17392+
17393+ // Test NUMERIC with negative scale
17394+ test_parse_data_type!(
17395+ dialect,
17396+ "NUMERIC(10,-5)",
17397+ DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, -5))
17398+ );
17399+
17400+ // Test DECIMAL with negative scale
17401+ test_parse_data_type!(
17402+ dialect,
17403+ "DECIMAL(20,-10)",
17404+ DataType::Decimal(ExactNumberInfo::PrecisionAndScale(20, -10))
17405+ );
17406+
17407+ // Test DEC with negative scale
17408+ test_parse_data_type!(
17409+ dialect,
17410+ "DEC(5,-2)",
17411+ DataType::Dec(ExactNumberInfo::PrecisionAndScale(5, -2))
17412+ );
17413+
17414+ // Test with explicit positive scale (note: +5 parses as 5, so display shows NUMERIC(10,5))
17415+ dialect.run_parser_method("NUMERIC(10,+5)", |parser| {
17416+ let data_type = parser.parse_data_type().unwrap();
17417+ assert_eq!(
17418+ DataType::Numeric(ExactNumberInfo::PrecisionAndScale(10, 5)),
17419+ data_type
17420+ );
17421+ assert_eq!("NUMERIC(10,5)", data_type.to_string());
17422+ });
1732217423 }
1732317424
1732417425 #[test]
0 commit comments