sql_udf.cc 15.3 KB
Newer Older
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1 2
/* Copyright (C) 2000 MySQL AB

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7 8 9 10
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
11

bk@work.mysql.com's avatar
bk@work.mysql.com committed
12 13 14 15 16 17 18
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* This implements 'user defined functions' */

/*
paul@frost.snake.net's avatar
paul@frost.snake.net committed
19 20 21 22 23 24 25 26 27
   Known bugs:
  
   Memory for functions is never freed!
   Shared libraries are not closed before mysqld exits;
     - This is because we can't be sure if some threads are using
       a function.
  
   The bugs only affect applications that create and free a lot of
   dynamic functions, so this shouldn't be a real problem.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
28 29
*/

30
#ifdef USE_PRAGMA_IMPLEMENTATION
31
#pragma implementation				// gcc: Class implementation
bk@work.mysql.com's avatar
bk@work.mysql.com committed
32 33 34
#endif

#include "mysql_priv.h"
35
#include <my_pthread.h>
36

bk@work.mysql.com's avatar
bk@work.mysql.com committed
37 38 39 40 41 42 43 44 45 46
#ifdef HAVE_DLOPEN
extern "C"
{
#include <stdarg.h>
#include <hash.h>
}

static bool initialized = 0;
static MEM_ROOT mem;
static HASH udf_hash;
47
static rw_lock_t THR_LOCK_udf;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
48 49


serg@serg.mylan's avatar
merge  
serg@serg.mylan committed
50
static udf_func *add_udf(LEX_STRING *name, Item_result ret,
51
                         char *dl, Item_udftype typ);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
52 53 54
static void del_udf(udf_func *udf);
static void *find_udf_dl(const char *dl);

55
static char *init_syms(udf_func *tmp, char *nm)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
56
{
57 58
  char *end;

59
  if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
serg@serg.mylan's avatar
serg@serg.mylan committed
60
    return tmp->name.str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
61

62
  end=strmov(nm,tmp->name.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
63 64 65

  if (tmp->type == UDFTYPE_AGGREGATE)
  {
serg@serg.mylan's avatar
merge  
serg@serg.mylan committed
66
    (void)strmov(end, "_clear");
67
    if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
68 69
      return nm;
    (void)strmov(end, "_add");
70
    if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
71 72 73 74
      return nm;
  }

  (void) strmov(end,"_deinit");
75
  tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
76 77

  (void) strmov(end,"_init");
78
  tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
79 80 81 82 83 84 85

  /*
    to prefent loading "udf" from, e.g. libc.so
    let's ensure that at least one auxiliary symbol is defined
  */
  if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
  {
86
    if (!opt_allow_suspicious_udfs)
87
      return nm;
88 89
    if (current_thd->variables.log_warnings)
      sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
90
  }
91
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
92 93
}

94

95
extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
96
			      my_bool not_used __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
97 98
{
  udf_func *udf=(udf_func*) buff;
99
  *length=(uint) udf->name.length;
100
  return (uchar*) udf->name.str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
101 102
}

103

bk@work.mysql.com's avatar
bk@work.mysql.com committed
104
/*
105 106
  Read all predeclared functions from mysql.func and accept all that
  can be used.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
107 108 109 110 111 112 113
*/

void udf_init()
{
  udf_func *tmp;
  TABLE_LIST tables;
  READ_RECORD read_record_info;
114
  TABLE *table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
115 116
  int error;
  DBUG_ENTER("ufd_init");
117
  char db[]= "mysql"; /* A subject to casednstr, can't be constant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
118 119 120 121

  if (initialized)
    DBUG_VOID_RETURN;

122 123
  my_rwlock_init(&THR_LOCK_udf,NULL);
  
124
  init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
125 126
  THD *new_thd = new THD;
  if (!new_thd ||
127
      hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
128 129 130
  {
    sql_print_error("Can't allocate memory for udf structures");
    hash_free(&udf_hash);
131
    free_root(&mem,MYF(0));
132
    delete new_thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
133 134 135
    DBUG_VOID_RETURN;
  }
  initialized = 1;
136
  new_thd->thread_stack= (char*) &new_thd;
137
  new_thd->store_globals();
138
  new_thd->set_db(db, sizeof(db)-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
139

140
  bzero((uchar*) &tables,sizeof(tables));
141
  tables.alias= tables.table_name= (char*) "func";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
142
  tables.lock_type = TL_READ;
143
  tables.db= db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
144

145
  if (simple_open_n_lock_tables(new_thd, &tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
146 147
  {
    DBUG_PRINT("error",("Can't open udf table"));
148 149
    sql_print_error("Can't open the mysql.func table. Please "
                    "run mysql_upgrade to create it.");
150
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
151 152
  }

153
  table= tables.table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
154
  init_read_record(&read_record_info, new_thd, table, NULL,1,0);
155 156
  table->use_all_columns();
  while (!(error= read_record_info.read_record(&read_record_info)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
157 158
  {
    DBUG_PRINT("info",("init udf record"));
159
    LEX_STRING name;
160
    name.str=get_field(&mem, table->field[0]);
161
    name.length = strlen(name.str);
162
    char *dl_name= get_field(&mem, table->field[2]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
163 164
    bool new_dl=0;
    Item_udftype udftype=UDFTYPE_FUNCTION;
165
    if (table->s->fields >= 4)			// New func table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
166 167
      udftype=(Item_udftype) table->field[3]->val_int();

168 169 170 171
    /*
      Ensure that the .dll doesn't have a path
      This is done to ensure that only approved dll from the system
      directories are used (to make this even remotely secure).
172 173

      On windows we must check both FN_LIBCHAR and '/'.
174
    */
175
    if (my_strchr(files_charset_info, dl_name,
176 177 178 179 180
                  dl_name + strlen(dl_name), FN_LIBCHAR) ||
        IF_WIN(my_strchr(files_charset_info, dl_name,
                         dl_name + strlen(dl_name), '/'), 0) ||
        check_string_char_length(&name, "", NAME_CHAR_LEN,
                                 system_charset_info, 1))
181 182
    {
      sql_print_error("Invalid row in mysql.func table for function '%.64s'",
serg@serg.mylan's avatar
serg@serg.mylan committed
183
                      name.str);
184 185 186
      continue;
    }

serg@serg.mylan's avatar
merge  
serg@serg.mylan committed
187 188
    if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
                       dl_name, udftype)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
189
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
190
      sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
191 192 193 194 195 196
      continue;
    }

    void *dl = find_udf_dl(tmp->dl);
    if (dl == NULL)
    {
197 198 199 200
      char dlpath[FN_REFLEN];
      strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
               NullS);
      if (!(dl= dlopen(dlpath, RTLD_NOW)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
201
      {
202
	/* Print warning to log */
203
        sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl, errno, dlerror());
204
	/* Keep the udf in the hash so that we can remove it later */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206 207 208 209 210
	continue;
      }
      new_dl=1;
    }
    tmp->dlhandle = dl;
    {
211
      char buf[NAME_LEN+16], *missing;
212 213 214 215 216 217 218
      if ((missing= init_syms(tmp, buf)))
      {
        sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), missing);
        del_udf(tmp);
        if (new_dl)
          dlclose(dl);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
219 220 221
    }
  }
  if (error > 0)
222
    sql_print_error("Got unknown error: %d", my_errno);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
223 224
  end_read_record(&read_record_info);
  new_thd->version--;				// Force close to free memory
225 226

end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
227 228
  close_thread_tables(new_thd);
  delete new_thd;
229 230
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
231 232 233 234 235 236 237 238 239 240 241
  DBUG_VOID_RETURN;
}


void udf_free()
{
  /* close all shared libraries */
  DBUG_ENTER("udf_free");
  for (uint idx=0 ; idx < udf_hash.records ; idx++)
  {
    udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
242
    if (udf->dlhandle)				// Not closed before
bk@work.mysql.com's avatar
bk@work.mysql.com committed
243
    {
244
      /* Mark all versions using the same handler as closed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
245 246 247
      for (uint j=idx+1 ;  j < udf_hash.records ; j++)
      {
	udf_func *tmp=(udf_func*) hash_element(&udf_hash,j);
248 249
	if (udf->dlhandle == tmp->dlhandle)
	  tmp->dlhandle=0;			// Already closed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
250
      }
251
      dlclose(udf->dlhandle);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
252 253 254
    }
  }
  hash_free(&udf_hash);
255
  free_root(&mem,MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
256 257 258
  if (initialized)
  {
    initialized= 0;
259
    rwlock_destroy(&THR_LOCK_udf);
serg@serg.mylan's avatar
serg@serg.mylan committed
260
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
261 262 263 264 265 266 267 268 269
  DBUG_VOID_RETURN;
}


static void del_udf(udf_func *udf)
{
  DBUG_ENTER("del_udf");
  if (!--udf->usage_count)
  {
270
    hash_delete(&udf_hash,(uchar*) udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
271 272 273 274 275
    using_udf_functions=udf_hash.records != 0;
  }
  else
  {
    /*
276 277 278
      The functions is in use ; Rename the functions instead of removing it.
      The functions will be automaticly removed when the least threads
      doesn't use it anymore
bk@work.mysql.com's avatar
bk@work.mysql.com committed
279
    */
280 281 282 283
    char *name= udf->name.str;
    uint name_length=udf->name.length;
    udf->name.str=(char*) "*";
    udf->name.length=1;
284
    hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
285 286 287 288 289 290 291 292
  }
  DBUG_VOID_RETURN;
}


void free_udf(udf_func *udf)
{
  DBUG_ENTER("free_udf");
293 294 295 296
  
  if (!initialized)
    DBUG_VOID_RETURN;

297
  rw_wrlock(&THR_LOCK_udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
298 299
  if (!--udf->usage_count)
  {
300 301 302 303
    /*
      We come here when someone has deleted the udf function
      while another thread still was using the udf
    */
304
    hash_delete(&udf_hash,(uchar*) udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
305 306 307 308
    using_udf_functions=udf_hash.records != 0;
    if (!find_udf_dl(udf->dl))
      dlclose(udf->dlhandle);
  }
309
  rw_unlock(&THR_LOCK_udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
310 311 312
  DBUG_VOID_RETURN;
}

313

bk@work.mysql.com's avatar
bk@work.mysql.com committed
314 315 316 317 318 319 320
/* This is only called if using_udf_functions != 0 */

udf_func *find_udf(const char *name,uint length,bool mark_used)
{
  udf_func *udf=0;
  DBUG_ENTER("find_udf");

321 322 323
  if (!initialized)
    DBUG_RETURN(NULL);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
324
  /* TODO: This should be changed to reader locks someday! */
325 326 327 328 329
  if (mark_used)
    rw_wrlock(&THR_LOCK_udf);  /* Called during fix_fields */
  else
    rw_rdlock(&THR_LOCK_udf);  /* Called during parsing */

330
  if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
331 332 333 334 335 336 337
				   length ? length : (uint) strlen(name))))
  {
    if (!udf->dlhandle)
      udf=0;					// Could not be opened
    else if (mark_used)
      udf->usage_count++;
  }
338
  rw_unlock(&THR_LOCK_udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
339 340 341
  DBUG_RETURN(udf);
}

342

bk@work.mysql.com's avatar
bk@work.mysql.com committed
343 344 345 346
static void *find_udf_dl(const char *dl)
{
  DBUG_ENTER("find_udf_dl");

347 348 349
  /*
    Because only the function name is hashed, we have to search trough
    all rows to find the dl.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
350 351 352 353 354 355 356 357 358 359 360 361 362
  */
  for (uint idx=0 ; idx < udf_hash.records ; idx++)
  {
    udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
    if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
      DBUG_RETURN(udf->dlhandle);
  }
  DBUG_RETURN(0);
}


/* Assume that name && dl is already allocated */

363
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
364 365
			 Item_udftype type)
{
366
  if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
367 368 369 370 371
    return 0;
  udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
  if (!tmp)
    return 0;
  bzero((char*) tmp,sizeof(*tmp));
372
  tmp->name = *name; //dup !!
bk@work.mysql.com's avatar
bk@work.mysql.com committed
373 374 375 376
  tmp->dl = dl;
  tmp->returns = ret;
  tmp->type = type;
  tmp->usage_count=1;
377
  if (my_hash_insert(&udf_hash,(uchar*)  tmp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    return 0;
  using_udf_functions=1;
  return tmp;
}


int mysql_create_function(THD *thd,udf_func *udf)
{
  int error;
  void *dl=0;
  bool new_dl=0;
  TABLE *table;
  TABLE_LIST tables;
  udf_func *u_d;
  DBUG_ENTER("mysql_create_function");

  if (!initialized)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
396
    my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
397 398 399 400 401 402 403
    DBUG_RETURN(1);
  }

  /*
    Ensure that the .dll doesn't have a path
    This is done to ensure that only approved dll from the system
    directories are used (to make this even remotely secure).
404 405

    On windows we must check both FN_LIBCHAR and '/'.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
406
  */
407 408 409 410
  if (my_strchr(files_charset_info, udf->dl,
                udf->dl + strlen(udf->dl), FN_LIBCHAR) ||
      IF_WIN(my_strchr(files_charset_info, udf->dl,
                       udf->dl + strlen(udf->dl), '/'), 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
411
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
412
    my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
413 414
    DBUG_RETURN(1);
  }
415 416
  if (check_string_char_length(&udf->name, "", NAME_CHAR_LEN,
                               system_charset_info, 1))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
417
  {
418
    my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
419 420 421
    DBUG_RETURN(1);
  }

422 423 424 425 426 427 428
  /* 
    Turn off row binlogging of this statement and use statement-based 
    so that all supporting tables are updated for CREATE FUNCTION command.
  */
  if (thd->current_stmt_binlog_row_based)
    thd->clear_current_stmt_binlog_row_based();

429
  rw_wrlock(&THR_LOCK_udf);
430
  if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
431
  {
432
    my_error(ER_UDF_EXISTS, MYF(0), udf->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
433 434 435 436
    goto err;
  }
  if (!(dl = find_udf_dl(udf->dl)))
  {
437 438 439
    char dlpath[FN_REFLEN];
    strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
    if (!(dl = dlopen(dlpath, RTLD_NOW)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
440 441
    {
      DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
442
                          udf->dl, errno, dlerror()));
443
      my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
444
               udf->dl, errno, dlerror());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
445 446 447 448 449 450
      goto err;
    }
    new_dl=1;
  }
  udf->dlhandle=dl;
  {
451
    char buf[NAME_LEN+16], *missing;
452 453
    if ((missing= init_syms(udf, buf)))
    {
ingo@mysql.com's avatar
ingo@mysql.com committed
454
      my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
455 456
      goto err;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
457
  }
458
  udf->name.str=strdup_root(&mem,udf->name.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
459
  udf->dl=strdup_root(&mem,udf->dl);
460
  if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
461 462 463 464 465
    goto err;
  u_d->dlhandle = dl;
  u_d->func=udf->func;
  u_d->func_init=udf->func_init;
  u_d->func_deinit=udf->func_deinit;
466
  u_d->func_clear=udf->func_clear;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
467 468
  u_d->func_add=udf->func_add;

469
  /* create entry in mysql.func table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
470 471 472

  bzero((char*) &tables,sizeof(tables));
  tables.db= (char*) "mysql";
473
  tables.table_name= tables.alias= (char*) "func";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
474
  /* Allow creation of functions even if we can't open func table */
475
  if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
476
    goto err;
477
  table->use_all_columns();
478
  restore_record(table, s->default_values);	// Default values for fields
479
  table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
480
  table->field[1]->store((longlong) u_d->returns, TRUE);
481
  table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
482
  if (table->s->fields >= 4)			// If not old func format
483
    table->field[3]->store((longlong) u_d->type, TRUE);
484
  error = table->file->ha_write_row(table->record[0]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
485 486 487 488

  close_thread_tables(thd);
  if (error)
  {
ingo@mysql.com's avatar
ingo@mysql.com committed
489
    my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
490 491 492
    del_udf(u_d);
    goto err;
  }
493
  rw_unlock(&THR_LOCK_udf);
494 495

  /* Binlog the create function. */
496
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
497

bk@work.mysql.com's avatar
bk@work.mysql.com committed
498 499 500 501 502
  DBUG_RETURN(0);

 err:
  if (new_dl)
    dlclose(dl);
503
  rw_unlock(&THR_LOCK_udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
504 505 506 507
  DBUG_RETURN(1);
}


508
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509 510 511 512
{
  TABLE *table;
  TABLE_LIST tables;
  udf_func *udf;
gkodinov/kgeorge@rakia.gmz's avatar
gkodinov/kgeorge@rakia.gmz committed
513 514
  char *exact_name_str;
  uint exact_name_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
515
  DBUG_ENTER("mysql_drop_function");
516

bk@work.mysql.com's avatar
bk@work.mysql.com committed
517 518
  if (!initialized)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
519
    my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
520 521
    DBUG_RETURN(1);
  }
522 523 524 525 526 527 528 529

  /* 
    Turn off row binlogging of this statement and use statement-based
    so that all supporting tables are updated for DROP FUNCTION command.
  */
  if (thd->current_stmt_binlog_row_based)
    thd->clear_current_stmt_binlog_row_based();

530
  rw_wrlock(&THR_LOCK_udf);  
531
  if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str,
532
				    (uint) udf_name->length)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
533
  {
534
    my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
535 536
    goto err;
  }
gkodinov/kgeorge@rakia.gmz's avatar
gkodinov/kgeorge@rakia.gmz committed
537 538
  exact_name_str= udf->name.str;
  exact_name_len= udf->name.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
539
  del_udf(udf);
540 541 542 543 544
  /*
    Close the handle if this was function that was found during boot or
    CREATE FUNCTION and it's not in use by any other udf function
  */
  if (udf->dlhandle && !find_udf_dl(udf->dl))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
545 546 547 548
    dlclose(udf->dlhandle);

  bzero((char*) &tables,sizeof(tables));
  tables.db=(char*) "mysql";
549
  tables.table_name= tables.alias= (char*) "func";
550
  if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
551
    goto err;
552
  table->use_all_columns();
gkodinov/kgeorge@rakia.gmz's avatar
gkodinov/kgeorge@rakia.gmz committed
553
  table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
554
  if (!table->file->index_read_idx(table->record[0], 0,
555
				   (uchar*) table->field[0]->ptr, HA_WHOLE_KEY,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
556 557 558
				   HA_READ_KEY_EXACT))
  {
    int error;
559
    if ((error = table->file->ha_delete_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
560 561 562 563
      table->file->print_error(error, MYF(0));
  }
  close_thread_tables(thd);

564
  rw_unlock(&THR_LOCK_udf);
565 566

  /* Binlog the drop function. */
567
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
568

bk@work.mysql.com's avatar
bk@work.mysql.com committed
569 570
  DBUG_RETURN(0);
 err:
571
  rw_unlock(&THR_LOCK_udf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
572 573 574 575
  DBUG_RETURN(1);
}

#endif /* HAVE_DLOPEN */