Drizzled Public API Documentation

loader.cc

00001 /* Copyright (C) 2005 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include <config.h>
00017 
00018 #include <dlfcn.h>
00019 
00020 #include <cstdio>
00021 #include <string>
00022 #include <vector>
00023 #include <map>
00024 #include <algorithm>
00025 #include <iostream>
00026 
00027 #include <boost/program_options.hpp>
00028 
00029 #include <drizzled/option.h>
00030 #include <drizzled/internal/m_string.h>
00031 
00032 #include <drizzled/plugin.h>
00033 #include <drizzled/module/load_list.h>
00034 #include <drizzled/module/library.h>
00035 #include <drizzled/module/registry.h>
00036 #include <drizzled/module/option_context.h>
00037 #include <drizzled/sql_parse.h>
00038 #include <drizzled/show.h>
00039 #include <drizzled/cursor.h>
00040 #include <drizzled/set_var.h>
00041 #include <drizzled/session.h>
00042 #include <drizzled/item/null.h>
00043 #include <drizzled/error.h>
00044 #include <drizzled/gettext.h>
00045 #include <drizzled/errmsg_print.h>
00046 #include <drizzled/pthread_globals.h>
00047 #include <drizzled/util/tokenize.h>
00048 
00049 #include <boost/foreach.hpp>
00050 
00051 /* FreeBSD 2.2.2 does not define RTLD_NOW) */
00052 #ifndef RTLD_NOW
00053 #define RTLD_NOW 1
00054 #endif
00055 
00056 namespace po=boost::program_options;
00057 
00058 using namespace std;
00059 
00061 typedef drizzled::module::Manifest drizzled_builtin_list[];
00062 extern drizzled_builtin_list PANDORA_BUILTIN_SYMBOLS_LIST;
00063 extern drizzled_builtin_list PANDORA_BUILTIN_LOAD_SYMBOLS_LIST;
00064 drizzled::module::Manifest *drizzled_builtins[]=
00065 {
00066   PANDORA_BUILTIN_SYMBOLS_LIST, NULL
00067 };
00068 drizzled::module::Manifest *drizzled_load_builtins[]=
00069 {
00070   PANDORA_BUILTIN_LOAD_SYMBOLS_LIST, NULL
00071 };
00072 
00073 namespace drizzled {
00074  
00075 
00076 typedef vector<string> PluginOptions;
00077 static PluginOptions opt_plugin_load;
00078 static PluginOptions opt_plugin_add;
00079 static PluginOptions opt_plugin_remove;
00080 const char *builtin_plugins= PANDORA_BUILTIN_LIST;
00081 const char *builtin_load_plugins= PANDORA_BUILTIN_LOAD_LIST;
00082 
00083 /* Note that 'int version' must be the first field of every plugin
00084    sub-structure (plugin->info).
00085 */
00086 
00087 static bool initialized= false;
00088 
00089 
00090 static bool reap_needed= false;
00091 
00092 /*
00093   write-lock on LOCK_system_variables_hash is required before modifying
00094   the following variables/structures
00095 */
00096 static memory::Root plugin_mem_root(4096);
00097 static uint32_t global_variables_dynamic_size= 0;
00098 
00099 
00100 /*
00101   hidden part of opaque value passed to variable check functions.
00102   Used to provide a object-like structure to non C++ consumers.
00103 */
00104 struct st_item_value_holder : public drizzle_value
00105 {
00106   Item *item;
00107 };
00108 
00109 class Bookmark
00110 {
00111 public:
00112   Bookmark() :
00113     type_code(0),
00114     offset(0),
00115     version(0),
00116     key("")
00117   {}
00118   uint8_t type_code;
00119   int offset;
00120   uint32_t version;
00121   string key;
00122 };
00123 
00124 typedef boost::unordered_map<string, Bookmark> bookmark_unordered_map;
00125 static bookmark_unordered_map bookmark_hash;
00126 
00127 
00128 
00129 /* prototypes */
00130 static void plugin_prune_list(vector<string> &plugin_list,
00131                               const vector<string> &plugins_to_remove);
00132 static bool plugin_load_list(module::Registry &registry,
00133                              memory::Root *tmp_root,
00134                              const set<string> &plugin_list,
00135                              po::options_description &long_options,
00136                              bool builtin= false);
00137 static int test_plugin_options(memory::Root *, module::Module *,
00138                                po::options_description &long_options);
00139 static void unlock_variables(Session *session, drizzle_system_variables *vars);
00140 static void cleanup_variables(drizzle_system_variables *vars);
00141 
00142 
00143 /****************************************************************************
00144   Plugin support code
00145 ****************************************************************************/
00146 
00147 
00148 
00149 
00150 /*
00151   NOTE
00152     Requires that a write-lock is held on LOCK_system_variables_hash
00153 */
00154 static bool plugin_add(module::Registry &registry, memory::Root *tmp_root,
00155                        module::Library *library,
00156                        po::options_description &long_options)
00157 {
00158   if (! initialized)
00159     return true;
00160 
00161   if (registry.find(library->getName()))
00162   {
00163     errmsg_printf(error::WARN, ER(ER_PLUGIN_EXISTS),
00164                   library->getName().c_str());
00165     return false;
00166   }
00167 
00168   /* Find plugin by name */
00169   const module::Manifest *manifest= library->getManifest();
00170 
00171   if (registry.find(manifest->name))
00172   {
00173     errmsg_printf(error::ERROR, 
00174                   _("Plugin '%s' contains the name '%s' in its manifest, which "
00175                     "has already been registered.\n"),
00176                   library->getName().c_str(),
00177                   manifest->name);
00178     return true;
00179   }
00180 
00181   module::Module* tmp= new (std::nothrow) module::Module(manifest, library);
00182   if (tmp == NULL)
00183     return true;
00184 
00185   if (!test_plugin_options(tmp_root, tmp, long_options))
00186   {
00187     registry.add(tmp);
00188     return false;
00189   }
00190   errmsg_printf(error::ERROR, ER(ER_CANT_FIND_DL_ENTRY),
00191                 library->getName().c_str());
00192   return true;
00193 }
00194 
00195 
00196 static void reap_plugins(module::Registry &registry)
00197 {
00198   BOOST_FOREACH(module::Registry::ModuleMap::const_reference module, registry.getModulesMap())
00199     delete module.second;
00200 }
00201 
00202 
00203 static bool plugin_initialize(module::Registry &registry,
00204                               module::Module *module)
00205 {
00206   assert(module->isInited == false);
00207 
00208   module::Context loading_context(registry, module);
00209   if (module->getManifest().init)
00210   {
00211     if (module->getManifest().init(loading_context))
00212     {
00213       errmsg_printf(error::ERROR,
00214                     _("Plugin '%s' init function returned error.\n"),
00215                     module->getName().c_str());
00216       return true;
00217     }
00218   }
00219   module->isInited= true;
00220   return false;
00221 }
00222 
00223 static void compose_plugin_options(vector<string> &target,
00224                                    vector<string> options)
00225 {
00226   BOOST_FOREACH(vector<string>::reference it, options)
00227     tokenize(it, target, ",", true);
00228   BOOST_FOREACH(vector<string>::reference it, target)
00229     std::replace(it.begin(), it.end(), '-', '_');
00230 }
00231 
00232 void compose_plugin_add(vector<string> options)
00233 {
00234   compose_plugin_options(opt_plugin_add, options);
00235 }
00236 
00237 void compose_plugin_remove(vector<string> options)
00238 {
00239   compose_plugin_options(opt_plugin_remove, options);
00240 }
00241 
00242 void notify_plugin_load(string in_plugin_load)
00243 {
00244   tokenize(in_plugin_load, opt_plugin_load, ",", true);
00245 }
00246 
00247 /*
00248   The logic is that we first load and initialize all compiled in plugins.
00249   From there we load up the dynamic types (assuming we have not been told to
00250   skip this part).
00251 
00252   Finally we initialize everything, aka the dynamic that have yet to initialize.
00253 */
00254 bool plugin_init(module::Registry &registry,
00255                  po::options_description &long_options)
00256 {
00257   if (initialized)
00258     return false;
00259 
00260   initialized= true;
00261 
00262   PluginOptions builtin_load_list;
00263   tokenize(builtin_load_plugins, builtin_load_list, ",", true);
00264 
00265   PluginOptions builtin_list;
00266   tokenize(builtin_plugins, builtin_list, ",", true);
00267 
00268   bool load_failed= false;
00269 
00270   if (opt_plugin_add.size() > 0)
00271   {
00272     for (PluginOptions::iterator iter= opt_plugin_add.begin();
00273          iter != opt_plugin_add.end();
00274          ++iter)
00275     {
00276       if (find(builtin_list.begin(),
00277                builtin_list.end(), *iter) != builtin_list.end())
00278       {
00279         builtin_load_list.push_back(*iter);
00280       }
00281       else
00282       {
00283         opt_plugin_load.push_back(*iter);
00284       }
00285     }
00286   }
00287 
00288   if (opt_plugin_remove.size() > 0)
00289   {
00290     plugin_prune_list(opt_plugin_load, opt_plugin_remove);
00291     plugin_prune_list(builtin_load_list, opt_plugin_remove);
00292   }
00293 
00294   memory::Root tmp_root(4096);
00295   /*
00296     First we register builtin plugins
00297   */
00298   const set<string> builtin_list_set(builtin_load_list.begin(),
00299                                      builtin_load_list.end());
00300   load_failed= plugin_load_list(registry, &tmp_root,
00301                                 builtin_list_set, long_options, true);
00302   if (load_failed)
00303   {
00304     tmp_root.free_root(MYF(0));
00305     return true;
00306   }
00307 
00308   /* Uniquify the list */
00309   const set<string> plugin_list_set(opt_plugin_load.begin(),
00310                                     opt_plugin_load.end());
00311   
00312   /* Register all dynamic plugins */
00313   load_failed= plugin_load_list(registry, &tmp_root,
00314                                 plugin_list_set, long_options);
00315   if (load_failed)
00316   {
00317     tmp_root.free_root(MYF(0));
00318     return true;
00319   }
00320 
00321   tmp_root.free_root(MYF(0));
00322 
00323   return false;
00324 }
00325 
00326 bool plugin_finalize(module::Registry &registry)
00327 {
00328   /*
00329     Now we initialize all remaining plugins
00330   */
00331   BOOST_FOREACH(module::Registry::ModuleList::const_reference module, registry.getList())
00332   {
00333     if (not module->isInited && plugin_initialize(registry, module))
00334     {
00335       registry.remove(module);
00336       delete module;
00337       return true;
00338     }
00339   }
00340   BOOST_FOREACH(plugin::Plugin::map::value_type value, registry.getPluginsMap())
00341   {
00342     value.second->prime();
00343   }
00344   return false;
00345 }
00346 
00347 /*
00348   Window of opportunity for plugins to issue any queries with the database up and running but with no user's connected.
00349 */
00350 void plugin_startup_window(module::Registry &registry, drizzled::Session &session)
00351 {
00352   BOOST_FOREACH(plugin::Plugin::map::value_type value, registry.getPluginsMap())
00353   {
00354     value.second->startup(session);
00355   }
00356 }
00357 
00358 class PrunePlugin :
00359   public unary_function<string, bool>
00360 {
00361   const string to_match;
00362   PrunePlugin();
00363   PrunePlugin& operator=(const PrunePlugin&);
00364 public:
00365   explicit PrunePlugin(const string &match_in) :
00366     to_match(match_in)
00367   { }
00368 
00369   result_type operator()(const string &match_against)
00370   {
00371     return match_against == to_match;
00372   }
00373 };
00374 
00375 static void plugin_prune_list(vector<string> &plugin_list,
00376                               const vector<string> &plugins_to_remove)
00377 {
00378   for (vector<string>::const_iterator iter= plugins_to_remove.begin();
00379        iter != plugins_to_remove.end();
00380        ++iter)
00381   {
00382     plugin_list.erase(remove_if(plugin_list.begin(),
00383                                 plugin_list.end(),
00384                                 PrunePlugin(*iter)),
00385                       plugin_list.end());
00386   }
00387 }
00388 
00389 /*
00390   called only by plugin_init()
00391 */
00392 static bool plugin_load_list(module::Registry &registry,
00393                              memory::Root *tmp_root,
00394                              const set<string> &plugin_list,
00395                              po::options_description &long_options,
00396                              bool builtin)
00397 {
00398   BOOST_FOREACH(const string& plugin_name, plugin_list)
00399   {
00400     module::Library* library= registry.addLibrary(plugin_name, builtin);
00401     if (library == NULL)
00402     {
00403       errmsg_printf(error::ERROR,
00404                     _("Couldn't load plugin library named '%s'.\n"),
00405                     plugin_name.c_str());
00406       return true;
00407     }
00408 
00409     tmp_root->free_root(MYF(memory::MARK_BLOCKS_FREE));
00410     if (plugin_add(registry, tmp_root, library, long_options))
00411     {
00412       registry.removeLibrary(plugin_name);
00413       errmsg_printf(error::ERROR,
00414                     _("Couldn't load plugin named '%s'.\n"),
00415                     plugin_name.c_str());
00416       return true;
00417     }
00418   }
00419   return false;
00420 }
00421 
00422 
00423 void module_shutdown(module::Registry &registry)
00424 {
00425 
00426   if (initialized)
00427   {
00428     reap_needed= true;
00429 
00430     reap_plugins(registry);
00431     unlock_variables(NULL, &global_system_variables);
00432     unlock_variables(NULL, &max_system_variables);
00433 
00434     cleanup_variables(&global_system_variables);
00435     cleanup_variables(&max_system_variables);
00436 
00437     initialized= 0;
00438   }
00439 
00440   /* Dispose of the memory */
00441   plugin_mem_root.free_root(MYF(0));
00442 
00443   global_variables_dynamic_size= 0;
00444 }
00445 
00446 
00447 /****************************************************************************
00448   System Variables support
00449 ****************************************************************************/
00450 
00451 
00452 
00453 void plugin_sessionvar_init(Session *session)
00454 {
00455   session->variables.storage_engine= NULL;
00456   cleanup_variables(&session->variables);
00457 
00458   session->variables= global_system_variables;
00459   session->variables.storage_engine= NULL;
00460 
00461   /* we are going to allocate these lazily */
00462   session->variables.dynamic_variables_version= 0;
00463   session->variables.dynamic_variables_size= 0;
00464   session->variables.dynamic_variables_ptr= 0;
00465 
00466   session->variables.storage_engine= global_system_variables.storage_engine;
00467 }
00468 
00469 
00470 /*
00471   Unlocks all system variables which hold a reference
00472 */
00473 static void unlock_variables(Session *, struct drizzle_system_variables *vars)
00474 {
00475   vars->storage_engine= NULL;
00476 }
00477 
00478 
00479 /*
00480   Frees memory used by system variables
00481 
00482   Unlike plugin_vars_free_values() it frees all variables of all plugins,
00483   it's used on shutdown.
00484 */
00485 static void cleanup_variables(drizzle_system_variables *vars)
00486 {
00487   assert(vars->storage_engine == NULL);
00488 
00489   free(vars->dynamic_variables_ptr);
00490   vars->dynamic_variables_ptr= NULL;
00491   vars->dynamic_variables_size= 0;
00492   vars->dynamic_variables_version= 0;
00493 }
00494 
00495 
00496 void plugin_sessionvar_cleanup(Session *session)
00497 {
00498   unlock_variables(session, &session->variables);
00499   cleanup_variables(&session->variables);
00500 }
00501 
00502 
00503 
00504 /*
00505   SYNOPSIS
00506     test_plugin_options()
00507     tmp_root                    temporary scratch space
00508     plugin                      internal plugin structure
00509     default_enabled             default plugin enable status
00510   RETURNS:
00511     0 SUCCESS - plugin should be enabled/loaded
00512   NOTE:
00513     Requires that a write-lock is held on LOCK_system_variables_hash
00514 */
00515 static int test_plugin_options(memory::Root *,
00516                                module::Module *test_module,
00517                                po::options_description &long_options)
00518 {
00519 
00520   if (test_module->getManifest().init_options != NULL)
00521   {
00522     string plugin_section_title("Options used by ");
00523     plugin_section_title.append(test_module->getName());
00524     po::options_description module_options(plugin_section_title);
00525     module::option_context opt_ctx(test_module->getName(),
00526                                    module_options.add_options());
00527     test_module->getManifest().init_options(opt_ctx);
00528     long_options.add(module_options);
00529   }
00530 
00531   return 0;
00532 }
00533 
00534 
00535 /****************************************************************************
00536   Help Verbose text with Plugin System Variables
00537 ****************************************************************************/
00538 
00539 class OptionCmp
00540 {
00541 public:
00542   bool operator() (const option &a, const option &b)
00543   {
00544     return my_strcasecmp(&my_charset_utf8_general_ci, a.name, b.name);
00545   }
00546 };
00547 
00548 
00549 void my_print_help_inc_plugins(option *main_options)
00550 {
00551   module::Registry &registry= module::Registry::singleton();
00552   vector<option> all_options;
00553   memory::Root mem_root(4096);
00554 
00555 
00556   if (initialized)
00557   {
00558     std::map<std::string, module::Module *>::const_iterator modules=
00559       registry.getModulesMap().begin();
00560     
00561     while (modules != registry.getModulesMap().end())
00562     {
00563       module::Module *p= (*modules).second;
00564       ++modules;
00565 
00566       /* If we have an init_options function, we are registering
00567          commmand line options that way, so don't do them this way */
00568       if (p->getManifest().init_options != NULL)
00569         continue;
00570 
00571     }
00572   }
00573 
00574   for (;main_options->id; main_options++)
00575   {
00576     if (main_options->comment)
00577     {
00578       all_options.push_back(*main_options);
00579     }
00580   }
00581 
00588   /* main_options now points to the empty option terminator */
00589   all_options.push_back(*main_options);
00590 
00591   my_print_help(&*(all_options.begin()));
00592 
00593   mem_root.free_root(MYF(0));
00594 }
00595 
00596 } /* namespace drizzled */
00597 
00598