1 /* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /* This test component register 3 UDFs in init (install) and
24 don't unregister it. The unregister can be done by calling the test component
25 udf_unreg_3_func.cc, doing it in init (install). */
26
27 #include <ctype.h>
28 #include <mysql/components/component_implementation.h>
29 #include <mysql/components/service_implementation.h>
30 #include <mysql/components/services/udf_registration.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <string>
36
37 REQUIRES_SERVICE_PLACEHOLDER(udf_registration);
38 REQUIRES_SERVICE_PLACEHOLDER(udf_registration_aggregate);
39
40 /***************************************************************************
41 ** UDF double function.
42 ** Arguments:
43 ** initid Structure filled by xxx_init
44 ** args The same structure as to xxx_init. This structure
45 ** contains values for all parameters.
46 ** Note that the functions MUST check and convert all
47 ** to the type it wants! Null values are represented by
48 ** a NULL pointer
49 ** is_null If the result is null, one should store 1 here.
50 ** error If something goes fatally wrong one should store 1 here.
51 **
52 ** This function should return the result.
53 ***************************************************************************/
54
myfunc_double_init(UDF_INIT * initid,UDF_ARGS * args,char * message)55 bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
56 unsigned i;
57
58 if (!args->arg_count) {
59 strcpy(message, "myfunc_double must have at least one argument");
60 return true;
61 }
62 /*
63 ** As this function wants to have everything as strings, force all arguments
64 ** to strings.
65 */
66 for (i = 0; i < args->arg_count; i++) args->arg_type[i] = STRING_RESULT;
67 initid->maybe_null = true; /* The result may be null */
68 initid->decimals = 2; /* We want 2 decimals in the result */
69 initid->max_length = 6; /* 3 digits + . + 2 decimals */
70 return false;
71 }
72
myfunc_double(UDF_INIT *,UDF_ARGS * args,unsigned char * is_null,unsigned char *)73 double myfunc_double(UDF_INIT *, UDF_ARGS *args, unsigned char *is_null,
74 unsigned char *) {
75 unsigned long val = 0;
76 unsigned long v = 0;
77 unsigned i, j;
78
79 for (i = 0; i < args->arg_count; i++) {
80 if (args->args[i] == nullptr) continue;
81 val += args->lengths[i];
82 for (j = args->lengths[i]; j-- > 0;) v += args->args[i][j];
83 }
84 if (val) return (double)v / (double)val;
85 *is_null = 1;
86 return 0.0;
87 }
88
89 /***************************************************************************
90 ** UDF long long function.
91 ** Arguments:
92 ** initid Return value from xxxx_init
93 ** args The same structure as to xxx_init. This structure
94 ** contains values for all parameters.
95 ** Note that the functions MUST check and convert all
96 ** to the type it wants! Null values are represented by
97 ** a NULL pointer
98 ** is_null If the result is null, one should store 1 here.
99 ** error If something goes fatally wrong one should store 1 here.
100 **
101 ** This function should return the result as a long long
102 ***************************************************************************/
103
104 /* This function returns the sum of all arguments */
105
myfunc_int(UDF_INIT *,UDF_ARGS * args,unsigned char *,unsigned char *)106 long long myfunc_int(UDF_INIT *, UDF_ARGS *args, unsigned char *,
107 unsigned char *) {
108 long long val = 0;
109 unsigned i;
110
111 for (i = 0; i < args->arg_count; i++) {
112 if (args->args[i] == nullptr) continue;
113 switch (args->arg_type[i]) {
114 case STRING_RESULT: /* Add string lengths */
115 val += args->lengths[i];
116 break;
117 case INT_RESULT: /* Add numbers */
118 val += *((long long *)args->args[i]);
119 break;
120 case REAL_RESULT: /* Add numers as long long */
121 val += (long long)*((double *)args->args[i]);
122 break;
123 default:
124 break;
125 }
126 }
127 return val;
128 }
129
myfunc_int_init(UDF_INIT *,UDF_ARGS *,char *)130 bool myfunc_int_init(UDF_INIT *, UDF_ARGS *, char *) { return false; }
131
132 /***************************************************************************
133 ** Syntax for the new aggregate commands are:
134 ** create aggregate function <function_name> returns {string|real|integer}
135 ** soname <name_of_shared_library>
136 **
137 ** Syntax for avgcost: avgcost( t.quantity, t.price )
138 ** with t.quantity=integer, t.price=double
139 ** (this example is provided by Andreas F. Bobak <bobak@relog.ch>)
140 ****************************************************************************/
141
142 struct avgcost_data {
143 unsigned long long count;
144 long long totalquantity;
145 double totalprice;
146 };
147
148 /*
149 ** Average Cost Aggregate Function.
150 */
avgcost_init(UDF_INIT * initid,UDF_ARGS * args,char * message)151 bool avgcost_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
152 struct avgcost_data *data;
153
154 if (args->arg_count != 2) {
155 strcpy(message,
156 "wrong number of arguments: AVGCOST() requires two arguments");
157 return true;
158 }
159
160 if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT)) {
161 strcpy(message,
162 "wrong argument type: AVGCOST() requires an INT and a REAL");
163 return true;
164 }
165
166 /*
167 ** force arguments to double.
168 */
169 /*args->arg_type[0] = REAL_RESULT;
170 args->arg_type[1] = REAL_RESULT;*/
171
172 initid->maybe_null = false; /* The result may be null */
173 initid->decimals = 4; /* We want 4 decimals in the result */
174 initid->max_length = 20; /* 6 digits + . + 10 decimals */
175
176 if (!(data = new (std::nothrow) avgcost_data)) {
177 strcpy(message, "Couldn't allocate memory");
178 return true;
179 }
180 data->totalquantity = 0;
181 data->totalprice = 0.0;
182
183 initid->ptr = (char *)data;
184
185 return false;
186 }
187
avgcost_deinit(UDF_INIT * initid)188 void avgcost_deinit(UDF_INIT *initid) {
189 void *void_ptr = initid->ptr;
190 avgcost_data *data = static_cast<avgcost_data *>(void_ptr);
191 delete data;
192 }
193
194 /* This is needed to get things to work in MySQL 4.1.1 and above */
195
avgcost_clear(UDF_INIT * initid,unsigned char *,unsigned char *)196 void avgcost_clear(UDF_INIT *initid, unsigned char *, unsigned char *) {
197 struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
198 data->totalprice = 0.0;
199 data->totalquantity = 0;
200 data->count = 0;
201 }
202
avgcost_add(UDF_INIT * initid,UDF_ARGS * args,unsigned char *,unsigned char *)203 void avgcost_add(UDF_INIT *initid, UDF_ARGS *args, unsigned char *,
204 unsigned char *) {
205 if (args->args[0] && args->args[1]) {
206 struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
207 long long quantity = *((long long *)args->args[0]);
208 long long newquantity = data->totalquantity + quantity;
209 double price = *((double *)args->args[1]);
210
211 data->count++;
212
213 if (((data->totalquantity >= 0) && (quantity < 0)) ||
214 ((data->totalquantity < 0) && (quantity > 0))) {
215 /*
216 ** passing from + to - or from - to +
217 */
218 if (((quantity < 0) && (newquantity < 0)) ||
219 ((quantity > 0) && (newquantity > 0))) {
220 data->totalprice = price * (double)newquantity;
221 }
222 /*
223 ** sub q if totalq > 0
224 ** add q if totalq < 0
225 */
226 else {
227 price = data->totalprice / (double)data->totalquantity;
228 data->totalprice = price * (double)newquantity;
229 }
230 data->totalquantity = newquantity;
231 } else {
232 data->totalquantity += quantity;
233 data->totalprice += price * (double)quantity;
234 }
235
236 if (data->totalquantity == 0) data->totalprice = 0.0;
237 }
238 }
239
avgcost(UDF_INIT * initid,UDF_ARGS *,unsigned char * is_null,unsigned char *)240 double avgcost(UDF_INIT *initid, UDF_ARGS *, unsigned char *is_null,
241 unsigned char *) {
242 struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
243 if (!data->count || !data->totalquantity) {
244 *is_null = 1;
245 return 0.0;
246 }
247
248 *is_null = 0;
249 return data->totalprice / (double)data->totalquantity;
250 }
251
252 /**************************************************************************************/
253
init()254 static mysql_service_status_t init() {
255 bool ret_int = false;
256 ret_int = mysql_service_udf_registration->udf_register(
257 "myfunc_int", INT_RESULT, (Udf_func_any)myfunc_int, myfunc_int_init,
258 nullptr);
259 // myfunc_double_deinit);
260 bool ret_double = false;
261 ret_double = mysql_service_udf_registration->udf_register(
262 "myfunc_double", REAL_RESULT, (Udf_func_any)myfunc_double,
263 myfunc_double_init, nullptr);
264 bool ret_avgcost = false;
265 ret_avgcost = mysql_service_udf_registration_aggregate->udf_register(
266 "avgcost", REAL_RESULT, (Udf_func_any)avgcost, avgcost_init,
267 avgcost_deinit, avgcost_add, avgcost_clear);
268 return ret_int && ret_double && ret_avgcost;
269 }
270
deinit()271 static mysql_service_status_t deinit() { return false; }
272
273 BEGIN_COMPONENT_PROVIDES(test_udf_registration)
274 END_COMPONENT_PROVIDES();
275
276 BEGIN_COMPONENT_REQUIRES(test_udf_registration)
277 REQUIRES_SERVICE(udf_registration),
278 REQUIRES_SERVICE(udf_registration_aggregate), END_COMPONENT_REQUIRES();
279
280 BEGIN_COMPONENT_METADATA(test_udf_registration)
281 METADATA("mysql.author", "Oracle Corporation"),
282 METADATA("mysql.license", "GPL"), METADATA("test_property", "1"),
283 END_COMPONENT_METADATA();
284
285 DECLARE_COMPONENT(test_udf_registration, "mysql:test_udf_registration")
286 init, deinit END_DECLARE_COMPONENT();
287
288 DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(test_udf_registration)
289 END_DECLARE_LIBRARY_COMPONENTS
290