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