Change Value::Long() to u64, use u64 instead of usize

The tokenizer emits a separate Token for +/- signs, so the value of
Value::Long() (as well as of parse_literal_int()) may never be negative.

Also we have been using both u64 and usize to represent a parsed
unsigned number. Change to using u64 for consistency.
This commit is contained in:
Nickolay Ponomarev 2019-05-06 04:39:13 +03:00
parent 0407ed2b57
commit d9edc2588b
4 changed files with 34 additions and 46 deletions

View file

@ -1,26 +1,26 @@
use super::SQLObjectName;
/// SQL datatypes for literals in SQL statements
/// SQL data types
#[derive(Debug, Clone, PartialEq)]
pub enum SQLType {
/// Fixed-length character type e.g. CHAR(10)
Char(Option<usize>),
Char(Option<u64>),
/// Variable-length character type e.g. VARCHAR(10)
Varchar(Option<usize>),
Varchar(Option<u64>),
/// Uuid type
Uuid,
/// Large character object e.g. CLOB(1000)
Clob(usize),
Clob(u64),
/// Fixed-length binary type e.g. BINARY(10)
Binary(usize),
Binary(u64),
/// Variable-length binary type e.g. VARBINARY(10)
Varbinary(usize),
Varbinary(u64),
/// Large binary object e.g. BLOB(1000)
Blob(usize),
Blob(u64),
/// Decimal type with optional precision and scale e.g. DECIMAL(10,2)
Decimal(Option<usize>, Option<usize>),
Decimal(Option<u64>, Option<u64>),
/// Floating point with optional precision e.g. FLOAT(8)
Float(Option<usize>),
Float(Option<u64>),
/// Small integer
SmallInt,
/// Integer
@ -87,7 +87,7 @@ impl ToString for SQLType {
}
}
fn format_type_with_optional_length(sql_type: &str, len: &Option<usize>) -> String {
fn format_type_with_optional_length(sql_type: &str, len: &Option<u64>) -> String {
let mut s = sql_type.to_string();
if let Some(len) = len {
s += &format!("({})", len);

View file

@ -1,15 +1,15 @@
/// SQL values such as int, double, string, timestamp
/// Primitive SQL values such as number and string
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
/// Literal signed long
Long(i64),
/// Literal floating point value
/// Unsigned integer value
Long(u64),
/// Unsigned floating point value
Double(f64),
/// 'string value'
SingleQuotedString(String),
/// N'string value'
NationalStringLiteral(String),
/// Boolean value true or false,
/// Boolean value true or false
Boolean(bool),
/// NULL value in insert statements,
Null,

View file

@ -351,14 +351,8 @@ impl Parser {
let rows = if self.parse_keyword("UNBOUNDED") {
None
} else {
let rows = self.parse_literal_int()?;
if rows < 0 {
parser_err!(format!(
"The number of rows must be non-negative, got {}",
rows
))?;
}
Some(rows as u64)
let rows = self.parse_literal_uint()?;
Some(rows)
};
if self.parse_keyword("PRECEDING") {
Ok(SQLWindowFrameBound::Preceding(rows))
@ -1059,9 +1053,9 @@ impl Parser {
Ok(n) => Ok(Value::Double(n)),
Err(e) => parser_err!(format!("Could not parse '{}' as f64: {}", n, e)),
},
Token::Number(ref n) => match n.parse::<i64>() {
Token::Number(ref n) => match n.parse::<u64>() {
Ok(n) => Ok(Value::Long(n)),
Err(e) => parser_err!(format!("Could not parse '{}' as i64: {}", n, e)),
Err(e) => parser_err!(format!("Could not parse '{}' as u64: {}", n, e)),
},
Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())),
Token::NationalStringLiteral(ref s) => {
@ -1073,13 +1067,13 @@ impl Parser {
}
}
/// Parse a literal integer/long
pub fn parse_literal_int(&mut self) -> Result<i64, ParserError> {
/// Parse an unsigned literal integer/long
pub fn parse_literal_uint(&mut self) -> Result<u64, ParserError> {
match self.next_token() {
Some(Token::Number(s)) => s.parse::<i64>().map_err(|e| {
ParserError::ParserError(format!("Could not parse '{}' as i64: {}", s, e))
Some(Token::Number(s)) => s.parse::<u64>().map_err(|e| {
ParserError::ParserError(format!("Could not parse '{}' as u64: {}", s, e))
}),
other => parser_err!(format!("Expected literal int, found {:?}", other)),
other => self.expected("literal int", other),
}
}
@ -1258,17 +1252,11 @@ impl Parser {
}
}
pub fn parse_precision(&mut self) -> Result<usize, ParserError> {
//TODO: error handling
Ok(self.parse_optional_precision()?.unwrap())
}
pub fn parse_optional_precision(&mut self) -> Result<Option<usize>, ParserError> {
pub fn parse_optional_precision(&mut self) -> Result<Option<u64>, ParserError> {
if self.consume_token(&Token::LParen) {
let n = self.parse_literal_int()?;
//TODO: check return value of reading rparen
let n = self.parse_literal_uint()?;
self.expect_token(&Token::RParen)?;
Ok(Some(n as usize))
Ok(Some(n))
} else {
Ok(None)
}
@ -1276,16 +1264,16 @@ impl Parser {
pub fn parse_optional_precision_scale(
&mut self,
) -> Result<(Option<usize>, Option<usize>), ParserError> {
) -> Result<(Option<u64>, Option<u64>), ParserError> {
if self.consume_token(&Token::LParen) {
let n = self.parse_literal_int()?;
let n = self.parse_literal_uint()?;
let scale = if self.consume_token(&Token::Comma) {
Some(self.parse_literal_int()? as usize)
Some(self.parse_literal_uint()?)
} else {
None
};
self.expect_token(&Token::RParen)?;
Ok((Some(n as usize), scale))
Ok((Some(n), scale))
} else {
Ok((None, None))
}
@ -1725,7 +1713,7 @@ impl Parser {
if self.parse_keyword("ALL") {
Ok(None)
} else {
self.parse_literal_int()
self.parse_literal_uint()
.map(|n| Some(ASTNode::SQLValue(Value::Long(n))))
}
}
@ -1733,7 +1721,7 @@ impl Parser {
/// Parse an OFFSET clause
pub fn parse_offset(&mut self) -> Result<ASTNode, ParserError> {
let value = self
.parse_literal_int()
.parse_literal_uint()
.map(|n| ASTNode::SQLValue(Value::Long(n)))?;
self.expect_one_of_keywords(&["ROW", "ROWS"])?;
Ok(value)

View file

@ -29,7 +29,7 @@ use super::dialect::Dialect;
pub enum Token {
/// A keyword (like SELECT) or an optionally quoted SQL identifier
SQLWord(SQLWord),
/// Numeric literal
/// An unsigned numeric literal
Number(String),
/// A character that could not be tokenized
Char(char),