clickhouse源码:函数分析和自定义函数UDF

2023-10-27

clickhouse函数介绍

clickhouse官方提供了许多的函数,包括常规的数学函数,聚合函数,时间函数,逻辑函数,比较函数等等,关于官方的函数可以在官方文档中查看:

官方文档

当然随着clickhouse的流行,国内也有不少的博主已经开始介绍函数的使用:

clickhouse function

clickhouse高阶函数

clickhouse详细函数介绍

clickhouse还支持一些自定义的逻辑函数:

例如:

select arrayFilter(x -> x = 10,[1,2,3,4,5,10]);

返回的结果为:

[10]

类似的函数还有多个,可以传入lambda表达式,上述的函数为过滤数组中等于10的数。

 

clickhouse自定义函数

clickhouse除了上述的函数外,修改源码可以编写自己需要的函数,首先来看源码中一个简单的函数的实现过程。注:源码基于19.5.3.1版本。

  • 源码分析sleep()函数:

自定义函数存在于src文件夹下的Functions文件夹中。

sleep.h文件:

#include <unistd.h>
#include <Functions/IFunction.h>
#include <Functions/FunctionHelpers.h>
#include <Columns/ColumnConst.h>
#include <DataTypes/DataTypesNumber.h>
#include <Common/FieldVisitors.h>
#include <IO/WriteHelpers.h>


namespace DB
{

namespace ErrorCodes
{
    extern const int TOO_SLOW;
    extern const int ILLEGAL_COLUMN;
    extern const int BAD_ARGUMENTS;
}

/** sleep(seconds) - the specified number of seconds sleeps each block.
  */

enum class FunctionSleepVariant
{
    PerBlock,
    PerRow
};

template <FunctionSleepVariant variant>
class FunctionSleep : public IFunction
{
public:
    static constexpr auto name = variant == FunctionSleepVariant::PerBlock ? "sleep" : "sleepEachRow";
    static FunctionPtr create(const Context &)
    {
        return std::make_shared<FunctionSleep<variant>>();
    }

    /// Get the name of the function.
    String getName() const override
    {
        return name;
    }

    /// Do not sleep during query analysis.
    bool isSuitableForConstantFolding() const override
    {
        return false;
    }

    size_t getNumberOfArguments() const override
    {
        return 1;
    }

    DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
    {
        WhichDataType which(arguments[0]);

        if (!which.isFloat()
            && !which.isNativeUInt())
            throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName() + ", expected Float64",
                ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);

        return std::make_shared<DataTypeUInt8>();
    }

    void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
    {
        const IColumn * col = block.getByPosition(arguments[0]).column.get();

        if (!col->isColumnConst())
            throw Exception("The argument of function " + getName() + " must be constant.", ErrorCodes::ILLEGAL_COLUMN);

        Float64 seconds = applyVisitor(FieldVisitorConvertToNumber<Float64>(), static_cast<const ColumnConst &>(*col).getField());

        if (seconds < 0)
            throw Exception("Cannot sleep negative amount of time (not implemented)", ErrorCodes::BAD_ARGUMENTS);

        size_t size = col->size();

        /// We do not sleep if the block is empty.
        if (size > 0)
        {
            /// When sleeping, the query cannot be cancelled. For abitily to cancel query, we limit sleep time.
            if (seconds > 3.0)   /// The choice is arbitrary
                throw Exception("The maximum sleep time is 3 seconds. Requested: " + toString(seconds), ErrorCodes::TOO_SLOW);

            UInt64 useconds = seconds * (variant == FunctionSleepVariant::PerBlock ? 1 : size) * 1e6;
            ::usleep(useconds);
        }

        /// convertToFullColumn needed, because otherwise (constant expression case) function will not get called on each block.
        block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(size, 0u)->convertToFullColumnIfConst();
    }
};

}

sleep.cpp文件:

#include <Functions/sleep.h>
#include <Functions/FunctionFactory.h>


namespace DB
{

void registerFunctionSleep(FunctionFactory & factory)
{
    factory.registerFunction<FunctionSleep<FunctionSleepVariant::PerBlock>>();
}

}

分析:

cpp文件中需要将函数注册即registerFunctionSleep函数。h文件中需要实现IFunction中的一些方法,主要有getName函数名,getNumberOfArguments传入参数,getReturnTypeImpl返回的类型,executeImpl为具体的执行过程,官方的sleep函数的实现还是比较简单明了的,主要部分函数的类型,在自定义中需要一一对应。

关于isSuitableForConstantFolding,sleep函数调用时是个反例,返回为false,类似于分析时是否应该评估该函数,具体的还需要再研究,以下是官方的解析,在IFunction.h中:

/** Should we evaluate this function while constant folding, if arguments are constants?
  * Usually this is true. Notable counterexample is function 'sleep'.
  * If we will call it during query analysis, we will sleep extra amount of time.
  */
virtual bool isSuitableForConstantFolding() const { return true; }
  • 简单的无参数自定义函数sayHello():

先看执行效果:

select sayHello();

返回结果为:

hello clickhouse by iceyung test!

具体的实现代码:

sayHello.h:

#include <Functions/IFunction.h>
#include <Functions/FunctionHelpers.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypesNumber.h>

namespace DB
{

    class FunctionSayHello : public IFunction
    {
    public:
        static constexpr auto name = "sayHello";
        static FunctionPtr create(const Context &)
        {
            return std::make_shared<FunctionSayHello>();
        }

        /// Get the name of the function.
        String getName() const override
        {
            return name;
        }
        
        size_t getNumberOfArguments() const override
        {
            return 0;
        }

        DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override
        {
            return std::make_shared<DataTypeString>();
        }

        void executeImpl(Block & block, const ColumnNumbers & /*arguments*/, size_t result, size_t /*input_rows_count*/) override
        {
            block.getByPosition(result).column = DataTypeString().createColumnConst(1, "hello clickhouse by iceyung test!");
        }
    };

}

sayHello.cpp:

#include <Functions/sayHello.h>
#include <Functions/FunctionFactory.h>

namespace DB
{
void registerFunctionSayHello(FunctionFactory & factory)
{
    factory.registerFunction<FunctionSayHello>(FunctionFactory::CaseInsensitive);
}

}

简单分析:

cpp中registerFunctionSayHello注册函数,registerFunctionsString.cpp中注册该函数,当然你也可以在其它的文件中注册,注册比较简单,直接仿照正常的进行注册即可,此处不再赘述。

注意返回参数和返回类型的问题,返回的类型可在src的DataTypes中找到,最终executeImpl返回的类型为ColumnPtr类型,不能简单的输出,具体也可以看目前已有的函数的样例,找到符合自己的参数。

  • 有参数自定义函数sayHello(String str):

分析同上,将传入的参数打印出,效果如下:

select sayHello('clickhouse args');

返回:

hello clickhouse args by iceyung test!

主要为sayHello.h修改,如下:

#include <Functions/IFunction.h>
#include <Functions/FunctionHelpers.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypesNumber.h>

namespace DB
{

    class FunctionSayHello : public IFunction
    {
    public:
        static constexpr auto name = "sayHello";
        static FunctionPtr create(const Context &)
        {
            return std::make_shared<FunctionSayHello>();
        }

        /// Get the name of the function.
        String getName() const override
        {
            return name;
        }

        size_t getNumberOfArguments() const override
        {
            return 1;
        }

        DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
        {
            WhichDataType which(arguments[0]);
            if (!which.isString())
                throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName() + ", expected String",
                                ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
            return std::make_shared<DataTypeString>();
        }

        void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
        {
            const auto & col = static_cast<const ColumnConst *>(block.getByPosition(arguments[0]).column.get())->getValue<String>();

            block.getByPosition(result).column = DataTypeString().createColumnConst(input_rows_count, "hello " + col  + " by iceyung test!");
        }
    };

}

当出错了或者参数类型不对时,可提示错误:

sql> select sayHello()
[2020-03-01 23:47:56] Code: 42, e.displayText() = DB::Exception: Number of arguments for function sayHello doesn't match: passed 0, should be 1 (version 19.5.3.1)
sql> select sayHello(1)
[2020-03-01 23:48:04] Code: 43, e.displayText() = DB::Exception: Illegal type UInt8 of argument of function sayHello, expected String (version 19.5.3.1)

 

注意:添加新的文件后需要重新Cmake编译才能正常获取编译的内容。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

clickhouse源码:函数分析和自定义函数UDF 的相关文章

随机推荐

  • ELK+Wazuh搭建笔记

    本文借鉴https www cnblogs com backlion p 10394369 html 在此谢谢大佬指明方向 本人又总结了wazuh界面上opencat Vulnerabilities的后台配置情况 以及agent版本升级情况
  • 页面刷新 Vuex 数据丢失

    用 Vuex 的时候发现一个问题 在页面刷新的时候 vuex 的 state 里面存储的数据会丢失 问题产生原因 因为 store 里的数据是保存在运行内存中的 当页面刷新时 页面会重新加载 vue 实例 store 里面的数据就会被重新赋
  • 子类覆盖父类方法时参数以及方法的访问权限问题

    一 子类覆盖父类可继承方法时子类同名方法的访问权限必须大等于父类 父类private不被子类继承 也无覆盖一说 报错 Fatal error Access level to Sun3 a must be protected as in cl
  • 高精度高精度乘法(C++)

    高精度加法以及高精度单精度乘法这里就不过多赘述了 今天咱们的主角是高精度高精度乘法 咱们先回顾一下竖式乘法 我们先不急着进位 先来看看 对应位置上的数字都是这么来的 对于不足位我们补充零后 不难发现 对应位置的最后答案 是由该位置起以后的全
  • Timing Borrow的理解

    在集成电路设计中 静态时序分析 Static Timing Analysis STA 是一种常用的验证方法 用于确保芯片在运行时的时序约束得到满足 在STA分析过程中 Timing Borrow是一种时序收敛技术 即在某些情况下 可以借用下
  • PowerMock--Mock静态方法

    1 PowerMock静态方法 写单元测试时 经常会遇到测试方法体内调用了某些工具类的静态方法的情况 而这些静态方法一般是读取配置中心里的文件数据 或者是一些其他涉及到需要启动项目的操作 往往这些操作会造成Mock单元测试的不彻底 有些流水
  • 排序算法之快速排序

    高快省的排序算法 有没有既不浪费空间又可以快一点的排序算法呢 那就是 快速排序 啦 光听这个名字是不是就觉得很高端呢 假设我们现在对 6 1 2 7 9 3 4 5 10 8 这个10个数进行排序 首先在这个序列中随便找一个数作为基准数 不
  • 【福利】南昌大学NCU主题电子笔记本,免费获取!

    转载请注明出处 小锋学长生活大爆炸 xfxuezhang cn 禁止转卖 恰烂钱可耻 下载链接 https pan baidu com s 1RWVrQgbmth CwoTcY 1yVw pwd 5gu8
  • MATLAB——基于自回归积分移动平均线 (ARIMA) 模型对数据进行预测

    通过1985至20222年各年的参加测试人数为实际数据 通过ARIMA模型对未来十年参加测试人数进行预测 clc clear Y data 2 N length Y t 1985 2022 figure 1 plot t Y title 高
  • 数据库性能优化的误区!

    常见的数据库系统优化中的一些观点 系统性能出现问题进行优化 一定要深入了解数据库内部参数 等待事件 Latch 缓冲池 trace文件 查询 优化引擎等底层细节 这种观点往往出自数据库 高手 这部分人以了解数据库底层实现细节而感到非常骄傲
  • Python文件指针

    实验文件data txt如下 This file is data txt hello world My name is mark 实验一 文件指针初始化 f open data txt r print f tell f write nice
  • matlab读取.dat数据

    任务 手上有一个dat后缀名文件 需要导入matlab 并对其中的每一列数据进行处理 解决方案 导入数据 S1 点击 导入数据 按钮 弹出导入数据窗口 S2 选择 范围 因为第一行是标题 所以我不想导入 导入范围从A2开始 到G436结束
  • Notes Twenty one days-渗透攻击-红队-权限提升

    Notes Twenty one days 渗透攻击 红队 权限提升 dayu 作者 大余 时间 2020 10 7 请注意 对于所有笔记中复现的这些终端或者服务器 都是自行搭建的环境进行渗透的 我将使用Kali Linux作为此次学习的攻
  • 获取系统磁盘类型信息以及判断是否是U盘

    使用WIN API函数实现 include
  • Web渗透测试实战——(1)Web渗透测试简介

    一 什么是渗透测试 渗透测试 penetration testing 是对计算机系统的一种授权攻击 旨在评估系统 网络的安全性 执行测试以识别漏洞及其带来的风险 一般而言 渗透测试过程分为五个阶段 包括识别目标系统 检测存在的漏洞以及每个漏
  • Java基础系列30-单列 Collection集合

    文章目录 一 集合的概述 1 1 为什么会出现集合类 1 2 集合类体系结构图 二 Collection集合 2 1 Collection集合入门 2 2 Collection集合的成员方法 2 3 Collection集合的遍历 2 4
  • vue使用vue-amap 高德地图进行选点和搜索

    vue使用vue amap进行地图点的搜索和点击选点 npm install vue amap save 下载npm 包 在main js主文件中进行引用 import VueAMap from vue amap Vue use VueAM
  • C++连接CTP接口实现简单量化交易(行情、交易、k线、策略)

    对于量化交易来说 量化策略和技术系统缺一不可 为了知其所以然 本文实现了一个C 连接CTP接口进行仿真交易的demo 从接收行情 下订单 数据处理到添加策略 挂载运行交易等多个环节来看一下量化交易的最简单流程 管中窥豹 一探究竟 准备工作
  • NetworkManager 使用

    Network Manager Network Manager aims for Network Connectivity which Just Works The computer should use the wired network
  • clickhouse源码:函数分析和自定义函数UDF

    clickhouse函数介绍 clickhouse官方提供了许多的函数 包括常规的数学函数 聚合函数 时间函数 逻辑函数 比较函数等等 关于官方的函数可以在官方文档中查看 官方文档 当然随着clickhouse的流行 国内也有不少的博主已经