~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to server/sql_udf.cc

  • Committer: Mark Atwood
  • Date: 2008-07-12 07:25:25 UTC
  • mto: This revision was merged to the branch mainline in revision 139.
  • Revision ID: me@mark.atwood.name-20080712072525-s1dq9mtwo5td7af7
more hackery to get plugin UDFs working

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
/*
19
19
   Known bugs:
20
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
21
*/
29
22
 
30
23
#ifdef USE_PRAGMA_IMPLEMENTATION
34
27
#include "mysql_priv.h"
35
28
#include <my_pthread.h>
36
29
 
37
 
#ifdef HAVE_DLOPEN
38
30
extern "C"
39
31
{
40
32
#include <stdarg.h>
41
33
#include <hash.h>
42
34
}
43
35
 
44
 
static bool initialized = 0;
 
36
static bool udf_startup= false; /* We do not lock because startup is single threaded */
45
37
static MEM_ROOT mem;
46
38
static HASH udf_hash;
47
 
static rw_lock_t THR_LOCK_udf;
48
 
 
49
39
 
50
40
static udf_func *add_udf(LEX_STRING *name, Item_result ret,
51
41
                         char *dl, Item_udftype typ);
52
42
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)
85
 
    return nm;
86
 
  return 0;
87
 
}
88
 
 
89
43
 
90
44
extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
91
45
                              my_bool not_used __attribute__((unused)))
92
46
{
93
 
  udf_func *udf=(udf_func*) buff;
94
 
  *length=(uint) udf->name.length;
 
47
  udf_func *udf= (udf_func*) buff;
 
48
  *length= (uint) udf->name.length;
95
49
  return (uchar*) udf->name.str;
96
50
}
97
51
 
98
52
 
99
 
/*
100
 
  Read all predeclared functions from mysql.func and accept all that
101
 
  can be used.
102
 
*/
103
 
 
104
53
void udf_init()
105
54
{
106
 
  TABLE_LIST tables;
107
 
 
108
 
  if (initialized)
109
 
    return;
110
 
 
111
 
  my_rwlock_init(&THR_LOCK_udf,NULL);
112
 
 
113
55
  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))
 
56
 
 
57
  if (hash_init(&udf_hash, system_charset_info, 32, 0, 0, get_hash_key, NULL, 0))
117
58
  {
118
59
    sql_print_error("Can't allocate memory for udf structures");
119
60
    hash_free(&udf_hash);
120
 
    free_root(&mem,MYF(0));
121
 
    delete new_thd;
 
61
    free_root(&mem, MYF(0));
122
62
    return;
123
63
  }
124
 
 
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);
130
 
  return;
131
64
}
132
65
 
133
 
 
 
66
/* called by mysqld.cc clean_up() */
134
67
void udf_free()
135
68
{
136
 
  /* close all shared libraries */
137
 
  
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
69
  hash_free(&udf_hash);
154
 
  free_root(&mem,MYF(0));
155
 
  if (initialized)
156
 
  {
157
 
    initialized= 0;
158
 
    rwlock_destroy(&THR_LOCK_udf);
159
 
  }
160
 
  return;
161
 
}
162
 
 
163
 
 
164
 
static void del_udf(udf_func *udf)
165
 
{
166
 
  
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
 
  }
185
 
  return;
186
 
}
187
 
 
188
 
 
189
 
void free_udf(udf_func *udf)
190
 
{
191
 
  
192
 
  
193
 
  if (!initialized)
194
 
    return;
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);
209
 
  return;
210
 
}
211
 
 
 
70
  free_root(&mem, MYF(0));
 
71
}
212
72
 
213
73
/* 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;
218
 
  
219
 
 
220
 
  if (!initialized)
221
 
    return(NULL);
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);
238
 
  return(udf);
239
 
}
240
 
 
241
 
 
242
 
static void *find_udf_dl(const char *dl)
243
 
{
244
 
  
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)
254
 
      return(udf->dlhandle);
255
 
  }
256
 
  return(0);
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;
299
 
  
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));
309
 
    return(1);
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));
325
 
    return(1);
326
 
  }
327
 
  if (check_identifier_name(&udf->name, ER_TOO_LONG_IDENT))
328
 
    return(1);
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
 
 
403
 
  return(0);
404
 
 
405
 
 err:
406
 
  if (new_dl)
407
 
    dlclose(dl);
408
 
  rw_unlock(&THR_LOCK_udf);
409
 
  return(1);
410
 
}
411
 
 
412
 
 
413
 
int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
414
 
{
415
 
  TABLE *table;
416
 
  TABLE_LIST tables;
 
74
udf_func *find_udf(const char *name, uint length, bool mark_used)
 
75
{
417
76
  udf_func *udf;
418
 
  char *exact_name_str;
419
 
  uint exact_name_len;
 
77
 
 
78
  if (udf_startup == false)
 
79
    return NULL;
 
80
 
 
81
  udf= (udf_func*) hash_search(&udf_hash,
 
82
                               (uchar*) name,
 
83
                               length ? length : (uint) strlen(name));
 
84
 
 
85
  return (udf);
 
86
}
 
87
 
 
88
static bool add_udf(udf_func *udf)
 
89
{
 
90
  if (my_hash_insert(&udf_hash, (uchar*) udf))
 
91
    return false;
 
92
 
 
93
  using_udf_functions= 1;
 
94
 
 
95
  return true;
 
96
}
 
97
 
 
98
int initialize_udf(st_plugin_int *plugin)
 
99
{
 
100
  udf_func *udff;
 
101
 
 
102
  if (udf_startup == false)
 
103
  {
 
104
    udf_init();
 
105
    udf_startup= true;
 
106
  }
 
107
          
 
108
  /* allocate the udf_func structure */
 
109
  udff = (udf_func *)my_malloc(sizeof(udf_func), MYF(MY_WME | MY_ZEROFILL));
 
110
  if (udff == NULL) return 1;
 
111
 
 
112
  plugin->data= (void *)udff;
 
113
 
 
114
  if (plugin->plugin->init)
 
115
  {
 
116
    /* todo, if the plugin doesnt have an init, bail out */
 
117
 
 
118
    if (plugin->plugin->init((void *)udff))
 
119
    {
 
120
      sql_print_error("Plugin '%s' init function returned error.",
 
121
                      plugin->name.str);
 
122
      goto err;
 
123
    }
 
124
  }
420
125
  
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));
428
 
    return(1);
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
 
 
478
 
  return(0);
479
 
 err:
480
 
  rw_unlock(&THR_LOCK_udf);
481
 
  return(1);
482
 
}
483
 
 
484
 
#endif /* HAVE_DLOPEN */
 
126
  add_udf(udff);
 
127
 
 
128
  return 0;
 
129
err:
 
130
  my_free(udff, MYF(0));
 
131
  return 1;
 
132
}
 
133
 
 
134
int finalize_udf(st_plugin_int *plugin)
 
135
 
136
  udf_func *udff = (udf_func *)plugin->data;
 
137
 
 
138
  /* TODO: Issue warning on failure */
 
139
  if (udff && plugin->plugin->deinit)
 
140
    (void)plugin->plugin->deinit(udff);
 
141
 
 
142
  if (udff)
 
143
    my_free(udff, MYF(0));
 
144
 
 
145
  return 0;
 
146
}
 
147