Skip to content

Commit f4df340

Browse files
authored
Merge pull request #130 from andygrove/decimal
Don't lose precision when parsing decimal fractions
2 parents 2bef9ec + a0aca82 commit f4df340

File tree

7 files changed

+123
-80
lines changed

7 files changed

+123
-80
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ script:
4949
- travis-cargo clippy -- --all-targets --all-features -- -D warnings
5050
- travis-cargo build
5151
- travis-cargo test
52+
- travis-cargo test -- all-features
5253
- cargo +nightly fmt -- --check --config-path <(echo 'license_template_path = "HEADER"')
5354

5455
after_success:

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ name = "sqlparser"
1919
path = "src/lib.rs"
2020

2121
[dependencies]
22+
bigdecimal = { version = "0.1.0", optional = true }
2223
log = "0.4.5"
23-
ordered-float = "1.0.2"
2424

2525
[dev-dependencies]
2626
simple_logger = "1.0.1"

src/ast/value.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@
1010
// See the License for the specific language governing permissions and
1111
// limitations under the License.
1212

13-
use ordered_float::OrderedFloat;
13+
#[cfg(feature = "bigdecimal")]
14+
use bigdecimal::BigDecimal;
1415
use std::fmt;
1516

1617
/// Primitive SQL values such as number and string
1718
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1819
pub enum Value {
19-
/// Unsigned integer value
20-
Long(u64),
21-
/// Unsigned floating point value
22-
Double(OrderedFloat<f64>),
20+
/// Numeric literal
21+
#[cfg(not(feature = "bigdecimal"))]
22+
Number(String),
23+
#[cfg(feature = "bigdecimal")]
24+
Number(BigDecimal),
2325
/// 'string value'
2426
SingleQuotedString(String),
2527
/// N'string value'
@@ -60,8 +62,7 @@ pub enum Value {
6062
impl fmt::Display for Value {
6163
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6264
match self {
63-
Value::Long(v) => write!(f, "{}", v),
64-
Value::Double(v) => write!(f, "{}", v),
65+
Value::Number(v) => write!(f, "{}", v),
6566
Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
6667
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
6768
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),

src/parser.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,13 +1183,12 @@ impl Parser {
11831183
return parser_err!(format!("No value parser for keyword {}", k.keyword));
11841184
}
11851185
},
1186-
Token::Number(ref n) if n.contains('.') => match n.parse::<f64>() {
1187-
Ok(n) => Ok(Value::Double(n.into())),
1188-
Err(e) => parser_err!(format!("Could not parse '{}' as f64: {}", n, e)),
1189-
},
1190-
Token::Number(ref n) => match n.parse::<u64>() {
1191-
Ok(n) => Ok(Value::Long(n)),
1192-
Err(e) => parser_err!(format!("Could not parse '{}' as u64: {}", n, e)),
1186+
// The call to n.parse() returns a bigdecimal when the
1187+
// bigdecimal feature is enabled, and is otherwise a no-op
1188+
// (i.e., it returns the input string).
1189+
Token::Number(ref n) => match n.parse() {
1190+
Ok(n) => Ok(Value::Number(n)),
1191+
Err(e) => parser_err!(format!("Could not parse '{}' as number: {}", n, e)),
11931192
},
11941193
Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())),
11951194
Token::NationalStringLiteral(ref s) => {
@@ -1202,6 +1201,16 @@ impl Parser {
12021201
}
12031202
}
12041203

1204+
pub fn parse_number_value(&mut self) -> Result<Value, ParserError> {
1205+
match self.parse_value()? {
1206+
v @ Value::Number(_) => Ok(v),
1207+
_ => {
1208+
self.prev_token();
1209+
self.expected("literal number", self.peek_token())
1210+
}
1211+
}
1212+
}
1213+
12051214
/// Parse an unsigned literal integer/long
12061215
pub fn parse_literal_uint(&mut self) -> Result<u64, ParserError> {
12071216
match self.next_token() {
@@ -1863,16 +1872,13 @@ impl Parser {
18631872
if self.parse_keyword("ALL") {
18641873
Ok(None)
18651874
} else {
1866-
self.parse_literal_uint()
1867-
.map(|n| Some(Expr::Value(Value::Long(n))))
1875+
Ok(Some(Expr::Value(self.parse_number_value()?)))
18681876
}
18691877
}
18701878

18711879
/// Parse an OFFSET clause
18721880
pub fn parse_offset(&mut self) -> Result<Expr, ParserError> {
1873-
let value = self
1874-
.parse_literal_uint()
1875-
.map(|n| Expr::Value(Value::Long(n)))?;
1881+
let value = Expr::Value(self.parse_number_value()?);
18761882
self.expect_one_of_keywords(&["ROW", "ROWS"])?;
18771883
Ok(value)
18781884
}

src/test_utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,7 @@ pub fn expr_from_projection(item: &SelectItem) -> &Expr {
136136
_ => panic!("Expected UnnamedExpr"),
137137
}
138138
}
139+
140+
pub fn number(n: &'static str) -> Value {
141+
Value::Number(n.parse().unwrap())
142+
}

0 commit comments

Comments
 (0)