use std::collections::HashMap;
use std::hash::Hash;
use std::marker::{PhantomData, Send, Sized};
use codec::prelude::NumberDecoder;
use tidb_query_codegen::rpn_fn;
use tidb_query_datatype::{EvalType, FieldTypeAccessor, FieldTypeFlag};
use tipb::{Expr, ExprType, FieldType};
use tidb_query_common::{Error, Result};
use tidb_query_datatype::codec::collation::*;
use tidb_query_datatype::codec::data_type::*;
use tidb_query_datatype::codec::mysql::{Decimal, EnumDecoder, MAX_FSP};
pub trait InByHash {
type Key: EvaluableRet + Extract + Eq;
type StoreKey: 'static + Hash + Eq + Sized + Send;
fn map(key: Self::Key) -> Result<Self::StoreKey>;
fn map_ref(key: &Self::Key) -> Result<&Self::StoreKey>;
}
pub struct NormalInByHash<K: EvaluableRet + Extract + Hash + Eq + Sized + Send>(PhantomData<K>);
impl<K: EvaluableRet + Extract + Hash + Eq + Sized> InByHash for NormalInByHash<K> {
type Key = K;
type StoreKey = K;
#[inline]
fn map(key: Self::Key) -> Result<Self::StoreKey> {
Ok(key)
}
#[inline]
fn map_ref(key: &Self::Key) -> Result<&Self::StoreKey> {
Ok(key)
}
}
pub struct CollationAwareBytesInByHash<C: Collator>(PhantomData<C>);
impl<C: Collator> InByHash for CollationAwareBytesInByHash<C> {
type Key = Bytes;
type StoreKey = SortKey<Bytes, C>;
fn map(key: Self::Key) -> Result<Self::StoreKey> {
SortKey::new(key).map_err(Error::from)
}
fn map_ref(key: &Self::Key) -> Result<&Self::StoreKey> {
SortKey::new_ref(key).map_err(Error::from)
}
}
pub trait Extract: Sized {
fn extract(expr_tp: ExprType, val: Vec<u8>, field_type: &FieldType) -> Result<Self>;
}
#[inline]
fn type_error(eval_type: EvalType, expr_type: ExprType) -> Error {
return other_err!(
"Unexpected ExprType {:?} and EvalType {:?}",
expr_type,
eval_type
);
}
impl Extract for Int {
#[inline]
fn extract(expr_tp: ExprType, val: Vec<u8>, _field_type: &FieldType) -> Result<Self> {
if expr_tp == ExprType::Int64 {
let value = val
.as_slice()
.read_i64()
.map_err(|_| other_err!("Unable to decode int64 from the request"))?;
Ok(value)
} else if expr_tp == ExprType::Uint64 || expr_tp == ExprType::MysqlEnum {
let value = val
.as_slice()
.read_u64()
.map_err(|_| other_err!("Unable to decode uint64 from the request"))?;
Ok(value as i64)
} else {
Err(type_error(<Int as Evaluable>::EVAL_TYPE, expr_tp))
}
}
}
impl Extract for Real {
#[inline]
fn extract(expr_tp: ExprType, val: Vec<u8>, _field_type: &FieldType) -> Result<Self> {
if expr_tp != ExprType::Float32 && expr_tp != ExprType::Float64 {
return Err(type_error(<Real as Evaluable>::EVAL_TYPE, expr_tp));
}
let value = val
.as_slice()
.read_f64()
.map_err(|_| other_err!("Unable to decode float from the request"))?;
Real::new(value).map_err(|_| other_err!("Unable to convert float to real"))
}
}
impl Extract for Bytes {
#[inline]
fn extract(expr_tp: ExprType, val: Vec<u8>, field_type: &FieldType) -> Result<Self> {
match expr_tp {
ExprType::Bytes | ExprType::String => Ok(val),
ExprType::MysqlEnum => {
let value = val
.as_slice()
.read_enum_uint(field_type)
.map_err(|_| other_err!("Unable to decode enum from the request"))?;
Ok(value.name().into())
}
_ => Err(type_error(Bytes::EVAL_TYPE, expr_tp)),
}
}
}
impl Extract for Decimal {
#[inline]
fn extract(expr_tp: ExprType, val: Vec<u8>, _field_type: &FieldType) -> Result<Self> {
if expr_tp != ExprType::MysqlDecimal {
return Err(type_error(<Decimal as Evaluable>::EVAL_TYPE, expr_tp));
}
use tidb_query_datatype::codec::mysql::DecimalDecoder;
let value = val
.as_slice()
.read_decimal()
.map_err(|_| other_err!("Unable to decode decimal from the request"))?;
Ok(value)
}
}
impl Extract for Duration {
#[inline]
fn extract(expr_tp: ExprType, val: Vec<u8>, _field_type: &FieldType) -> Result<Self> {
if expr_tp != ExprType::MysqlDuration {
return Err(type_error(<Duration as Evaluable>::EVAL_TYPE, expr_tp));
}
let n = val
.as_slice()
.read_i64()
.map_err(|_| other_err!("Unable to decode duration from the request"))?;
let value = Duration::from_nanos(n, MAX_FSP)
.map_err(|_| other_err!("Unable to decode duration from the request"))?;
Ok(value)
}
}
pub trait InByCompare: Evaluable + EvaluableRet + Eq {}
impl InByCompare for Int {}
impl InByCompare for Real {}
impl InByCompare for Decimal {}
impl InByCompare for Duration {}
impl InByCompare for DateTime {}
#[derive(Debug)]
pub struct CompareInMeta<T: Eq + Hash> {
lookup_map: HashMap<T, bool>,
has_null: bool,
unsigned_flags: Vec<bool>,
}
#[rpn_fn(nullable, varg, capture = [metadata], min_args = 1, metadata_mapper = init_compare_in_data::<T>)]
#[inline]
pub fn compare_in_by_hash<T: InByHash>(
metadata: &CompareInMeta<T::StoreKey>,
args: &[Option<&T::Key>],
) -> Result<Option<Int>>
where
T::Key: Evaluable,
{
assert!(!args.is_empty());
let base_val = args[0];
match base_val {
None => Ok(None),
Some(base_val) => {
let base_val = T::map_ref(base_val)?;
if metadata.lookup_map.contains_key(base_val) {
return Ok(Some(1));
}
let mut default_ret = if metadata.has_null { None } else { Some(0) };
for arg in &args[1..] {
match arg {
None => {
default_ret = None;
}
Some(v) => {
let v = T::map_ref(v)?;
if base_val == v {
return Ok(Some(1));
}
}
}
}
Ok(default_ret)
}
}
}
#[rpn_fn(nullable, varg, capture = [metadata], min_args = 1, metadata_mapper = init_compare_in_data::<NormalInByHash::<Int>>)]
#[inline]
pub fn compare_in_int_type_by_hash(
metadata: &CompareInMeta<Int>,
args: &[Option<&Int>],
) -> Result<Option<Int>> {
assert!(!args.is_empty());
let base_val = args[0];
let arg0_unsigned = metadata.unsigned_flags[0];
match base_val {
None => Ok(None),
Some(base_val) => {
if let Some(&argi_unsigned) = metadata.lookup_map.get(base_val) {
if (*base_val >= 0)
|| (arg0_unsigned && argi_unsigned)
|| (!arg0_unsigned && !argi_unsigned)
{
return Ok(Some(1));
}
}
let mut default_ret = if metadata.has_null { None } else { Some(0) };
for (i, arg) in (&args[1..]).iter().enumerate() {
let argi_unsigned = metadata.unsigned_flags[i + 1];
match arg {
None => {
default_ret = None;
}
Some(&v) => {
if (*base_val == v)
&& ((*base_val >= 0)
|| (arg0_unsigned && argi_unsigned)
|| (!arg0_unsigned && !argi_unsigned))
{
return Ok(Some(1));
}
}
}
}
Ok(default_ret)
}
}
}
#[rpn_fn(nullable, varg, capture = [metadata], min_args = 1, metadata_mapper = init_compare_in_data::<CollationAwareBytesInByHash::<C>>)]
#[inline]
pub fn compare_in_by_hash_bytes<C: Collator>(
metadata: &CompareInMeta<SortKey<Bytes, C>>,
args: &[Option<BytesRef>],
) -> Result<Option<Int>> {
assert!(!args.is_empty());
let base_val = args[0];
match base_val {
None => Ok(None),
Some(base_val) => {
let base_val = CollationAwareBytesInByHash::<C>::map(base_val.to_vec())?;
if metadata.lookup_map.contains_key(&base_val) {
return Ok(Some(1));
}
let mut default_ret = if metadata.has_null { None } else { Some(0) };
for arg in &args[1..] {
match arg {
None => {
default_ret = None;
}
Some(v) => {
let v = CollationAwareBytesInByHash::<C>::map(v.to_vec())?;
if base_val == v {
return Ok(Some(1));
}
}
}
}
Ok(default_ret)
}
}
}
fn init_compare_in_data<T: InByHash>(expr: &mut Expr) -> Result<CompareInMeta<T::StoreKey>> {
let mut lookup_map = HashMap::new();
let mut has_null = false;
let children = expr.mut_children();
assert!(!children.is_empty());
let mut unsigned_flags = vec![false; children.len()];
unsigned_flags[0] = children[0]
.get_field_type()
.as_accessor()
.flag()
.contains(FieldTypeFlag::UNSIGNED);
let n = children.len();
let mut tail_index = n - 1;
for i in (1..n).rev() {
let tree_node = &mut children[i];
let mut is_constant = true;
let is_unsigned = tree_node
.get_field_type()
.as_accessor()
.flag()
.contains(FieldTypeFlag::UNSIGNED);
unsigned_flags[i] = is_unsigned;
match tree_node.get_tp() {
ExprType::ScalarFunc | ExprType::ColumnRef => {
is_constant = false;
}
ExprType::Null => {
has_null = true;
}
expr_type => {
let val =
T::Key::extract(expr_type, tree_node.take_val(), tree_node.get_field_type())?;
let val = T::map(val)?;
lookup_map.insert(val, is_unsigned);
}
}
if is_constant {
children.as_mut_slice().swap(i, tail_index);
unsigned_flags.swap(i, tail_index);
tail_index -= 1;
}
}
children.truncate(tail_index + 1);
unsigned_flags.truncate(tail_index + 1);
Ok(CompareInMeta {
lookup_map,
has_null,
unsigned_flags,
})
}
#[rpn_fn(nullable, varg, min_args = 1)]
#[inline]
pub fn compare_in_by_compare<T: InByCompare>(args: &[Option<&T>]) -> Result<Option<Int>> {
assert!(!args.is_empty());
let base_val = args[0];
match base_val {
None => Ok(None),
Some(base_val) => {
let mut default_ret = Some(0);
for arg in &args[1..] {
match arg {
None => {
default_ret = None;
}
Some(v) => {
if *v == base_val {
return Ok(Some(1));
}
}
}
}
Ok(default_ret)
}
}
}
#[rpn_fn(nullable, varg, min_args = 1)]
#[inline]
pub fn compare_in_by_compare_json(args: &[Option<JsonRef>]) -> Result<Option<Int>> {
assert!(!args.is_empty());
let base_val = args[0];
match base_val {
None => Ok(None),
Some(base_val) => {
let mut default_ret = Some(0);
for arg in &args[1..] {
match arg {
None => {
default_ret = None;
}
Some(v) => {
if v == &base_val {
return Ok(Some(1));
}
}
}
}
Ok(default_ret)
}
}
}
#[cfg(test)]
mod tests {
use super::super::map_expr_node_to_rpn_func;
use super::*;
use test::{black_box, Bencher};
use tidb_query_datatype::builder::FieldTypeBuilder;
use tidb_query_datatype::{Collation, FieldTypeTp};
use tipb::{FieldType, ScalarFuncSig};
use tipb_helper::ExprDefBuilder;
use crate::types::RpnFnMeta;
use crate::{RpnExpressionBuilder, RpnExpressionNode};
use tidb_query_datatype::codec::batch::{LazyBatchColumn, LazyBatchColumnVec};
use tidb_query_datatype::expr::EvalContext;
#[test]
fn test_in_constant() {
fn by_compare_mapper(expr: &Expr) -> Result<RpnFnMeta> {
match expr.get_sig() {
ScalarFuncSig::InInt => Ok(compare_in_by_compare_fn_meta::<Int>()),
_ => map_expr_node_to_rpn_func(expr),
}
}
fn test_with_mapper<F>(mapper: F, by_hash: bool)
where
F: Fn(&Expr) -> Result<RpnFnMeta> + Copy,
{
let cases = vec![
(vec![Some(1)], Some(0)),
(vec![Some(1), Some(2)], Some(0)),
(vec![Some(1), Some(2), Some(1)], Some(1)),
(vec![Some(1), Some(2), None], None),
(vec![Some(1), Some(2), None, Some(1)], Some(1)),
(vec![None, Some(2), Some(1)], None),
];
for (args, expected) in cases {
let mut builder =
ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong);
for arg in args {
builder = builder.push_child(match arg {
Some(v) => ExprDefBuilder::constant_int(v),
None => ExprDefBuilder::constant_null(FieldTypeTp::LongLong),
});
}
let node = builder.build();
let exp =
RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(node, mapper, 1)
.unwrap();
if by_hash {
if let RpnExpressionNode::FnCall { args_len, .. } = exp[0] {
assert_eq!(args_len, 1);
}
}
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let mut columns = LazyBatchColumnVec::empty();
let result = exp.eval(&mut ctx, schema, &mut columns, &[], 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[expected]
);
}
}
test_with_mapper(map_expr_node_to_rpn_func, true);
test_with_mapper(by_compare_mapper, false);
}
#[test]
fn test_in_string() {
let cases = vec![
(
vec![Some("times naive"), Some("young"), Some("simple"), None],
Collation::Binary,
None,
),
(
vec![
Some("times naive"),
Some("young"),
Some("simple"),
Some("times naive"),
],
Collation::Binary,
Some(1),
),
(
vec![
Some("pINGcap"),
Some("PINGCAP"),
Some("PingCAP"),
Some("pingcap"),
Some("pingCap"),
],
Collation::Utf8Mb4Bin,
Some(0),
),
(
vec![
Some("pINGcap"),
Some("PINGCAP"),
Some("PingCAP"),
Some("pingcap"),
Some("pingCap"),
],
Collation::Utf8Mb4GeneralCi,
Some(1),
),
(
vec![Some("breeswish"), Some("breezewish")],
Collation::Utf8Mb4GeneralCi,
Some(0),
),
(
vec![Some("breeswish"), Some("breezewish")],
Collation::Utf8Mb4GeneralCi,
Some(0),
),
(
vec![Some("codeworm96"), Some("CODEWORM96"), Some("CoDeWorm99")],
Collation::Utf8Mb4GeneralCi,
Some(1),
),
(
vec![
Some("扩散性百万甜🍞"),
Some("扩散性百万辣🍞"),
Some("扩散性百万咸🍞"),
],
Collation::Utf8Mb4GeneralCi,
Some(0),
),
(
vec![Some("🐰"), Some("🐇"), Some("🐻"), Some("🐰")],
Collation::Utf8Mb4GeneralCi,
Some(1),
),
(
vec![Some("🐰"), Some("🐇"), Some("🐻"), Some("🐰")],
Collation::Utf8Mb4UnicodeCi,
Some(1),
),
];
for (args, collation, expected) in cases {
let ft = FieldTypeBuilder::new()
.tp(FieldTypeTp::LongLong)
.collation(collation)
.build();
let mut builder = ExprDefBuilder::scalar_func(ScalarFuncSig::InString, ft);
for arg in args {
builder = builder.push_child(match arg {
Some(v) => ExprDefBuilder::constant_bytes(String::from(v).into_bytes()),
None => ExprDefBuilder::constant_null(FieldTypeTp::String),
});
}
let node = builder.build();
let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
1,
)
.unwrap();
if let RpnExpressionNode::FnCall { args_len, .. } = exp[0] {
assert_eq!(args_len, 1);
}
let mut ctx = EvalContext::default();
let schema = &[];
let mut columns = LazyBatchColumnVec::empty();
let result = exp.eval(&mut ctx, schema, &mut columns, &[], 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[expected]
);
}
}
#[test]
fn test_in_complex() {
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_int(11))
.push_child(ExprDefBuilder::constant_int(22))
.push_child(
ExprDefBuilder::scalar_func(ScalarFuncSig::PlusInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_int(6))
.push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong)),
)
.push_child(ExprDefBuilder::column_ref(1, FieldTypeTp::LongLong))
.build();
let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
if let RpnExpressionNode::FnCall { args_len, .. } = exp[0] {
assert_eq!(args_len, 3);
}
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into(), FieldTypeTp::LongLong.into()];
let mut columns = LazyBatchColumnVec::from(vec![
{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
col.mut_decoded().push_int(Some(5));
col.mut_decoded().push_int(Some(1));
col.mut_decoded().push_int(Some(1));
col
},
{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(3, EvalType::Int);
col.mut_decoded().push_int(Some(8));
col.mut_decoded().push_int(Some(11));
col.mut_decoded().push_int(Some(1));
col
},
]);
let result = exp.eval(&mut ctx, schema, &mut columns, &[1, 0, 2], 3);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1), Some(1), Some(0)],
);
}
#[test]
fn test_unsigned_signed_int() {
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::Long))
.push_child(ExprDefBuilder::constant_uint(18446744073709551615))
.push_child(ExprDefBuilder::constant_int(1))
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::Long.into()];
let log_rows = vec![0];
let mut phy_rows = LazyBatchColumnVec::from(vec![{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1, EvalType::Int);
col.mut_decoded().push_int(Some(-1));
col
}]);
let result = expr.eval(&mut ctx, schema, &mut phy_rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(0)]
);
let mut bigint_unsigned_col: FieldType = FieldTypeTp::LongLong.into();
bigint_unsigned_col
.as_mut_accessor()
.set_flag(FieldTypeFlag::UNSIGNED);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::column_ref(0, bigint_unsigned_col))
.push_child(ExprDefBuilder::constant_int(-1))
.push_child(ExprDefBuilder::constant_int(1))
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::Long.into()];
let log_rows = vec![0];
let mut phy_rows = LazyBatchColumnVec::from(vec![{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1, EvalType::Int);
col.mut_decoded().push_int(Some(-1));
col
}]);
let result = expr.eval(&mut ctx, schema, &mut phy_rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(0)]
);
let mut bigint_unsigned_col: FieldType = FieldTypeTp::LongLong.into();
bigint_unsigned_col
.as_mut_accessor()
.set_flag(FieldTypeFlag::UNSIGNED);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_int(-1))
.push_child(ExprDefBuilder::column_ref(0, bigint_unsigned_col))
.push_child(ExprDefBuilder::constant_int(1))
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::Long.into()];
let log_rows = vec![0];
let mut phy_rows = LazyBatchColumnVec::from(vec![{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1, EvalType::Int);
col.mut_decoded().push_int(Some(-1));
col
}]);
let result = expr.eval(&mut ctx, schema, &mut phy_rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(0)]
);
let mut bigint_unsigned_col: FieldType = FieldTypeTp::LongLong.into();
bigint_unsigned_col
.as_mut_accessor()
.set_flag(FieldTypeFlag::UNSIGNED);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_int(-1))
.push_child(ExprDefBuilder::column_ref(0, bigint_unsigned_col))
.push_child(ExprDefBuilder::constant_int(-1))
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::Long.into()];
let log_rows = vec![0];
let mut phy_rows = LazyBatchColumnVec::from(vec![{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1, EvalType::Int);
col.mut_decoded().push_int(Some(-1));
col
}]);
let result = expr.eval(&mut ctx, schema, &mut phy_rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
let mut bigint_unsigned_col: FieldType = FieldTypeTp::LongLong.into();
bigint_unsigned_col
.as_mut_accessor()
.set_flag(FieldTypeFlag::UNSIGNED);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_uint(18446744073709551615))
.push_child(ExprDefBuilder::column_ref(0, bigint_unsigned_col))
.push_child(ExprDefBuilder::constant_int(-1))
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::Long.into()];
let log_rows = vec![0];
let mut phy_rows = LazyBatchColumnVec::from(vec![{
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1, EvalType::Int);
col.mut_decoded().push_int(Some(-1));
col
}]);
let result = expr.eval(&mut ctx, schema, &mut phy_rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
}
#[bench]
fn bench_compare_in(b: &mut Bencher) {
let mut builder = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong));
for i in 0..1024 {
builder = builder.push_child(ExprDefBuilder::constant_int(i));
}
let node = builder.build();
profiler::start("./bench_compare_in.profile");
let exp = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
1,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(1024, EvalType::Int);
for i in 0..1024 {
col.mut_decoded().push_int(Some(i));
}
let mut columns = LazyBatchColumnVec::from(vec![col]);
let logical_rows: &[usize] = &(0..1024).collect::<Vec<usize>>();
profiler::start("./bench_compare_in.profile");
b.iter(|| {
let result = black_box(&exp).eval(
black_box(&mut ctx),
black_box(schema),
black_box(&mut columns),
black_box(&logical_rows),
black_box(1024),
);
assert!(result.is_ok());
});
profiler::stop();
}
use codec::prelude::NumberEncoder;
#[test]
fn test_enum() {
let mut enum_expr = Expr::default();
enum_expr.set_tp(ExprType::MysqlEnum);
enum_expr.mut_val().write_u64(2u64).unwrap();
let mut field_type = FieldType::new();
field_type.set_tp(FieldTypeTp::Enum.to_u8().unwrap() as i32);
let elems = protobuf::RepeatedField::from_slice(&[
String::from("c"),
String::from("b"),
String::from("a"),
]);
field_type.set_elems(elems);
enum_expr.set_field_type(field_type);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InString, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_bytes("b".into()))
.push_child(enum_expr.clone())
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let log_rows = vec![0];
let mut rows = LazyBatchColumnVec::empty();
let result = expr.eval(&mut ctx, schema, &mut rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(ExprDefBuilder::constant_uint(2))
.push_child(enum_expr.clone())
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let log_rows = vec![0];
let mut rows = LazyBatchColumnVec::empty();
let result = expr.eval(&mut ctx, schema, &mut rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InString, FieldTypeTp::LongLong)
.push_child(enum_expr.clone())
.push_child(ExprDefBuilder::constant_bytes("a".into()))
.push_child(enum_expr.clone())
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let log_rows = vec![0];
let mut rows = LazyBatchColumnVec::empty();
let result = expr.eval(&mut ctx, schema, &mut rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
let node = ExprDefBuilder::scalar_func(ScalarFuncSig::InInt, FieldTypeTp::LongLong)
.push_child(enum_expr.clone())
.push_child(ExprDefBuilder::constant_uint(1))
.push_child(enum_expr)
.build();
let expr = RpnExpressionBuilder::build_from_expr_tree_with_fn_mapper(
node,
map_expr_node_to_rpn_func,
2,
)
.unwrap();
let mut ctx = EvalContext::default();
let schema = &[FieldTypeTp::LongLong.into()];
let log_rows = vec![0];
let mut rows = LazyBatchColumnVec::empty();
let result = expr.eval(&mut ctx, schema, &mut rows, &log_rows, 1);
let val = result.unwrap();
assert!(val.is_vector());
assert_eq!(
val.vector_value().unwrap().as_ref().to_int_vec(),
&[Some(1)]
);
}
}