Apache Doris ネイティブ C++ UDF のコーディング (2)
1. 環境情報
1.1 ハードウェア情報
- CPU:4C
- CPUモデル:x64(AVX2)
- メモリ:10GB
- ハードディスク:66GB SSD
1.2 ソフトウェア情報
- Linuxバージョン:CentOS-7
- Apahce Doris バージョン: 0.15-リリース
- コードブロックのバージョン: 20.03mingw
2、カスタム TIME_TO_SEC 関数
時間パラメータを渡し、その時間部分を秒に変換する UDF を実装します。
2.1 ソースコードの開発と実装 1
2.1.1 main 関数をテストする
//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.1.2 UDFヘッダファイル
C++
#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);
}
2.1.3 UDFソースファイル
C++
#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) {
}
}
2.1.4 実装方法の概要
使用はお勧めできません。doris は正規表現関連の関数に適していないため、be のすべてのノードが直接クラッシュする原因になります。
2.2 ソースコードの開発と実装 II
2.2.1 main 関数のテスト
C++
//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.2.2 UDFヘッダファイル
C++
#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);
}
2.2.3 UDFソースファイル
C++
#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) {
}
}
2.2.4 実装方法の概要2
基本的には、文字列を使用した API 実装がシンプルで効率的で互換性が高いため、最終的に 2 番目の実装が選択されます。
3. コンパイル結果
4. 機能の使い方
4.1 UDF関数の作成
CREATE FUNCTION
TIME_TO_SEC(String)
RETURNS INT PROPERTIES (
"symbol" = "_ZN9doris_udf11TIME_TO_SECEPNS_15FunctionContextERKNS_9StringValE",
"object_file" = "http://10.192.119.68:8088/udf/udf_samples/build/src/udf_samples/libtime_to_sec.so" );
4.2 UDF 関数の使用
TIME_TO_SEC と互換性がなかった Tableau の Solidified SQL が正常に実行できるようになりました。
V. まとめ
- カスタム C++ UDF の使用は通常の関数の使用と一貫しています。唯一の違いは、組み込み関数のスコープがグローバルであるのに対し、UDF のスコープは DB 内にあることです。
- 1.2 以降の新しいバージョンでは、互換性が低く、アップグレードすると GLIBC が機能しなくなるため、ネイティブ C++ UDF を使用することはお勧めできません。JAVA UDF を使用することをお勧めします。
Apache Doris カスタム C++ UDF のコーディングは終了しました。レビュー プロセス中に問題が発生した場合は、メッセージを残して連絡してください。