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