Skip to content

Commit 3deb139

Browse files
fix: udf example and docs (#3743)
1 parent 1731881 commit 3deb139

File tree

2 files changed

+134
-25
lines changed

2 files changed

+134
-25
lines changed

docs/zh/openmldb_sql/udf_develop_guide.md

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
内置C++函数的参数类型限定为:BOOL类型,数值类型,时间戳日期类型和字符串类型。C++类型SQL类型对应关系如下:
1818

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` |
2727
| STRING | `StringRef` |
2828
| TIMESTAMP | `Timestamp` |
2929
| DATE | `Date` |
@@ -44,6 +44,15 @@
4444
};
4545
```
4646

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+
4756
函数声明:
4857
* 函数必须用extern "C"来声明
4958
@@ -57,17 +66,6 @@
5766
```
5867
- 一次分配空间的最大长度不能超过2M字节
5968
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-
7169
#### 单行函数开发
7270
7371
单行函数(scalar function)对单行数据进行处理,返回单个值,比如 `abs`, `sin`, `cos`, `date`, `year` 等。
@@ -95,6 +93,8 @@ void cut2(::openmldb::base::UDFContext* ctx, ::openmldb::base::StringRef* input,
9593
}
9694
```
9795

96+
因为返回值是string类型,所以此处需要通过函数最后一个参数返回。如果返回值是基本类型,通过函数返回值返回,可参考[test_udf.cc](https://github.com/4paradigm/OpenMLDB/blob/main/src/examples/test_udf.cc)中的strlength。
97+
9898
#### 聚合函数开发
9999

100100
聚合函数(aggregate function)对一个数据集(比如一列数据)执行计算,返回单个值,比如 `sum`, `avg`, `max`, `min`, `count` 等。
@@ -141,9 +141,75 @@ int64_t special_sum_output(::openmldb::base::UDFContext* ctx) {
141141
return *(reinterpret_cast<int64_t*>(ctx->ptr)) + 5;
142142
}
143143

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+
}
144210
```
145211
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)。
147213
148214
### 编译动态库
149215
- 拷贝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
185251
### 注册、删除和查看函数
186252
注册函数使用[CREATE FUNCTION](../openmldb_sql/ddl/CREATE_FUNCTION.md)
187253

188-
注册单行函数
254+
注册单行函数,cut2函数将字符串的前两个字符返回:
189255
```sql
190256
CREATE FUNCTION cut2(x STRING) RETURNS STRING OPTIONS (FILE='libtest_udf.so');
191257
```
192-
注册聚合函数
258+
注册聚合函数,special_sum函数初始为10,再将输入的值累加,并且最后加上5返回(演示函数,无特殊意义):
193259
```sql
194260
CREATE AGGREGATE FUNCTION special_sum(x BIGINT) RETURNS BIGINT OPTIONS (FILE='libtest_udf.so');
195261
```
196-
注册聚合函数,并且输入参数和返回值都支持null
262+
注册聚合函数,并且输入参数和返回值都支持null,third函数返回第三个非null的值,如果非null的值不足3个,返回null:
197263
```sql
198264
CREATE AGGREGATE FUNCTION third(x BIGINT) RETURNS BIGINT OPTIONS (FILE='libtest_udf.so', ARG_NULLABLE=true, RETURN_NULLABLE=true);
199265
```
@@ -212,3 +278,9 @@ SHOW FUNCTIONS;
212278
```
213279
DROP FUNCTION cut2;
214280
```
281+
282+
```{warning}
283+
同一个udf so如果注册了多个函数,只删除一个函数时,该so不会从Tablet Server内存中删除。此时替换so文件是无用的,并且如果此时增删udf,有一定危险影响Tablet Server运行。
284+
285+
建议:**删除所有udf后,替换udf so文件**。
286+
```

src/examples/test_udf.cc

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ int64_t count_null_output(::openmldb::base::UDFContext* ctx) {
9191
return *(reinterpret_cast<int64_t*>(ctx->ptr));
9292
}
9393

94-
// Get the second non-null value of all values
94+
// Get the third non-null value of all values
9595
extern "C"
9696
::openmldb::base::UDFContext* third_init(::openmldb::base::UDFContext* ctx) {
9797
ctx->ptr = reinterpret_cast<void*>(new std::vector<int64_t>());
@@ -114,8 +114,45 @@ void third_output(::openmldb::base::UDFContext* ctx, int64_t* output, bool* is_n
114114
*is_null = true;
115115
} else {
116116
*is_null = false;
117-
*output = vec->at(3);
117+
*output = vec->at(2);
118118
}
119119
// free the memory allocated in init function with new/malloc
120120
delete vec;
121121
}
122+
123+
// Get the first non-null value >= threshold
124+
extern "C"
125+
::openmldb::base::UDFContext* first_ge_init(::openmldb::base::UDFContext* ctx) {
126+
// threshold init in update
127+
// threshold, thresh_flag, first_ge, first_ge_flag
128+
ctx->ptr = reinterpret_cast<void*>(new std::vector<int64_t>(4, 0));
129+
return ctx;
130+
}
131+
132+
extern "C"
133+
::openmldb::base::UDFContext* first_ge_update(::openmldb::base::UDFContext* ctx, int64_t input, bool is_null, int64_t threshold, bool threshold_is_null) {
134+
auto pair = reinterpret_cast<std::vector<int64_t>*>(ctx->ptr);
135+
if (!threshold_is_null && pair->at(1) == 0) {
136+
pair->at(0) = threshold;
137+
pair->at(1) = 1;
138+
}
139+
if (!is_null && pair->at(3) == 0 && input >= pair->at(0)) {
140+
pair->at(2) = input;
141+
pair->at(3) = 1;
142+
}
143+
return ctx;
144+
}
145+
146+
extern "C"
147+
void first_ge_output(::openmldb::base::UDFContext* ctx, int64_t* output, bool* is_null) {
148+
auto pair = reinterpret_cast<std::vector<int64_t>*>(ctx->ptr);
149+
// threshold is null or no value >= threshold
150+
if (pair->at(1) == 0 || pair->at(3) == 0) {
151+
*is_null = true;
152+
} else {
153+
*is_null = false;
154+
*output = pair->at(2);
155+
}
156+
// free the memory allocated in init function with new/malloc
157+
delete pair;
158+
}

0 commit comments

Comments
 (0)