Apache Doris ネイティブ C++ UDF のコーディング (2)

1. 環境情報

1.1 ハードウェア情報

  1. CPU:4C
  2. CPUモデル:x64(AVX2)
  3. メモリ:10GB
  4. ハードディスク:66GB SSD

1.2 ソフトウェア情報

  1. Linuxバージョン:CentOS-7
  2. Apahce Doris バージョン: 0.15-リリース
  3. コードブロックのバージョン: 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 のコーディングは終了しました。レビュー プロセス中に問題が発生した場合は、メッセージを残して連絡してください。

おすすめ

転載: blog.csdn.net/ith321/article/details/132164442
おすすめ