在 Databend 中按函数完成分为了:scalars 函数和 aggregates 函数。
Scalar 函数: 根据输入值,回来单个值。常见的 Scalar function 有 now, round 等。
Aggregate 函数: 用于对列的值进行操作并回来单个值。常见的 Agg function 有 sum, count, avg 等。
github.com/datafuselab…
该系列共两篇,本文首要介绍 Scalar Function 从注册到履行是如何在 Databend 运行起来的。
函数注册
由 FunctionRegistry 接管函数注册。
#[derive(Default)]
pubstructFunctionRegistry{
pubfuncs:HashMap<&'staticstr,Vec<Arc<Function>>>,
#[allow(clippy::type_complexity)]
pubfactories:HashMap<
&'staticstr,
Vec<Box<dynFn(&[usize],&[DataType])->Option<Arc<Function>>+'static>>,
>,
pubaliases:HashMap<&'staticstr,&'staticstr>,
}
三个 item 都是 Hashmap。
其间,funcs 和 factories 都用来存储被注册的函数。不同之处在于 funcs 注册的都是固定参数个数的函数(现在支持最少参数个数为0,最多参数个数为 5),分为 register_0_arg, register_1_arg 等等。而 factories 注册的都是参数不定长的函数(如 concat),调用 register_function_factory 函数。
由于一个函数可能有多个别名(如 minus 的别名有 subtract 和 neg),因此有了 alias,它的 key 是某个函数的别名,v 是当时的存在的函数名,调用 register_aliases 函数。
别的, 依据不同的功能需求, 咱们供给了不同级别的 register api。
函数构成
已知 funcs 的 value 是函数主体,咱们来看一下 Function 在 Databend 中是怎么构建的。
pubstructFunction{
pubsignature:FunctionSignature,
#[allow(clippy::type_complexity)]
pubcalc_domain:Box<dynFn(&[Domain])->Option<Domain>>,
#[allow(clippy::type_complexity)]
pubeval:Box<dynFn(&[ValueRef<AnyType>],FunctionContext)->Result<Value<AnyType>,String>>,
}
其间,signature
包含 函数名,参数类型,回来类型以及函数特性(现在暂未有函数运用特性,仅作为保留位)。要特别注意的是,在注册时函数名需要是小写。而一些 token 会经过 src/query/ast/src/parser/token.rs 转化。
#[allow(non_camel_case_types)]
#[derive(Logos,Clone,Copy,Debug,PartialEq,Eq,Hash)]
pubenumTokenKind{
...
#[token("+")]
Plus,
...
}
以完成 `select 1+2` 的加法函数为比方,`+` 被转化为 Plus,而函数名需要小写,因此咱们在注册时函数名运用 `plus`。
with_number_mapped_type!(|NUM_TYPE|matchleft{
NumberDataType::NUM_TYPE=>{
registry.register_1_arg::<NumberType<NUM_TYPE>,NumberType<NUM_TYPE>,_,_>(
"plus",
FunctionProperty::default(),
|lhs|Some(lhs.clone()),
|a,_|a,
);
}
});
calc_domain 用来计算输出值的输入值的调集。用数学公式描绘的话比方 `y = f(x)` 其间域便是 x 值的调集,能够作为f的参数生成 y 值。这能够使咱们在索引数据时轻松过滤掉不在域内的值,极大提升响应效率。
eval 能够理解成函数的具体完成内容。实质是承受一些字符或许数字,将他们解析成表达式,再转化成别的一组值。
示例
现在在 function-v2 中完成的函数有这几类:arithmetric, array, boolean, control, comparison, datetime, math, string, string_mult_args, variant
以 length 的完成为例:
length 承受一个 String 类型的值为参数,回来一个 Number 类型。姓名为 length,domain 不做限制(由于任何 string 都有长度)最后一个参数是一个闭包函数,作为 length 的 eval 完成部分。
registry.register_1_arg::<StringType,NumberType<u64>,_,_>(
"length",
FunctionProperty::default(),
|_|None,
|val,_|val.len()asu64,
);
在 register_1_arg 的完成中,咱们看到调用的函数是 register_passthrough_nullable_1_arg,函数名包含一个 nullable。而 eval 被 vectorize_1_arg 调用。
注意:请不要手动修正 register_1_arg 所在的文件 src/query/expression/src/register.rs 。由于它是被 src/query/codegen/src/writes/register.rs 生成的。
pubfnregister_1_arg<I1:ArgType,O:ArgType,F,G>(
&mutself,
name:&'staticstr,
property:FunctionProperty,
calc_domain:F,
func:G,
)where
F:Fn(&I1::Domain)->Option<O::Domain>+'static+Clone+Copy,
G:Fn(I1::ScalarRef<'_>,FunctionContext)->O::Scalar+'static+Clone+Copy,
{
self.register_passthrough_nullable_1_arg::<I1,O,_,_>(
name,
property,
calc_domain,
vectorize_1_arg(func),
)
}
这是由于 eval 在实际使用场景中承受的不只是字符或许数字,还可能是 null 或许其他各种类型。而 null 无疑是最特别的一种。而咱们接纳的参数也可能是一个列或许一个值。比方
selectlength(null);
+--------------+
|length(null)|
+--------------+
|NULL|
+--------------+
selectlength(id)fromt;
+------------+
|length(id)|
+------------+
|2|
|3|
+------------+
根据此,假如咱们在函数中无需对 null 类型的值做特别处理,直接运用 register_x_arg 即可。假如需要对 null 类型做特别处理,参阅 try_to_timestamp。
而对于需要在 vectorize 中进行特化的函数则需要调用 register_passthrough_nullable_x_arg,对要完成的函数进行特定的向量化优化。
例如 comparison 函数 regexp 的完成:regexp 接纳两个 String 类型的值,回来 Bool 值。在向量化履行中,为了进一步优化削减重复正则表达式的解析,引入了 HashMap 结构。因此独自完成了 `vectorize_regexp`。
registry.register_passthrough_nullable_2_arg::<StringType,StringType,BooleanType,_,_>(
"regexp",
FunctionProperty::default(),
|_,_|None,
vectorize_regexp(|str,pat,map,_|{
letpattern=ifletSome(pattern)=map.get(pat){
pattern
}else{
letre=regexp::build_regexp_from_pattern("regexp",pat,None)?;
map.insert(pat.to_vec(),re);
map.get(pat).unwrap()
};
Ok(pattern.is_match(str))
}),
);
函数测试
Unit Test
函数相关单元测试在 scalars 目录中。
Logic Test
Functions 相关的 logic 测试在 02_function 目录中。
关于 Databend
Databend 是一款开源、弹性、低成本,根据对象存储也能够做实时分析的新式数仓。期待您的重视,一同探索云原生数仓解决方案,打造新一代开源 Data Cloud。
-
Databend 文档:databend.rs/
-
Twitter:twitter.com/Datafuse_La…
-
Slack:datafusecloud.slack.com/
-
Wechat:Databend
-
GitHub :github.com/datafuselab…