1 /* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /* This implements 'user defined functions' */
24 
25 #include "sql/sql_udf.h"
26 
27 #include "my_config.h"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <iterator>
32 #include <memory>
33 #include <new>
34 #include <string>
35 #include <unordered_map>
36 #include <utility>
37 
38 #include "m_ctype.h"
39 #include "m_string.h"  // my_stpcpy
40 #include "map_helpers.h"
41 #include "my_alloc.h"
42 #include "my_base.h"
43 #include "my_dbug.h"
44 #include "my_inttypes.h"
45 #include "my_io.h"
46 #include "my_loglevel.h"
47 #include "my_macros.h"
48 #include "my_psi_config.h"
49 #include "my_sharedlib.h"
50 #include "my_sys.h"
51 #include "my_thread_local.h"
52 #include "mysql/components/service_implementation.h"
53 #include "mysql/components/services/log_builtins.h"
54 #include "mysql/components/services/log_shared.h"
55 #include "mysql/components/services/mysql_rwlock_bits.h"
56 #include "mysql/components/services/psi_memory_bits.h"
57 #include "mysql/components/services/psi_rwlock_bits.h"
58 #include "mysql/psi/mysql_memory.h"
59 #include "mysql/psi/mysql_rwlock.h"
60 #include "mysql/psi/psi_base.h"
61 #include "mysql_com.h"
62 #include "mysqld_error.h"  // ER_*
63 #include "sql/field.h"
64 #include "sql/handler.h"
65 #include "sql/mdl.h"
66 #include "sql/mysqld.h"   // opt_allow_suspicious_udfs
67 #include "sql/records.h"  // unique_ptr_destroy_only<RowIterator>
68 #include "sql/row_iterator.h"
69 #include "sql/sql_base.h"   // close_mysql_tables
70 #include "sql/sql_class.h"  // THD
71 #include "sql/sql_const.h"
72 #include "sql/sql_parse.h"               // check_string_char_length
73 #include "sql/sql_plugin.h"              // check_valid_path
74 #include "sql/sql_system_table_check.h"  // System_table_intact
75 #include "sql/sql_table.h"               // write_bin_log
76 #include "sql/table.h"                   // TABLE_LIST
77 #include "sql/thd_raii.h"
78 #include "sql/thr_malloc.h"
79 #include "sql/transaction.h"  // trans_*
80 #include "thr_lock.h"
81 #include "udf_registration_imp.h"
82 
83 #ifdef HAVE_DLFCN_H
84 #include <dlfcn.h>
85 #endif
86 
87 /**
88   @page page_ext_udf User Defined Functions
89 
90   @todo Document me
91 
92   @sa add_udf, udf_hash_delete.
93 */
94 
95 /**
96   A local flag indicating whether SQL based UDF operations are allowed.
97   Now the UDF structures are always allocated/deallocated due to
98   the component service.
99 
100   So this variable does not cover initialization/deinitialization of these.
101   \ref mem and \ref THR_LOCK_udf are always initialized, even in
102   --skip-grant-tables mode.
103 */
104 static bool initialized = false;
105 static MEM_ROOT mem;
106 static collation_unordered_map<std::string, udf_func *> *udf_hash;
107 static mysql_rwlock_t THR_LOCK_udf;
108 
109 static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
110                          Item_udftype typ);
111 static void udf_hash_delete(udf_func *udf);
112 static void *find_udf_dl(const char *dl);
113 
114 // mysql.func table definition.
115 static const int MYSQL_UDF_TABLE_FIELD_COUNT = 4;
116 static const TABLE_FIELD_TYPE
117     mysql_udf_table_fields[MYSQL_UDF_TABLE_FIELD_COUNT] = {
118         {{STRING_WITH_LEN("name")},
119          {STRING_WITH_LEN("char(64)")},
120          {nullptr, 0}},
121         {{STRING_WITH_LEN("ret")}, {STRING_WITH_LEN("tinyint")}, {nullptr, 0}},
122         {{STRING_WITH_LEN("dl")}, {STRING_WITH_LEN("char(128)")}, {nullptr, 0}},
123         {{STRING_WITH_LEN("type")},
124          {STRING_WITH_LEN("enum('function','aggregate')")},
125          {STRING_WITH_LEN("utf8")}}};
126 static const TABLE_FIELD_DEF mysql_udf_table_def = {MYSQL_UDF_TABLE_FIELD_COUNT,
127                                                     mysql_udf_table_fields};
128 
init_syms(udf_func * tmp,char * nm)129 static char *init_syms(udf_func *tmp, char *nm) {
130   char *end;
131 
132   if (!((tmp->func = (Udf_func_any)dlsym(tmp->dlhandle, tmp->name.str))))
133     return tmp->name.str;
134 
135   end = my_stpcpy(nm, tmp->name.str);
136 
137   if (tmp->type == UDFTYPE_AGGREGATE) {
138     (void)my_stpcpy(end, "_clear");
139     if (!((tmp->func_clear = (Udf_func_clear)dlsym(tmp->dlhandle, nm))))
140       return nm;
141     (void)my_stpcpy(end, "_add");
142     if (!((tmp->func_add = (Udf_func_add)dlsym(tmp->dlhandle, nm)))) return nm;
143   }
144 
145   (void)my_stpcpy(end, "_deinit");
146   tmp->func_deinit = (Udf_func_deinit)dlsym(tmp->dlhandle, nm);
147 
148   (void)my_stpcpy(end, "_init");
149   tmp->func_init = (Udf_func_init)dlsym(tmp->dlhandle, nm);
150 
151   /*
152     to prevent loading "udf" from, e.g. libc.so
153     let's ensure that at least one auxiliary symbol is defined
154   */
155   if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE) {
156     if (!opt_allow_suspicious_udfs) return nm;
157     LogErr(WARNING_LEVEL, ER_FAILED_TO_FIND_DL_ENTRY, nm);
158   }
159   return nullptr;
160 }
161 
162 static PSI_memory_key key_memory_udf_mem;
163 
164 #ifdef HAVE_PSI_INTERFACE
165 static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
166 
167 static PSI_rwlock_info all_udf_rwlocks[] = {{&key_rwlock_THR_LOCK_udf,
168                                              "THR_LOCK_udf", PSI_FLAG_SINGLETON,
169                                              0, PSI_DOCUMENT_ME}};
170 
171 static PSI_memory_info all_udf_memory[] = {{&key_memory_udf_mem, "udf_mem",
172                                             PSI_FLAG_ONLY_GLOBAL_STAT, 0,
173                                             PSI_DOCUMENT_ME}};
174 
init_udf_psi_keys(void)175 static void init_udf_psi_keys(void) {
176   const char *category = "sql";
177   int count;
178 
179   count = static_cast<int>(array_elements(all_udf_rwlocks));
180   mysql_rwlock_register(category, all_udf_rwlocks, count);
181 
182   count = static_cast<int>(array_elements(all_udf_memory));
183   mysql_memory_register(category, all_udf_memory, count);
184 }
185 #endif
186 
187 /**
188   Initialize the UDF global structures.
189   This is done as a separate step so that the UDF registration
190   service can work when initalizing plugins, which happens
191   before reading the UDF table.
192 */
udf_init_globals()193 void udf_init_globals() {
194   DBUG_TRACE;
195   if (initialized) return;
196 
197 #ifdef HAVE_PSI_INTERFACE
198   init_udf_psi_keys();
199 #endif
200 
201   mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
202   init_sql_alloc(key_memory_udf_mem, &mem, UDF_ALLOC_BLOCK_SIZE, 0);
203 
204   udf_hash = new collation_unordered_map<std::string, udf_func *>(
205       system_charset_info, key_memory_udf_mem);
206 }
207 
208 /*
209   Read all predeclared functions from mysql.func and accept all that
210   can be used.
211   The global structures must be initialized first.
212 */
udf_read_functions_table()213 void udf_read_functions_table() {
214   udf_func *tmp;
215   TABLE *table;
216   unique_ptr_destroy_only<RowIterator> iterator;
217   int error;
218   DBUG_TRACE;
219   char db[] = "mysql"; /* A subject to casednstr, can't be constant */
220 
221   if (initialized) {
222     DBUG_ASSERT("wrong init order: reading UDFs from the table twice");
223     return;
224   }
225 
226   initialized = true;
227 
228   THD *new_thd = new (std::nothrow) THD;
229   if (new_thd == nullptr) {
230     LogErr(ERROR_LEVEL, ER_UDF_CANT_ALLOC_FOR_STRUCTURES);
231     free_root(&mem, MYF(0));
232     delete new_thd;
233     return;
234   }
235   new_thd->thread_stack = (char *)&new_thd;
236   new_thd->store_globals();
237   {
238     LEX_CSTRING db_lex_cstr = {STRING_WITH_LEN(db)};
239     new_thd->set_db(db_lex_cstr);
240   }
241 
242   TABLE_LIST tables(db, "func", TL_READ, MDL_SHARED_READ_ONLY);
243 
244   if (open_trans_system_tables_for_read(new_thd, &tables)) {
245     DBUG_PRINT("error", ("Can't open udf table"));
246     LogErr(ERROR_LEVEL, ER_UDF_CANT_OPEN_FUNCTION_TABLE);
247     goto end;
248   }
249 
250   table = tables.table;
251   iterator = init_table_iterator(new_thd, table, nullptr, false,
252                                  /*ignore_not_found_rows=*/false);
253   if (iterator == nullptr) goto end;
254   while (!(error = iterator->Read())) {
255     DBUG_PRINT("info", ("init udf record"));
256     LEX_STRING name;
257     name.str = get_field(&mem, table->field[0]);
258     name.length = strlen(name.str);
259     char *dl_name = get_field(&mem, table->field[2]);
260     bool new_dl = false;
261     Item_udftype udftype = UDFTYPE_FUNCTION;
262     if (table->s->fields >= 4)  // New func table
263       udftype = (Item_udftype)table->field[3]->val_int();
264 
265     /*
266       Ensure that the .dll doesn't have a path
267       This is done to ensure that only approved dll from the system
268       directories are used (to make this even remotely secure).
269 
270       On windows we must check both FN_LIBCHAR and '/'.
271     */
272 
273     LEX_CSTRING name_cstr = {name.str, name.length};
274     if (check_valid_path(dl_name, strlen(dl_name)) ||
275         check_string_char_length(name_cstr, "", NAME_CHAR_LEN,
276                                  system_charset_info, true)) {
277       LogErr(ERROR_LEVEL, ER_UDF_INVALID_ROW_IN_FUNCTION_TABLE, name.str);
278       continue;
279     }
280 
281     if (!(tmp = add_udf(&name, (Item_result)table->field[1]->val_int(), dl_name,
282                         udftype))) {
283       LogErr(ERROR_LEVEL, ER_UDF_CANT_ALLOC_FOR_FUNCTION, name.str);
284       continue;
285     }
286 
287     void *dl = find_udf_dl(tmp->dl);
288     if (dl == nullptr) {
289       char dlpath[FN_REFLEN];
290       strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl, NullS);
291       (void)unpack_filename(dlpath, dlpath);
292       if (!(dl = dlopen(dlpath, RTLD_NOW))) {
293         const char *errmsg;
294         int error_number = dlopen_errno;
295         DLERROR_GENERATE(errmsg, error_number);
296 
297         // Print warning to log
298         LogErr(ERROR_LEVEL, ER_FAILED_TO_OPEN_SHARED_LIBRARY, tmp->dl,
299                error_number, errmsg);
300         // Keep the udf in the hash so that we can remove it later
301         continue;
302       }
303       new_dl = true;
304     }
305     tmp->dlhandle = dl;
306     {
307       char buf[NAME_LEN + 16], *missing;
308       if ((missing = init_syms(tmp, buf))) {
309         LogErr(ERROR_LEVEL, ER_FAILED_TO_FIND_DL_ENTRY, missing);
310         udf_hash_delete(tmp);
311         if (new_dl) dlclose(dl);
312       }
313     }
314   }
315   if (error > 0) LogErr(ERROR_LEVEL, ER_UNKNOWN_ERROR_NUMBER, my_errno());
316   iterator.reset();
317   table->m_needs_reopen = true;  // Force close to free memory
318 
319 end:
320   close_trans_system_tables(new_thd);
321   delete new_thd;
322 }
323 
324 /**
325    Deintialize the UDF subsystem.
326 
327    This function closes the shared libaries.
328 */
udf_unload_udfs()329 void udf_unload_udfs() {
330   DBUG_TRACE;
331   if (udf_hash != nullptr) {
332     for (auto it1 = udf_hash->begin(); it1 != udf_hash->end(); ++it1) {
333       udf_func *udf = it1->second;
334       if (udf->dlhandle)  // Not closed before
335       {
336         /* Mark all versions using the same handler as closed */
337         for (auto it2 = std::next(it1); it2 != udf_hash->end(); ++it2) {
338           udf_func *tmp = it2->second;
339           if (udf->dlhandle == tmp->dlhandle)
340             tmp->dlhandle = nullptr;  // Already closed
341         }
342         dlclose(udf->dlhandle);
343       }
344     }
345   }
346 }
347 
348 /**
349    Deintialize the UDF subsystem.
350 
351    This function does the following:
352    1. Free the UDF hash.
353    2. Free the memroot allocated.
354    3. Destroy the RW mutex object.
355 */
udf_deinit_globals()356 void udf_deinit_globals() {
357   DBUG_TRACE;
358   if (udf_hash != nullptr) {
359     delete udf_hash;
360     udf_hash = nullptr;
361   }
362   free_root(&mem, MYF(0));
363   initialized = false;
364 
365   mysql_rwlock_destroy(&THR_LOCK_udf);
366 }
367 
368 /**
369    Delete the UDF function from the UDF hash.
370 
371    @param udf  Pointer to the UDF function.
372 
373    @note The function remove the udf function from the udf
374          hash if it is not in use. If the function is in use,
375          the function name is renamed so that it is not used.
376          The function shall be removed when no threads use it.
377 */
udf_hash_delete(udf_func * udf)378 static void udf_hash_delete(udf_func *udf) {
379   DBUG_TRACE;
380 
381   mysql_rwlock_wrlock(&THR_LOCK_udf);
382 
383   const auto it = udf_hash->find(to_string(udf->name));
384   if (it == udf_hash->end()) {
385     DBUG_ASSERT(false);
386     return;
387   }
388 
389   if (!--udf->usage_count) {
390     udf_hash->erase(it);
391     using_udf_functions = !udf_hash->empty();
392   } else {
393     /*
394       The functions is in use ; Rename the functions instead of removing it.
395       The functions will be automaticly removed when the least threads
396       doesn't use it anymore
397     */
398     udf_hash->erase(it);
399     char new_name[32];
400     snprintf(new_name, sizeof(new_name), "*<%p>", udf);
401     udf_hash->emplace(new_name, udf);
402   }
403   mysql_rwlock_unlock(&THR_LOCK_udf);
404 }
405 
free_udf(udf_func * udf)406 void free_udf(udf_func *udf) {
407   DBUG_TRACE;
408 
409   if (!initialized) return;
410 
411   mysql_rwlock_wrlock(&THR_LOCK_udf);
412   if (!--udf->usage_count) {
413     /*
414       We come here when someone has deleted the udf function
415       while another thread still was using the udf
416     */
417     const auto it = udf_hash->find(to_string(udf->name));
418     if (it == udf_hash->end()) {
419       DBUG_ASSERT(false);
420       return;
421     }
422     udf_hash->erase(it);
423     using_udf_functions = !udf_hash->empty();
424     if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle);
425   }
426   mysql_rwlock_unlock(&THR_LOCK_udf);
427 }
428 
429 /* This is only called if using_udf_functions != 0 */
430 
find_udf(const char * name,size_t length,bool mark_used)431 udf_func *find_udf(const char *name, size_t length, bool mark_used) {
432   udf_func *udf = nullptr;
433   DBUG_TRACE;
434 
435   if (!initialized) return nullptr;
436 
437   /* TODO: This should be changed to reader locks someday! */
438   if (mark_used)
439     mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
440   else
441     mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */
442 
443   std::string key = length ? std::string(name, length) : std::string(name);
444   const auto it = udf_hash->find(key);
445 
446   if (it != udf_hash->end()) {
447     udf = it->second;
448     if (mark_used) udf->usage_count++;
449   }
450   mysql_rwlock_unlock(&THR_LOCK_udf);
451   return udf;
452 }
453 
find_udf_dl(const char * dl)454 static void *find_udf_dl(const char *dl) {
455   DBUG_TRACE;
456 
457   if (!dl) return nullptr;
458   /*
459     Because only the function name is hashed, we have to search trough
460     all rows to find the dl.
461   */
462   for (const auto &key_and_value : *udf_hash) {
463     udf_func *udf = key_and_value.second;
464     if (udf->dl && !strcmp(dl, udf->dl) && udf->dlhandle != nullptr)
465       return udf->dlhandle;
466   }
467   return nullptr;
468 }
469 
470 /* Assume that name && dl is already allocated */
471 
add_udf(LEX_STRING * name,Item_result ret,char * dl,Item_udftype type)472 static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
473                          Item_udftype type) {
474   if (!name || !dl || !(uint)type || (uint)type > (uint)UDFTYPE_AGGREGATE)
475     return nullptr;
476 
477   udf_func *tmp = (udf_func *)mem.Alloc(sizeof(udf_func));
478   if (!tmp) return nullptr;
479   memset(tmp, 0, sizeof(*tmp));
480   tmp->name = *name;  // dup !!
481   tmp->dl = dl;
482   tmp->returns = ret;
483   tmp->type = type;
484   tmp->usage_count = 1;
485 
486   mysql_rwlock_wrlock(&THR_LOCK_udf);
487 
488   udf_hash->emplace(to_string(tmp->name), tmp);
489   using_udf_functions = true;
490 
491   mysql_rwlock_unlock(&THR_LOCK_udf);
492   return tmp;
493 }
494 
495 /**
496    Commit or rollback a transaction. Also close tables
497    which it has opened and release metadata locks.
498    Add/Remove from the in-memory hash depending on transaction
499    commit or rollback and the bool flag passed to this function.
500 
501    @param thd                 THD context.
502    @param rollback            Rollback transaction if true.
503    @param udf                 Pointer to UDF function.
504    @param insert_udf          Insert UDF in hash if true.
505 
506    @retval False - Success.
507    @retval True  - Error.
508 */
509 
udf_end_transaction(THD * thd,bool rollback,udf_func * udf,bool insert_udf)510 static bool udf_end_transaction(THD *thd, bool rollback, udf_func *udf,
511                                 bool insert_udf) {
512   bool result;
513   bool rollback_transaction = thd->transaction_rollback_request || rollback;
514   udf_func *u_f = nullptr;
515 
516   DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
517 
518   if (!rollback_transaction && insert_udf) {
519     udf->name.str = strdup_root(&mem, udf->name.str);
520     udf->dl = strdup_root(&mem, udf->dl);
521     // create entry in mysql.func table
522     u_f = add_udf(&udf->name, udf->returns, udf->dl, udf->type);
523     if (u_f != nullptr) {
524       u_f->dlhandle = udf->dlhandle;
525       u_f->func = udf->func;
526       u_f->func_init = udf->func_init;
527       u_f->func_deinit = udf->func_deinit;
528       u_f->func_clear = udf->func_clear;
529       u_f->func_add = udf->func_add;
530     }
531   }
532 
533   rollback_transaction = rollback_transaction || (insert_udf && u_f == nullptr);
534 
535   /*
536     CREATE/DROP UDF operations must acquire IX Backup Lock in order
537     to be mutually exclusive with LOCK INSTANCE FOR BACKUP.
538   */
539   DBUG_ASSERT(thd->mdl_context.owns_equal_or_stronger_lock(
540       MDL_key::BACKUP_LOCK, "", "", MDL_INTENTION_EXCLUSIVE));
541 
542   /*
543     Rollback the transaction if there is an error or there is a request by the
544     SE (which is unlikely).
545   */
546   if (rollback_transaction) {
547     result = trans_rollback_stmt(thd);
548     result = result || trans_rollback_implicit(thd);
549   } else {
550     result = trans_commit_stmt(thd);
551     result = result || trans_commit_implicit(thd);
552   }
553 
554   /*
555     Delete UDF from the hash if
556       * the transaction commit fails for CREATE UDF operation
557       * OR if the transaction is committed successfully for the DROP UDF
558         operation.
559   */
560   if (!rollback_transaction &&
561       ((insert_udf && result) || (!insert_udf && !result)))
562     udf_hash_delete(udf);
563 
564   close_thread_tables(thd);
565   thd->mdl_context.release_transactional_locks();
566 
567   return result || rollback || (insert_udf && u_f == nullptr);
568 }
569 
570 /**
571   Create a user defined function.
572 
573   Atomicity:
574     The operation to create a user defined function is atomic/crash-safe.
575     Changes to the Data-dictionary and writing event to binlog are
576     part of the same transaction. All the changes are done as part
577     of the same transaction or do not have any side effects on the
578     operation failure. UDF hash is in sync with operation state.
579     UDF hash do not contain any stale/incorrect data in case of failure.
580     In case of crash, there won't be any discrepancy between the
581     data-dictionary table and the binary log.
582 
583   @param thd                 THD context.
584   @param udf                 Pointer to UDF function.
585 
586   @note Like implementations of other DDL/DML in MySQL, this function
587   relies on the caller to close the thread tables. This is done in the
588   end of dispatch_command().
589 */
590 
mysql_create_function(THD * thd,udf_func * udf)591 bool mysql_create_function(THD *thd, udf_func *udf) {
592   bool error = true;
593   void *dl = nullptr;
594   int new_dl = 0;
595   TABLE *table;
596 
597   DBUG_TRACE;
598 
599   if (!initialized) {
600     if (opt_noacl)
601       my_error(ER_CANT_INITIALIZE_UDF, MYF(0), udf->name.str,
602                "UDFs are unavailable with the --skip-grant-tables option");
603     else
604       my_error(ER_OUT_OF_RESOURCES, MYF(0));
605     return error;
606   }
607 
608   /* must not be dynamically registered */
609   DBUG_ASSERT(udf->dl);
610 
611   /*
612     Ensure that the .dll doesn't have a path
613     This is done to ensure that only approved dll from the system
614     directories are used (to make this even remotely secure).
615   */
616   if (check_valid_path(udf->dl, strlen(udf->dl))) {
617     my_error(ER_UDF_NO_PATHS, MYF(0));
618     return error;
619   }
620   LEX_CSTRING udf_name_cstr = {udf->name.str, udf->name.length};
621   if (check_string_char_length(udf_name_cstr, "", NAME_CHAR_LEN,
622                                system_charset_info, true)) {
623     my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name.str);
624     return error;
625   }
626 
627   /*
628     Acquire MDL SNRW for TL_WRITE type so that deadlock and
629     timeout errors are avoided from the Storage Engine.
630   */
631   TABLE_LIST tables("mysql", "func", TL_WRITE, MDL_SHARED_NO_READ_WRITE);
632 
633   if (open_and_lock_tables(thd, &tables, MYSQL_LOCK_IGNORE_TIMEOUT))
634     return error;
635   table = tables.table;
636 
637   /*
638     System table mysql.func is supported by only InnoDB engine. Changing system
639     table's engine is not allowed. But to support logical upgrade creating
640     system table is allowed in MyISAM engine. CREATE FUNCTION operation is
641     *not* allowed in this case.
642   */
643   if ((table->file->ht->is_supported_system_table != nullptr) &&
644       !table->file->ht->is_supported_system_table(tables.db, tables.table_name,
645                                                   true)) {
646     my_error(ER_UNSUPPORTED_ENGINE, MYF(0),
647              ha_resolve_storage_engine_name(table->file->ht), tables.db,
648              tables.table_name);
649     return error;
650   }
651 
652   // CREATE FUNCTION operation is *not* allowed if table structure is changed.
653   System_table_intact table_intact(thd);
654   if (table_intact.check(thd, table, &mysql_udf_table_def)) return error;
655 
656   /*
657     Turn off row binlogging of this statement and use statement-based
658     so that all supporting tables are updated for CREATE FUNCTION command.
659   */
660   Save_and_Restore_binlog_format_state binlog_format_state(thd);
661 
662   mysql_rwlock_rdlock(&THR_LOCK_udf);
663   if (udf_hash->count(to_string(udf->name)) != 0) {
664     my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
665     mysql_rwlock_unlock(&THR_LOCK_udf);
666     return error;
667   }
668   dl = find_udf_dl(udf->dl);
669   mysql_rwlock_unlock(&THR_LOCK_udf);
670 
671   if (dl == nullptr) {
672     char dlpath[FN_REFLEN];
673     strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
674     (void)unpack_filename(dlpath, dlpath);
675 
676     if (!(dl = dlopen(dlpath, RTLD_NOW))) {
677       const char *errmsg;
678       int error_number = dlopen_errno;
679       DLERROR_GENERATE(errmsg, error_number);
680 
681       DBUG_PRINT("error", ("dlopen of %s failed, error: %d (%s)", udf->dl,
682                            error_number, errmsg));
683       my_error(ER_CANT_OPEN_LIBRARY, MYF(0), udf->dl, error_number, errmsg);
684       return error;
685     }
686     new_dl = 1;
687   }
688   udf->dlhandle = dl;
689   {
690     char buf[NAME_LEN + 16], *missing;
691     if ((missing = init_syms(udf, buf))) {
692       my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
693       if (new_dl) dlclose(dl);
694       return error;
695     }
696   }
697 
698   // create entry in mysql.func table
699 
700   table->use_all_columns();
701   restore_record(table, s->default_values);  // Default values for fields
702   table->field[0]->store(udf->name.str, udf->name.length, system_charset_info);
703   table->field[1]->store((longlong)udf->returns, true);
704   table->field[2]->store(udf->dl, strlen(udf->dl), system_charset_info);
705   if (table->s->fields >= 4)  // If not old func format
706     table->field[3]->store((longlong)udf->type, true);
707   error = (table->file->ha_write_row(table->record[0]) != 0);
708 
709   // Binlog the create function.
710   if (!error)
711     error = (write_bin_log(thd, true, thd->query().str, thd->query().length,
712                            true) != 0);
713 
714   error = udf_end_transaction(thd, error, udf, true);
715 
716   if (error) {
717     char errbuf[MYSYS_STRERROR_SIZE];
718     my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error,
719              my_strerror(errbuf, sizeof(errbuf), error));
720     if (new_dl) dlclose(dl);
721   }
722   return error;
723 }
724 
725 /**
726   Drop a user defined function.
727 
728   Atomicity:
729     The operation to drop a user defined function is atomic/crash-safe.
730     Changes to the Data-dictionary and writing event to binlog are
731     part of the same transaction. All the changes are done as part
732     of the same transaction or do not have any side effects on the
733     operation failure. UDF hash is in sync with operation state.
734     UDF hash do not contain any stale/incorrect data in case of failure.
735     In case of crash, there won't be any discrepancy between the
736     data-dictionary table and the binary log.
737 
738   @param thd                 THD context.
739   @param udf_name            Name of the UDF function.
740 */
741 
mysql_drop_function(THD * thd,const LEX_STRING * udf_name)742 bool mysql_drop_function(THD *thd, const LEX_STRING *udf_name) {
743   TABLE *table;
744   udf_func *udf;
745   bool error = true;
746 
747   DBUG_TRACE;
748 
749   if (!initialized) {
750     if (opt_noacl)
751       my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
752     else
753       my_error(ER_OUT_OF_RESOURCES, MYF(0));
754     return error;
755   }
756 
757   TABLE_LIST tables("mysql", "func", TL_WRITE, MDL_SHARED_NO_READ_WRITE);
758 
759   if (open_and_lock_tables(thd, &tables, MYSQL_LOCK_IGNORE_TIMEOUT))
760     return error;
761   table = tables.table;
762 
763   /*
764     System table mysql.func is supported by only InnoDB engine. Changing system
765     table's engine is not allowed. But to support logical upgrade creating
766     system table is allowed in MyISAM engine. DROP and CREATE FUNCTION
767     operations are not allowed in this case.
768   */
769   if (!table->file->ht->is_supported_system_table(tables.db, tables.table_name,
770                                                   true)) {
771     my_error(ER_UNSUPPORTED_ENGINE, MYF(0),
772              ha_resolve_storage_engine_name(table->file->ht), tables.db,
773              tables.table_name);
774     return error;
775   }
776 
777   // DROP FUNCTION operation is *not* allowed if table structure is changed.
778   System_table_intact table_intact(thd);
779   if (table_intact.check(thd, table, &mysql_udf_table_def)) return error;
780 
781   /*
782     Turn off row binlogging of this statement and use statement-based
783     so that all supporting tables are updated for DROP FUNCTION command.
784   */
785   Save_and_Restore_binlog_format_state binlog_format_state(thd);
786 
787   mysql_rwlock_rdlock(&THR_LOCK_udf);
788   const auto it = udf_hash->find(to_string(*udf_name));
789   if (it == udf_hash->end()) {
790     my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
791     mysql_rwlock_unlock(&THR_LOCK_udf);
792     return error;
793   }
794   udf = it->second;
795   if (!udf->dl) {
796     mysql_rwlock_unlock(&THR_LOCK_udf);
797     my_error(ER_UDF_DROP_DYNAMICALLY_REGISTERED, MYF(0));
798     return error;
799   }
800 
801   mysql_rwlock_unlock(&THR_LOCK_udf);
802 
803   table->use_all_columns();
804   table->field[0]->store(udf->name.str, udf->name.length, &my_charset_bin);
805   if (!table->file->ha_index_read_idx_map(table->record[0], 0,
806                                           table->field[0]->field_ptr(),
807                                           HA_WHOLE_KEY, HA_READ_KEY_EXACT)) {
808     int delete_err;
809     if ((delete_err = table->file->ha_delete_row(table->record[0])))
810       table->file->print_error(delete_err, MYF(0));
811     error = delete_err != 0;
812   }
813 
814   /*
815     Binlog the drop function. Keep the table open and locked
816     while binlogging, to avoid binlog inconsistency.
817   */
818   if (!error)
819     error = (write_bin_log(thd, true, thd->query().str, thd->query().length,
820                            true) != 0);
821 
822   error = udf_end_transaction(thd, error, udf, false);
823 
824   /*
825     Close the handle if this was function that was found during boot or
826     CREATE FUNCTION and it's not in use by any other udf function
827   */
828   if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle);
829 
830   return error;
831 }
832 
udf_register_inner(udf_func * ufunc)833 bool mysql_udf_registration_imp::udf_register_inner(udf_func *ufunc) {
834   mysql_rwlock_wrlock(&THR_LOCK_udf);
835 
836   DBUG_ASSERT(ufunc->dl == nullptr);
837   DBUG_ASSERT(ufunc->dlhandle == nullptr);
838 
839   auto res = udf_hash->emplace(to_string(ufunc->name), ufunc);
840   if (!res.second)
841     ufunc = nullptr;
842   else
843     using_udf_functions = true;
844 
845   mysql_rwlock_unlock(&THR_LOCK_udf);
846   return ufunc == nullptr;
847 }
848 
alloc_udf(const char * name,Item_result return_type,Udf_func_any func,Udf_func_init init_func,Udf_func_deinit deinit_func)849 udf_func *mysql_udf_registration_imp::alloc_udf(const char *name,
850                                                 Item_result return_type,
851                                                 Udf_func_any func,
852                                                 Udf_func_init init_func,
853                                                 Udf_func_deinit deinit_func) {
854   udf_func *ufunc;
855 
856   ufunc = (udf_func *)mem.Alloc(sizeof(udf_func));
857   if (!ufunc) return nullptr;
858   memset(ufunc, 0, sizeof(udf_func));
859   ufunc->name.str = strdup_root(&mem, name);
860   ufunc->name.length = strlen(name);
861   ufunc->func = func;
862   ufunc->func_init = init_func;
863   ufunc->func_deinit = deinit_func;
864   ufunc->returns = return_type;
865   ufunc->usage_count = 1;
866 
867   return ufunc;
868 }
869 
870 DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register,
871                    (const char *name, Item_result return_type,
872                     Udf_func_any func, Udf_func_init init_func,
873                     Udf_func_deinit deinit_func)) {
874   udf_func *ufunc;
875 
876   if (!func && !init_func && !deinit_func) return true;
877 
878   ufunc = alloc_udf(name, return_type, func, init_func, deinit_func);
879   if (!ufunc) return true;
880   ufunc->type = Item_udftype::UDFTYPE_FUNCTION;
881 
882   return udf_register_inner(ufunc);
883 }
884 
885 DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_register_aggregate,
886                    (const char *name, enum Item_result return_type,
887                     Udf_func_any func, Udf_func_init init_func,
888                     Udf_func_deinit deinit_func, Udf_func_add add_func,
889                     Udf_func_clear clear_func)) {
890   udf_func *ufunc;
891 
892   if (!func && !add_func && !clear_func && !init_func && !deinit_func)
893     return true;
894 
895   ufunc = alloc_udf(name, return_type, func, init_func, deinit_func);
896   if (!ufunc) return true;
897   ufunc->type = Item_udftype::UDFTYPE_AGGREGATE;
898   ufunc->func_add = add_func;
899   ufunc->func_clear = clear_func;
900 
901   return udf_register_inner(ufunc);
902 }
903 
904 DEFINE_BOOL_METHOD(mysql_udf_registration_imp::udf_unregister,
905                    (const char *name, int *was_present)) {
906   udf_func *udf = nullptr;
907 
908   if (was_present) *was_present = 0;
909   mysql_rwlock_wrlock(&THR_LOCK_udf);
910   const auto it = udf_hash->find(name);
911   if (it != udf_hash->end()) {
912     if (was_present) *was_present = 1;
913 
914     udf = it->second;
915 
916     if (!udf->dl && !udf->dlhandle &&  // Not registered via CREATE FUNCTION
917         !--udf->usage_count)           // Not used
918     {
919       udf_hash->erase(it);
920       using_udf_functions = !udf_hash->empty();
921     } else  // error
922       udf = nullptr;
923   }
924   mysql_rwlock_unlock(&THR_LOCK_udf);
925   return udf != nullptr ? false : true;
926 }
927 
udf_hash_rlock(void)928 void udf_hash_rlock(void) { mysql_rwlock_rdlock(&THR_LOCK_udf); }
929 
udf_hash_unlock(void)930 void udf_hash_unlock(void) { mysql_rwlock_unlock(&THR_LOCK_udf); }
931 
udf_hash_size(void)932 ulong udf_hash_size(void) { return udf_hash->size(); }
933 
udf_hash_for_each(udf_hash_for_each_func_t * func,void * arg)934 void udf_hash_for_each(udf_hash_for_each_func_t *func, void *arg) {
935   for (auto it : *udf_hash) func(it.second, arg);
936 }
937