Skip to content

Commit

Permalink
Merge pull request #10275 from youngsofun/expr
Browse files Browse the repository at this point in the history
feat: parse decimal in expr.
  • Loading branch information
mergify[bot] authored Mar 5, 2023
2 parents bbc02fe + 12ec225 commit 2354482
Show file tree
Hide file tree
Showing 58 changed files with 1,208 additions and 682 deletions.
20 changes: 6 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/common/auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ chrono = { workspace = true }
common-base = { path = "../../common/base" }
http = "0.2"
[dev-dependencies]
tempfile = "3.1.0"
tempfile = "3.4.0"
2 changes: 1 addition & 1 deletion src/common/http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ anyerror = { workspace = true }
futures = "0.3.24"
poem = { version = "1", features = ["rustls"] }
serde = { workspace = true }
tempfile = { version = "3.3.0", optional = true }
tempfile = { version = "3.4.0", optional = true }
thiserror = { workspace = true }
tracing = "0.1.36"

Expand Down
2 changes: 1 addition & 1 deletion src/meta/embedded/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ common-meta-types = { path = "../types" }
# Crates.io dependencies
async-trait = "0.1.57"
once_cell = "1.15.0"
tempfile = "3.3.0"
tempfile = "3.4.0"
tracing = "0.1.36"

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion src/meta/raft-store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ common-base = { path = "../../common/base" }

async-entry = "0.3.1"
pretty_assertions = "1.3.0"
tempfile = "3.3.0"
tempfile = "3.4.0"
2 changes: 1 addition & 1 deletion src/meta/service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pretty_assertions = "1.3.0"
regex = "1.6.0"
reqwest = { workspace = true }
temp-env = "0.3.0"
tempfile = "3.3.0"
tempfile = "3.4.0"

[build-dependencies]
common-building = { path = "../../common/building" }
2 changes: 1 addition & 1 deletion src/meta/sled-store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ byteorder = "1.4.3"
once_cell = "1.15.0"
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = "3.3.0"
tempfile = "3.4.0"
thiserror = { workspace = true }
tracing = "0.1.36"

Expand Down
2 changes: 2 additions & 0 deletions src/query/ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ doctest = false
[dependencies] # In alphabetical order
# Workspace dependencies
common-exception = { path = "../../common/exception" }
common-io = { path = "../../common/io" }
common-meta-app = { path = "../../meta/app" }

# Crates.io dependencies
ethnum = "1.3.2"
fast-float = "0.2.0"
itertools = "0.10.5"
logos = "0.12.1"
Expand Down
183 changes: 174 additions & 9 deletions src/query/ast/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cmp::Ordering;
use std::fmt::Display;
use std::fmt::Formatter;

use common_exception::ErrorCode;
use common_exception::Result;
use common_exception::Span;
use common_io::display_decimal_128;
use common_io::display_decimal_256;
use ethnum::i256;

use crate::ast::write_comma_separated_list;
use crate::ast::write_period_separated_list;
use crate::ast::Identifier;
use crate::ast::Query;
use crate::ErrorKind;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum IntervalKind {
Expand Down Expand Up @@ -221,16 +226,173 @@ pub enum SubqueryModifier {

#[derive(Debug, Clone, PartialEq)]
pub enum Literal {
Integer(u64),
UInt64(u64),
Int64(i64),
Decimal128 {
value: i128,
precision: u8,
scale: u8,
},
Decimal256 {
value: i256,
precision: u8,
scale: u8,
},
Float(f64),
BigInt { lit: String, is_hex: bool },
// Quoted string literal value
String(String),
Boolean(bool),
CurrentTimestamp,
Null,
}

impl Literal {
pub(crate) fn neg(&self) -> Self {
match self {
Literal::UInt64(u) => match u.cmp(&(i64::MAX as u64 + 1)) {
Ordering::Greater => Literal::Decimal128 {
value: -(*u as i128),
precision: 19,
scale: 0,
},
Ordering::Less => Literal::Int64(-(*u as i64)),
Ordering::Equal => Literal::Int64(i64::MIN),
},
Literal::Float(f) => Literal::Float(-*f),
Literal::Decimal128 {
value,
precision,
scale,
} => Literal::Decimal128 {
value: -*value,
precision: *precision,
scale: *scale,
},
Literal::Decimal256 {
value,
precision,
scale,
} => Literal::Decimal256 {
value: -*value,
precision: *precision,
scale: *scale,
},
_ => unreachable!(),
}
}

/// assume text is from
/// used only for expr, so put more weight on readability
pub fn parse_decimal(text: &str) -> std::result::Result<Self, ErrorKind> {
let mut start = 0;
let bytes = text.as_bytes();
while bytes[start] == b'0' {
start += 1
}
let text = &text[start..];
let point_pos = text.find('.');
let e_pos = text.find(|c| c == 'e' || c == 'E');
let (i_part, f_part, e_part) = match (point_pos, e_pos) {
(Some(p1), Some(p2)) => (&text[..p1], &text[(p1 + 1)..p2], Some(&text[(p2 + 1)..])),
(Some(p), None) => (&text[..p], &text[(p + 1)..], None),
(None, Some(p)) => (&text[..p], "", Some(&text[(p + 1)..])),
_ => {
unreachable!()
}
};
let exp = match e_part {
Some(s) => match s.parse::<i32>() {
Ok(i) => i,
Err(_) => return Ok(Literal::Float(fast_float::parse(text)?)),
},
None => 0,
};
if i_part.len() as i32 + exp > 76 {
Ok(Literal::Float(fast_float::parse(text)?))
} else {
let mut digits = Vec::with_capacity(76);
digits.extend_from_slice(i_part.as_bytes());
digits.extend_from_slice(f_part.as_bytes());
if digits.is_empty() {
digits.push(b'0')
}
let mut scale = f_part.len() as i32 - exp;
if scale < 0 {
// e.g 123.1e3
for _ in 0..(-scale) {
digits.push(b'0')
}
scale = 0;
};

// truncate
if digits.len() > 76 {
scale -= digits.len() as i32 - 76;
}
let precision = std::cmp::min(digits.len(), 76);
let digits = unsafe { std::str::from_utf8_unchecked(&digits[..precision]) };

let scale = scale as u8;
let precision = std::cmp::max(precision as u8, scale);
if precision > 38 {
Ok(Literal::Decimal256 {
value: i256::from_str_radix(digits, 10)?,
precision,
scale,
})
} else {
Ok(Literal::Decimal128 {
value: digits.parse::<i128>()?,
precision,
scale,
})
}
}
}

pub fn parse_decimal_uint(text: &str) -> std::result::Result<Self, ErrorKind> {
let mut start = 0;
let bytes = text.as_bytes();
while start < bytes.len() && bytes[start] == b'0' {
start += 1
}
let text = &text[start..];
if text.is_empty() {
return Ok(Literal::UInt64(0));
}
let precision = text.len() as u8;
match precision {
0..=19 => Ok(Literal::UInt64(text.parse::<u64>()?)),
20 => {
if text <= "18446744073709551615" {
Ok(Literal::UInt64(text.parse::<u64>()?))
} else {
Ok(Literal::Decimal128 {
value: text.parse::<i128>()?,
precision,
scale: 0,
})
}
}
21..=38 => Ok(Literal::Decimal128 {
value: text.parse::<i128>()?,
precision,
scale: 0,
}),
39..=76 => Ok(Literal::Decimal256 {
value: i256::from_str_radix(text, 10)?,
precision,
scale: 0,
}),
_ => {
// lost precision
// 2.2250738585072014 E - 308 to 1.7976931348623158 E + 308
Ok(Literal::Float(fast_float::parse(text)?))
}
}
}
}

/// The display style for a map access expression
#[derive(Debug, Clone, PartialEq)]
pub enum MapAccessor {
Expand Down Expand Up @@ -632,17 +794,20 @@ impl Display for TrimWhere {
impl Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Literal::Integer(val) => {
Literal::UInt64(val) => {
write!(f, "{val}")
}
Literal::Float(val) => {
Literal::Int64(val) => {
write!(f, "{val}")
}
Literal::BigInt { lit, is_hex } => {
if *is_hex {
write!(f, "0x")?;
}
write!(f, "{lit}")
Literal::Decimal128 { value, scale, .. } => {
write!(f, "{}", display_decimal_128(*value, *scale))
}
Literal::Decimal256 { value, scale, .. } => {
write!(f, "{}", display_decimal_256(*value, *scale))
}
Literal::Float(val) => {
write!(f, "{val}")
}
Literal::String(val) => {
write!(f, "\'{val}\'")
Expand Down
Loading

1 comment on commit 2354482

@vercel
Copy link

@vercel vercel bot commented on 2354482 Mar 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

databend – ./

databend.vercel.app
databend-git-main-databend.vercel.app
databend.rs
databend-databend.vercel.app

Please sign in to comment.