~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000 MySQL AB
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 as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/* This implements 'user defined functions' */
17
18
/*
19
   Known bugs:
20
  
21
   Memory for functions is never freed!
22
   Shared libraries are not closed before mysqld exits;
23
     - This is because we can't be sure if some threads are using
24
       a function.
25
  
26
   The bugs only affect applications that create and free a lot of
27
   dynamic functions, so this shouldn't be a real problem.
28
*/
29
30
#ifdef USE_PRAGMA_IMPLEMENTATION
31
#pragma implementation				// gcc: Class implementation
32
#endif
33
34
#include "mysql_priv.h"
35
#include <my_pthread.h>
36
37
#ifdef HAVE_DLOPEN
38
extern "C"
39
{
40
#include <stdarg.h>
41
#include <hash.h>
42
}
43
44
static bool initialized = 0;
45
static MEM_ROOT mem;
46
static HASH udf_hash;
47
static rw_lock_t THR_LOCK_udf;
48
49
50
static udf_func *add_udf(LEX_STRING *name, Item_result ret,
51
                         char *dl, Item_udftype typ);
52
static void del_udf(udf_func *udf);
53
static void *find_udf_dl(const char *dl);
54
55
static char *init_syms(udf_func *tmp, char *nm)
56
{
57
  char *end;
58
59
  if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
60
    return tmp->name.str;
61
62
  end=strmov(nm,tmp->name.str);
63
64
  if (tmp->type == UDFTYPE_AGGREGATE)
65
  {
66
    (void)strmov(end, "_clear");
67
    if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
68
      return nm;
69
    (void)strmov(end, "_add");
70
    if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
71
      return nm;
72
  }
73
74
  (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
    to prefent loading "udf" from, e.g. libc.so
82
    let's ensure that at least one auxiliary symbol is defined
83
  */
84
  if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
80.2.1 by mark
remove handling of suspicious UDFs
85
    return nm;
1 by brian
clean slate
86
  return 0;
87
}
88
89
90
extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
91
			      my_bool not_used __attribute__((unused)))
92
{
93
  udf_func *udf=(udf_func*) buff;
94
  *length=(uint) udf->name.length;
95
  return (uchar*) udf->name.str;
96
}
97
98
99
/*
100
  Read all predeclared functions from mysql.func and accept all that
101
  can be used.
102
*/
103
104
void udf_init()
105
{
106
  TABLE_LIST tables;
107
108
  if (initialized)
51.2.2 by Patrick Galbraith
Removed DBUGs from
109
    return;
1 by brian
clean slate
110
111
  my_rwlock_init(&THR_LOCK_udf,NULL);
77.1.46 by Monty Taylor
Finished the warnings work!
112
1 by brian
clean slate
113
  init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
114
  THD *new_thd = new THD;
115
  if (!new_thd ||
116
      hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
117
  {
118
    sql_print_error("Can't allocate memory for udf structures");
119
    hash_free(&udf_hash);
120
    free_root(&mem,MYF(0));
121
    delete new_thd;
51.2.2 by Patrick Galbraith
Removed DBUGs from
122
    return;
1 by brian
clean slate
123
  }
86 by Brian Aker
Remove bootstrap error on udf.
124
1 by brian
clean slate
125
  initialized = 1;
126
  close_thread_tables(new_thd);
127
  delete new_thd;
128
  /* Remember that we don't have a THD */
129
  my_pthread_setspecific_ptr(THR_THD,  0);
51.2.2 by Patrick Galbraith
Removed DBUGs from
130
  return;
1 by brian
clean slate
131
}
132
133
134
void udf_free()
135
{
136
  /* close all shared libraries */
51.2.2 by Patrick Galbraith
Removed DBUGs from
137
  
1 by brian
clean slate
138
  for (uint idx=0 ; idx < udf_hash.records ; idx++)
139
  {
140
    udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
141
    if (udf->dlhandle)				// Not closed before
142
    {
143
      /* Mark all versions using the same handler as closed */
144
      for (uint j=idx+1 ;  j < udf_hash.records ; j++)
145
      {
146
	udf_func *tmp=(udf_func*) hash_element(&udf_hash,j);
147
	if (udf->dlhandle == tmp->dlhandle)
148
	  tmp->dlhandle=0;			// Already closed
149
      }
150
      dlclose(udf->dlhandle);
151
    }
152
  }
153
  hash_free(&udf_hash);
154
  free_root(&mem,MYF(0));
155
  if (initialized)
156
  {
157
    initialized= 0;
158
    rwlock_destroy(&THR_LOCK_udf);
159
  }
51.2.2 by Patrick Galbraith
Removed DBUGs from
160
  return;
1 by brian
clean slate
161
}
162
163
164
static void del_udf(udf_func *udf)
165
{
51.2.2 by Patrick Galbraith
Removed DBUGs from
166
  
1 by brian
clean slate
167
  if (!--udf->usage_count)
168
  {
169
    hash_delete(&udf_hash,(uchar*) udf);
170
    using_udf_functions=udf_hash.records != 0;
171
  }
172
  else
173
  {
174
    /*
175
      The functions is in use ; Rename the functions instead of removing it.
176
      The functions will be automaticly removed when the least threads
177
      doesn't use it anymore
178
    */
179
    char *name= udf->name.str;
180
    uint name_length=udf->name.length;
181
    udf->name.str=(char*) "*";
182
    udf->name.length=1;
183
    hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
184
  }
51.2.2 by Patrick Galbraith
Removed DBUGs from
185
  return;
1 by brian
clean slate
186
}
187
188
189
void free_udf(udf_func *udf)
190
{
51.2.2 by Patrick Galbraith
Removed DBUGs from
191
  
1 by brian
clean slate
192
  
193
  if (!initialized)
51.2.2 by Patrick Galbraith
Removed DBUGs from
194
    return;
1 by brian
clean slate
195
196
  rw_wrlock(&THR_LOCK_udf);
197
  if (!--udf->usage_count)
198
  {
199
    /*
200
      We come here when someone has deleted the udf function
201
      while another thread still was using the udf
202
    */
203
    hash_delete(&udf_hash,(uchar*) udf);
204
    using_udf_functions=udf_hash.records != 0;
205
    if (!find_udf_dl(udf->dl))
206
      dlclose(udf->dlhandle);
207
  }
208
  rw_unlock(&THR_LOCK_udf);
51.2.2 by Patrick Galbraith
Removed DBUGs from
209
  return;
1 by brian
clean slate
210
}
211
212
213
/* This is only called if using_udf_functions != 0 */
214
215
udf_func *find_udf(const char *name,uint length,bool mark_used)
216
{
217
  udf_func *udf=0;
51.2.2 by Patrick Galbraith
Removed DBUGs from
218
  
1 by brian
clean slate
219
220
  if (!initialized)
51.2.2 by Patrick Galbraith
Removed DBUGs from
221
    return(NULL);
1 by brian
clean slate
222
223
  /* TODO: This should be changed to reader locks someday! */
224
  if (mark_used)
225
    rw_wrlock(&THR_LOCK_udf);  /* Called during fix_fields */
226
  else
227
    rw_rdlock(&THR_LOCK_udf);  /* Called during parsing */
228
229
  if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name,
230
				   length ? length : (uint) strlen(name))))
231
  {
232
    if (!udf->dlhandle)
233
      udf=0;					// Could not be opened
234
    else if (mark_used)
235
      udf->usage_count++;
236
  }
237
  rw_unlock(&THR_LOCK_udf);
51.2.2 by Patrick Galbraith
Removed DBUGs from
238
  return(udf);
1 by brian
clean slate
239
}
240
241
242
static void *find_udf_dl(const char *dl)
243
{
51.2.2 by Patrick Galbraith
Removed DBUGs from
244
  
1 by brian
clean slate
245
246
  /*
247
    Because only the function name is hashed, we have to search trough
248
    all rows to find the dl.
249
  */
250
  for (uint idx=0 ; idx < udf_hash.records ; idx++)
251
  {
252
    udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
253
    if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
51.2.2 by Patrick Galbraith
Removed DBUGs from
254
      return(udf->dlhandle);
1 by brian
clean slate
255
  }
51.2.2 by Patrick Galbraith
Removed DBUGs from
256
  return(0);
1 by brian
clean slate
257
}
258
259
260
/* Assume that name && dl is already allocated */
261
262
static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
263
			 Item_udftype type)
264
{
265
  if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
266
    return 0;
267
  udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
268
  if (!tmp)
269
    return 0;
270
  bzero((char*) tmp,sizeof(*tmp));
271
  tmp->name = *name; //dup !!
272
  tmp->dl = dl;
273
  tmp->returns = ret;
274
  tmp->type = type;
275
  tmp->usage_count=1;
276
  if (my_hash_insert(&udf_hash,(uchar*)  tmp))
277
    return 0;
278
  using_udf_functions=1;
279
  return tmp;
280
}
281
282
283
/**
284
  Create a user defined function. 
285
286
  @note Like implementations of other DDL/DML in MySQL, this function
287
  relies on the caller to close the thread tables. This is done in the
288
  end of dispatch_command().
289
*/
290
291
int mysql_create_function(THD *thd,udf_func *udf)
292
{
293
  int error;
294
  void *dl=0;
295
  bool new_dl=0;
296
  TABLE *table;
297
  TABLE_LIST tables;
298
  udf_func *u_d;
51.2.2 by Patrick Galbraith
Removed DBUGs from
299
  
1 by brian
clean slate
300
301
  if (!initialized)
302
  {
303
    if (opt_noacl)
304
      my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
305
               udf->name.str,
306
               "UDFs are unavailable with the --skip-grant-tables option");
307
    else
308
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
51.2.2 by Patrick Galbraith
Removed DBUGs from
309
    return(1);
1 by brian
clean slate
310
  }
311
312
  /*
313
    Ensure that the .dll doesn't have a path
314
    This is done to ensure that only approved dll from the system
315
    directories are used (to make this even remotely secure).
316
317
    On windows we must check both FN_LIBCHAR and '/'.
318
  */
319
  if (my_strchr(files_charset_info, udf->dl,
320
                udf->dl + strlen(udf->dl), FN_LIBCHAR) ||
321
      IF_WIN(my_strchr(files_charset_info, udf->dl,
322
                       udf->dl + strlen(udf->dl), '/'), 0))
323
  {
324
    my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
51.2.2 by Patrick Galbraith
Removed DBUGs from
325
    return(1);
1 by brian
clean slate
326
  }
327
  if (check_identifier_name(&udf->name, ER_TOO_LONG_IDENT))
51.2.2 by Patrick Galbraith
Removed DBUGs from
328
    return(1);
1 by brian
clean slate
329
330
  /* 
331
    Turn off row binlogging of this statement and use statement-based 
332
    so that all supporting tables are updated for CREATE FUNCTION command.
333
  */
334
  if (thd->current_stmt_binlog_row_based)
335
    thd->clear_current_stmt_binlog_row_based();
336
337
  rw_wrlock(&THR_LOCK_udf);
338
  if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
339
  {
340
    my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
341
    goto err;
342
  }
343
  if (!(dl = find_udf_dl(udf->dl)))
344
  {
345
    char dlpath[FN_REFLEN];
346
    strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
347
    if (!(dl = dlopen(dlpath, RTLD_NOW)))
348
    {
349
      my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
350
               udf->dl, errno, dlerror());
351
      goto err;
352
    }
353
    new_dl=1;
354
  }
355
  udf->dlhandle=dl;
356
  {
357
    char buf[NAME_LEN+16], *missing;
358
    if ((missing= init_syms(udf, buf)))
359
    {
360
      my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
361
      goto err;
362
    }
363
  }
364
  udf->name.str=strdup_root(&mem,udf->name.str);
365
  udf->dl=strdup_root(&mem,udf->dl);
366
  if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
367
    goto err;
368
  u_d->dlhandle = dl;
369
  u_d->func=udf->func;
370
  u_d->func_init=udf->func_init;
371
  u_d->func_deinit=udf->func_deinit;
372
  u_d->func_clear=udf->func_clear;
373
  u_d->func_add=udf->func_add;
374
375
  /* create entry in mysql.func table */
376
377
  bzero((char*) &tables,sizeof(tables));
378
  tables.db= (char*) "mysql";
379
  tables.table_name= tables.alias= (char*) "func";
380
  /* Allow creation of functions even if we can't open func table */
381
  if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
382
    goto err;
383
  table->use_all_columns();
384
  restore_record(table, s->default_values);	// Default values for fields
385
  table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
386
  table->field[1]->store((longlong) u_d->returns, TRUE);
387
  table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
388
  if (table->s->fields >= 4)			// If not old func format
389
    table->field[3]->store((longlong) u_d->type, TRUE);
390
  error = table->file->ha_write_row(table->record[0]);
391
392
  if (error)
393
  {
394
    my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
395
    del_udf(u_d);
396
    goto err;
397
  }
398
  rw_unlock(&THR_LOCK_udf);
399
400
  /* Binlog the create function. */
401
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
402
51.2.2 by Patrick Galbraith
Removed DBUGs from
403
  return(0);
1 by brian
clean slate
404
405
 err:
406
  if (new_dl)
407
    dlclose(dl);
408
  rw_unlock(&THR_LOCK_udf);
51.2.2 by Patrick Galbraith
Removed DBUGs from
409
  return(1);
1 by brian
clean slate
410
}
411
412
413
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
414
{
415
  TABLE *table;
416
  TABLE_LIST tables;
417
  udf_func *udf;
418
  char *exact_name_str;
419
  uint exact_name_len;
51.2.2 by Patrick Galbraith
Removed DBUGs from
420
  
1 by brian
clean slate
421
422
  if (!initialized)
423
  {
424
    if (opt_noacl)
425
      my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
426
    else
427
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
51.2.2 by Patrick Galbraith
Removed DBUGs from
428
    return(1);
1 by brian
clean slate
429
  }
430
431
  /* 
432
    Turn off row binlogging of this statement and use statement-based
433
    so that all supporting tables are updated for DROP FUNCTION command.
434
  */
435
  if (thd->current_stmt_binlog_row_based)
436
    thd->clear_current_stmt_binlog_row_based();
437
438
  rw_wrlock(&THR_LOCK_udf);  
439
  if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str,
440
				    (uint) udf_name->length)))
441
  {
442
    my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
443
    goto err;
444
  }
445
  exact_name_str= udf->name.str;
446
  exact_name_len= udf->name.length;
447
  del_udf(udf);
448
  /*
449
    Close the handle if this was function that was found during boot or
450
    CREATE FUNCTION and it's not in use by any other udf function
451
  */
452
  if (udf->dlhandle && !find_udf_dl(udf->dl))
453
    dlclose(udf->dlhandle);
454
455
  bzero((char*) &tables,sizeof(tables));
456
  tables.db=(char*) "mysql";
457
  tables.table_name= tables.alias= (char*) "func";
458
  if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
459
    goto err;
460
  table->use_all_columns();
461
  table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
462
  if (!table->file->index_read_idx_map(table->record[0], 0,
463
                                       (uchar*) table->field[0]->ptr,
464
                                       HA_WHOLE_KEY,
465
                                       HA_READ_KEY_EXACT))
466
  {
467
    int error;
468
    if ((error = table->file->ha_delete_row(table->record[0])))
469
      table->file->print_error(error, MYF(0));
470
  }
471
  close_thread_tables(thd);
472
473
  rw_unlock(&THR_LOCK_udf);
474
475
  /* Binlog the drop function. */
476
  write_bin_log(thd, TRUE, thd->query, thd->query_length);
477
51.2.2 by Patrick Galbraith
Removed DBUGs from
478
  return(0);
1 by brian
clean slate
479
 err:
480
  rw_unlock(&THR_LOCK_udf);
51.2.2 by Patrick Galbraith
Removed DBUGs from
481
  return(1);
1 by brian
clean slate
482
}
483
484
#endif /* HAVE_DLOPEN */