Skip to content

Commit

Permalink
cherry pick tikv#7486 to release-2.1
Browse files Browse the repository at this point in the history
Signed-off-by: sre-bot <sre-bot@pingcap.com>
  • Loading branch information
zhongzc authored and sre-bot committed Apr 20, 2020
1 parent 2f64951 commit b9576f2
Showing 1 changed file with 185 additions and 0 deletions.
185 changes: 185 additions & 0 deletions components/tidb_query_datatype/src/codec/data_type/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

mod scalar;
mod vector;

// Concrete eval types without a nullable wrapper.
pub type Int = i64;
pub type Real = ordered_float::NotNan<f64>;
pub type Bytes = Vec<u8>;
pub use crate::codec::mysql::{Decimal, Duration, Json, JsonType, Time as DateTime};

// Dynamic eval types.
pub use self::scalar::{ScalarValue, ScalarValueRef};
pub use self::vector::{VectorValue, VectorValueExt};

use crate::EvalType;

use crate::codec::convert::ConvertTo;
use crate::expr::EvalContext;
use tidb_query_common::error::Result;

/// A trait of evaluating current concrete eval type into a MySQL logic value, represented by
/// Rust's `bool` type.
pub trait AsMySQLBool {
/// Evaluates into a MySQL logic value.
fn as_mysql_bool(&self, context: &mut EvalContext) -> Result<bool>;
}

impl AsMySQLBool for Int {
#[inline]
fn as_mysql_bool(&self, _context: &mut EvalContext) -> Result<bool> {
Ok(*self != 0)
}
}

impl AsMySQLBool for Real {
#[inline]
fn as_mysql_bool(&self, _context: &mut EvalContext) -> Result<bool> {
Ok(self.round() != 0f64)
}
}

impl AsMySQLBool for Bytes {
#[inline]
fn as_mysql_bool(&self, context: &mut EvalContext) -> Result<bool> {
Ok(!self.is_empty() && ConvertTo::<f64>::convert(self, context)? != 0f64)
}
}

impl<T> AsMySQLBool for Option<T>
where
T: AsMySQLBool,
{
fn as_mysql_bool(&self, context: &mut EvalContext) -> Result<bool> {
match self {
None => Ok(false),
Some(ref v) => v.as_mysql_bool(context),
}
}
}

pub macro match_template_evaluable($t:tt, $($tail:tt)*) {
match_template::match_template! {
$t = [Int, Real, Decimal, Bytes, DateTime, Duration, Json],
$($tail)*
}
}

/// A trait of all types that can be used during evaluation (eval type).
pub trait Evaluable: Clone + std::fmt::Debug + Send + Sync + 'static {
const EVAL_TYPE: EvalType;

/// Borrows this concrete type from a `ScalarValue` in the same type;
/// panics if the varient mismatches.
fn borrow_scalar_value(v: &ScalarValue) -> &Option<Self>;

/// Borrows this concrete type from a `ScalarValueRef` in the same type;
/// panics if the varient mismatches.
fn borrow_scalar_value_ref<'a>(v: &'a ScalarValueRef<'a>) -> &'a Option<Self>;

/// Borrows a slice of this concrete type from a `VectorValue` in the same type;
/// panics if the varient mismatches.
fn borrow_vector_value(v: &VectorValue) -> &[Option<Self>];

/// Converts a vector of this concrete type into a `VectorValue` in the same type;
/// panics if the varient mismatches.
fn into_vector_value(vec: Vec<Option<Self>>) -> VectorValue;
}

macro_rules! impl_evaluable_type {
($ty:tt) => {
impl Evaluable for $ty {
const EVAL_TYPE: EvalType = EvalType::$ty;

#[inline]
fn borrow_scalar_value(v: &ScalarValue) -> &Option<Self> {
v.as_ref()
}

#[inline]
fn borrow_scalar_value_ref<'a>(v: &'a ScalarValueRef<'a>) -> &'a Option<Self> {
v.as_ref()
}

#[inline]
fn borrow_vector_value(v: &VectorValue) -> &[Option<Self>] {
v.as_ref()
}

#[inline]
fn into_vector_value(vec: Vec<Option<Self>>) -> VectorValue {
VectorValue::from(vec)
}
}
};
}

impl_evaluable_type! { Int }
impl_evaluable_type! { Real }
impl_evaluable_type! { Decimal }
impl_evaluable_type! { Bytes }
impl_evaluable_type! { DateTime }
impl_evaluable_type! { Duration }
impl_evaluable_type! { Json }

#[cfg(test)]
mod tests {
use super::*;
use std::f64;

#[test]
fn test_bytes_to_bool() {
let tests: Vec<(&'static [u8], Option<bool>)> = vec![
(b"", Some(false)),
(b" 23", Some(true)),
(b"-1", Some(true)),
(b"1.11", Some(true)),
(b"1.11.00", None),
(b"xx", None),
(b"0x00", None),
(b"11.xx", None),
(b"xx.11", None),
(
b".0000000000000000000000000000000000000000000000000000001",
Some(true),
),
];

let mut ctx = EvalContext::default();
for (i, (v, expect)) in tests.into_iter().enumerate() {
let rb: Result<bool> = v.to_vec().as_mysql_bool(&mut ctx);
match expect {
Some(val) => {
assert_eq!(rb.unwrap(), val);
}
None => {
assert!(
rb.is_err(),
"index: {}, {:?} should not be converted, but got: {:?}",
i,
v,
rb
);
}
}
}

// test overflow
let mut ctx = EvalContext::default();
let val: Result<bool> = f64::INFINITY
.to_string()
.as_bytes()
.to_vec()
.as_mysql_bool(&mut ctx);
assert!(val.is_err());

let mut ctx = EvalContext::default();
let val: Result<bool> = f64::NEG_INFINITY
.to_string()
.as_bytes()
.to_vec()
.as_mysql_bool(&mut ctx);
assert!(val.is_err());
}
}

0 comments on commit b9576f2

Please sign in to comment.