"Fossies" - the Fresh Open Source Software Archive

Member "mysql-8.0.36/sql/udf_example.cc" (12 Dec 2023, 32434 Bytes) of package /linux/misc/mysql-8.0.36.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "udf_example.cc" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 8.0.32_vs_8.0.33.

    1 /* Copyright (c) 2000, 2023, Oracle and/or its affiliates.
    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 dynamically 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 dynamically 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 <assert.h>
  122 #include <ctype.h>
  123 #include <stdio.h>
  124 #include <stdlib.h>
  125 #include <string.h>
  126 #include <algorithm>
  127 #include <mutex>
  128 #include <new>
  129 #include <regex>
  130 #include <string>
  131 #include <vector>
  132 
  133 #include "mysql.h"  // IWYU pragma: keep
  134 #include "mysql/udf_registration_types.h"
  135 
  136 #ifdef _WIN32
  137 /* inet_aton needs winsock library */
  138 #pragma comment(lib, "ws2_32")
  139 #endif
  140 
  141 /*
  142   Not all platforms have gethostbyaddr_r, so we use a global lock here instead.
  143   Production-quality code should use getaddrinfo where available.
  144 */
  145 static std::mutex *LOCK_hostname{nullptr};
  146 
  147 /* All function signatures must be right or mysqld will not find the symbol! */
  148 
  149 /*************************************************************************
  150 ** Example of init function
  151 ** Arguments:
  152 ** initid   Points to a structure that the init function should fill.
  153 **      This argument is given to all other functions.
  154 **  bool maybe_null 1 if function can return NULL
  155 **              Default value is 1 if any of the arguments
  156 **              is declared maybe_null.
  157 **  unsigned int decimals   Number of decimals.
  158 **              Default value is max decimals in any of the
  159 **              arguments.
  160 **  unsigned int max_length  Length of string result.
  161 **              The default value for integer functions is 21
  162 **              The default value for real functions is 13+
  163 **              default number of decimals.
  164 **              The default value for string functions is
  165 **              the longest string argument.
  166 **  char *ptr;      A pointer that the function can use.
  167 **
  168 ** args     Points to a structure which contains:
  169 **  unsigned int arg_count      Number of arguments
  170 **  enum Item_result *arg_type  Types for each argument.
  171 **                  Types are STRING_RESULT, REAL_RESULT
  172 **                  and INT_RESULT.
  173 **  char **args         Pointer to constant arguments.
  174 **                  Contains 0 for not constant argument.
  175 **  unsigned long *lengths;     max string length for each argument
  176 **  char *maybe_null        Information of which arguments
  177 **                  may be NULL
  178 **
  179 ** message  Error message that should be passed to the user on fail.
  180 **      The message buffer is MYSQL_ERRMSG_SIZE big, but one should
  181 **      try to keep the error message less than 80 bytes long!
  182 **
  183 ** This function should return 1 if something goes wrong. In this case
  184 ** message should contain something useful!
  185 **************************************************************************/
  186 
  187 #define MAXMETAPH 8
  188 
  189 extern "C" bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
  190   if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) {
  191     strcpy(message, "Wrong arguments to metaphon;  Use the source");
  192     return true;
  193   }
  194   initid->max_length = MAXMETAPH;
  195   return false;
  196 }
  197 
  198 /****************************************************************************
  199 ** Deinit function. This should free all resources allocated by
  200 ** this function.
  201 ** Arguments:
  202 ** initid   Return value from xxxx_init
  203 ****************************************************************************/
  204 
  205 extern "C" void metaphon_deinit(UDF_INIT *) {}
  206 
  207 /***************************************************************************
  208 ** UDF string function.
  209 ** Arguments:
  210 ** initid   Structure filled by xxx_init
  211 ** args     The same structure as to xxx_init. This structure
  212 **      contains values for all parameters.
  213 **      Note that the functions MUST check and convert all
  214 **      to the type it wants!  Null values are represented by
  215 **      a NULL pointer
  216 ** result   Possible buffer to save result. At least 255 byte long.
  217 ** length   Pointer to length of the above buffer.  In this the function
  218 **      should save the result length
  219 ** is_null  If the result is null, one should store 1 here.
  220 ** error    If something goes fatally wrong one should store 1 here.
  221 **
  222 ** This function should return a pointer to the result string.
  223 ** Normally this is 'result' but may also be an allocated string.
  224 ***************************************************************************/
  225 
  226 /* Character coding array */
  227 static char codes[26] = {
  228     1, 16, 4, 16, 9, 2, 4, 16, 9, 2, 0, 2, 2, 2,
  229     1, 4,  0, 2,  4, 4, 1, 0,  0, 0, 8, 0
  230     /* 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*/
  231 };
  232 
  233 /*--- Macros to access character coding array -------------*/
  234 
  235 #define ISVOWEL(x) (codes[(x) - 'A'] & 1) /* AEIOU */
  236 
  237 /* Following letters are not changed */
  238 #define NOCHANGE(x) (codes[(x) - 'A'] & 2) /* FJLMNR */
  239 
  240 /* These form diphthongs when preceding H */
  241 #define AFFECTH(x) (codes[(x) - 'A'] & 4) /* CGPST */
  242 
  243 /* These make C and G soft */
  244 #define MAKESOFT(x) (codes[(x) - 'A'] & 8) /* EIY */
  245 
  246 /* These prevent GH from becoming F */
  247 #define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */
  248 
  249 extern "C" char *metaphon(UDF_INIT *, UDF_ARGS *args, char *result,
  250                           unsigned long *length, unsigned char *is_null,
  251                           unsigned char *) {
  252   const char *word = args->args[0];
  253   const char *w_end;
  254   char *org_result;
  255   char *n, *n_start, *n_end; /* pointers to string */
  256   char *metaph_end;          /* pointers to end of metaph */
  257   char ntrans[32];           /* word with uppercase letters */
  258   int KSflag;                /* state flag for X to KS */
  259 
  260   if (!word) /* Null argument */
  261   {
  262     /* The length is expected to be zero when the argument is NULL. */
  263     assert(args->lengths[0] == 0);
  264     *is_null = 1;
  265     return nullptr;
  266   }
  267 
  268   w_end = word + args->lengths[0];
  269   org_result = result;
  270 
  271   /*--------------------------------------------------------
  272    *  Copy word to internal buffer, dropping non-alphabetic
  273    *  characters and converting to uppercase.
  274    *-------------------------------------------------------*/
  275 
  276   for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans) - 2;
  277        word != w_end && n < n_end; word++)
  278     if (isalpha(*word)) *n++ = toupper(*word);
  279 
  280   if (n == ntrans + 1) /* return empty string if 0 bytes */
  281   {
  282     *length = 0;
  283     return result;
  284   }
  285   n_end = n;       /* set n_end to end of string */
  286   ntrans[0] = 'Z'; /* ntrans[0] should be a neutral char */
  287   n[0] = n[1] = 0; /* pad with nulls */
  288   n = ntrans + 1;  /* assign pointer to start */
  289 
  290   /*------------------------------------------------------------
  291    *  check for all prefixes:
  292    *        PN KN GN AE WR WH and X at start.
  293    *----------------------------------------------------------*/
  294 
  295   switch (*n) {
  296     case 'P':
  297     case 'K':
  298     case 'G':
  299       if (n[1] == 'N') *n++ = 0;
  300       break;
  301     case 'A':
  302       if (n[1] == 'E') *n++ = 0;
  303       break;
  304     case 'W':
  305       if (n[1] == 'R')
  306         *n++ = 0;
  307       else if (*(n + 1) == 'H') {
  308         n[1] = *n;
  309         *n++ = 0;
  310       }
  311       break;
  312     case 'X':
  313       *n = 'S';
  314       break;
  315   }
  316 
  317   /*------------------------------------------------------------
  318    *  Now, loop step through string, stopping at end of string
  319    *  or when the computed metaph is MAXMETAPH characters long
  320    *----------------------------------------------------------*/
  321 
  322   KSflag = 0; /* state flag for KS translation */
  323 
  324   for (metaph_end = result + MAXMETAPH, n_start = n;
  325        n < n_end && result < metaph_end; n++) {
  326     if (KSflag) {
  327       KSflag = 0;
  328       *result++ = *n;
  329     } else {
  330       /* drop duplicates except for CC */
  331       if (*(n - 1) == *n && *n != 'C') continue;
  332 
  333       /* check for F J L M N R or first letter vowel */
  334       if (NOCHANGE(*n) || (n == n_start && ISVOWEL(*n)))
  335         *result++ = *n;
  336       else
  337         switch (*n) {
  338           case 'B': /* check for -MB */
  339             if (n < n_end || *(n - 1) != 'M') *result++ = *n;
  340             break;
  341 
  342           case 'C': /* C = X ("sh" sound) in CH and CIA */
  343             /*   = S in CE CI and CY          */
  344             /*   dropped in SCI SCE SCY       */
  345             /* else K                 */
  346             if (*(n - 1) != 'S' || !MAKESOFT(n[1])) {
  347               if (n[1] == 'I' && n[2] == 'A')
  348                 *result++ = 'X';
  349               else if (MAKESOFT(n[1]))
  350                 *result++ = 'S';
  351               else if (n[1] == 'H')
  352                 *result++ =
  353                     ((n == n_start && !ISVOWEL(n[2])) || *(n - 1) == 'S') ? 'K'
  354                                                                           : 'X';
  355               else
  356                 *result++ = 'K';
  357             }
  358             break;
  359 
  360           case 'D': /* J before DGE, DGI, DGY, else T */
  361             *result++ = (n[1] == 'G' && MAKESOFT(n[2])) ? 'J' : 'T';
  362             break;
  363 
  364           case 'G': /* complicated, see table in text */
  365             if ((n[1] != 'H' || ISVOWEL(n[2])) &&
  366                 (n[1] != 'N' ||
  367                  ((n + 1) < n_end && (n[2] != 'E' || *(n + 3) != 'D'))) &&
  368                 (*(n - 1) != 'D' || !MAKESOFT(n[1])))
  369               *result++ = (MAKESOFT(*(n + 1)) && n[2] != 'G') ? 'J' : 'K';
  370             else if (n[1] == 'H' && !NOGHTOF(*(n - 3)) && *(n - 4) != 'H')
  371               *result++ = 'F';
  372             break;
  373 
  374           case 'H': /* H if before a vowel and not after */
  375             /* C, G, P, S, T */
  376 
  377             if (!AFFECTH(*(n - 1)) && (!ISVOWEL(*(n - 1)) || ISVOWEL(n[1])))
  378               *result++ = 'H';
  379             break;
  380 
  381           case 'K': /* K = K, except dropped after C */
  382             if (*(n - 1) != 'C') *result++ = 'K';
  383             break;
  384 
  385           case 'P': /* PH = F, else P = P */
  386             *result++ = *(n + 1) == 'H' ? 'F' : 'P';
  387             break;
  388           case 'Q': /* Q = K (U after Q is already gone */
  389             *result++ = 'K';
  390             break;
  391 
  392           case 'S': /* SH, SIO, SIA = X ("sh" sound) */
  393             *result++ = (n[1] == 'H' ||
  394                          (*(n + 1) == 'I' && (n[2] == 'O' || n[2] == 'A')))
  395                             ? 'X'
  396                             : 'S';
  397             break;
  398 
  399           case 'T': /* TIO, TIA = X ("sh" sound) */
  400             /* TH = 0, ("th" sound ) */
  401             if (*(n + 1) == 'I' && (n[2] == 'O' || n[2] == 'A'))
  402               *result++ = 'X';
  403             else if (n[1] == 'H')
  404               *result++ = '0';
  405             else if (*(n + 1) != 'C' || n[2] != 'H')
  406               *result++ = 'T';
  407             break;
  408 
  409           case 'V': /* V = F */
  410             *result++ = 'F';
  411             break;
  412 
  413           case 'W': /* only exist if a vowel follows */
  414           case 'Y':
  415             if (ISVOWEL(n[1])) *result++ = *n;
  416             break;
  417 
  418           case 'X': /* X = KS, except at start */
  419             if (n == n_start)
  420               *result++ = 'S';
  421             else {
  422               *result++ = 'K'; /* insert K, then S */
  423               KSflag = 1;      /* this flag will cause S to be
  424                                   inserted on next pass thru loop */
  425             }
  426             break;
  427 
  428           case 'Z':
  429             *result++ = 'S';
  430             break;
  431         }
  432     }
  433   }
  434   *length = (unsigned long)(result - org_result);
  435   return org_result;
  436 }
  437 
  438 /***************************************************************************
  439 ** UDF double function.
  440 ** Arguments:
  441 ** initid   Structure filled by xxx_init
  442 ** args     The same structure as to xxx_init. This structure
  443 **      contains values for all parameters.
  444 **      Note that the functions MUST check and convert all
  445 **      to the type it wants!  Null values are represented by
  446 **      a NULL pointer
  447 ** is_null  If the result is null, one should store 1 here.
  448 ** error    If something goes fatally wrong one should store 1 here.
  449 **
  450 ** This function should return the result.
  451 ***************************************************************************/
  452 
  453 extern "C" bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args,
  454                                    char *message) {
  455   unsigned i;
  456 
  457   if (!args->arg_count) {
  458     strcpy(message, "myfunc_double must have at least one argument");
  459     return true;
  460   }
  461   /*
  462   ** As this function wants to have everything as strings, force all arguments
  463   ** to strings.
  464   */
  465   for (i = 0; i < args->arg_count; i++) args->arg_type[i] = STRING_RESULT;
  466   initid->maybe_null = true; /* The result may be null */
  467   initid->decimals = 2;      /* We want 2 decimals in the result */
  468   initid->max_length = 6;    /* 3 digits + . + 2 decimals */
  469   return false;
  470 }
  471 
  472 extern "C" double myfunc_double(UDF_INIT *, UDF_ARGS *args,
  473                                 unsigned char *is_null, unsigned char *) {
  474   unsigned long val = 0;
  475   unsigned long v = 0;
  476   unsigned i, j;
  477 
  478   for (i = 0; i < args->arg_count; i++) {
  479     if (args->args[i] == nullptr) continue;
  480     val += args->lengths[i];
  481     for (j = args->lengths[i]; j-- > 0;) v += args->args[i][j];
  482   }
  483   if (val) return (double)v / (double)val;
  484   *is_null = 1;
  485   return 0.0;
  486 }
  487 
  488 /***************************************************************************
  489 ** UDF long long function.
  490 ** Arguments:
  491 ** initid   Return value from xxxx_init
  492 ** args     The same structure as to xxx_init. This structure
  493 **      contains values for all parameters.
  494 **      Note that the functions MUST check and convert all
  495 **      to the type it wants!  Null values are represented by
  496 **      a NULL pointer
  497 ** is_null  If the result is null, one should store 1 here.
  498 ** error    If something goes fatally wrong one should store 1 here.
  499 **
  500 ** This function should return the result as a long long
  501 ***************************************************************************/
  502 
  503 /* This function returns the sum of all arguments */
  504 
  505 extern "C" long long myfunc_int(UDF_INIT *, UDF_ARGS *args, unsigned char *,
  506                                 unsigned char *) {
  507   long long val = 0;
  508   unsigned i;
  509 
  510   for (i = 0; i < args->arg_count; i++) {
  511     if (args->args[i] == nullptr) continue;
  512     switch (args->arg_type[i]) {
  513       case STRING_RESULT: /* Add string lengths */
  514         val += args->lengths[i];
  515         break;
  516       case INT_RESULT: /* Add numbers */
  517         val += *((long long *)args->args[i]);
  518         break;
  519       case REAL_RESULT: /* Add numbers as long long */
  520         val += (long long)*((double *)args->args[i]);
  521         break;
  522       default:
  523         break;
  524     }
  525   }
  526   return val;
  527 }
  528 
  529 /*
  530   At least one of _init/_deinit is needed unless the server is started
  531   with --allow_suspicious_udfs.
  532 */
  533 extern "C" bool myfunc_int_init(UDF_INIT *, UDF_ARGS *, char *) {
  534   return false;
  535 }
  536 
  537 /*
  538   Simple example of how to get a sequences starting from the first argument
  539   or 1 if no arguments have been given
  540 */
  541 
  542 extern "C" bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
  543   if (args->arg_count > 1) {
  544     strcpy(message, "This function takes none or 1 argument");
  545     return true;
  546   }
  547   if (args->arg_count)
  548     args->arg_type[0] = INT_RESULT; /* Force argument to int */
  549 
  550   if (!(initid->ptr = (char *)malloc(sizeof(long long)))) {
  551     strcpy(message, "Couldn't allocate memory");
  552     return true;
  553   }
  554   memset(initid->ptr, 0, sizeof(long long));
  555   /*
  556     sequence() is a non-deterministic function : it has different value
  557     even if called with the same arguments.
  558   */
  559   initid->const_item = false;
  560   return false;
  561 }
  562 
  563 extern "C" void sequence_deinit(UDF_INIT *initid) {
  564   if (initid->ptr) free(initid->ptr);
  565 }
  566 
  567 extern "C" long long sequence(UDF_INIT *initid, UDF_ARGS *args, unsigned char *,
  568                               unsigned char *) {
  569   unsigned long long val = 0;
  570   if (args->arg_count) val = *((long long *)args->args[0]);
  571   return ++*((long long *)initid->ptr) + val;
  572 }
  573 
  574 /****************************************************************************
  575 ** Some functions that handles IP and hostname conversions
  576 ** The original function was from Zeev Suraski.
  577 **
  578 ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
  579 ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
  580 **
  581 ****************************************************************************/
  582 
  583 #ifndef _WIN32
  584 #include <arpa/inet.h>
  585 #include <netdb.h>
  586 #include <netinet/in.h>
  587 #include <sys/socket.h>
  588 #endif
  589 
  590 /****************************************************************************
  591 ** lookup IP for an hostname.
  592 **
  593 ** This code assumes that inet_ntoa() is thread safe (As it is in Solaris)
  594 ****************************************************************************/
  595 
  596 extern "C" bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
  597   if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) {
  598     strcpy(message, "Wrong arguments to lookup;  Use the source");
  599     return true;
  600   }
  601   initid->max_length = 11;
  602   initid->maybe_null = true;
  603   LOCK_hostname = new std::mutex;
  604   return false;
  605 }
  606 
  607 extern "C" void lookup_deinit(UDF_INIT *) {
  608   delete LOCK_hostname;
  609   LOCK_hostname = nullptr;
  610 }
  611 
  612 extern "C" char *lookup(UDF_INIT *, UDF_ARGS *args, char *result,
  613                         unsigned long *res_length, unsigned char *null_value,
  614                         unsigned char *) {
  615   unsigned length;
  616   char name_buff[256];
  617   struct hostent *hostent;
  618   struct in_addr in;
  619 
  620   if (!args->args[0] || !(length = args->lengths[0])) {
  621     *null_value = 1;
  622     return nullptr;
  623   }
  624   if (length >= sizeof(name_buff)) length = sizeof(name_buff) - 1;
  625   memcpy(name_buff, args->args[0], length);
  626   name_buff[length] = 0;
  627   {
  628     std::lock_guard<std::mutex> lock(*LOCK_hostname);
  629     if (!(hostent = gethostbyname((char *)name_buff))) {
  630       *null_value = 1;
  631       return nullptr;
  632     }
  633   }
  634   memcpy(&in, *hostent->h_addr_list, sizeof(in.s_addr));
  635   strcpy(result, inet_ntoa(in));
  636   *res_length = strlen(result);
  637   return result;
  638 }
  639 
  640 /****************************************************************************
  641 ** return hostname for an IP number.
  642 ** The functions can take as arguments a string "xxx.xxx.xxx.xxx" or
  643 ** four numbers.
  644 ****************************************************************************/
  645 
  646 extern "C" bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args,
  647                                     char *message) {
  648   if (args->arg_count == 1)
  649     args->arg_type[0] = STRING_RESULT;
  650   else if (args->arg_count == 4)
  651     args->arg_type[0] = args->arg_type[1] = args->arg_type[2] =
  652         args->arg_type[3] = INT_RESULT;
  653   else {
  654     strcpy(message,
  655            "Wrong number of arguments to reverse_lookup;  Use the source");
  656     return true;
  657   }
  658   initid->max_length = 32;
  659   initid->maybe_null = true;
  660   LOCK_hostname = new std::mutex;
  661   return false;
  662 }
  663 
  664 extern "C" void reverse_lookup_deinit(UDF_INIT *) {
  665   delete LOCK_hostname;
  666   LOCK_hostname = nullptr;
  667 }
  668 
  669 extern "C" char *reverse_lookup(UDF_INIT *, UDF_ARGS *args, char *result,
  670                                 unsigned long *res_length,
  671                                 unsigned char *null_value, unsigned char *) {
  672   struct hostent *hp;
  673   unsigned long taddr;
  674   unsigned length;
  675 
  676   if (args->arg_count == 4) {
  677     if (!args->args[0] || !args->args[1] || !args->args[2] || !args->args[3]) {
  678       *null_value = 1;
  679       return nullptr;
  680     }
  681     sprintf(result, "%d.%d.%d.%d", (int)*((long long *)args->args[0]),
  682             (int)*((long long *)args->args[1]),
  683             (int)*((long long *)args->args[2]),
  684             (int)*((long long *)args->args[3]));
  685   } else {              /* string argument */
  686     if (!args->args[0]) /* Return NULL for NULL values */
  687     {
  688       *null_value = 1;
  689       return nullptr;
  690     }
  691     length = args->lengths[0];
  692     if (length >= (unsigned)*res_length - 1) length = (unsigned)*res_length;
  693     memcpy(result, args->args[0], length);
  694     result[length] = 0;
  695   }
  696 
  697   taddr = inet_addr(result);
  698   if (taddr == (unsigned long)-1L) {
  699     *null_value = 1;
  700     return nullptr;
  701   }
  702   {
  703     std::lock_guard<std::mutex> lock(*LOCK_hostname);
  704     if (!(hp = gethostbyaddr((char *)&taddr, sizeof(taddr), AF_INET))) {
  705       *null_value = 1;
  706       return nullptr;
  707     }
  708   }
  709   strcpy(result, hp->h_name);
  710   *res_length = strlen(result);
  711   return result;
  712 }
  713 
  714 /*
  715 ** Syntax for the new aggregate commands are:
  716 ** create aggregate function <function_name> returns {string|real|integer}
  717 **        soname <name_of_shared_library>
  718 **
  719 ** Syntax for avgcost: avgcost( t.quantity, t.price )
  720 **  with t.quantity=integer, t.price=double
  721 ** (this example is provided by Andreas F. Bobak <bobak@relog.ch>)
  722 */
  723 
  724 struct avgcost_data {
  725   unsigned long long count;
  726   long long totalquantity;
  727   double totalprice;
  728 };
  729 
  730 /*
  731 ** Average Cost Aggregate Function.
  732 */
  733 extern "C" bool avgcost_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
  734   struct avgcost_data *data;
  735 
  736   if (args->arg_count != 2) {
  737     strcpy(message,
  738            "wrong number of arguments: AVGCOST() requires two arguments");
  739     return true;
  740   }
  741 
  742   if ((args->arg_type[0] != INT_RESULT) || (args->arg_type[1] != REAL_RESULT)) {
  743     strcpy(message,
  744            "wrong argument type: AVGCOST() requires an INT and a REAL");
  745     return true;
  746   }
  747 
  748   /*
  749   **    force arguments to double.
  750   */
  751   /*args->arg_type[0]   = REAL_RESULT;
  752     args->arg_type[1]   = REAL_RESULT;*/
  753 
  754   initid->maybe_null = false; /* The result may be null */
  755   initid->decimals = 4;       /* We want 4 decimals in the result */
  756   initid->max_length = 20;    /* 6 digits + . + 10 decimals */
  757 
  758   if (!(data = new (std::nothrow) avgcost_data)) {
  759     strcpy(message, "Couldn't allocate memory");
  760     return true;
  761   }
  762   data->totalquantity = 0;
  763   data->totalprice = 0.0;
  764 
  765   initid->ptr = (char *)data;
  766 
  767   return false;
  768 }
  769 
  770 extern "C" void avgcost_deinit(UDF_INIT *initid) {
  771   void *void_ptr = initid->ptr;
  772   avgcost_data *data = static_cast<avgcost_data *>(void_ptr);
  773   delete data;
  774 }
  775 
  776 /* This is needed to get things to work in MySQL 4.1.1 and above */
  777 
  778 extern "C" void avgcost_clear(UDF_INIT *initid, unsigned char *,
  779                               unsigned char *) {
  780   struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
  781   data->totalprice = 0.0;
  782   data->totalquantity = 0;
  783   data->count = 0;
  784 }
  785 
  786 extern "C" void avgcost_add(UDF_INIT *initid, UDF_ARGS *args, unsigned char *,
  787                             unsigned char *) {
  788   if (args->args[0] && args->args[1]) {
  789     struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
  790     long long quantity = *((long long *)args->args[0]);
  791     long long newquantity = data->totalquantity + quantity;
  792     double price = *((double *)args->args[1]);
  793 
  794     data->count++;
  795 
  796     if (((data->totalquantity >= 0) && (quantity < 0)) ||
  797         ((data->totalquantity < 0) && (quantity > 0))) {
  798       /*
  799       **    passing from + to - or from - to +
  800       */
  801       if (((quantity < 0) && (newquantity < 0)) ||
  802           ((quantity > 0) && (newquantity > 0))) {
  803         data->totalprice = price * (double)newquantity;
  804       }
  805       /*
  806       **    sub q if totalq > 0
  807       **    add q if totalq < 0
  808       */
  809       else {
  810         price = data->totalprice / (double)data->totalquantity;
  811         data->totalprice = price * (double)newquantity;
  812       }
  813       data->totalquantity = newquantity;
  814     } else {
  815       data->totalquantity += quantity;
  816       data->totalprice += price * (double)quantity;
  817     }
  818 
  819     if (data->totalquantity == 0) data->totalprice = 0.0;
  820   }
  821 }
  822 
  823 extern "C" double avgcost(UDF_INIT *initid, UDF_ARGS *, unsigned char *is_null,
  824                           unsigned char *) {
  825   struct avgcost_data *data = (struct avgcost_data *)initid->ptr;
  826   if (!data->count || !data->totalquantity) {
  827     *is_null = 1;
  828     return 0.0;
  829   }
  830 
  831   *is_null = 0;
  832   return data->totalprice / (double)data->totalquantity;
  833 }
  834 
  835 extern "C" bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
  836                                           char *message) {
  837   if (args->arg_count != 1) {
  838     strcpy(message, "myfunc_argument_name_init accepts only one argument");
  839     return true;
  840   }
  841   initid->max_length = args->attribute_lengths[0];
  842   initid->maybe_null = true;
  843   initid->const_item = true;
  844   return false;
  845 }
  846 
  847 extern "C" char *myfunc_argument_name(UDF_INIT *, UDF_ARGS *args, char *result,
  848                                       unsigned long *length,
  849                                       unsigned char *null_value,
  850                                       unsigned char *) {
  851   if (!args->attributes[0]) {
  852     *null_value = 1;
  853     return nullptr;
  854   }
  855   (*length)--; /* space for ending \0 (for debugging purposes) */
  856   if (*length > args->attribute_lengths[0])
  857     *length = args->attribute_lengths[0];
  858   memcpy(result, args->attributes[0], *length);
  859   result[*length] = 0;
  860   return result;
  861 }
  862 
  863 extern "C" bool is_const_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
  864   if (args->arg_count != 1) {
  865     strcpy(message, "IS_CONST accepts only one argument");
  866     return true;
  867   }
  868   initid->ptr = (char *)((args->args[0] != nullptr) ? 1ULL : 0);
  869   return false;
  870 }
  871 
  872 extern "C" char *is_const(UDF_INIT *initid, UDF_ARGS *, char *result,
  873                           unsigned long *length, unsigned char *is_null,
  874                           unsigned char *) {
  875   if (initid->ptr != nullptr) {
  876     sprintf(result, "const");
  877   } else {
  878     sprintf(result, "not const");
  879   }
  880   *is_null = 0;
  881   *length = (unsigned)strlen(result);
  882   return result;
  883 }
  884 
  885 extern "C" bool check_const_len_init(UDF_INIT *initid, UDF_ARGS *args,
  886                                      char *message) {
  887   if (args->arg_count != 1) {
  888     strcpy(message, "CHECK_CONST_LEN accepts only one argument");
  889     return true;
  890   }
  891   if (args->args[0] == nullptr) {
  892     initid->ptr = const_cast<char *>("Not constant");
  893   } else if (strlen(args->args[0]) == args->lengths[0]) {
  894     initid->ptr = const_cast<char *>("Correct length");
  895   } else {
  896     initid->ptr = const_cast<char *>("Wrong length");
  897   }
  898   initid->max_length = 100;
  899   return false;
  900 }
  901 
  902 extern "C" char *check_const_len(UDF_INIT *initid, UDF_ARGS *, char *result,
  903                                  unsigned long *length, unsigned char *is_null,
  904                                  unsigned char *) {
  905   strcpy(result, initid->ptr);
  906   *length = strlen(result);
  907   *is_null = 0;
  908   return result;
  909 }
  910 
  911 struct My_median_data {
  912   std::vector<long long> vec;
  913 };
  914 
  915 extern "C" bool my_median_init(UDF_INIT *initid, UDF_ARGS *, char *message) {
  916   My_median_data *data = new (std::nothrow) My_median_data;
  917   if (!data) {
  918     strcpy(message, "Could not allocate memory");
  919     return true;
  920   }
  921   initid->ptr = static_cast<char *>(static_cast<void *>(data));
  922   return false;
  923 }
  924 
  925 extern "C" void my_median_deinit(UDF_INIT *initid) {
  926   My_median_data *data =
  927       static_cast<My_median_data *>(static_cast<void *>(initid->ptr));
  928   delete data;
  929 }
  930 
  931 extern "C" void my_median_add(UDF_INIT *initid, UDF_ARGS *args, unsigned char *,
  932                               unsigned char *) {
  933   My_median_data *data =
  934       static_cast<My_median_data *>(static_cast<void *>(initid->ptr));
  935   if (args->args[0]) {
  936     void *arg0 = args->args[0];
  937     long long number = *(static_cast<long long *>(arg0));
  938     data->vec.push_back(number);
  939   }
  940 }
  941 
  942 extern "C" void my_median_clear(UDF_INIT *initid, unsigned char *,
  943                                 unsigned char *) {
  944   My_median_data *data =
  945       static_cast<My_median_data *>(static_cast<void *>(initid->ptr));
  946   data->vec.clear();
  947 }
  948 
  949 extern "C" long long my_median(UDF_INIT *initid, UDF_ARGS *,
  950                                unsigned char *is_null, unsigned char *) {
  951   My_median_data *data =
  952       static_cast<My_median_data *>(static_cast<void *>(initid->ptr));
  953   if (data->vec.size() == 0) {
  954     *is_null = 1;
  955     return 0;
  956   }
  957   const size_t ix = data->vec.size() / 2;
  958   std::nth_element(data->vec.begin(), data->vec.begin() + ix, data->vec.end());
  959   return data->vec[ix];
  960 }