From 3c9ba0208ae236435f5bad462b41f0df72aaad28 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Jul 2006 04:41:50 +0200 Subject: udf_example.c, udf.test, Makefile.am: Converted "udf_example.cc" to C, avoids C++ runtime lib dependency (bug#21336) sql/Makefile.am: "udf_example.cc" converted to C, avoids C++ runtime lib dependency (bug#21336) mysql-test/t/udf.test: "udf_example.cc" converted to C, avoids C++ runtime lib dependency (bug#21336) sql/udf_example.c: Changes to be strict ansi, except long long --- mysql-test/t/udf.test | 2 +- sql/Makefile.am | 4 +- sql/udf_example.c | 1067 +++++++++++++++++++++++++++++++++++++++++++++++++ sql/udf_example.cc | 1044 ----------------------------------------------- 4 files changed, 1070 insertions(+), 1047 deletions(-) create mode 100644 sql/udf_example.c delete mode 100644 sql/udf_example.cc diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index f3be08c8537..e0c2493c616 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -1,6 +1,6 @@ --source include/have_udf.inc # -# To run this tests the "sql/udf_example.cc" need to be compiled into +# To run this tests the "sql/udf_example.c" need to be compiled into # udf_example.so and LD_LIBRARY_PATH should be setup to point out where # the library are. # diff --git a/sql/Makefile.am b/sql/Makefile.am index 416f0faf1a6..8428d6401b5 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -116,7 +116,7 @@ DEFS = -DMYSQL_SERVER \ @DEFS@ BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h -EXTRA_DIST = udf_example.cc $(BUILT_SOURCES) +EXTRA_DIST = $(BUILT_SOURCES) DISTCLEANFILES = lex_hash.h AM_YFLAGS = -d @@ -155,7 +155,7 @@ lex_hash.h: gen_lex_hash$(EXEEXT) # For testing of udf_example.so noinst_LTLIBRARIES= udf_example.la -udf_example_la_SOURCES= udf_example.cc +udf_example_la_SOURCES= udf_example.c udf_example_la_LDFLAGS= -module -rpath $(pkglibdir) diff --git a/sql/udf_example.c b/sql/udf_example.c new file mode 100644 index 00000000000..62995085599 --- /dev/null +++ b/sql/udf_example.c @@ -0,0 +1,1067 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* +** example file of UDF (user definable functions) that are dynamicly loaded +** into the standard mysqld core. +** +** The functions name, type and shared library is saved in the new system +** table 'func'. To be able to create new functions one must have write +** privilege for the database 'mysql'. If one starts MySQL with +** --skip-grant, then UDF initialization will also be skipped. +** +** Syntax for the new commands are: +** create function returns {string|real|integer} +** soname +** drop function +** +** Each defined function may have a xxxx_init function and a xxxx_deinit +** function. The init function should alloc memory for the function +** and tell the main function about the max length of the result +** (for string functions), number of decimals (for double functions) and +** if the result may be a null value. +** +** If a function sets the 'error' argument to 1 the function will not be +** called anymore and mysqld will return NULL for all calls to this copy +** of the function. +** +** All strings arguments to functions are given as string pointer + length +** to allow handling of binary data. +** Remember that all functions must be thread safe. This means that one is not +** allowed to alloc any global or static variables that changes! +** If one needs memory one should alloc this in the init function and free +** this on the __deinit function. +** +** Note that the init and __deinit functions are only called once per +** SQL statement while the value function may be called many times +** +** Function 'metaphon' returns a metaphon string of the string argument. +** This is something like a soundex string, but it's more tuned for English. +** +** Function 'myfunc_double' returns summary of codes of all letters +** of arguments divided by summary length of all its arguments. +** +** Function 'myfunc_int' returns summary length of all its arguments. +** +** Function 'sequence' returns an sequence starting from a certain number. +** +** Function 'myfunc_argument_name' returns name of argument. +** +** On the end is a couple of functions that converts hostnames to ip and +** vice versa. +** +** A dynamicly loadable file should be compiled shared. +** (something like: gcc -shared -o my_func.so myfunc.cc). +** You can easily get all switches right by doing: +** cd sql ; make udf_example.o +** Take the compile line that make writes, remove the '-c' near the end of +** the line and add -shared -o udf_example.so to the end of the compile line. +** The resulting library (udf_example.so) should be copied to some dir +** searched by ld. (/usr/lib ?) +** If you are using gcc, then you should be able to create the udf_example.so +** by simply doing 'make udf_example.so'. +** +** After the library is made one must notify mysqld about the new +** functions with the commands: +** +** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; +** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; +** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; +** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so"; +** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; +** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; +** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; +** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"; +** +** After this the functions will work exactly like native MySQL functions. +** Functions should be created only once. +** +** The functions can be deleted by: +** +** DROP FUNCTION metaphon; +** DROP FUNCTION myfunc_double; +** DROP FUNCTION myfunc_int; +** DROP FUNCTION lookup; +** DROP FUNCTION reverse_lookup; +** DROP FUNCTION avgcost; +** DROP FUNCTION myfunc_argument_name; +** +** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All +** Active function will be reloaded on every restart of server +** (if --skip-grant-tables is not given) +** +** If you ge problems with undefined symbols when loading the shared +** library, you should verify that mysqld is compiled with the -rdynamic +** option. +** +** If you can't get AGGREGATES to work, check that you have the column +** 'type' in the mysql.func table. If not, run 'mysql_fix_privilege_tables'. +** +*/ + +#ifdef STANDARD +/* STANDARD is defined, don't use any mysql functions */ +#include +#include +#include +#ifdef __WIN__ +typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ +typedef __int64 longlong; +#else +typedef unsigned long long ulonglong; +typedef long long longlong; +#endif /*__WIN__*/ +#else +#include +#include +#include /* To get strmov() */ +#endif +#include +#include + +static pthread_mutex_t LOCK_hostname; + +#ifdef HAVE_DLOPEN + +/* These must be right or mysqld will not find the symbol! */ + +my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +void metaphon_deinit(UDF_INIT *initid); +char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *length, char *is_null, char *error); +my_bool myfunc_double_init(UDF_INIT *, UDF_ARGS *args, char *message); +double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); + void sequence_deinit(UDF_INIT *initid); +longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); +void avgcost_deinit( UDF_INIT* initid ); +void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); +void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); +void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); +double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); + + +/************************************************************************* +** Example of init function +** Arguments: +** initid Points to a structure that the init function should fill. +** This argument is given to all other functions. +** my_bool maybe_null 1 if function can return NULL +** Default value is 1 if any of the arguments +** is declared maybe_null. +** unsigned int decimals Number of decimals. +** Default value is max decimals in any of the +** arguments. +** unsigned int max_length Length of string result. +** The default value for integer functions is 21 +** The default value for real functions is 13+ +** default number of decimals. +** The default value for string functions is +** the longest string argument. +** char *ptr; A pointer that the function can use. +** +** args Points to a structure which contains: +** unsigned int arg_count Number of arguments +** enum Item_result *arg_type Types for each argument. +** Types are STRING_RESULT, REAL_RESULT +** and INT_RESULT. +** char **args Pointer to constant arguments. +** Contains 0 for not constant argument. +** unsigned long *lengths; max string length for each argument +** char *maybe_null Information of which arguments +** may be NULL +** +** message Error message that should be passed to the user on fail. +** The message buffer is MYSQL_ERRMSG_SIZE big, but one should +** try to keep the error message less than 80 bytes long! +** +** This function should return 1 if something goes wrong. In this case +** message should contain something usefull! +**************************************************************************/ + +#define MAXMETAPH 8 + +my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) + { + strcpy(message,"Wrong arguments to metaphon; Use the source"); + return 1; + } + initid->max_length=MAXMETAPH; + return 0; +} + +/**************************************************************************** +** Deinit function. This should free all resources allocated by +** this function. +** Arguments: +** initid Return value from xxxx_init +****************************************************************************/ + + +void metaphon_deinit(UDF_INIT *initid __attribute__((unused))) +{ +} + +/*************************************************************************** +** UDF string function. +** Arguments: +** initid Structure filled by xxx_init +** args The same structure as to xxx_init. This structure +** contains values for all parameters. +** Note that the functions MUST check and convert all +** to the type it wants! Null values are represented by +** a NULL pointer +** result Possible buffer to save result. At least 255 byte long. +** length Pointer to length of the above buffer. In this the function +** should save the result length +** is_null If the result is null, one should store 1 here. +** error If something goes fatally wrong one should store 1 here. +** +** This function should return a pointer to the result string. +** Normally this is 'result' but may also be an alloced string. +***************************************************************************/ + +/* Character coding array */ +static char codes[26] = { + 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 + /* 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*/ + }; + +/*--- Macros to access character coding array -------------*/ + +#define ISVOWEL(x) (codes[(x) - 'A'] & 1) /* AEIOU */ + + /* Following letters are not changed */ +#define NOCHANGE(x) (codes[(x) - 'A'] & 2) /* FJLMNR */ + + /* These form diphthongs when preceding H */ +#define AFFECTH(x) (codes[(x) - 'A'] & 4) /* CGPST */ + + /* These make C and G soft */ +#define MAKESOFT(x) (codes[(x) - 'A'] & 8) /* EIY */ + + /* These prevent GH from becoming F */ +#define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */ + + +char *metaphon(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args, char *result, unsigned long *length, + char *is_null, char *error __attribute__((unused))) +{ + const char *word=args->args[0]; + const char *w_end; + char *org_result; + char *n, *n_start, *n_end; /* pointers to string */ + char *metaph_end; /* pointers to end of metaph */ + char ntrans[32]; /* word with uppercase letters */ + int KSflag; /* state flag for X to KS */ + + if (!word) /* Null argument */ + { + *is_null=1; + return 0; + } + w_end=word+args->lengths[0]; + org_result=result; + + /*-------------------------------------------------------- + * Copy word to internal buffer, dropping non-alphabetic + * characters and converting to uppercase. + *-------------------------------------------------------*/ + + for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; + word != w_end && n < n_end; word++ ) + if ( isalpha ( *word )) + *n++ = toupper ( *word ); + + if ( n == ntrans + 1 ) /* return empty string if 0 bytes */ + { + *length=0; + return result; + } + n_end = n; /* set n_end to end of string */ + ntrans[0] = 'Z'; /* ntrans[0] should be a neutral char */ + n[0]=n[1]=0; /* pad with nulls */ + n = ntrans + 1; /* assign pointer to start */ + + /*------------------------------------------------------------ + * check for all prefixes: + * PN KN GN AE WR WH and X at start. + *----------------------------------------------------------*/ + + switch ( *n ) { + case 'P': + case 'K': + case 'G': + if ( n[1] == 'N') + *n++ = 0; + break; + case 'A': + if ( n[1] == 'E') + *n++ = 0; + break; + case 'W': + if ( n[1] == 'R' ) + *n++ = 0; + else + if ( *(n + 1) == 'H') + { + n[1] = *n; + *n++ = 0; + } + break; + case 'X': + *n = 'S'; + break; + } + + /*------------------------------------------------------------ + * Now, loop step through string, stopping at end of string + * or when the computed metaph is MAXMETAPH characters long + *----------------------------------------------------------*/ + + KSflag = 0; /* state flag for KS translation */ + + for (metaph_end = result + MAXMETAPH, n_start = n; + n < n_end && result < metaph_end; n++ ) + { + + if ( KSflag ) + { + KSflag = 0; + *result++ = *n; + } + else + { + /* drop duplicates except for CC */ + if ( *( n - 1 ) == *n && *n != 'C' ) + continue; + + /* check for F J L M N R or first letter vowel */ + if ( NOCHANGE ( *n ) || + ( n == n_start && ISVOWEL ( *n ))) + *result++ = *n; + else + switch ( *n ) { + case 'B': /* check for -MB */ + if ( n < n_end || *( n - 1 ) != 'M' ) + *result++ = *n; + break; + + case 'C': /* C = X ("sh" sound) in CH and CIA */ + /* = S in CE CI and CY */ + /* dropped in SCI SCE SCY */ + /* else K */ + if ( *( n - 1 ) != 'S' || + !MAKESOFT ( n[1])) + { + if ( n[1] == 'I' && n[2] == 'A' ) + *result++ = 'X'; + else + if ( MAKESOFT ( n[1])) + *result++ = 'S'; + else + if ( n[1] == 'H' ) + *result++ = (( n == n_start && + !ISVOWEL ( n[2])) || + *( n - 1 ) == 'S' ) ? + (char)'K' : (char)'X'; + else + *result++ = 'K'; + } + break; + + case 'D': /* J before DGE, DGI, DGY, else T */ + *result++ = + ( n[1] == 'G' && + MAKESOFT ( n[2])) ? + (char)'J' : (char)'T'; + break; + + case 'G': /* complicated, see table in text */ + if (( n[1] != 'H' || ISVOWEL ( n[2])) + && ( + n[1] != 'N' || + ( + (n + 1) < n_end && + ( + n[2] != 'E' || + *( n + 3 ) != 'D' + ) + ) + ) + && ( + *( n - 1 ) != 'D' || + !MAKESOFT ( n[1]) + ) + ) + *result++ = + ( MAKESOFT ( *( n + 1 )) && + n[2] != 'G' ) ? + (char)'J' : (char)'K'; + else + if ( n[1] == 'H' && + !NOGHTOF( *( n - 3 )) && + *( n - 4 ) != 'H') + *result++ = 'F'; + break; + + case 'H': /* H if before a vowel and not after */ + /* C, G, P, S, T */ + + if ( !AFFECTH ( *( n - 1 )) && + ( !ISVOWEL ( *( n - 1 )) || + ISVOWEL ( n[1]))) + *result++ = 'H'; + break; + + case 'K': /* K = K, except dropped after C */ + if ( *( n - 1 ) != 'C') + *result++ = 'K'; + break; + + case 'P': /* PH = F, else P = P */ + *result++ = *( n + 1 ) == 'H' + ? (char)'F' : (char)'P'; + break; + case 'Q': /* Q = K (U after Q is already gone */ + *result++ = 'K'; + break; + + case 'S': /* SH, SIO, SIA = X ("sh" sound) */ + *result++ = ( n[1] == 'H' || + ( *(n + 1) == 'I' && + ( n[2] == 'O' || + n[2] == 'A'))) ? + (char)'X' : (char)'S'; + break; + + case 'T': /* TIO, TIA = X ("sh" sound) */ + /* TH = 0, ("th" sound ) */ + if ( *( n + 1 ) == 'I' && ( n[2] == 'O' + || n[2] == 'A') ) + *result++ = 'X'; + else + if ( n[1] == 'H' ) + *result++ = '0'; + else + if ( *( n + 1) != 'C' || n[2] != 'H') + *result++ = 'T'; + break; + + case 'V': /* V = F */ + *result++ = 'F'; + break; + + case 'W': /* only exist if a vowel follows */ + case 'Y': + if ( ISVOWEL ( n[1])) + *result++ = *n; + break; + + case 'X': /* X = KS, except at start */ + if ( n == n_start ) + *result++ = 'S'; + else + { + *result++ = 'K'; /* insert K, then S */ + KSflag = 1; /* this flag will cause S to be + inserted on next pass thru loop */ + } + break; + + case 'Z': + *result++ = 'S'; + break; + } + } + } + *length= (unsigned long) (result - org_result); + return org_result; +} + + +/*************************************************************************** +** UDF double function. +** Arguments: +** initid Structure filled by xxx_init +** args The same structure as to xxx_init. This structure +** contains values for all parameters. +** Note that the functions MUST check and convert all +** to the type it wants! Null values are represented by +** a NULL pointer +** is_null If the result is null, one should store 1 here. +** error If something goes fatally wrong one should store 1 here. +** +** This function should return the result. +***************************************************************************/ + +my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + uint i; + + if (!args->arg_count) + { + strcpy(message,"myfunc_double must have at least one argument"); + return 1; + } + /* + ** As this function wants to have everything as strings, force all arguments + ** to strings. + */ + for (i=0 ; i < args->arg_count; i++) + args->arg_type[i]=STRING_RESULT; + initid->maybe_null=1; /* The result may be null */ + initid->decimals=2; /* We want 2 decimals in the result */ + initid->max_length=6; /* 3 digits + . + 2 decimals */ + return 0; +} + + +double myfunc_double(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null, char *error __attribute__((unused))) +{ + unsigned long val = 0; + unsigned long v = 0; + uint i, j; + + for (i = 0; i < args->arg_count; i++) + { + if (args->args[i] == NULL) + continue; + val += args->lengths[i]; + for (j=args->lengths[i] ; j-- > 0 ;) + v += args->args[i][j]; + } + if (val) + return (double) v/ (double) val; + *is_null=1; + return 0.0; +} + + +/*************************************************************************** +** UDF long long function. +** Arguments: +** initid Return value from xxxx_init +** args The same structure as to xxx_init. This structure +** contains values for all parameters. +** Note that the functions MUST check and convert all +** to the type it wants! Null values are represented by +** a NULL pointer +** is_null If the result is null, one should store 1 here. +** error If something goes fatally wrong one should store 1 here. +** +** This function should return the result as a long long +***************************************************************************/ + +/* This function returns the sum of all arguments */ + +longlong myfunc_int(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) +{ + longlong val = 0; + uint i; + + for (i = 0; i < args->arg_count; i++) + { + if (args->args[i] == NULL) + continue; + switch (args->arg_type[i]) { + case STRING_RESULT: /* Add string lengths */ + val += args->lengths[i]; + break; + case INT_RESULT: /* Add numbers */ + val += *((longlong*) args->args[i]); + break; + case REAL_RESULT: /* Add numers as longlong */ + val += (longlong) *((double*) args->args[i]); + break; + default: + break; + } + } + return val; +} + +/* + At least one of _init/_deinit is needed unless the server is started + with --allow_suspicious_udfs. +*/ +my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args __attribute__((unused)), + char *message __attribute__((unused))) +{ + return 0; +} + +/* + Simple example of how to get a sequences starting from the first argument + or 1 if no arguments have been given +*/ + +my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count > 1) + { + strmov(message,"This function takes none or 1 argument"); + return 1; + } + if (args->arg_count) + args->arg_type[0]= INT_RESULT; /* Force argument to int */ + + if (!(initid->ptr=(char*) malloc(sizeof(longlong)))) + { + strmov(message,"Couldn't allocate memory"); + return 1; + } + bzero(initid->ptr,sizeof(longlong)); + /* + Fool MySQL to think that this function is a constant + This will ensure that MySQL only evalutes the function + when the rows are sent to the client and not before any ORDER BY + clauses + */ + initid->const_item=1; + return 0; +} + +void sequence_deinit(UDF_INIT *initid) +{ + if (initid->ptr) + free(initid->ptr); +} + +longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) +{ + ulonglong val=0; + if (args->arg_count) + val= *((longlong*) args->args[0]); + return ++*((longlong*) initid->ptr) + val; +} + + +/**************************************************************************** +** Some functions that handles IP and hostname conversions +** The orignal function was from Zeev Suraski. +** +** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; +** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; +** +****************************************************************************/ + +#include +#include +#include +#include + +my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +void lookup_deinit(UDF_INIT *initid); +char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *length, char *null_value, char *error); +my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +void reverse_lookup_deinit(UDF_INIT *initid); +char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *length, char *null_value, char *error); + + +/**************************************************************************** +** lookup IP for an hostname. +** +** This code assumes that gethostbyname_r exists and inet_ntoa() is thread +** safe (As it is in Solaris) +****************************************************************************/ + + +my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) + { + strmov(message,"Wrong arguments to lookup; Use the source"); + return 1; + } + initid->max_length=11; + initid->maybe_null=1; +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) + (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); +#endif + return 0; +} + +void lookup_deinit(UDF_INIT *initid __attribute__((unused))) +{ +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) + (void) pthread_mutex_destroy(&LOCK_hostname); +#endif +} + +char *lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *result, unsigned long *res_length, char *null_value, + char *error __attribute__((unused))) +{ + uint length; + char name_buff[256]; + struct hostent *hostent; +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) + int tmp_errno; + char hostname_buff[2048]; + struct hostent tmp_hostent; +#endif + struct in_addr in; + + if (!args->args[0] || !(length=args->lengths[0])) + { + *null_value=1; + return 0; + } + if (length >= sizeof(name_buff)) + length=sizeof(name_buff)-1; + memcpy(name_buff,args->args[0],length); + name_buff[length]=0; +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) + if (!(hostent=gethostbyname_r(name_buff,&tmp_hostent,hostname_buff, + sizeof(hostname_buff), &tmp_errno))) + { + *null_value=1; + return 0; + } +#else + VOID(pthread_mutex_lock(&LOCK_hostname)); + if (!(hostent= gethostbyname((char*) name_buff))) + { + VOID(pthread_mutex_unlock(&LOCK_hostname)); + *null_value= 1; + return 0; + } + VOID(pthread_mutex_unlock(&LOCK_hostname)); +#endif + memcpy_fixed((char*) &in,(char*) *hostent->h_addr_list, sizeof(in.s_addr)); + *res_length= (ulong) (strmov(result, inet_ntoa(in)) - result); + return result; +} + + +/**************************************************************************** +** return hostname for an IP number. +** The functions can take as arguments a string "xxx.xxx.xxx.xxx" or +** four numbers. +****************************************************************************/ + +my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count == 1) + args->arg_type[0]= STRING_RESULT; + else if (args->arg_count == 4) + args->arg_type[0]=args->arg_type[1]=args->arg_type[2]=args->arg_type[3]= + INT_RESULT; + else + { + strmov(message, + "Wrong number of arguments to reverse_lookup; Use the source"); + return 1; + } + initid->max_length=32; + initid->maybe_null=1; +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) + (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); +#endif + return 0; +} + +void reverse_lookup_deinit(UDF_INIT *initid __attribute__((unused))) +{ +#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) + (void) pthread_mutex_destroy(&LOCK_hostname); +#endif +} + +char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *result, unsigned long *res_length, + char *null_value, char *error __attribute__((unused))) +{ +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) + char name_buff[256]; + struct hostent tmp_hostent; +#endif + struct hostent *hp; + unsigned long taddr; + uint length; + + if (args->arg_count == 4) + { + if (!args->args[0] || !args->args[1] ||!args->args[2] ||!args->args[3]) + { + *null_value=1; + return 0; + } + sprintf(result,"%d.%d.%d.%d", + (int) *((longlong*) args->args[0]), + (int) *((longlong*) args->args[1]), + (int) *((longlong*) args->args[2]), + (int) *((longlong*) args->args[3])); + } + else + { /* string argument */ + if (!args->args[0]) /* Return NULL for NULL values */ + { + *null_value=1; + return 0; + } + length=args->lengths[0]; + if (length >= (uint) *res_length-1) + length=(uint) *res_length; + memcpy(result,args->args[0],length); + result[length]=0; + } + + taddr = inet_addr(result); + if (taddr == (unsigned long) -1L) + { + *null_value=1; + return 0; + } +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) + int tmp_errno; + if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET, + &tmp_hostent, name_buff,sizeof(name_buff), + &tmp_errno))) + { + *null_value=1; + return 0; + } +#else + VOID(pthread_mutex_lock(&LOCK_hostname)); + if (!(hp= gethostbyaddr((char*) &taddr, sizeof(taddr), AF_INET))) + { + VOID(pthread_mutex_unlock(&LOCK_hostname)); + *null_value= 1; + return 0; + } + VOID(pthread_mutex_unlock(&LOCK_hostname)); +#endif + *res_length=(ulong) (strmov(result,hp->h_name) - result); + return result; +} + +/* +** Syntax for the new aggregate commands are: +** create aggregate function returns {string|real|integer} +** soname +** +** Syntax for avgcost: avgcost( t.quantity, t.price ) +** with t.quantity=integer, t.price=double +** (this example is provided by Andreas F. Bobak ) +*/ + + +struct avgcost_data +{ + ulonglong count; + longlong totalquantity; + double totalprice; +}; + + +/* +** Average Cost Aggregate Function. +*/ +my_bool +avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ) +{ + struct avgcost_data* data; + + if (args->arg_count != 2) + { + strcpy( + message, + "wrong number of arguments: AVGCOST() requires two arguments" + ); + return 1; + } + + if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT) ) + { + strcpy( + message, + "wrong argument type: AVGCOST() requires an INT and a REAL" + ); + return 1; + } + + /* + ** force arguments to double. + */ + /*args->arg_type[0] = REAL_RESULT; + args->arg_type[1] = REAL_RESULT;*/ + + initid->maybe_null = 0; /* The result may be null */ + initid->decimals = 4; /* We want 4 decimals in the result */ + initid->max_length = 20; /* 6 digits + . + 10 decimals */ + + if (!(data = (struct avgcost_data*) malloc(sizeof(struct avgcost_data)))) + { + strmov(message,"Couldn't allocate memory"); + return 1; + } + data->totalquantity = 0; + data->totalprice = 0.0; + + initid->ptr = (char*)data; + + return 0; +} + +void +avgcost_deinit( UDF_INIT* initid ) +{ + free(initid->ptr); +} + + +/* This is only for MySQL 4.0 compability */ +void +avgcost_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) +{ + avgcost_clear(initid, is_null, message); + avgcost_add(initid, args, is_null, message); +} + +/* This is needed to get things to work in MySQL 4.1.1 and above */ + +void +avgcost_clear(UDF_INIT* initid, char* is_null __attribute__((unused)), + char* message __attribute__((unused))) +{ + struct avgcost_data* data = (struct avgcost_data*)initid->ptr; + data->totalprice= 0.0; + data->totalquantity= 0; + data->count= 0; +} + + +void +avgcost_add(UDF_INIT* initid, UDF_ARGS* args, + char* is_null __attribute__((unused)), + char* message __attribute__((unused))) +{ + if (args->args[0] && args->args[1]) + { + struct avgcost_data* data = (struct avgcost_data*)initid->ptr; + longlong quantity = *((longlong*)args->args[0]); + longlong newquantity = data->totalquantity + quantity; + double price = *((double*)args->args[1]); + + data->count++; + + if ( ((data->totalquantity >= 0) && (quantity < 0)) + || ((data->totalquantity < 0) && (quantity > 0)) ) + { + /* + ** passing from + to - or from - to + + */ + if ( ((quantity < 0) && (newquantity < 0)) + || ((quantity > 0) && (newquantity > 0)) ) + { + data->totalprice = price * (double)newquantity; + } + /* + ** sub q if totalq > 0 + ** add q if totalq < 0 + */ + else + { + price = data->totalprice / (double)data->totalquantity; + data->totalprice = price * (double)newquantity; + } + data->totalquantity = newquantity; + } + else + { + data->totalquantity += quantity; + data->totalprice += price * (double)quantity; + } + + if (data->totalquantity == 0) + data->totalprice = 0.0; + } +} + + +double +avgcost( UDF_INIT* initid, UDF_ARGS* args __attribute__((unused)), + char* is_null, char* error __attribute__((unused))) +{ + struct avgcost_data* data = (struct avgcost_data*)initid->ptr; + if (!data->count || !data->totalquantity) + { + *is_null = 1; + return 0.0; + } + + *is_null = 0; + return data->totalprice/(double)data->totalquantity; +} + +my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, + char *message); +char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *length, char *null_value, + char *error); + +my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, + char *message) +{ + if (args->arg_count != 1) + { + strmov(message,"myfunc_argument_name_init accepts only one argument"); + return 1; + } + initid->max_length= args->attribute_lengths[0]; + initid->maybe_null= 1; + initid->const_item= 1; + return 0; +} + +char *myfunc_argument_name(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args, char *result, + unsigned long *length, char *null_value, + char *error __attribute__((unused))) +{ + if (!args->attributes[0]) + { + null_value= 0; + return 0; + } + (*length)--; /* space for ending \0 (for debugging purposes) */ + if (*length > args->attribute_lengths[0]) + *length= args->attribute_lengths[0]; + memcpy(result, args->attributes[0], *length); + result[*length]= 0; + return result; +} + +#endif /* HAVE_DLOPEN */ diff --git a/sql/udf_example.cc b/sql/udf_example.cc deleted file mode 100644 index 6ad066eacc2..00000000000 --- a/sql/udf_example.cc +++ /dev/null @@ -1,1044 +0,0 @@ -/* Copyright (C) 2002 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* -** example file of UDF (user definable functions) that are dynamicly loaded -** into the standard mysqld core. -** -** The functions name, type and shared library is saved in the new system -** table 'func'. To be able to create new functions one must have write -** privilege for the database 'mysql'. If one starts MySQL with -** --skip-grant, then UDF initialization will also be skipped. -** -** Syntax for the new commands are: -** create function returns {string|real|integer} -** soname -** drop function -** -** Each defined function may have a xxxx_init function and a xxxx_deinit -** function. The init function should alloc memory for the function -** and tell the main function about the max length of the result -** (for string functions), number of decimals (for double functions) and -** if the result may be a null value. -** -** If a function sets the 'error' argument to 1 the function will not be -** called anymore and mysqld will return NULL for all calls to this copy -** of the function. -** -** All strings arguments to functions are given as string pointer + length -** to allow handling of binary data. -** Remember that all functions must be thread safe. This means that one is not -** allowed to alloc any global or static variables that changes! -** If one needs memory one should alloc this in the init function and free -** this on the __deinit function. -** -** Note that the init and __deinit functions are only called once per -** SQL statement while the value function may be called many times -** -** Function 'metaphon' returns a metaphon string of the string argument. -** This is something like a soundex string, but it's more tuned for English. -** -** Function 'myfunc_double' returns summary of codes of all letters -** of arguments divided by summary length of all its arguments. -** -** Function 'myfunc_int' returns summary length of all its arguments. -** -** Function 'sequence' returns an sequence starting from a certain number. -** -** Function 'myfunc_argument_name' returns name of argument. -** -** On the end is a couple of functions that converts hostnames to ip and -** vice versa. -** -** A dynamicly loadable file should be compiled shared. -** (something like: gcc -shared -o my_func.so myfunc.cc). -** You can easily get all switches right by doing: -** cd sql ; make udf_example.o -** Take the compile line that make writes, remove the '-c' near the end of -** the line and add -shared -o udf_example.so to the end of the compile line. -** The resulting library (udf_example.so) should be copied to some dir -** searched by ld. (/usr/lib ?) -** If you are using gcc, then you should be able to create the udf_example.so -** by simply doing 'make udf_example.so'. -** -** After the library is made one must notify mysqld about the new -** functions with the commands: -** -** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; -** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; -** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; -** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so"; -** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; -** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; -** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; -** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"; -** -** After this the functions will work exactly like native MySQL functions. -** Functions should be created only once. -** -** The functions can be deleted by: -** -** DROP FUNCTION metaphon; -** DROP FUNCTION myfunc_double; -** DROP FUNCTION myfunc_int; -** DROP FUNCTION lookup; -** DROP FUNCTION reverse_lookup; -** DROP FUNCTION avgcost; -** DROP FUNCTION myfunc_argument_name; -** -** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All -** Active function will be reloaded on every restart of server -** (if --skip-grant-tables is not given) -** -** If you ge problems with undefined symbols when loading the shared -** library, you should verify that mysqld is compiled with the -rdynamic -** option. -** -** If you can't get AGGREGATES to work, check that you have the column -** 'type' in the mysql.func table. If not, run 'mysql_fix_privilege_tables'. -** -*/ - -#ifdef STANDARD -/* STANDARD is defined, don't use any mysql functions */ -#include -#include -#include -#ifdef __WIN__ -typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ -typedef __int64 longlong; -#else -typedef unsigned long long ulonglong; -typedef long long longlong; -#endif /*__WIN__*/ -#else -#include -#include -#include // To get strmov() -#endif -#include -#include - -static pthread_mutex_t LOCK_hostname; - -#ifdef HAVE_DLOPEN - -/* These must be right or mysqld will not find the symbol! */ - -extern "C" { -my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message); -void metaphon_deinit(UDF_INIT *initid); -char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *is_null, char *error); -my_bool myfunc_double_init(UDF_INIT *, UDF_ARGS *args, char *message); -double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error); -my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); -longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error); -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); - void sequence_deinit(UDF_INIT *initid); -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error); -my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); -void avgcost_deinit( UDF_INIT* initid ); -void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); -void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); -void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); -double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); -} - - -/************************************************************************* -** Example of init function -** Arguments: -** initid Points to a structure that the init function should fill. -** This argument is given to all other functions. -** my_bool maybe_null 1 if function can return NULL -** Default value is 1 if any of the arguments -** is declared maybe_null. -** unsigned int decimals Number of decimals. -** Default value is max decimals in any of the -** arguments. -** unsigned int max_length Length of string result. -** The default value for integer functions is 21 -** The default value for real functions is 13+ -** default number of decimals. -** The default value for string functions is -** the longest string argument. -** char *ptr; A pointer that the function can use. -** -** args Points to a structure which contains: -** unsigned int arg_count Number of arguments -** enum Item_result *arg_type Types for each argument. -** Types are STRING_RESULT, REAL_RESULT -** and INT_RESULT. -** char **args Pointer to constant arguments. -** Contains 0 for not constant argument. -** unsigned long *lengths; max string length for each argument -** char *maybe_null Information of which arguments -** may be NULL -** -** message Error message that should be passed to the user on fail. -** The message buffer is MYSQL_ERRMSG_SIZE big, but one should -** try to keep the error message less than 80 bytes long! -** -** This function should return 1 if something goes wrong. In this case -** message should contain something usefull! -**************************************************************************/ - -#define MAXMETAPH 8 - -my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) - { - strcpy(message,"Wrong arguments to metaphon; Use the source"); - return 1; - } - initid->max_length=MAXMETAPH; - return 0; -} - -/**************************************************************************** -** Deinit function. This should free all resources allocated by -** this function. -** Arguments: -** initid Return value from xxxx_init -****************************************************************************/ - - -void metaphon_deinit(UDF_INIT *initid) -{ -} - -/*************************************************************************** -** UDF string function. -** Arguments: -** initid Structure filled by xxx_init -** args The same structure as to xxx_init. This structure -** contains values for all parameters. -** Note that the functions MUST check and convert all -** to the type it wants! Null values are represented by -** a NULL pointer -** result Possible buffer to save result. At least 255 byte long. -** length Pointer to length of the above buffer. In this the function -** should save the result length -** is_null If the result is null, one should store 1 here. -** error If something goes fatally wrong one should store 1 here. -** -** This function should return a pointer to the result string. -** Normally this is 'result' but may also be an alloced string. -***************************************************************************/ - -/* Character coding array */ -static char codes[26] = { - 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 - /* 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*/ - }; - -/*--- Macros to access character coding array -------------*/ - -#define ISVOWEL(x) (codes[(x) - 'A'] & 1) /* AEIOU */ - - /* Following letters are not changed */ -#define NOCHANGE(x) (codes[(x) - 'A'] & 2) /* FJLMNR */ - - /* These form diphthongs when preceding H */ -#define AFFECTH(x) (codes[(x) - 'A'] & 4) /* CGPST */ - - /* These make C and G soft */ -#define MAKESOFT(x) (codes[(x) - 'A'] & 8) /* EIY */ - - /* These prevent GH from becoming F */ -#define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */ - - -char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *is_null, char *error) -{ - const char *word=args->args[0]; - if (!word) // Null argument - { - *is_null=1; - return 0; - } - const char *w_end=word+args->lengths[0]; - char *org_result=result; - - char *n, *n_start, *n_end; /* pointers to string */ - char *metaph, *metaph_end; /* pointers to metaph */ - char ntrans[32]; /* word with uppercase letters */ - char newm[8]; /* new metaph for comparison */ - int KSflag; /* state flag for X to KS */ - - /*-------------------------------------------------------- - * Copy word to internal buffer, dropping non-alphabetic - * characters and converting to uppercase. - *-------------------------------------------------------*/ - - for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; - word != w_end && n < n_end; word++ ) - if ( isalpha ( *word )) - *n++ = toupper ( *word ); - - if ( n == ntrans + 1 ) /* return empty string if 0 bytes */ - { - *length=0; - return result; - } - n_end = n; /* set n_end to end of string */ - ntrans[0] = 'Z'; /* ntrans[0] should be a neutral char */ - n[0]=n[1]=0; /* pad with nulls */ - n = ntrans + 1; /* assign pointer to start */ - - /*------------------------------------------------------------ - * check for all prefixes: - * PN KN GN AE WR WH and X at start. - *----------------------------------------------------------*/ - - switch ( *n ) { - case 'P': - case 'K': - case 'G': - if ( n[1] == 'N') - *n++ = 0; - break; - case 'A': - if ( n[1] == 'E') - *n++ = 0; - break; - case 'W': - if ( n[1] == 'R' ) - *n++ = 0; - else - if ( *(n + 1) == 'H') - { - n[1] = *n; - *n++ = 0; - } - break; - case 'X': - *n = 'S'; - break; - } - - /*------------------------------------------------------------ - * Now, loop step through string, stopping at end of string - * or when the computed metaph is MAXMETAPH characters long - *----------------------------------------------------------*/ - - KSflag = 0; /* state flag for KS translation */ - - for (metaph_end = result + MAXMETAPH, n_start = n; - n < n_end && result < metaph_end; n++ ) - { - - if ( KSflag ) - { - KSflag = 0; - *result++ = *n; - } - else - { - /* drop duplicates except for CC */ - if ( *( n - 1 ) == *n && *n != 'C' ) - continue; - - /* check for F J L M N R or first letter vowel */ - if ( NOCHANGE ( *n ) || - ( n == n_start && ISVOWEL ( *n ))) - *result++ = *n; - else - switch ( *n ) { - case 'B': /* check for -MB */ - if ( n < n_end || *( n - 1 ) != 'M' ) - *result++ = *n; - break; - - case 'C': /* C = X ("sh" sound) in CH and CIA */ - /* = S in CE CI and CY */ - /* dropped in SCI SCE SCY */ - /* else K */ - if ( *( n - 1 ) != 'S' || - !MAKESOFT ( n[1])) - { - if ( n[1] == 'I' && n[2] == 'A' ) - *result++ = 'X'; - else - if ( MAKESOFT ( n[1])) - *result++ = 'S'; - else - if ( n[1] == 'H' ) - *result++ = (( n == n_start && - !ISVOWEL ( n[2])) || - *( n - 1 ) == 'S' ) ? - (char)'K' : (char)'X'; - else - *result++ = 'K'; - } - break; - - case 'D': /* J before DGE, DGI, DGY, else T */ - *result++ = - ( n[1] == 'G' && - MAKESOFT ( n[2])) ? - (char)'J' : (char)'T'; - break; - - case 'G': /* complicated, see table in text */ - if (( n[1] != 'H' || ISVOWEL ( n[2])) - && ( - n[1] != 'N' || - ( - (n + 1) < n_end && - ( - n[2] != 'E' || - *( n + 3 ) != 'D' - ) - ) - ) - && ( - *( n - 1 ) != 'D' || - !MAKESOFT ( n[1]) - ) - ) - *result++ = - ( MAKESOFT ( *( n + 1 )) && - n[2] != 'G' ) ? - (char)'J' : (char)'K'; - else - if ( n[1] == 'H' && - !NOGHTOF( *( n - 3 )) && - *( n - 4 ) != 'H') - *result++ = 'F'; - break; - - case 'H': /* H if before a vowel and not after */ - /* C, G, P, S, T */ - - if ( !AFFECTH ( *( n - 1 )) && - ( !ISVOWEL ( *( n - 1 )) || - ISVOWEL ( n[1]))) - *result++ = 'H'; - break; - - case 'K': /* K = K, except dropped after C */ - if ( *( n - 1 ) != 'C') - *result++ = 'K'; - break; - - case 'P': /* PH = F, else P = P */ - *result++ = *( n + 1 ) == 'H' - ? (char)'F' : (char)'P'; - break; - case 'Q': /* Q = K (U after Q is already gone */ - *result++ = 'K'; - break; - - case 'S': /* SH, SIO, SIA = X ("sh" sound) */ - *result++ = ( n[1] == 'H' || - ( *(n + 1) == 'I' && - ( n[2] == 'O' || - n[2] == 'A'))) ? - (char)'X' : (char)'S'; - break; - - case 'T': /* TIO, TIA = X ("sh" sound) */ - /* TH = 0, ("th" sound ) */ - if ( *( n + 1 ) == 'I' && ( n[2] == 'O' - || n[2] == 'A') ) - *result++ = 'X'; - else - if ( n[1] == 'H' ) - *result++ = '0'; - else - if ( *( n + 1) != 'C' || n[2] != 'H') - *result++ = 'T'; - break; - - case 'V': /* V = F */ - *result++ = 'F'; - break; - - case 'W': /* only exist if a vowel follows */ - case 'Y': - if ( ISVOWEL ( n[1])) - *result++ = *n; - break; - - case 'X': /* X = KS, except at start */ - if ( n == n_start ) - *result++ = 'S'; - else - { - *result++ = 'K'; /* insert K, then S */ - KSflag = 1; /* this flag will cause S to be - inserted on next pass thru loop */ - } - break; - - case 'Z': - *result++ = 'S'; - break; - } - } - } - *length= (unsigned long) (result - org_result); - return org_result; -} - - -/*************************************************************************** -** UDF double function. -** Arguments: -** initid Structure filled by xxx_init -** args The same structure as to xxx_init. This structure -** contains values for all parameters. -** Note that the functions MUST check and convert all -** to the type it wants! Null values are represented by -** a NULL pointer -** is_null If the result is null, one should store 1 here. -** error If something goes fatally wrong one should store 1 here. -** -** This function should return the result. -***************************************************************************/ - -my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (!args->arg_count) - { - strcpy(message,"myfunc_double must have at least one argument"); - return 1; - } - /* - ** As this function wants to have everything as strings, force all arguments - ** to strings. - */ - for (uint i=0 ; i < args->arg_count; i++) - args->arg_type[i]=STRING_RESULT; - initid->maybe_null=1; // The result may be null - initid->decimals=2; // We want 2 decimals in the result - initid->max_length=6; // 3 digits + . + 2 decimals - return 0; -} - - -double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) -{ - unsigned long val = 0; - unsigned long v = 0; - - for (uint i = 0; i < args->arg_count; i++) - { - if (args->args[i] == NULL) - continue; - val += args->lengths[i]; - for (uint j=args->lengths[i] ; j-- > 0 ;) - v += args->args[i][j]; - } - if (val) - return (double) v/ (double) val; - *is_null=1; - return 0.0; -} - - -/*************************************************************************** -** UDF long long function. -** Arguments: -** initid Return value from xxxx_init -** args The same structure as to xxx_init. This structure -** contains values for all parameters. -** Note that the functions MUST check and convert all -** to the type it wants! Null values are represented by -** a NULL pointer -** is_null If the result is null, one should store 1 here. -** error If something goes fatally wrong one should store 1 here. -** -** This function should return the result as a long long -***************************************************************************/ - -/* This function returns the sum of all arguments */ - -longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) -{ - longlong val = 0; - for (uint i = 0; i < args->arg_count; i++) - { - if (args->args[i] == NULL) - continue; - switch (args->arg_type[i]) { - case STRING_RESULT: // Add string lengths - val += args->lengths[i]; - break; - case INT_RESULT: // Add numbers - val += *((longlong*) args->args[i]); - break; - case REAL_RESULT: // Add numers as longlong - val += (longlong) *((double*) args->args[i]); - break; - default: - break; - } - } - return val; -} - -/* - At least one of _init/_deinit is needed unless the server is started - with --allow_suspicious_udfs. -*/ -my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - return 0; -} - -/* - Simple example of how to get a sequences starting from the first argument - or 1 if no arguments have been given -*/ - -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count > 1) - { - strmov(message,"This function takes none or 1 argument"); - return 1; - } - if (args->arg_count) - args->arg_type[0]= INT_RESULT; // Force argument to int - - if (!(initid->ptr=(char*) malloc(sizeof(longlong)))) - { - strmov(message,"Couldn't allocate memory"); - return 1; - } - bzero(initid->ptr,sizeof(longlong)); - /* - Fool MySQL to think that this function is a constant - This will ensure that MySQL only evalutes the function - when the rows are sent to the client and not before any ORDER BY - clauses - */ - initid->const_item=1; - return 0; -} - -void sequence_deinit(UDF_INIT *initid) -{ - if (initid->ptr) - free(initid->ptr); -} - -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) -{ - ulonglong val=0; - if (args->arg_count) - val= *((longlong*) args->args[0]); - return ++*((longlong*) initid->ptr) + val; -} - - -/**************************************************************************** -** Some functions that handles IP and hostname conversions -** The orignal function was from Zeev Suraski. -** -** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; -** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; -** -****************************************************************************/ - -#include -#include -#include -#include - -extern "C" { -my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); -void lookup_deinit(UDF_INIT *initid); -char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *null_value, char *error); -my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); -void reverse_lookup_deinit(UDF_INIT *initid); -char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *null_value, char *error); -} - - -/**************************************************************************** -** lookup IP for an hostname. -** -** This code assumes that gethostbyname_r exists and inet_ntoa() is thread -** safe (As it is in Solaris) -****************************************************************************/ - - -my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) - { - strmov(message,"Wrong arguments to lookup; Use the source"); - return 1; - } - initid->max_length=11; - initid->maybe_null=1; -#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) - (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); -#endif - return 0; -} - -void lookup_deinit(UDF_INIT *initid) -{ -#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) - (void) pthread_mutex_destroy(&LOCK_hostname); -#endif -} - -char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *null_value, char *error) -{ - uint length; - int tmp_errno; - char name_buff[256],hostname_buff[2048]; - struct hostent tmp_hostent,*hostent; - - if (!args->args[0] || !(length=args->lengths[0])) - { - *null_value=1; - return 0; - } - if (length >= sizeof(name_buff)) - length=sizeof(name_buff)-1; - memcpy(name_buff,args->args[0],length); - name_buff[length]=0; -#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) - if (!(hostent=gethostbyname_r(name_buff,&tmp_hostent,hostname_buff, - sizeof(hostname_buff), &tmp_errno))) - { - *null_value=1; - return 0; - } -#else - VOID(pthread_mutex_lock(&LOCK_hostname)); - if (!(hostent= gethostbyname((char*) name_buff))) - { - VOID(pthread_mutex_unlock(&LOCK_hostname)); - *null_value= 1; - return 0; - } - VOID(pthread_mutex_unlock(&LOCK_hostname)); -#endif - struct in_addr in; - memcpy_fixed((char*) &in,(char*) *hostent->h_addr_list, sizeof(in.s_addr)); - *res_length= (ulong) (strmov(result, inet_ntoa(in)) - result); - return result; -} - - -/**************************************************************************** -** return hostname for an IP number. -** The functions can take as arguments a string "xxx.xxx.xxx.xxx" or -** four numbers. -****************************************************************************/ - -my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count == 1) - args->arg_type[0]= STRING_RESULT; - else if (args->arg_count == 4) - args->arg_type[0]=args->arg_type[1]=args->arg_type[2]=args->arg_type[3]= - INT_RESULT; - else - { - strmov(message, - "Wrong number of arguments to reverse_lookup; Use the source"); - return 1; - } - initid->max_length=32; - initid->maybe_null=1; -#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) - (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); -#endif - return 0; -} - -void reverse_lookup_deinit(UDF_INIT *initid) -{ -#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) - (void) pthread_mutex_destroy(&LOCK_hostname); -#endif -} - -char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *null_value, char *error) -{ - char name_buff[256]; - struct hostent tmp_hostent; - uint length; - - if (args->arg_count == 4) - { - if (!args->args[0] || !args->args[1] ||!args->args[2] ||!args->args[3]) - { - *null_value=1; - return 0; - } - sprintf(result,"%d.%d.%d.%d", - (int) *((longlong*) args->args[0]), - (int) *((longlong*) args->args[1]), - (int) *((longlong*) args->args[2]), - (int) *((longlong*) args->args[3])); - } - else - { // string argument - if (!args->args[0]) // Return NULL for NULL values - { - *null_value=1; - return 0; - } - length=args->lengths[0]; - if (length >= (uint) *res_length-1) - length=(uint) *res_length; - memcpy(result,args->args[0],length); - result[length]=0; - } - - unsigned long taddr = inet_addr(result); - if (taddr == (unsigned long) -1L) - { - *null_value=1; - return 0; - } - struct hostent *hp; -#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) - int tmp_errno; - if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET, - &tmp_hostent, name_buff,sizeof(name_buff), - &tmp_errno))) - { - *null_value=1; - return 0; - } -#else - VOID(pthread_mutex_lock(&LOCK_hostname)); - if (!(hp= gethostbyaddr((char*) &taddr, sizeof(taddr), AF_INET))) - { - VOID(pthread_mutex_unlock(&LOCK_hostname)); - *null_value= 1; - return 0; - } - VOID(pthread_mutex_unlock(&LOCK_hostname)); -#endif - *res_length=(ulong) (strmov(result,hp->h_name) - result); - return result; -} - -/* -** Syntax for the new aggregate commands are: -** create aggregate function returns {string|real|integer} -** soname -** -** Syntax for avgcost: avgcost( t.quantity, t.price ) -** with t.quantity=integer, t.price=double -** (this example is provided by Andreas F. Bobak ) -*/ - - -struct avgcost_data -{ - ulonglong count; - longlong totalquantity; - double totalprice; -}; - - -/* -** Average Cost Aggregate Function. -*/ -my_bool -avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ) -{ - struct avgcost_data* data; - - if (args->arg_count != 2) - { - strcpy( - message, - "wrong number of arguments: AVGCOST() requires two arguments" - ); - return 1; - } - - if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT) ) - { - strcpy( - message, - "wrong argument type: AVGCOST() requires an INT and a REAL" - ); - return 1; - } - - /* - ** force arguments to double. - */ - /*args->arg_type[0] = REAL_RESULT; - args->arg_type[1] = REAL_RESULT;*/ - - initid->maybe_null = 0; // The result may be null - initid->decimals = 4; // We want 4 decimals in the result - initid->max_length = 20; // 6 digits + . + 10 decimals - - data = new struct avgcost_data; - data->totalquantity = 0; - data->totalprice = 0.0; - - initid->ptr = (char*)data; - - return 0; -} - -void -avgcost_deinit( UDF_INIT* initid ) -{ - delete initid->ptr; -} - - -/* This is only for MySQL 4.0 compability */ -void -avgcost_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) -{ - avgcost_clear(initid, is_null, message); - avgcost_add(initid, args, is_null, message); -} - -/* This is needed to get things to work in MySQL 4.1.1 and above */ - -void -avgcost_clear(UDF_INIT* initid, char* is_null, char* message) -{ - struct avgcost_data* data = (struct avgcost_data*)initid->ptr; - data->totalprice= 0.0; - data->totalquantity= 0; - data->count= 0; -} - - -void -avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) -{ - if (args->args[0] && args->args[1]) - { - struct avgcost_data* data = (struct avgcost_data*)initid->ptr; - longlong quantity = *((longlong*)args->args[0]); - longlong newquantity = data->totalquantity + quantity; - double price = *((double*)args->args[1]); - - data->count++; - - if ( ((data->totalquantity >= 0) && (quantity < 0)) - || ((data->totalquantity < 0) && (quantity > 0)) ) - { - /* - ** passing from + to - or from - to + - */ - if ( ((quantity < 0) && (newquantity < 0)) - || ((quantity > 0) && (newquantity > 0)) ) - { - data->totalprice = price * double(newquantity); - } - /* - ** sub q if totalq > 0 - ** add q if totalq < 0 - */ - else - { - price = data->totalprice / double(data->totalquantity); - data->totalprice = price * double(newquantity); - } - data->totalquantity = newquantity; - } - else - { - data->totalquantity += quantity; - data->totalprice += price * double(quantity); - } - - if (data->totalquantity == 0) - data->totalprice = 0.0; - } -} - - -double -avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error ) -{ - struct avgcost_data* data = (struct avgcost_data*)initid->ptr; - if (!data->count || !data->totalquantity) - { - *is_null = 1; - return 0.0; - } - - *is_null = 0; - return data->totalprice/double(data->totalquantity); -} - -extern "C" { -my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, - char *message); -char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *null_value, - char *error); -} - -my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, - char *message) -{ - if (args->arg_count != 1) - { - strmov(message,"myfunc_argument_name_init accepts only one argument"); - return 1; - } - initid->max_length= args->attribute_lengths[0]; - initid->maybe_null= 1; - initid->const_item= 1; - return 0; -} - -char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *null_value, - char *error) -{ - if (!args->attributes[0]) - { - null_value= 0; - return 0; - } - (*length)--; // space for ending \0 (for debugging purposes) - if (*length > args->attribute_lengths[0]) - *length= args->attribute_lengths[0]; - memcpy(result, args->attributes[0], *length); - result[*length]= 0; - return result; -} - -#endif /* HAVE_DLOPEN */ -- cgit v1.2.1