Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add *AnyValue functions #4629

Merged
merged 4 commits into from May 7, 2019
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -1,6 +1,8 @@
// Copyright 2017 TiKV Project Authors. Licensed under Apache-2.0.

use super::{EvalContext, Result, ScalarFunc};
use crate::coprocessor::codec::data_type::Duration;
use crate::coprocessor::codec::mysql::{Decimal, Json, Time};
use crate::coprocessor::codec::Datum;
use std::borrow::Cow;
use std::convert::TryInto;
@@ -12,6 +14,82 @@ const IPV4_LENGTH: usize = 4;
const PREFIX_COMPAT: [u8; 12] = [0x00; 12];

impl ScalarFunc {
pub fn int_any_value(&self, ctx: &mut EvalContext, row: &[Datum]) -> Result<Option<i64>> {
if self.children.is_empty() {
This conversation was marked as resolved by kennytm

This comment has been minimized.

Copy link
@kennytm

kennytm May 6, 2019

Contributor

I'd prefer writing these without panic possibility:

if let Some(child) = self.children.first() {
    child.eval_int(ctx, row)
} else {
    Ok(None)
}

or maybe through combinators (but it is harder to read):

self.children.first().map_or(Ok(None), |child| child.eval_int(ctx, row))
Ok(None)
} else {
self.children[0].eval_int(ctx, row)
}
}

pub fn real_any_value(&self, ctx: &mut EvalContext, row: &[Datum]) -> Result<Option<f64>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_real(ctx, row)
}
}

pub fn string_any_value<'a, 'b: 'a>(
&'b self,
ctx: &mut EvalContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, [u8]>>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_string(ctx, row)
}
}

pub fn time_any_value<'a, 'b: 'a>(
&'b self,
ctx: &mut EvalContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Time>>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_time(ctx, row)
}
}

pub fn decimal_any_value<'a, 'b: 'a>(
&'b self,
ctx: &mut EvalContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Decimal>>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_decimal(ctx, row)
}
}

pub fn json_any_value<'a, 'b: 'a>(
&'b self,
ctx: &mut EvalContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Json>>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_json(ctx, row)
}
}

pub fn duration_any_value<'a, 'b: 'a>(
&'b self,
ctx: &mut EvalContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Duration>>> {
if self.children.is_empty() {
Ok(None)
} else {
self.children[0].eval_duration(ctx, row)
}
}

pub fn is_ipv4(&self, ctx: &mut EvalContext, row: &[Datum]) -> Result<Option<i64>> {
let input = try_opt!(self.children[0].eval_string_and_decode(ctx, row));
if Ipv4Addr::from_str(&input).is_ok() {
@@ -156,11 +234,254 @@ impl ScalarFunc {

#[cfg(test)]
mod tests {
use crate::coprocessor::codec::data_type::Duration;
use crate::coprocessor::codec::mysql::Tz;
use crate::coprocessor::codec::Datum;
use crate::coprocessor::dag::expr::tests::{datum_expr, scalar_func_expr};
use crate::coprocessor::dag::expr::{EvalContext, Expression};
use tipb::expression::ScalarFuncSig;

#[test]
fn test_int_any_value() {
let cases = vec![
// input, expected
(1i64, 2i64, 3i64, 4i64, 1i64),
];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::I64(n1));
let input2 = datum_expr(Datum::I64(n2));
let input3 = datum_expr(Datum::I64(n3));
let input4 = datum_expr(Datum::I64(n4));

let op = scalar_func_expr(
ScalarFuncSig::IntAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::IntAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_real_any_value() {
let cases = vec![
// input, expected
(1.2f64, 2.3f64, 3f64, 4f64, 1.2f64),
];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::F64(n1));
let input2 = datum_expr(Datum::F64(n2));
let input3 = datum_expr(Datum::F64(n3));
let input4 = datum_expr(Datum::F64(n4));

let op = scalar_func_expr(
ScalarFuncSig::RealAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::RealAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_string_any_value() {
let cases = vec![
// input, expected
("abc", "def", "ojk", "hij", "abc"),
];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::Bytes(n1.as_bytes().to_vec()));
let input2 = datum_expr(Datum::Bytes(n2.as_bytes().to_vec()));
let input3 = datum_expr(Datum::Bytes(n3.as_bytes().to_vec()));
let input4 = datum_expr(Datum::Bytes(n4.as_bytes().to_vec()));

let op = scalar_func_expr(
ScalarFuncSig::StringAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected.as_bytes().to_vec());
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::StringAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_duration_any_value() {
let cases = vec![
// input, expected
(
Duration::from_nanos(10, 0).unwrap(),
Duration::from_nanos(11, 0).unwrap(),
Duration::from_nanos(12, 0).unwrap(),
Duration::from_nanos(13, 0).unwrap(),
Duration::from_nanos(10, 0).unwrap(),
),
];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::Dur(n1));
let input2 = datum_expr(Datum::Dur(n2));
let input3 = datum_expr(Datum::Dur(n3));
let input4 = datum_expr(Datum::Dur(n4));

let op = scalar_func_expr(
ScalarFuncSig::DurationAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::DurationAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_json_any_value() {
use crate::coprocessor::codec::mysql::json::Json;

let cases = vec![
// input, expected
(
Json::U64(1),
Json::U64(2),
Json::U64(3),
Json::U64(4),
Json::U64(1),
),
];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::Json(n1));
let input2 = datum_expr(Datum::Json(n2));
let input3 = datum_expr(Datum::Json(n3));
let input4 = datum_expr(Datum::Json(n4));

let op = scalar_func_expr(
ScalarFuncSig::JSONAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::JSONAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_time_any_value() {
use crate::coprocessor::codec::mysql::Time;
let cases = vec![(
Time::parse_datetime("1000-01-01 00:00:00", 0, &Tz::utc()).unwrap(),
Time::parse_datetime("1000-01-01 00:00:01", 0, &Tz::utc()).unwrap(),
Time::parse_datetime("1000-01-01 00:00:02", 0, &Tz::utc()).unwrap(),
Time::parse_datetime("1000-01-01 00:00:03", 0, &Tz::utc()).unwrap(),
Time::parse_datetime("1000-01-01 00:00:00", 0, &Tz::utc()).unwrap(),
)];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::Time(n1));
let input2 = datum_expr(Datum::Time(n2));
let input3 = datum_expr(Datum::Time(n3));
let input4 = datum_expr(Datum::Time(n4));

let op = scalar_func_expr(
ScalarFuncSig::TimeAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::TimeAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_decimal_any_value() {
use crate::coprocessor::codec::mysql::Decimal;
let cases: Vec<(Decimal, Decimal, Decimal, Decimal, Decimal)> =
vec![(10.into(), 20.into(), 30.into(), 40.into(), 10.into())];

let mut ctx = EvalContext::default();
for (n1, n2, n3, n4, expected) in cases {
let input1 = datum_expr(Datum::Dec(n1));
let input2 = datum_expr(Datum::Dec(n2));
let input3 = datum_expr(Datum::Dec(n3));
let input4 = datum_expr(Datum::Dec(n4));

let op = scalar_func_expr(
ScalarFuncSig::DecimalAnyValue,
&[input1, input2, input3, input4],
);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]).unwrap();
let exp = Datum::from(expected);
assert_eq!(got, exp);
}
let op = scalar_func_expr(ScalarFuncSig::DecimalAnyValue, &[]);
let op = Expression::build(&ctx, op).unwrap();
let got = op.eval(&mut ctx, &[]);
match got {
Ok(x) => assert_eq!(x, Datum::Null),
_ => panic!("test failed, except Ok(Datum::Null)"),
}
}

#[test]
fn test_is_ipv4() {
let cases = vec![
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.