首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Apache Doris】自定义函数之C++ UDF详解

【Apache Doris】自定义函数之C++ UDF详解

作者头像
一臻数据
发布于 2024-12-24 07:19:44
发布于 2024-12-24 07:19:44
20400
代码可运行
举报
文章被收录于专栏:一臻数据一臻数据
运行总次数:0
代码可运行

导读 本文主要分享 Apache Doris 1.2版本之前如何构建 C++ UDF。


一、背景信息

【Apache Doris】自定义函数之JAVA UDF详解 发布后,有小伙伴表示由于各种因素还无法升级至 >= 1.2.0 的Doris版本,可否来个 1.2.0 之前的 C++ UDF 详解?安排!

C++自定义UDF函数主要适用于1.2.0版本之前,用户需要的分析能力 Doris 并不具备的场景,例如Tableau通过固化SQL直连Doris查询时出现部分函数不兼容问题。用户可以自行根据自己的需求,实现自定义的函数,并且通过 UDF 框架注册到 Doris 中,来扩展 Doris 的能力,并解决用户分析需求。

二、环境信息

1.硬件信息

  • CPU:48C
  • 内存:256G

2.软件信息

  • 系统:CentOS
  • Apache Doris版本:0.15

三、IDE准备

C++编程可以选择Visual Studio、CLion或Code::Blocks等IDE。

本文选择轻量级的Code::Blocks演示,环境准备好后来个传统美学:

四、C++ UDF 开发流程

1.源码准备

以官方源码为例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git clone https://github.com/apache/
incubator-doris/tree/branch-0.15/contrib/udf/src/udf_sample

2.文件上传

根据官文将文件放置对应的路径下,注意修改CMakeLists.txt文件,以官文内容为主。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
├── thirdparty
│ │── include
│ │ └── udf.h
│ └── lib
│   └── libDorisUdf.a
└── udf_samples
  ├── CMakeLists.txt
  ├── uda_sample.cpp
  ├── uda_sample.h
  ├── udf_sample.cpp
  └── udf_sample.h

3.文件编译

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#进入build文件夹
cd /opt/doris/udf/udf_samples/build

#生成Makefile
cmake ../

#生成对应动态库
make

#编译结果
├── thirdparty
├── udf_samples
  └── build
    └── src
      └── udf_samples
        ├── libudasample.so
        └── libudfsample.so

4.服务搭建

由于<1.2版本的doris client需要http服务获取so动态库,故需搭建nginx或其它http服务(过程省略)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#安装部署nginx步骤省略
 server {
        listen       8088;
        server_name  localhost;

        location /udf {
          alias   /opt/doris/udf;
        }
        
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
         root   html;
        }
   }

5.函数使用

创建 UDF 函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE FUNCTION 
MyADD00(INT,INT) 
RETURNS INT PROPERTIES ( 
"symbol" = "_ZN9doris_udf666",
"object_file" = "http://nginx_ip:8088/
udf/udf_samples/build/src/udf_samples/libudfsample.so" );

使用UDF函数:

五、自定义 TIME_TO_SEC 函数

TIME_TO_SEC:函数实现传入一个时间参数,将其时间部分转换成秒的UDF。

1.源码开发 & 实现一

(1) 主函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//time_to_sec 的语法格式
//  TIME_TO_SEC(time)
//语法格式说明
//time:传入时间,如果传入了日期部分,也不会管,只将时间部分转换成秒
//重点:是指将传入的时间转换成距离当天00:00:00的秒数,00:00:00为基数,等于 0 秒

#include <iostream>
#include <string>
#include <regex>
using namespace std;

int time_to_sec(string text)
{
    // clear other str
    regex r("^((?![0-9]{2}:[0-9]{2}:[0-9]{2}).)*");
    string time = regex_replace(text, r, "");
    cout << time << endl;

    // handle abnormal
    if(time.length() != 8)
        return NULL;

    // get hh mm ss
    int HH = atoi(time.substr(0,2).c_str());
    int MM = atoi(time.substr(3,2).c_str());
    int SS = atoi(time.substr(6,2).c_str());

    // return sum sec
    return HH*3600 + MM*60 + SS;
}

int main()
{
    cout<<time_to_sec("1987-01-01 00:39:38")<<endl;
    return 0;
}

(2) UDF头文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once

#include "udf.h"
#include <bits/stdc++.h>

namespace doris_udf {

IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time);

/// --- Prepare / Close Functions ---
/// ---------------------------------

/// The UDF can optionally include a prepare function. 
/// The prepare function is called
/// before any calls to the UDF to evaluate values.
void AddUdfPrepare
(FunctionContext* context, FunctionContext::FunctionStateScope scope);

/// The UDF can also optionally include a close function. 
/// The close function is called
/// after all calls to the UDF have completed.
void AddUdfClose
(FunctionContext* context, FunctionContext::FunctionStateScope scope);

}

(3) UDF源文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "time_to_sec.h"

namespace doris_udf {

IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time) {
    // handle null
    if (time.is_null) {
        return IntVal::null();
    }

    // clear other str
    using namespace std;
    const string timestr((char *)time.ptr);
    const regex r("^((?![0-9]{2}:[0-9]{2}:[0-9]{2}).)*");
    const string replace_str = "";
    string hms_time = regex_replace(timestr, r, replace_str);

    // handle str abnormal
    if(hms_time.length() != 8) {
        return IntVal::null();
    }

    // get hh mm ss
    int HH = atoi(hms_time.substr(0,2).c_str());
    int MM = atoi(hms_time.substr(3,2).c_str());
    int SS = atoi(hms_time.substr(6,2).c_str());

    // return sum sec
    return HH*3600 + MM*60 + SS;
}

/// --- Prepare / Close Functions ---
/// ---------------------------------
void AddUdfPrepare
(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
void AddUdfClose
(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
}

(4) 小结

不建议使用,doris对其中的regex相关函数并不友好,会直接导致be core:

2.源码开发 & 实现二

(1) 主函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//time_to_sec 的语法格式
//  TIME_TO_SEC(time)
//语法格式说明
//time:传入时间,如果传入了日期部分,也不会管,只将时间部分转换成秒
//重点:是指将传入的时间转换成距离当天00:00:00的秒数,00:00:00为基数,等于 0 秒

#include <iostream>
#include <string>
#include <regex>
using namespace std;

int time_to_sec(string text)
{
   // clear other str
   string segSign = ":";
   string::size_type pos1 = text.find(segSign);

   if(pos1 == string::npos)
      cout << "没找到!" << endl;
   else
      cout << "找到了!下标:" << pos1<<endl;

    string time = text.substr(pos1-2,8);
    cout << time << endl;

    // handle abnormal
    if(time.length() != 8)
       return NULL;

    // get hh mm ss
    int HH = atoi(time.substr(0,2).c_str());
    int MM = atoi(time.substr(3,2).c_str());
    int SS = atoi(time.substr(6,2).c_str());

    // return sum sec
    return HH*3600 + MM*60 + SS;
}

int main()
{
    cout<<time_to_sec("1987-01-01 00:39:38")<<endl;
    return 0;
}

(2) UDF头文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once

#include "udf.h"
#include <bits/stdc++.h>

namespace doris_udf {

IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time);

/// --- Prepare / Close Functions ---
/// ---------------------------------

/// The UDF can optionally include a prepare function. 
/// The prepare function is called
/// before any calls to the UDF to evaluate values.
void AddUdfPrepare
(FunctionContext* context, FunctionContext::FunctionStateScope scope);

/// The UDF can also optionally include a close function. 
/// The close function is called
/// after all calls to the UDF have completed.
void AddUdfClose
(FunctionContext* context, FunctionContext::FunctionStateScope scope);

}

(3) UDF源文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "time_to_sec.h"

namespace doris_udf {

IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time) {
    // handle null
    if (time.is_null) {
        return IntVal::null();
    }

    // clear other str
    using namespace std;
    string timestr((char *)time.ptr);
    string segSign = ":";
    string::size_type pos = timestr.find(segSign);
    string hms_time;
    if(pos == string::npos)
        return IntVal::null();
     else
        hms_time = timestr.substr(pos-2,8);

    // handle str abnormal
    if(hms_time.length() != 8) {
        return IntVal::null();
    }

    // get hh mm ss
    int HH = atoi(hms_time.substr(0,2).c_str());
    int MM = atoi(hms_time.substr(3,2).c_str());
    int SS = atoi(hms_time.substr(6,2).c_str());

    // return sum sec
    IntVal result;
    result.val = HH*3600 + MM*60 + SS;
    return {result.val};
}

/// --- Prepare / Close Functions ---
/// ---------------------------------
void AddUdfPrepare
(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
void AddUdfClose
(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
}

(4) 小结

基本完全使用字符串的API实现,简单高效并且兼容性较好,最终选定实现二。

3.编译结果

4.函数使用

创建 UDF 函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE FUNCTION 
TIME_TO_SEC(String) 
RETURNS INT PROPERTIES ( 
"symbol" = "_ZN9doris_udf999,
"object_file" = "http://nginx_ip:8088/libtime_to_sec.so" );

原先不兼容TIME_TO_SEC的Tableau固化SQL,现在可以正常运行。

六、常见问题

1.ROS问题

make时出现ROS问题;

注意:需要在CMakeFiles.txt头部加一条SET(CMAKE_CXX_FLAGS “-std=c++0x”)命令解决。

2.路径问题

将CMakeFiles.txt的相对路径都调整为绝对路径或新增路径变量。

七、总结

自定义 C++ UDF 的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而 UDF 的作用域是 DB 内部;

1.2后的新版本不建议使用原生C++ UDF,因为兼容性较差、GLIBC一升级就没法用了;建议使用JAVA UDF。

最后,愿各位看官们在2024新的一年里:身体健康、阖家欢乐、心想事成!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-01-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一臻数据 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Apache Doris】自定义函数之JAVA UDF详解
导读 本文主要分享 Apache Doris 1.2版本之后如何构建 JAVA UDF。
一臻数据
2024/12/24
9040
【Apache Doris】自定义函数之JAVA UDF详解
用户自定义函数UDF
Hive支持的函数除了内置函数,允许编写用户自定义函数(User Define Function)来扩充函数的功能。
十里桃花舞丶
2021/09/10
3K0
Hive自定义函数UDF、UDTF、UDAF入门
详细讲解Hive自定义函数UDF、UDTF、UDAF基础知识,带你快速入门,首先在Hive中新建表”apache_log”
星哥玩云
2022/08/16
3.1K0
Hive自定义函数UDF、UDTF、UDAF入门
0518-如何在Impala中使用UDF获取SessionId
Hive在UDF中获取sessionId可以直接使用提供的java API,但是该UDF如果移植到Impala中是无法获取到Impala连接的SessionId的,要想获取Impala的SessionId,需要用C++来编写。
Fayson
2019/11/27
1.2K0
Mysql的基本函数–与自定义函数
类似于java的方法将一组逻辑语句封装在方法体 对外暴露方法名 事先提供好的一些功能可以直接使用 函数可以用在select 语句及其子句上 也可以用在update ,delete 语句当中
全栈程序员站长
2022/09/18
2.9K0
Mysql的基本函数–与自定义函数
Apache Pig如何通过自定义UDF查询数据库(五)
image.png GMV(一定时间内的成交总额)是一个衡量电商网站营业收入的一项重要指标,例如淘宝,京东都有这样的衡量标准,感兴趣的朋友可以自己科普下这方面的概念知识。 当然散仙今天,并不是来解释概念的,而是记录下最近工作的一些东西,原来我们平台的GMV只有一个总的成交金额,并没有细分到各个系统的GMV的比重,比如搜索端,推荐端,移动端等等。 通过细粒度的分析各个系统所占的比重,对于指导各个系统完善和发展有一定的重要意义,这里不就深说了,下面先来看下散仙分析的搜索gmv的数据布局方式。
我是攻城师
2018/05/11
1.2K0
【Hive】Hive 的内置函数
这篇文章主要介绍 Hive 的一些内置函数,其目的在于了解和知道大概有哪些内置函数,以及我们能够做哪些操作。
阿泽 Crz
2020/07/20
1.8K0
Hive 用户自定义函数 UDF,UDAF
Hive有UDF:(普通)UDF,用户自定义聚合函数(UDAF)以及用户自定义生表函数(UDTF)。它们所接受的输入和生产的输出的数据行的数量的不同的。 UDF UDF操作作用于单个数据行,且产生一个数据行作为输出。 例:strip 我们这里创造一个strip UDF package Hive_UDF; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.ql.exec.UDF; import o
小爷毛毛_卓寿杰
2019/02/13
1.3K0
Hive 用户自定义函数 UDF,UDAF
自定义 java 日期、时间 处理函数集
废话少说,在shell下很容易: june@deepin :~> date -d@1353027149 2012年 11月 16日 星期五 08:52:29 CST june@deepin :~> 但是 java 下比较折腾,网上转来抄去的代码也都是错误一大堆。。。 java代码如下: import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; i
用户1177713
2018/02/24
9660
玩转Mysql系列 - 第10篇:常用的几十个函数详解
如果忽略mode参数,默认情况下WEEK函数将使用default_week_format系统变量的值。 要获取default_week_format变量的当前值,请使用SHOW VARIABLES语句如下:
路人甲Java
2019/09/18
3.2K0
跨平台 C++ 纯socket 访问webapi json
webapi.h #pragma once #include <string> #include <list> #include <map> class webapi { class urlitem { public: urlitem() { } //std::string url; std::string fullpath; // http://www.pic98.com:8080/file/beautileg/samansh
xiny120
2019/06/11
1.9K0
MySQL函数及用法示例(收藏大全)
1、字符串函数 ascii(str) 返回字符串str的第一个字符的ascii值(str是空串时返回0) mysql> select ascii('2');   -> 50 mysql> select ascii(2);   -> 50 mysql> select ascii('dete');   -> 100
java思维导图
2018/08/16
8340
C语言字符串处理提取时间(ffmpeg返回的时间字符串)
需求:有一个 “00:01:33.90” 这样格式的时间字符串,需要将这个字符串的时间值提取打印出来(提取时、分、秒、毫秒)。
DS小龙哥
2023/12/26
3580
C语言字符串处理提取时间(ffmpeg返回的时间字符串)
一文带你入坑JDK8的新日期时间类 LocalDate、LocalTime、LocalDateTime
参考 https://blog.csdn.net/duan196_118/article/details/111597682 https://blog.csdn.net/qq_24754061/article/details/95500209 https://xijia.blog.csdn.net/article/details/106007147
时间静止不是简史
2023/02/23
5.1K0
一文带你入坑JDK8的新日期时间类 LocalDate、LocalTime、LocalDateTime
C++搭建集群聊天室(十五):客户端
直接塞一个文件里面。 client.cpp #include "json.hpp" #include <iostream> #include <thread> #include <string> #include <vector> #include <chrono> #include <ctime> #include <unordered_map> #include <functional> using namespace std; using json = nlohmann::json; #includ
看、未来
2021/09/18
8950
Mysql-7-mysql函数
1.数学函数   用来处理数值数据方面的运算,主要的数学函数有:绝对值函数,三角函数,对数函数,随机函数。使用数学函数过程中,如果有错误产生,该函数会返回null值。 数学函数 功能介绍 组合键 abs(x) 返回x的绝对值 整数本身 pi() 返回圆周率 返回pa的值,默认显示6位 sqrt(x) 返回非负数x的二次方根 如为负数,返回null mod(x,y) 返回x/y的模,即相除余数
用户1173509
2018/01/17
8.2K0
C++ 一个例子说明.c_str()函数
该文介绍了C++中的字符串操作,包括字符串的赋值、拼接、比较、查找、转换等操作。同时介绍了在C++中使用字符串时需要注意的一些问题,如字符串指针、const char*类型、字符串长度等。
chaibubble
2018/01/02
1.5K0
关于Impala的use_local_tz_for_unix_timestamp_conversions参数探究
使用过Impala的同学都知道,impala默认对于timestamp都是当成UTC来处理的,并不会做任何的时区转换。这也就是说,当你写入一个timestamp的数据时,impala就会把它当成是UTC的时间存起来,而不是本地时间。但是Impala同时又提供了use_local_tz_for_unix_timestamp_conversions和convert_legacy_hive_parquet_utc_timestamps这两个参数来处理timestamp的时区问题。convert_legacy_hive_parquet_utc_timestamps这个参数主要是用来处理hive写parquet文件,impala读取的问题,本文暂不展开,这里主要介绍下use_local_tz_for_unix_timestamp_conversions这个参数的作用。首先,我们来看下官方的解释: The --use_local_tz_for_unix_timestamp_conversions setting affects conversions from TIMESTAMP to BIGINT, or from BIGINT to TIMESTAMP. By default, Impala treats all TIMESTAMP values as UTC, to simplify analysis of time-series data from different geographic regions. When you enable the --use_local_tz_for_unix_timestamp_conversions setting, these operations treat the input values as if they are in the local time zone of the host doing the processing. See Impala Date and Time Functions for the list of functions affected by the --use_local_tz_for_unix_timestamp_conversions setting. 简单来说,就是开启了这个参数之后(默认false,表示关闭),当SQL里面涉及到了timestamp->bigint/bigint->timestamp的转换操作时,impala会把timestamp当成是本地的时间来处理,而不是UTC时间。这个地方听起来似乎很简单,但是实际理解起来的时候非常容易出错,这里笔者将结合自己的实际测试结果来看一下use_local_tz_for_unix_timestamp_conversions这个参数究竟是如何起作用的。
skyyws
2022/05/20
6760
java对时间的操作,提供给你工具类,直接调用方法就可以操作时间了
-创建 SimpleDateFormat 对象时必须指定转换格式。 -转换格式区分大小写,yyyy 代表年份,MM 代表月份,dd 代表日期,HH 代表 24 进制的小时,hh 代表 12 进制的小时,mm 代表分钟,ss 代表秒。
一写代码就开心
2020/11/20
3.1K0
java对时间的操作,提供给你工具类,直接调用方法就可以操作时间了
如何给Apache Pig自定义UDF函数?
近日由于工作所需,需要使用到Pig来分析线上的搜索日志数据,本人本打算使用hive来分析的,但由于种种原因,没有用成,而Pig(pig0.12-cdh)本人一直没有接触过,所以只能临阵磨枪了,花了两天时间,大致看完了pig官网的文档,在看文档期间,也是边实战边学习,这样以来,对pig的学习,会更加容易,当然本篇不是介绍如何快速学好一门框架或语言的文章,正如标题所示,本人打算介绍下如何在Pig中,使用用户自定义的UDF函数,关于学习经验,本人会在后面的文章里介绍。
星哥玩云
2022/07/03
5200
如何给Apache Pig自定义UDF函数?
推荐阅读
相关推荐
【Apache Doris】自定义函数之JAVA UDF详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档