16
16
17
17
内置C++函数的参数类型限定为:BOOL类型,数值类型,时间戳日期类型和字符串类型。C++类型SQL类型对应关系如下:
18
18
19
- | SQL类型 | C/C++ 类型 |
20
- | :-------- | :----------------- |
21
- | BOOL | ` bool ` |
22
- | SMALLINT | ` int16_t ` |
23
- | INT | ` int32_t ` |
24
- | BIGINT | ` int64_t ` |
25
- | FLOAT | ` float ` |
26
- | DOUBLE | ` double ` |
19
+ | SQL类型 | C/C++ 类型 |
20
+ | :-------- | :---------- |
21
+ | BOOL | ` bool ` |
22
+ | SMALLINT | ` int16_t ` |
23
+ | INT | ` int32_t ` |
24
+ | BIGINT | ` int64_t ` |
25
+ | FLOAT | ` float ` |
26
+ | DOUBLE | ` double ` |
27
27
| STRING | ` StringRef ` |
28
28
| TIMESTAMP | ` Timestamp ` |
29
29
| DATE | ` Date ` |
44
44
};
45
45
```
46
46
47
+ - 如果参数声明为nullable的,那么所有参数都是nullable的,每一个输入参数其后需要添加bool参数(通常命名为is_null),其顺序为` arg1, arg1_is_null, arg2, arg2_is_null, ... ` 。不可以随意调整参数顺序。
48
+ - 如果返回值声明为nullable的,那么通过参数来返回,并且添加bool参数(通常命名为is_null)来表示返回值是否为null
49
+
50
+ 例如,函数sum有俩个参数,如果参数和返回值设置为nullable的话,单行函数原型如下:
51
+ ``` c++
52
+ extern "C"
53
+ void sum (::openmldb::base::UDFContext* ctx, int64_t input1, bool input1_is_null, int64_t input2, bool input2_is_null, int64_t* output, bool* is_null) {
54
+ ```
55
+
47
56
函数声明:
48
57
* 函数必须用extern "C"来声明
49
58
57
66
```
58
67
- 一次分配空间的最大长度不能超过2M字节
59
68
60
- ** 注** :
61
-
62
- - 如果参数声明为nullable的,那么所有参数都是nullable的,每一个输入参数都添加is_null参数
63
- - 如果返回值声明为nullable的,那么通过参数来返回,并且添加is_null的参数来表示返回值是否为null
64
-
65
- 如函数sum有俩个参数,如果参数和返回值设置为nullable的话,单行函数原型如下:
66
- ``` c++
67
- extern "C"
68
- void sum (::openmldb::base::UDFContext* ctx, int64_t input1, bool is_null, int64_t input2, bool is_null, int64_t* output, bool* is_null) {
69
- ```
70
-
71
69
#### 单行函数开发
72
70
73
71
单行函数(scalar function)对单行数据进行处理,返回单个值,比如 `abs`, `sin`, `cos`, `date`, `year` 等。
@@ -95,6 +93,8 @@ void cut2(::openmldb::base::UDFContext* ctx, ::openmldb::base::StringRef* input,
95
93
}
96
94
```
97
95
96
+ 因为返回值是string类型,所以此处需要通过函数最后一个参数返回。如果返回值是基本类型,通过函数返回值返回,可参考[ test_udf.cc] ( https://github.com/4paradigm/OpenMLDB/blob/main/src/examples/test_udf.cc ) 中的strlength。
97
+
98
98
#### 聚合函数开发
99
99
100
100
聚合函数(aggregate function)对一个数据集(比如一列数据)执行计算,返回单个值,比如 ` sum ` , ` avg ` , ` max ` , ` min ` , ` count ` 等。
@@ -141,9 +141,75 @@ int64_t special_sum_output(::openmldb::base::UDFContext* ctx) {
141
141
return * (reinterpret_cast<int64_t* >(ctx->ptr)) + 5;
142
142
}
143
143
144
+ // Get the third non-null value of all values
145
+ extern "C"
146
+ ::openmldb::base::UDFContext* third_init(::openmldb::base::UDFContext* ctx) {
147
+ ctx->ptr = reinterpret_cast<void* >(new std::vector<int64_t>());
148
+ return ctx;
149
+ }
150
+
151
+ extern "C"
152
+ ::openmldb::base::UDFContext* third_update(::openmldb::base::UDFContext* ctx, int64_t input, bool is_null) {
153
+ auto vec = reinterpret_cast< std::vector<int64_t > * >(ctx->ptr);
154
+ if (!is_null && vec->size() < 3) {
155
+ vec->push_back(input);
156
+ }
157
+ return ctx;
158
+ }
159
+
160
+ extern "C"
161
+ void third_output(::openmldb::base::UDFContext* ctx, int64_t* output, bool* is_null) {
162
+ auto vec = reinterpret_cast< std::vector<int64_t > * >(ctx->ptr);
163
+ if (vec->size() != 3) {
164
+ * is_null = true;
165
+ } else {
166
+ * is_null = false;
167
+ * output = vec->at(2);
168
+ }
169
+ // free the memory allocated in init function with new/malloc
170
+ delete vec;
171
+ }
172
+
173
+ // Get the first non-null value >= threshold
174
+ extern "C"
175
+ ::openmldb::base::UDFContext* first_ge_init(::openmldb::base::UDFContext* ctx) {
176
+ // threshold init in update
177
+ // threshold, thresh_flag, first_ge, first_ge_flag
178
+ ctx->ptr = reinterpret_cast<void* >(new std::vector<int64_t>(4, 0));
179
+ return ctx;
180
+ }
181
+
182
+ extern "C"
183
+ ::openmldb::base::UDFContext* first_ge_update(::openmldb::base::UDFContext* ctx, int64_t input, bool is_null, int64_t threshold, bool threshold_is_null) {
184
+ auto pair = reinterpret_cast< std::vector<int64_t > * >(ctx->ptr);
185
+ if (!threshold_is_null && pair->at(1) == 0) {
186
+ pair->at(0) = threshold;
187
+ pair->at(1) = 1;
188
+ }
189
+ if (!is_null && pair->at(3) == 0 && input >= pair->at(0)) {
190
+ pair->at(2) = input;
191
+ pair->at(3) = 1;
192
+ }
193
+ return ctx;
194
+ }
195
+
196
+ extern "C"
197
+ void first_ge_output(::openmldb::base::UDFContext* ctx, int64_t* output, bool* is_null) {
198
+ auto pair = reinterpret_cast< std::vector<int64_t > * >(ctx->ptr);
199
+ // threshold is null or no value >= threshold
200
+ if (pair->at(1) == 0 || pair->at(3) == 0) {
201
+ * is_null = true;
202
+ } else {
203
+ * is_null = false;
204
+ * output = pair->at(2);
205
+ }
206
+ // * is_null = true;
207
+ // free the memory allocated in init function with new/malloc
208
+ delete pair;
209
+ }
144
210
```
145
211
146
- 更多udf/udaf实现参考[这里](../../../src/examples/test_udf.cc)。
212
+ 如上所示,聚合函数init函数仅单参数,无论是几个参数的聚合函数,init中都只有一个参数UDFContext。update函数参数个数和类型,与聚合函数的参数个数和类型一致。同样的,如果想要聚合函数nullable,每个参数都需要添加一个bool参数,表示该参数是否为null。output函数只会有一个输出参数或返回值,nullable同理。 更多udf/udaf实现参考[这里](../../../src/examples/test_udf.cc)。
147
213
148
214
### 编译动态库
149
215
- 拷贝include目录 `https://github.com/4paradigm/OpenMLDB/tree/main/include` 到某个路径下,下一步编译会用到。如/work/OpenMLDB/
@@ -185,15 +251,15 @@ g++ -shared -o libtest_udf.so examples/test_udf.cc -I /work/OpenMLDB/include -st
185
251
### 注册、删除和查看函数
186
252
注册函数使用[ CREATE FUNCTION] ( ../openmldb_sql/ddl/CREATE_FUNCTION.md )
187
253
188
- 注册单行函数
254
+ 注册单行函数,cut2函数将字符串的前两个字符返回:
189
255
``` sql
190
256
CREATE FUNCTION cut2 (x STRING) RETURNS STRING OPTIONS (FILE= ' libtest_udf.so' );
191
257
```
192
- 注册聚合函数
258
+ 注册聚合函数,special_sum函数初始为10,再将输入的值累加,并且最后加上5返回(演示函数,无特殊意义):
193
259
``` sql
194
260
CREATE AGGREGATE FUNCTION special_sum(x BIGINT ) RETURNS BIGINT OPTIONS (FILE= ' libtest_udf.so' );
195
261
```
196
- 注册聚合函数,并且输入参数和返回值都支持null
262
+ 注册聚合函数,并且输入参数和返回值都支持null,third函数返回第三个非null的值,如果非null的值不足3个,返回null:
197
263
``` sql
198
264
CREATE AGGREGATE FUNCTION third(x BIGINT ) RETURNS BIGINT OPTIONS (FILE= ' libtest_udf.so' , ARG_NULLABLE= true, RETURN_NULLABLE= true);
199
265
```
@@ -212,3 +278,9 @@ SHOW FUNCTIONS;
212
278
```
213
279
DROP FUNCTION cut2;
214
280
```
281
+
282
+ ``` {warning}
283
+ 同一个udf so如果注册了多个函数,只删除一个函数时,该so不会从Tablet Server内存中删除。此时替换so文件是无用的,并且如果此时增删udf,有一定危险影响Tablet Server运行。
284
+
285
+ 建议:**删除所有udf后,替换udf so文件**。
286
+ ```
0 commit comments