1 | /* |
2 | Copyright (c) 2000, 2014, Oracle and/or its affiliates. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | /* |
18 | ** example file of UDF (user definable functions) that are dynamicly loaded |
19 | ** into the standard mysqld core. |
20 | ** |
21 | ** The functions name, type and shared library is saved in the new system |
22 | ** table 'func'. To be able to create new functions one must have write |
23 | ** privilege for the database 'mysql'. If one starts MySQL with |
24 | ** --skip-grant, then UDF initialization will also be skipped. |
25 | ** |
26 | ** Syntax for the new commands are: |
27 | ** create function <function_name> returns {string|real|integer} |
28 | ** soname <name_of_shared_library> |
29 | ** drop function <function_name> |
30 | ** |
31 | ** Each defined function may have a xxxx_init function and a xxxx_deinit |
32 | ** function. The init function should alloc memory for the function |
33 | ** and tell the main function about the max length of the result |
34 | ** (for string functions), number of decimals (for double functions) and |
35 | ** if the result may be a null value. |
36 | ** |
37 | ** If a function sets the 'error' argument to 1 the function will not be |
38 | ** called anymore and mysqld will return NULL for all calls to this copy |
39 | ** of the function. |
40 | ** |
41 | ** All strings arguments to functions are given as string pointer + length |
42 | ** to allow handling of binary data. |
43 | ** Remember that all functions must be thread safe. This means that one is not |
44 | ** allowed to alloc any global or static variables that changes! |
45 | ** If one needs memory one should alloc this in the init function and free |
46 | ** this on the __deinit function. |
47 | ** |
48 | ** Note that the init and __deinit functions are only called once per |
49 | ** SQL statement while the value function may be called many times |
50 | ** |
51 | ** Function 'metaphon' returns a metaphon string of the string argument. |
52 | ** This is something like a soundex string, but it's more tuned for English. |
53 | ** |
54 | ** Function 'myfunc_double' returns summary of codes of all letters |
55 | ** of arguments divided by summary length of all its arguments. |
56 | ** |
57 | ** Function 'myfunc_int' returns summary length of all its arguments. |
58 | ** |
59 | ** Function 'udf_sequence' returns an sequence starting from a certain number. |
60 | ** |
61 | ** Function 'myfunc_argument_name' returns name of argument. |
62 | ** |
63 | ** On the end is a couple of functions that converts hostnames to ip and |
64 | ** vice versa. |
65 | ** |
66 | ** A dynamicly loadable file should be compiled shared. |
67 | ** (something like: gcc -shared -o my_func.so myfunc.cc). |
68 | ** You can easily get all switches right by doing: |
69 | ** cd sql ; make udf_example.o |
70 | ** Take the compile line that make writes, remove the '-c' near the end of |
71 | ** the line and add -shared -o udf_example.so to the end of the compile line. |
72 | ** The resulting library (udf_example.so) should be copied to some dir |
73 | ** searched by ld. (/usr/lib ?) |
74 | ** If you are using gcc, then you should be able to create the udf_example.so |
75 | ** by simply doing 'make udf_example.so'. |
76 | ** |
77 | ** After the library is made one must notify mysqld about the new |
78 | ** functions with the commands: |
79 | ** |
80 | ** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; |
81 | ** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; |
82 | ** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; |
83 | ** CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "udf_example.so"; |
84 | ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; |
85 | ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; |
86 | ** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; |
87 | ** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"; |
88 | ** |
89 | ** After this the functions will work exactly like native MySQL functions. |
90 | ** Functions should be created only once. |
91 | ** |
92 | ** The functions can be deleted by: |
93 | ** |
94 | ** DROP FUNCTION metaphon; |
95 | ** DROP FUNCTION myfunc_double; |
96 | ** DROP FUNCTION myfunc_int; |
97 | ** DROP FUNCTION lookup; |
98 | ** DROP FUNCTION reverse_lookup; |
99 | ** DROP FUNCTION avgcost; |
100 | ** DROP FUNCTION myfunc_argument_name; |
101 | ** |
102 | ** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All |
103 | ** Active function will be reloaded on every restart of server |
104 | ** (if --skip-grant-tables is not given) |
105 | ** |
106 | ** If you ge problems with undefined symbols when loading the shared |
107 | ** library, you should verify that mysqld is compiled with the -rdynamic |
108 | ** option. |
109 | ** |
110 | ** If you can't get AGGREGATES to work, check that you have the column |
111 | ** 'type' in the mysql.func table. If not, run 'mysql_upgrade'. |
112 | ** |
113 | */ |
114 | |
115 | #ifdef _WIN32 |
116 | /* Silence warning about deprecated functions , gethostbyname etc*/ |
117 | #define _WINSOCK_DEPRECATED_NO_WARNINGS |
118 | #endif |
119 | |
120 | #ifdef STANDARD |
121 | /* STANDARD is defined, don't use any mysql functions */ |
122 | #include <stdlib.h> |
123 | #include <stdio.h> |
124 | #include <string.h> |
125 | #ifdef __WIN__ |
126 | typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ |
127 | typedef __int64 longlong; |
128 | #else |
129 | typedef unsigned long long ulonglong; |
130 | typedef long long longlong; |
131 | #endif /*__WIN__*/ |
132 | #else |
133 | #include "mariadb.h" |
134 | #include <my_sys.h> |
135 | #if defined(MYSQL_SERVER) |
136 | #include <m_string.h> /* To get strmov() */ |
137 | #else |
138 | /* when compiled as standalone */ |
139 | #include <string.h> |
140 | #define strmov(a,b) stpcpy(a,b) |
141 | #define bzero(a,b) memset(a,0,b) |
142 | #endif |
143 | #endif |
144 | #include <mysql.h> |
145 | #include <ctype.h> |
146 | |
147 | |
148 | #ifdef HAVE_DLOPEN |
149 | |
150 | #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) |
151 | static pthread_mutex_t LOCK_hostname; |
152 | #endif |
153 | |
154 | /* These must be right or mysqld will not find the symbol! */ |
155 | |
156 | my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
157 | void metaphon_deinit(UDF_INIT *initid); |
158 | char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, |
159 | unsigned long *length, char *is_null, char *error); |
160 | my_bool myfunc_double_init(UDF_INIT *, UDF_ARGS *args, char *message); |
161 | double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, |
162 | char *error); |
163 | my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
164 | longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, |
165 | char *error); |
166 | my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
167 | void udf_sequence_deinit(UDF_INIT *initid); |
168 | longlong udf_sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, |
169 | char *error); |
170 | my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); |
171 | void avgcost_deinit( UDF_INIT* initid ); |
172 | void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); |
173 | void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); |
174 | void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); |
175 | double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); |
176 | my_bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
177 | char *is_const(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long |
178 | *length, char *is_null, char *error); |
179 | |
180 | |
181 | /************************************************************************* |
182 | ** Example of init function |
183 | ** Arguments: |
184 | ** initid Points to a structure that the init function should fill. |
185 | ** This argument is given to all other functions. |
186 | ** my_bool maybe_null 1 if function can return NULL |
187 | ** Default value is 1 if any of the arguments |
188 | ** is declared maybe_null. |
189 | ** unsigned int decimals Number of decimals. |
190 | ** Default value is max decimals in any of the |
191 | ** arguments. |
192 | ** unsigned int max_length Length of string result. |
193 | ** The default value for integer functions is 21 |
194 | ** The default value for real functions is 13+ |
195 | ** default number of decimals. |
196 | ** The default value for string functions is |
197 | ** the longest string argument. |
198 | ** char *ptr; A pointer that the function can use. |
199 | ** |
200 | ** args Points to a structure which contains: |
201 | ** unsigned int arg_count Number of arguments |
202 | ** enum Item_result *arg_type Types for each argument. |
203 | ** Types are STRING_RESULT, REAL_RESULT |
204 | ** and INT_RESULT. |
205 | ** char **args Pointer to constant arguments. |
206 | ** Contains 0 for not constant argument. |
207 | ** unsigned long *lengths; max string length for each argument |
208 | ** char *maybe_null Information of which arguments |
209 | ** may be NULL |
210 | ** |
211 | ** message Error message that should be passed to the user on fail. |
212 | ** The message buffer is MYSQL_ERRMSG_SIZE big, but one should |
213 | ** try to keep the error message less than 80 bytes long! |
214 | ** |
215 | ** This function should return 1 if something goes wrong. In this case |
216 | ** message should contain something useful! |
217 | **************************************************************************/ |
218 | |
219 | #define MAXMETAPH 8 |
220 | |
221 | my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
222 | { |
223 | if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) |
224 | { |
225 | strcpy(message,"Wrong arguments to metaphon; Use the source" ); |
226 | return 1; |
227 | } |
228 | initid->max_length=MAXMETAPH; |
229 | return 0; |
230 | } |
231 | |
232 | /**************************************************************************** |
233 | ** Deinit function. This should free all resources allocated by |
234 | ** this function. |
235 | ** Arguments: |
236 | ** initid Return value from xxxx_init |
237 | ****************************************************************************/ |
238 | |
239 | |
240 | void metaphon_deinit(UDF_INIT *initid __attribute__((unused))) |
241 | { |
242 | } |
243 | |
244 | /*************************************************************************** |
245 | ** UDF string function. |
246 | ** Arguments: |
247 | ** initid Structure filled by xxx_init |
248 | ** args The same structure as to xxx_init. This structure |
249 | ** contains values for all parameters. |
250 | ** Note that the functions MUST check and convert all |
251 | ** to the type it wants! Null values are represented by |
252 | ** a NULL pointer |
253 | ** result Possible buffer to save result. At least 255 byte long. |
254 | ** length Pointer to length of the above buffer. In this the function |
255 | ** should save the result length |
256 | ** is_null If the result is null, one should store 1 here. |
257 | ** error If something goes fatally wrong one should store 1 here. |
258 | ** |
259 | ** This function should return a pointer to the result string. |
260 | ** Normally this is 'result' but may also be an alloced string. |
261 | ***************************************************************************/ |
262 | |
263 | /* Character coding array */ |
264 | static char codes[26] = { |
265 | 1,16,4,16,9,2,4,16,9,2,0,2,2,2,1,4,0,2,4,4,1,0,0,0,8,0 |
266 | /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z*/ |
267 | }; |
268 | |
269 | /*--- Macros to access character coding array -------------*/ |
270 | |
271 | #define ISVOWEL(x) (codes[(x) - 'A'] & 1) /* AEIOU */ |
272 | |
273 | /* Following letters are not changed */ |
274 | #define NOCHANGE(x) (codes[(x) - 'A'] & 2) /* FJLMNR */ |
275 | |
276 | /* These form diphthongs when preceding H */ |
277 | #define AFFECTH(x) (codes[(x) - 'A'] & 4) /* CGPST */ |
278 | |
279 | /* These make C and G soft */ |
280 | #define MAKESOFT(x) (codes[(x) - 'A'] & 8) /* EIY */ |
281 | |
282 | /* These prevent GH from becoming F */ |
283 | #define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */ |
284 | |
285 | |
286 | char *metaphon(UDF_INIT *initid __attribute__((unused)), |
287 | UDF_ARGS *args, char *result, unsigned long *length, |
288 | char *is_null, char *error __attribute__((unused))) |
289 | { |
290 | const char *word=args->args[0]; |
291 | const char *w_end; |
292 | char *org_result; |
293 | char *n, *n_start, *n_end; /* pointers to string */ |
294 | char *metaph_end; /* pointers to end of metaph */ |
295 | char ntrans[32]; /* word with uppercase letters */ |
296 | int KSflag; /* state flag for X to KS */ |
297 | |
298 | if (!word) /* Null argument */ |
299 | { |
300 | /* The length is expected to be zero when the argument is NULL. */ |
301 | assert(args->lengths[0] == 0); |
302 | *is_null=1; |
303 | return 0; |
304 | } |
305 | |
306 | w_end=word+args->lengths[0]; |
307 | org_result=result; |
308 | |
309 | /*-------------------------------------------------------- |
310 | * Copy word to internal buffer, dropping non-alphabetic |
311 | * characters and converting to uppercase. |
312 | *-------------------------------------------------------*/ |
313 | |
314 | for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; |
315 | word != w_end && n < n_end; word++ ) |
316 | if ( isalpha ( *word )) |
317 | *n++ = toupper ( *word ); |
318 | |
319 | if ( n == ntrans + 1 ) /* return empty string if 0 bytes */ |
320 | { |
321 | *length=0; |
322 | return result; |
323 | } |
324 | n_end = n; /* set n_end to end of string */ |
325 | ntrans[0] = 'Z'; /* ntrans[0] should be a neutral char */ |
326 | n[0]=n[1]=0; /* pad with nulls */ |
327 | n = ntrans + 1; /* assign pointer to start */ |
328 | |
329 | /*------------------------------------------------------------ |
330 | * check for all prefixes: |
331 | * PN KN GN AE WR WH and X at start. |
332 | *----------------------------------------------------------*/ |
333 | |
334 | switch ( *n ) { |
335 | case 'P': |
336 | case 'K': |
337 | case 'G': |
338 | if ( n[1] == 'N') |
339 | *n++ = 0; |
340 | break; |
341 | case 'A': |
342 | if ( n[1] == 'E') |
343 | *n++ = 0; |
344 | break; |
345 | case 'W': |
346 | if ( n[1] == 'R' ) |
347 | *n++ = 0; |
348 | else |
349 | if ( *(n + 1) == 'H') |
350 | { |
351 | n[1] = *n; |
352 | *n++ = 0; |
353 | } |
354 | break; |
355 | case 'X': |
356 | *n = 'S'; |
357 | break; |
358 | } |
359 | |
360 | /*------------------------------------------------------------ |
361 | * Now, loop step through string, stopping at end of string |
362 | * or when the computed metaph is MAXMETAPH characters long |
363 | *----------------------------------------------------------*/ |
364 | |
365 | KSflag = 0; /* state flag for KS translation */ |
366 | |
367 | for (metaph_end = result + MAXMETAPH, n_start = n; |
368 | n < n_end && result < metaph_end; n++ ) |
369 | { |
370 | |
371 | if ( KSflag ) |
372 | { |
373 | KSflag = 0; |
374 | *result++ = *n; |
375 | } |
376 | else |
377 | { |
378 | /* drop duplicates except for CC */ |
379 | if ( *( n - 1 ) == *n && *n != 'C' ) |
380 | continue; |
381 | |
382 | /* check for F J L M N R or first letter vowel */ |
383 | if ( NOCHANGE ( *n ) || |
384 | ( n == n_start && ISVOWEL ( *n ))) |
385 | *result++ = *n; |
386 | else |
387 | switch ( *n ) { |
388 | case 'B': /* check for -MB */ |
389 | if ( n < n_end || *( n - 1 ) != 'M' ) |
390 | *result++ = *n; |
391 | break; |
392 | |
393 | case 'C': /* C = X ("sh" sound) in CH and CIA */ |
394 | /* = S in CE CI and CY */ |
395 | /* dropped in SCI SCE SCY */ |
396 | /* else K */ |
397 | if ( *( n - 1 ) != 'S' || |
398 | !MAKESOFT ( n[1])) |
399 | { |
400 | if ( n[1] == 'I' && n[2] == 'A' ) |
401 | *result++ = 'X'; |
402 | else |
403 | if ( MAKESOFT ( n[1])) |
404 | *result++ = 'S'; |
405 | else |
406 | if ( n[1] == 'H' ) |
407 | *result++ = (( n == n_start && |
408 | !ISVOWEL ( n[2])) || |
409 | *( n - 1 ) == 'S' ) ? |
410 | (char)'K' : (char)'X'; |
411 | else |
412 | *result++ = 'K'; |
413 | } |
414 | break; |
415 | |
416 | case 'D': /* J before DGE, DGI, DGY, else T */ |
417 | *result++ = |
418 | ( n[1] == 'G' && |
419 | MAKESOFT ( n[2])) ? |
420 | (char)'J' : (char)'T'; |
421 | break; |
422 | |
423 | case 'G': /* complicated, see table in text */ |
424 | if (( n[1] != 'H' || ISVOWEL ( n[2])) |
425 | && ( |
426 | n[1] != 'N' || |
427 | ( |
428 | (n + 1) < n_end && |
429 | ( |
430 | n[2] != 'E' || |
431 | *( n + 3 ) != 'D' |
432 | ) |
433 | ) |
434 | ) |
435 | && ( |
436 | *( n - 1 ) != 'D' || |
437 | !MAKESOFT ( n[1]) |
438 | ) |
439 | ) |
440 | *result++ = |
441 | ( MAKESOFT ( *( n + 1 )) && |
442 | n[2] != 'G' ) ? |
443 | (char)'J' : (char)'K'; |
444 | else |
445 | if ( n[1] == 'H' && |
446 | !NOGHTOF( *( n - 3 )) && |
447 | *( n - 4 ) != 'H') |
448 | *result++ = 'F'; |
449 | break; |
450 | |
451 | case 'H': /* H if before a vowel and not after */ |
452 | /* C, G, P, S, T */ |
453 | |
454 | if ( !AFFECTH ( *( n - 1 )) && |
455 | ( !ISVOWEL ( *( n - 1 )) || |
456 | ISVOWEL ( n[1]))) |
457 | *result++ = 'H'; |
458 | break; |
459 | |
460 | case 'K': /* K = K, except dropped after C */ |
461 | if ( *( n - 1 ) != 'C') |
462 | *result++ = 'K'; |
463 | break; |
464 | |
465 | case 'P': /* PH = F, else P = P */ |
466 | *result++ = *( n + 1 ) == 'H' |
467 | ? (char)'F' : (char)'P'; |
468 | break; |
469 | case 'Q': /* Q = K (U after Q is already gone */ |
470 | *result++ = 'K'; |
471 | break; |
472 | |
473 | case 'S': /* SH, SIO, SIA = X ("sh" sound) */ |
474 | *result++ = ( n[1] == 'H' || |
475 | ( *(n + 1) == 'I' && |
476 | ( n[2] == 'O' || |
477 | n[2] == 'A'))) ? |
478 | (char)'X' : (char)'S'; |
479 | break; |
480 | |
481 | case 'T': /* TIO, TIA = X ("sh" sound) */ |
482 | /* TH = 0, ("th" sound ) */ |
483 | if ( *( n + 1 ) == 'I' && ( n[2] == 'O' |
484 | || n[2] == 'A') ) |
485 | *result++ = 'X'; |
486 | else |
487 | if ( n[1] == 'H' ) |
488 | *result++ = '0'; |
489 | else |
490 | if ( *( n + 1) != 'C' || n[2] != 'H') |
491 | *result++ = 'T'; |
492 | break; |
493 | |
494 | case 'V': /* V = F */ |
495 | *result++ = 'F'; |
496 | break; |
497 | |
498 | case 'W': /* only exist if a vowel follows */ |
499 | case 'Y': |
500 | if ( ISVOWEL ( n[1])) |
501 | *result++ = *n; |
502 | break; |
503 | |
504 | case 'X': /* X = KS, except at start */ |
505 | if ( n == n_start ) |
506 | *result++ = 'S'; |
507 | else |
508 | { |
509 | *result++ = 'K'; /* insert K, then S */ |
510 | KSflag = 1; /* this flag will cause S to be |
511 | inserted on next pass thru loop */ |
512 | } |
513 | break; |
514 | |
515 | case 'Z': |
516 | *result++ = 'S'; |
517 | break; |
518 | } |
519 | } |
520 | } |
521 | *length= (unsigned long) (result - org_result); |
522 | return org_result; |
523 | } |
524 | |
525 | |
526 | /*************************************************************************** |
527 | ** UDF double function. |
528 | ** Arguments: |
529 | ** initid Structure filled by xxx_init |
530 | ** args The same structure as to xxx_init. This structure |
531 | ** contains values for all parameters. |
532 | ** Note that the functions MUST check and convert all |
533 | ** to the type it wants! Null values are represented by |
534 | ** a NULL pointer |
535 | ** is_null If the result is null, one should store 1 here. |
536 | ** error If something goes fatally wrong one should store 1 here. |
537 | ** |
538 | ** This function should return the result. |
539 | ***************************************************************************/ |
540 | |
541 | my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
542 | { |
543 | uint i; |
544 | |
545 | if (!args->arg_count) |
546 | { |
547 | strcpy(message,"myfunc_double must have at least one argument" ); |
548 | return 1; |
549 | } |
550 | /* |
551 | ** As this function wants to have everything as strings, force all arguments |
552 | ** to strings. |
553 | */ |
554 | for (i=0 ; i < args->arg_count; i++) |
555 | args->arg_type[i]=STRING_RESULT; |
556 | initid->maybe_null=1; /* The result may be null */ |
557 | initid->decimals=2; /* We want 2 decimals in the result */ |
558 | initid->max_length=6; /* 3 digits + . + 2 decimals */ |
559 | return 0; |
560 | } |
561 | |
562 | |
563 | double myfunc_double(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, |
564 | char *is_null, char *error __attribute__((unused))) |
565 | { |
566 | unsigned long val = 0; |
567 | unsigned long v = 0; |
568 | uint i, j; |
569 | |
570 | for (i = 0; i < args->arg_count; i++) |
571 | { |
572 | if (args->args[i] == NULL) |
573 | continue; |
574 | val += args->lengths[i]; |
575 | for (j=args->lengths[i] ; j-- > 0 ;) |
576 | v += args->args[i][j]; |
577 | } |
578 | if (val) |
579 | return (double) v/ (double) val; |
580 | *is_null=1; |
581 | return 0.0; |
582 | } |
583 | |
584 | |
585 | /*************************************************************************** |
586 | ** UDF long long function. |
587 | ** Arguments: |
588 | ** initid Return value from xxxx_init |
589 | ** args The same structure as to xxx_init. This structure |
590 | ** contains values for all parameters. |
591 | ** Note that the functions MUST check and convert all |
592 | ** to the type it wants! Null values are represented by |
593 | ** a NULL pointer |
594 | ** is_null If the result is null, one should store 1 here. |
595 | ** error If something goes fatally wrong one should store 1 here. |
596 | ** |
597 | ** This function should return the result as a long long |
598 | ***************************************************************************/ |
599 | |
600 | /* This function returns the sum of all arguments */ |
601 | |
602 | longlong myfunc_int(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, |
603 | char *is_null __attribute__((unused)), |
604 | char *error __attribute__((unused))) |
605 | { |
606 | longlong val = 0; |
607 | uint i; |
608 | |
609 | for (i = 0; i < args->arg_count; i++) |
610 | { |
611 | if (args->args[i] == NULL) |
612 | continue; |
613 | switch (args->arg_type[i]) { |
614 | case STRING_RESULT: /* Add string lengths */ |
615 | val += args->lengths[i]; |
616 | break; |
617 | case INT_RESULT: /* Add numbers */ |
618 | val += *((longlong*) args->args[i]); |
619 | break; |
620 | case REAL_RESULT: /* Add numers as longlong */ |
621 | val += (longlong) *((double*) args->args[i]); |
622 | break; |
623 | default: |
624 | break; |
625 | } |
626 | } |
627 | return val; |
628 | } |
629 | |
630 | /* |
631 | At least one of _init/_deinit is needed unless the server is started |
632 | with --allow_suspicious_udfs. |
633 | */ |
634 | my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), |
635 | UDF_ARGS *args __attribute__((unused)), |
636 | char *message __attribute__((unused))) |
637 | { |
638 | return 0; |
639 | } |
640 | |
641 | /* |
642 | Simple example of how to get a sequences starting from the first argument |
643 | or 1 if no arguments have been given |
644 | */ |
645 | |
646 | my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
647 | { |
648 | if (args->arg_count > 1) |
649 | { |
650 | strmov(message,"This function takes none or 1 argument" ); |
651 | return 1; |
652 | } |
653 | if (args->arg_count) |
654 | args->arg_type[0]= INT_RESULT; /* Force argument to int */ |
655 | |
656 | if (!(initid->ptr=(char*) malloc(sizeof(longlong)))) |
657 | { |
658 | strmov(message,"Couldn't allocate memory" ); |
659 | return 1; |
660 | } |
661 | bzero(initid->ptr,sizeof(longlong)); |
662 | /* |
663 | udf_sequence() is a non-deterministic function : it has different value |
664 | even if called with the same arguments. |
665 | */ |
666 | initid->const_item=0; |
667 | return 0; |
668 | } |
669 | |
670 | void udf_sequence_deinit(UDF_INIT *initid) |
671 | { |
672 | if (initid->ptr) |
673 | free(initid->ptr); |
674 | } |
675 | |
676 | longlong udf_sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, |
677 | char *is_null __attribute__((unused)), |
678 | char *error __attribute__((unused))) |
679 | { |
680 | ulonglong val=0; |
681 | if (args->arg_count) |
682 | val= *((longlong*) args->args[0]); |
683 | return ++*((longlong*) initid->ptr) + val; |
684 | } |
685 | |
686 | |
687 | /**************************************************************************** |
688 | ** Some functions that handles IP and hostname conversions |
689 | ** The orignal function was from Zeev Suraski. |
690 | ** |
691 | ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; |
692 | ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; |
693 | ** |
694 | ****************************************************************************/ |
695 | |
696 | #ifdef __WIN__ |
697 | #include <winsock2.h> |
698 | #else |
699 | #include <sys/socket.h> |
700 | #include <netinet/in.h> |
701 | #include <arpa/inet.h> |
702 | #include <netdb.h> |
703 | #endif |
704 | |
705 | my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
706 | void lookup_deinit(UDF_INIT *initid); |
707 | char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, |
708 | unsigned long *length, char *null_value, char *error); |
709 | my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); |
710 | void reverse_lookup_deinit(UDF_INIT *initid); |
711 | char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, |
712 | unsigned long *length, char *null_value, char *error); |
713 | |
714 | |
715 | /**************************************************************************** |
716 | ** lookup IP for an hostname. |
717 | ** |
718 | ** This code assumes that gethostbyname_r exists and inet_ntoa() is thread |
719 | ** safe (As it is in Solaris) |
720 | ****************************************************************************/ |
721 | |
722 | |
723 | my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
724 | { |
725 | if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) |
726 | { |
727 | strmov(message,"Wrong arguments to lookup; Use the source" ); |
728 | return 1; |
729 | } |
730 | initid->max_length=11; |
731 | initid->maybe_null=1; |
732 | #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) |
733 | (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); |
734 | #endif |
735 | return 0; |
736 | } |
737 | |
738 | void lookup_deinit(UDF_INIT *initid __attribute__((unused))) |
739 | { |
740 | #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) |
741 | (void) pthread_mutex_destroy(&LOCK_hostname); |
742 | #endif |
743 | } |
744 | |
745 | char *lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, |
746 | char *result, unsigned long *res_length, char *null_value, |
747 | char *error __attribute__((unused))) |
748 | { |
749 | uint length; |
750 | char name_buff[256]; |
751 | struct hostent *hostent; |
752 | #if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) |
753 | int tmp_errno; |
754 | char hostname_buff[2048]; |
755 | struct hostent tmp_hostent; |
756 | #endif |
757 | struct in_addr in; |
758 | |
759 | if (!args->args[0] || !(length=args->lengths[0])) |
760 | { |
761 | *null_value=1; |
762 | return 0; |
763 | } |
764 | if (length >= sizeof(name_buff)) |
765 | length=sizeof(name_buff)-1; |
766 | memcpy(name_buff,args->args[0],length); |
767 | name_buff[length]=0; |
768 | #if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) |
769 | if (!(hostent=gethostbyname_r(name_buff,&tmp_hostent,hostname_buff, |
770 | sizeof(hostname_buff), &tmp_errno))) |
771 | { |
772 | *null_value=1; |
773 | return 0; |
774 | } |
775 | #else |
776 | pthread_mutex_lock(&LOCK_hostname); |
777 | if (!(hostent= gethostbyname((char*) name_buff))) |
778 | { |
779 | pthread_mutex_unlock(&LOCK_hostname); |
780 | *null_value= 1; |
781 | return 0; |
782 | } |
783 | pthread_mutex_unlock(&LOCK_hostname); |
784 | #endif |
785 | memcpy(&in, *hostent->h_addr_list, sizeof(in.s_addr)); |
786 | *res_length= (ulong) (strmov(result, inet_ntoa(in)) - result); |
787 | return result; |
788 | } |
789 | |
790 | |
791 | /**************************************************************************** |
792 | ** return hostname for an IP number. |
793 | ** The functions can take as arguments a string "xxx.xxx.xxx.xxx" or |
794 | ** four numbers. |
795 | ****************************************************************************/ |
796 | |
797 | my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
798 | { |
799 | if (args->arg_count == 1) |
800 | args->arg_type[0]= STRING_RESULT; |
801 | else if (args->arg_count == 4) |
802 | args->arg_type[0]=args->arg_type[1]=args->arg_type[2]=args->arg_type[3]= |
803 | INT_RESULT; |
804 | else |
805 | { |
806 | strmov(message, |
807 | "Wrong number of arguments to reverse_lookup; Use the source" ); |
808 | return 1; |
809 | } |
810 | initid->max_length=32; |
811 | initid->maybe_null=1; |
812 | #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) |
813 | (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); |
814 | #endif |
815 | return 0; |
816 | } |
817 | |
818 | void reverse_lookup_deinit(UDF_INIT *initid __attribute__((unused))) |
819 | { |
820 | #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) |
821 | (void) pthread_mutex_destroy(&LOCK_hostname); |
822 | #endif |
823 | } |
824 | |
825 | char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, |
826 | char *result, unsigned long *res_length, |
827 | char *null_value, char *error __attribute__((unused))) |
828 | { |
829 | #if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) |
830 | char name_buff[256]; |
831 | struct hostent tmp_hostent; |
832 | int tmp_errno; |
833 | #endif |
834 | struct hostent *hp; |
835 | unsigned long taddr; |
836 | uint length; |
837 | |
838 | if (args->arg_count == 4) |
839 | { |
840 | if (!args->args[0] || !args->args[1] ||!args->args[2] ||!args->args[3]) |
841 | { |
842 | *null_value=1; |
843 | return 0; |
844 | } |
845 | sprintf(result,"%d.%d.%d.%d" , |
846 | (int) *((longlong*) args->args[0]), |
847 | (int) *((longlong*) args->args[1]), |
848 | (int) *((longlong*) args->args[2]), |
849 | (int) *((longlong*) args->args[3])); |
850 | } |
851 | else |
852 | { /* string argument */ |
853 | if (!args->args[0]) /* Return NULL for NULL values */ |
854 | { |
855 | *null_value=1; |
856 | return 0; |
857 | } |
858 | length=args->lengths[0]; |
859 | if (length >= (uint) *res_length-1) |
860 | length=(uint) *res_length; |
861 | memcpy(result,args->args[0],length); |
862 | result[length]=0; |
863 | } |
864 | |
865 | taddr = inet_addr(result); |
866 | if (taddr == (unsigned long) -1L) |
867 | { |
868 | *null_value=1; |
869 | return 0; |
870 | } |
871 | #if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) |
872 | if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET, |
873 | &tmp_hostent, name_buff,sizeof(name_buff), |
874 | &tmp_errno))) |
875 | { |
876 | *null_value=1; |
877 | return 0; |
878 | } |
879 | #else |
880 | pthread_mutex_lock(&LOCK_hostname); |
881 | if (!(hp= gethostbyaddr((char*) &taddr, sizeof(taddr), AF_INET))) |
882 | { |
883 | pthread_mutex_unlock(&LOCK_hostname); |
884 | *null_value= 1; |
885 | return 0; |
886 | } |
887 | pthread_mutex_unlock(&LOCK_hostname); |
888 | #endif |
889 | *res_length=(ulong) (strmov(result,hp->h_name) - result); |
890 | return result; |
891 | } |
892 | |
893 | /* |
894 | ** Syntax for the new aggregate commands are: |
895 | ** create aggregate function <function_name> returns {string|real|integer} |
896 | ** soname <name_of_shared_library> |
897 | ** |
898 | ** Syntax for avgcost: avgcost( t.quantity, t.price ) |
899 | ** with t.quantity=integer, t.price=double |
900 | ** (this example is provided by Andreas F. Bobak <bobak@relog.ch>) |
901 | */ |
902 | |
903 | |
904 | struct avgcost_data |
905 | { |
906 | ulonglong count; |
907 | longlong totalquantity; |
908 | double totalprice; |
909 | }; |
910 | |
911 | |
912 | /* |
913 | ** Average Cost Aggregate Function. |
914 | */ |
915 | my_bool |
916 | avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ) |
917 | { |
918 | struct avgcost_data* data; |
919 | |
920 | if (args->arg_count != 2) |
921 | { |
922 | strcpy( |
923 | message, |
924 | "wrong number of arguments: AVGCOST() requires two arguments" |
925 | ); |
926 | return 1; |
927 | } |
928 | |
929 | if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT) ) |
930 | { |
931 | strcpy( |
932 | message, |
933 | "wrong argument type: AVGCOST() requires an INT and a REAL" |
934 | ); |
935 | return 1; |
936 | } |
937 | |
938 | /* |
939 | ** force arguments to double. |
940 | */ |
941 | /*args->arg_type[0] = REAL_RESULT; |
942 | args->arg_type[1] = REAL_RESULT;*/ |
943 | |
944 | initid->maybe_null = 0; /* The result may be null */ |
945 | initid->decimals = 4; /* We want 4 decimals in the result */ |
946 | initid->max_length = 20; /* 6 digits + . + 10 decimals */ |
947 | |
948 | if (!(data = (struct avgcost_data*) malloc(sizeof(struct avgcost_data)))) |
949 | { |
950 | strmov(message,"Couldn't allocate memory" ); |
951 | return 1; |
952 | } |
953 | data->totalquantity = 0; |
954 | data->totalprice = 0.0; |
955 | |
956 | initid->ptr = (char*)data; |
957 | |
958 | return 0; |
959 | } |
960 | |
961 | void |
962 | avgcost_deinit( UDF_INIT* initid ) |
963 | { |
964 | free(initid->ptr); |
965 | } |
966 | |
967 | |
968 | /* This is only for MySQL 4.0 compability */ |
969 | void |
970 | avgcost_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) |
971 | { |
972 | avgcost_clear(initid, is_null, message); |
973 | avgcost_add(initid, args, is_null, message); |
974 | } |
975 | |
976 | /* This is needed to get things to work in MySQL 4.1.1 and above */ |
977 | |
978 | void |
979 | avgcost_clear(UDF_INIT* initid, char* is_null __attribute__((unused)), |
980 | char* message __attribute__((unused))) |
981 | { |
982 | struct avgcost_data* data = (struct avgcost_data*)initid->ptr; |
983 | data->totalprice= 0.0; |
984 | data->totalquantity= 0; |
985 | data->count= 0; |
986 | } |
987 | |
988 | |
989 | void |
990 | avgcost_add(UDF_INIT* initid, UDF_ARGS* args, |
991 | char* is_null __attribute__((unused)), |
992 | char* message __attribute__((unused))) |
993 | { |
994 | if (args->args[0] && args->args[1]) |
995 | { |
996 | struct avgcost_data* data = (struct avgcost_data*)initid->ptr; |
997 | longlong quantity = *((longlong*)args->args[0]); |
998 | longlong newquantity = data->totalquantity + quantity; |
999 | double price = *((double*)args->args[1]); |
1000 | |
1001 | data->count++; |
1002 | |
1003 | if ( ((data->totalquantity >= 0) && (quantity < 0)) |
1004 | || ((data->totalquantity < 0) && (quantity > 0)) ) |
1005 | { |
1006 | /* |
1007 | ** passing from + to - or from - to + |
1008 | */ |
1009 | if ( ((quantity < 0) && (newquantity < 0)) |
1010 | || ((quantity > 0) && (newquantity > 0)) ) |
1011 | { |
1012 | data->totalprice = price * (double)newquantity; |
1013 | } |
1014 | /* |
1015 | ** sub q if totalq > 0 |
1016 | ** add q if totalq < 0 |
1017 | */ |
1018 | else |
1019 | { |
1020 | price = data->totalprice / (double)data->totalquantity; |
1021 | data->totalprice = price * (double)newquantity; |
1022 | } |
1023 | data->totalquantity = newquantity; |
1024 | } |
1025 | else |
1026 | { |
1027 | data->totalquantity += quantity; |
1028 | data->totalprice += price * (double)quantity; |
1029 | } |
1030 | |
1031 | if (data->totalquantity == 0) |
1032 | data->totalprice = 0.0; |
1033 | } |
1034 | } |
1035 | |
1036 | |
1037 | double |
1038 | avgcost( UDF_INIT* initid, UDF_ARGS* args __attribute__((unused)), |
1039 | char* is_null, char* error __attribute__((unused))) |
1040 | { |
1041 | struct avgcost_data* data = (struct avgcost_data*)initid->ptr; |
1042 | if (!data->count || !data->totalquantity) |
1043 | { |
1044 | *is_null = 1; |
1045 | return 0.0; |
1046 | } |
1047 | |
1048 | *is_null = 0; |
1049 | return data->totalprice/(double)data->totalquantity; |
1050 | } |
1051 | |
1052 | my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, |
1053 | char *message); |
1054 | char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, |
1055 | unsigned long *length, char *null_value, |
1056 | char *error); |
1057 | |
1058 | my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, |
1059 | char *message) |
1060 | { |
1061 | if (args->arg_count != 1) |
1062 | { |
1063 | strmov(message,"myfunc_argument_name_init accepts only one argument" ); |
1064 | return 1; |
1065 | } |
1066 | initid->max_length= args->attribute_lengths[0]; |
1067 | initid->maybe_null= 1; |
1068 | initid->const_item= 1; |
1069 | return 0; |
1070 | } |
1071 | |
1072 | char *myfunc_argument_name(UDF_INIT *initid __attribute__((unused)), |
1073 | UDF_ARGS *args, char *result, |
1074 | unsigned long *length, char *null_value, |
1075 | char *error __attribute__((unused))) |
1076 | { |
1077 | if (!args->attributes[0]) |
1078 | { |
1079 | *null_value= 1; |
1080 | return 0; |
1081 | } |
1082 | (*length)--; /* space for ending \0 (for debugging purposes) */ |
1083 | if (*length > args->attribute_lengths[0]) |
1084 | *length= args->attribute_lengths[0]; |
1085 | memcpy(result, args->attributes[0], *length); |
1086 | result[*length]= 0; |
1087 | return result; |
1088 | } |
1089 | |
1090 | |
1091 | |
1092 | my_bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
1093 | { |
1094 | if (args->arg_count != 1) |
1095 | { |
1096 | strmov(message, "IS_CONST accepts only one argument" ); |
1097 | return 1; |
1098 | } |
1099 | initid->ptr= (char*)((args->args[0] != NULL) ? (size_t)1 : (size_t)0); |
1100 | return 0; |
1101 | } |
1102 | |
1103 | char * is_const(UDF_INIT *initid, UDF_ARGS *args __attribute__((unused)), |
1104 | char *result, unsigned long *length, |
1105 | char *is_null, char *error __attribute__((unused))) |
1106 | { |
1107 | if (initid->ptr != 0) { |
1108 | sprintf(result, "const" ); |
1109 | } else { |
1110 | sprintf(result, "not const" ); |
1111 | } |
1112 | *is_null= 0; |
1113 | *length= (uint) strlen(result); |
1114 | return result; |
1115 | } |
1116 | |
1117 | |
1118 | |
1119 | my_bool check_const_len_init(UDF_INIT *initid, UDF_ARGS *args, char *message) |
1120 | { |
1121 | if (args->arg_count != 1) |
1122 | { |
1123 | strmov(message, "CHECK_CONST_LEN accepts only one argument" ); |
1124 | return 1; |
1125 | } |
1126 | if (args->args[0] == 0) |
1127 | { |
1128 | initid->ptr= (char*)"Not constant" ; |
1129 | } |
1130 | else if(strlen(args->args[0]) == args->lengths[0]) |
1131 | { |
1132 | initid->ptr= (char*)"Correct length" ; |
1133 | } |
1134 | else |
1135 | { |
1136 | initid->ptr= (char*)"Wrong length" ; |
1137 | } |
1138 | initid->max_length = 100; |
1139 | return 0; |
1140 | } |
1141 | |
1142 | char * check_const_len(UDF_INIT *initid, UDF_ARGS *args __attribute__((unused)), |
1143 | char *result, unsigned long *length, |
1144 | char *is_null, char *error __attribute__((unused))) |
1145 | { |
1146 | strmov(result, initid->ptr); |
1147 | *length= (uint) strlen(result); |
1148 | *is_null= 0; |
1149 | return result; |
1150 | } |
1151 | |
1152 | |
1153 | #endif /* HAVE_DLOPEN */ |
1154 | |