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