23.2.5. Writing Plugins

You can write plugins in C or C++ (or another language that can use C calling conventions). Plugins are loaded and unloaded dynamically, so your operating system must support dynamic loading and you must have compiled mysqld dynamically (not statically).

A plugin contains code that becomes part of the running server, so when you write a plugin, you are bound by any and all constraints that otherwise apply to writing server code. For example, you may have problems if you attempt to use functions from the libstdc++ library. These constraints may change in future versions of the server, so it is possible that server upgrades will require revisions to plugins originally written for older servers. For information about these constraints, see Section 2.11.4, “MySQL Source-Configuration Options”, and Section 2.11.5, “Dealing with Problems Compiling MySQL”.

This section provides a step-by-step guide to creating a plugin library. It shows how to develop a library that contains a full-text parsing plugin named simple_parser. This plugin performs parsing based on simpler rules than those used by the MySQL built-in full-text parser: Words are nonempty runs of whitespace characters. For example plugin source code, see the plugin/fulltext directory of MySQL source distributions.

Each plugin library has the following contents:

  • A general plugin descriptor that indicates the version number of the general plugin API that the library uses and that contains a general declaration for each plugin in the library. The framework for this descriptor is supplied by referring to macros that expand to provide the API version automatically.

  • Each general plugin descriptor contains information that is common to all types of plugin: A value that indicates the plugin type; the plugin name, author, description, and license type; and pointers to the initialization and deinitialization functions that the server invokes when it loads and unloads the plugin.

  • The general plugin descriptor also contains a pointer to a type-specific plugin descriptor. The structure of the type-specific descriptors varies from one plugin type to another because each type of plugin can have its own API. A type-specific plugin descriptor contains an API version number and pointers to the functions that are needed to implement that plugin type. For example, a full-text parser plugin has initialization and deinitialization functions, and a main parsing function. The server invokes these functions when it uses the plugin to parse text.

  • The plugin library contains the interface functions that are referenced by the general and type-specific descriptors for each plugin in the library.

The easiest way to follow the instructions in this section is to use the source code in the plugin/fulltext directory of MySQL source distributions. The instructions assume that you make a copy of that directory and use it to build the plugin library. To make a copy of the directory, use the following commands, which assume that the MySQL source tree is in a directory named mysql-5.5 under your current directory:

shell> mkdir fulltext_plugin
shell> cp mysql-5.5/plugin/fulltext/* fulltext_plugin

After copying the source files, use the following procedure to create a plugin library:

  1. Change location into the fulltext_plugin directory:

    shell> cd fulltext_plugin
    
  2. The plugin source file should include the header files that the plugin library needs. The plugin.h file is required, and the library might require other files as well. For example:

    #include <stdlib.h>
    #include <ctype.h>
    #include <mysql/plugin.h>
    
  3. Set up the general plugin descriptor for the plugin library file.

    Every plugin library must include a general descriptor that must define two symbols:

    • _mysql_plugin_interface_version_ specifies the version number of the general plugin framework. This is given by the MYSQL_PLUGIN_INTERFACE_VERSION symbol, which is defined in the plugin.h file.

    • _mysql_plugin_declarations_ defines an array of plugin declarations, terminated by a declaration with all members set to 0. Each declaration is an instance of the st_mysql_plugin structure (also defined in plugin.h). There must be one of these for each plugin in the library.

    If the server does not find these two symbols in a library, it does not accept it as a legal plugin library and rejects it with an error. This prevents use of a library for plugin purposes unless it was built specifically as a plugin library.

    The standard (and most convenient) way to define the two required symbols is by using the mysql_declare_plugin() and mysql_declare_plugin_end macros from the plugin.h file:

    mysql_declare_plugin(name)
     ... one or more plugin declarations here ...
    mysql_declare_plugin_end;
    

    For example, the general plugin descriptor for a library that contains a single plugin named simple_parser looks like this:

    mysql_declare_plugin(ftexample)
    {
      MYSQL_FTPARSER_PLUGIN,      /* type                            */
      &simple_parser_descriptor,  /* descriptor                      */
      "simple_parser",            /* name                            */
      "Oracle Corporation",       /* author                          */
      "Simple Full-Text Parser",  /* description                     */
      PLUGIN_LICENSE_GPL,         /* plugin license                  */
      simple_parser_plugin_init,  /* init function (when loaded)     */
      simple_parser_plugin_deinit,/* deinit function (when unloaded) */
      0x0001,                     /* version                         */
      simple_status,              /* status variables                */
      simple_system_variables,    /* system variables                */
      NULL
    }
    mysql_declare_plugin_end;
    

    For a full-text parser plugin, the type must be MYSQL_FTPARSER_PLUGIN. This is the value that identifies the plugin as being legal for use in a WITH PARSER clause when creating a FULLTEXT index. (No other plugin type is legal for this clause.)

    plugin.h defines the mysql_declare_plugin() and mysql_declare_plugin_end macros like this:

    #ifndef MYSQL_DYNAMIC_PLUGIN
    #define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS)            \
    MYSQL_PLUGIN_EXPORT int VERSION= MYSQL_PLUGIN_INTERFACE_VERSION;       \
    MYSQL_PLUGIN_EXPORT int PSIZE= sizeof(struct st_mysql_plugin);         \
    MYSQL_PLUGIN_EXPORT struct st_mysql_plugin DECLS[]= {
    #else
    #define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS)            \
    MYSQL_PLUGIN_EXPORT int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \
    MYSQL_PLUGIN_EXPORT int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin); \
    MYSQL_PLUGIN_EXPORT struct st_mysql_plugin _mysql_plugin_declarations_[]= {
    #endif
    
    #define mysql_declare_plugin(NAME) \
    __MYSQL_DECLARE_PLUGIN(NAME, \
                     builtin_ ## NAME ## _plugin_interface_version, \
                     builtin_ ## NAME ## _sizeof_struct_st_plugin, \
                     builtin_ ## NAME ## _plugin)
    
    #define mysql_declare_plugin_end ,{0,0,0,0,0,0,0,0,0,0,0,0}}
    
    Note

    Those declarations define the _mysql_plugin_interface_version_ symbol only if the MYSQL_DYNAMIC_PLUGIN symbol is defined. This means that you must provide -DMYSQL_DYNAMIC_PLUGIN as part of the compilation command to build the plugin as a shared library.

    When the macros are used as just shown, they expand to the following code, which defines both of the required symbols (_mysql_plugin_interface_version_ and _mysql_plugin_declarations_):

    int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION;
    int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin);
    struct st_mysql_plugin _mysql_plugin_declarations_[]= {
    {
      MYSQL_FTPARSER_PLUGIN,      /* type                            */
      &simple_parser_descriptor,  /* descriptor                      */
      "simple_parser",            /* name                            */
      "Oracle Corporation",       /* author                          */
      "Simple Full-Text Parser",  /* description                     */
      PLUGIN_LICENSE_GPL,         /* plugin license                  */
      simple_parser_plugin_init,  /* init function (when loaded)     */
      simple_parser_plugin_deinit,/* deinit function (when unloaded) */
      0x0001,                     /* version                         */
      simple_status,              /* status variables                */
      simple_system_variables,    /* system variables                */
      NULL
    }
      ,{0,0,0,0,0,0,0,0,0,0,0,0}}
    };
    

    The preceding example declares a single plugin in the general descriptor, but it is possible to declare multiple plugins. List the declarations one after the other between mysql_declare_plugin() and mysql_declare_plugin_end, separated by commas.

    MySQL plugins can be written in C or C++ (or another language that can use C calling conventions). If you write a C++ plugin, one C++ feature that you should not use is nonconstant variables to initialize global structures. Members of structures such as the st_mysql_plugin structure should be initialized only with constant variables. The simple_parser descriptor shown earlier is permissible in a C++ plugin because it satisfies that requirement:

    mysql_declare_plugin(ftexample)
    {
      MYSQL_FTPARSER_PLUGIN,      /* type                            */
      &simple_parser_descriptor,  /* descriptor                      */
      "simple_parser",            /* name                            */
      "Oracle Corporation",       /* author                          */
      "Simple Full-Text Parser",  /* description                     */
      PLUGIN_LICENSE_GPL,         /* plugin license                  */
      simple_parser_plugin_init,  /* init function (when loaded)     */
      simple_parser_plugin_deinit,/* deinit function (when unloaded) */
      0x0001,                     /* version                         */
      simple_status,              /* status variables                */
      simple_system_variables,    /* system variables                */
      NULL
    }
    mysql_declare_plugin_end;
    

    Here is another valid way to write the general descriptor. It uses constant variables to indicate the plugin name, author, and description:

    const char *simple_parser_name = "simple_parser";
    const char *simple_parser_author = "Oracle Corporation";
    const char *simple_parser_description = "Simple Full-Text Parser";
    
    mysql_declare_plugin(ftexample)
    {
      MYSQL_FTPARSER_PLUGIN,      /* type                            */
      &simple_parser_descriptor,  /* descriptor                      */
      simple_parser_name,         /* name                            */
      simple_parser_author,       /* author                          */
      simple_parser_description,  /* description                     */
      PLUGIN_LICENSE_GPL,         /* plugin license                  */
      simple_parser_plugin_init,  /* init function (when loaded)     */
      simple_parser_plugin_deinit,/* deinit function (when unloaded) */
      0x0001,                     /* version                         */
      simple_status,              /* status variables                */
      simple_system_variables,    /* system variables                */
      NULL
    }
    mysql_declare_plugin_end;
    

    However, the following general descriptor is invalid. It uses structure members to indicate the plugin name, author, and description, but structures are not considered constant initializers in C++:

    typedef struct
    {
      const char *name;
      const char *author;
      const char *description;
    } plugin_info;
    
    plugin_info parser_info = {
      "simple_parser",
      "Oracle Corporation",
      "Simple Full-Text Parser"
    };
    
    mysql_declare_plugin(ftexample)
    {
      MYSQL_FTPARSER_PLUGIN,      /* type                            */
      &simple_parser_descriptor,  /* descriptor                      */
      parser_info.name,           /* name                            */
      parser_info.author,         /* author                          */
      parser_info.description,    /* description                     */
      PLUGIN_LICENSE_GPL,         /* plugin license                  */
      simple_parser_plugin_init,  /* init function (when loaded)     */
      simple_parser_plugin_deinit,/* deinit function (when unloaded) */
      0x0001,                     /* version                         */
      simple_status,              /* status variables                */
      simple_system_variables,    /* system variables                */
      NULL
    }
    mysql_declare_plugin_end;
    
  4. Set up the type-specific plugin descriptor.

    Each plugin declaration in the library descriptor points to a type-specific descriptor for the corresponding plugin. In the simple_parser declaration, that descriptor is indicated by &simple_parser_descriptor. The descriptor specifies the version number for the full-text plugin interface (as given by MYSQL_FTPARSER_INTERFACE_VERSION), and the plugin's parsing, initialization, and deinitialization functions:

    static struct st_mysql_ftparser simple_parser_descriptor=
    {
      MYSQL_FTPARSER_INTERFACE_VERSION, /* interface version      */
      simple_parser_parse,              /* parsing function       */
      simple_parser_init,               /* parser init function   */
      simple_parser_deinit              /* parser deinit function */
    };
    
  5. Set up the plugin interface functions.

    The general plugin descriptor in the library descriptor names the initialization and deinitialization functions that the server should invoke when it loads and unloads the plugin. For simple_parser, these functions do nothing but return zero to indicate that they succeeded:

    static int simple_parser_plugin_init(void *arg __attribute__((unused)))
    {
      return(0);
    }
    
    static int simple_parser_plugin_deinit(void *arg __attribute__((unused)))
    {
      return(0);
    }
    

    Because those functions do not actually do anything, you could omit them and specify 0 for each of them in the plugin declaration.

    The type-specific plugin descriptor for simple_parser names the initialization, deinitialization, and parsing functions that the server invokes when the plugin is used. For simple_parser, the initialization and deinitialization functions do nothing:

    static int simple_parser_init(MYSQL_FTPARSER_PARAM *param
                                  __attribute__((unused)))
    {
      return(0);
    }
    
    static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param
                                    __attribute__((unused)))
    {
      return(0);
    }
    

    Here too, because those functions do nothing, you could omit them and specify 0 for each of them in the plugin descriptor.

    The main parsing function, simple_parser_parse(), acts as a replacement for the built-in full-text parser, so it needs to split text into words and pass each word to the server. The parsing function's first argument is a pointer to a structure that contains the parsing context. This structure has a doc member that points to the text to be parsed, and a length member that indicates how long the text is. The simple parsing done by the plugin considers nonempty runs of whitespace characters to be words, so it identifies words like this:

    static int simple_parser_parse(MYSQL_FTPARSER_PARAM *param)
    {
      char *end, *start, *docend= param->doc + param->length;
    
      for (end= start= param->doc;; end++)
      {
        if (end == docend)
        {
          if (end > start)
            add_word(param, start, end - start);
          break;
        }
        else if (isspace(*end))
        {
          if (end > start)
            add_word(param, start, end - start);
          start= end + 1;
        }
      }
      return(0);
    }
    

    As the parser finds each word, it invokes a function add_word() to pass the word to the server. add_word() is a helper function only; it is not part of the plugin interface. The parser passes the parsing context pointer to add_word(), as well as a pointer to the word and a length value:

    static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len)
    {
      MYSQL_FTPARSER_BOOLEAN_INFO bool_info=
        { FT_TOKEN_WORD, 0, 0, 0, 0, ' ', 0 };
    
      param->mysql_add_word(param, word, len, &bool_info);
    }
    

    For boolean-mode parsing, add_word() fills in the members of the bool_info structure as described in Section 23.2.4.3.1, “Full-Text Parser Plugin Data Structures and Functions”.

  6. Set up the status variables, if there are any. For the simple_parser plugin, the following status variable array sets up one status variable with a value that is static text, and another with a value that is stored in a long integer variable:

    long number_of_calls= 0;
    
    struct st_mysql_show_var simple_status[]=
    {
      {"static", (char *)"just a static text", SHOW_CHAR},
      {"called", (char *)&number_of_calls,     SHOW_LONG},
      {0,0,0}
    };
    

    When the plugin is installed, the plugin name and the name value are joined with an underscore to form the name displayed by SHOW STATUS. For the array just shown, the resulting status variable names are simple_parser_static and simple_parser_called. This convention means that you can easily display the variables for a plugin using its name:

    mysql> SHOW STATUS LIKE 'simple_parser%';
    +----------------------+--------------------+
    | Variable_name        | Value              |
    +----------------------+--------------------+
    | simple_parser_static | just a static text |
    | simple_parser_called | 0                  |
    +----------------------+--------------------+
    
  7. Compile the plugin library as a shared library and install it in the plugin directory.

    The procedure for compiling shared objects varies from system to system. If you build your library using CMake, it should be able to generate the correct compilation commands for your system. If the library is named mypluglib, you should end up with a shared object file that has a name something like libmypluglib.so. (The file name might have a different extension on your system.)

    To use CMake, you'll need to set up the configuration files to enable the plugin to be compiled and installed. Use the plugin examples under the plugin directory of a MySQL source distribution as a guide.

    Create CMakeLists.txt, which should look something like this:

    MYSQL_ADD_PLUGIN(mypluglib mypluglib.c
      MODULE_ONLY MODULE_OUTPUT_NAME "mypluglib")
    

    When CMake generates the Makefile, it should take care of passing to the compilation command the -DMYSQL_DYNAMIC_PLUGIN flag, and passing to the linker the -lmysqlservices flag, which is needed to link in any functions from services provided through the plugin services interface. See Section 23.2.6, “MySQL Services for Plugins”.

    Run CMake, then run make:

    shell> cmake .
    shell> make
    

    If you need to specify configuration options to CMake, see Section 2.11.4, “MySQL Source-Configuration Options”, for a list. For example, you might want to specify CMAKE_INSTALL_PREFIX to indicate the MySQL base directory under which the plugin should be installed. You can see what value to use for this option with SHOW VARIABLES:

    mysql> SHOW VARIABLES LIKE 'basedir';
    +---------------+------------------+
    | Variable_name | Value            |
    +---------------+------------------+
    | base          | /usr/local/mysql |
    +---------------+------------------+
    

    The location of the plugin directory where you should install the library is given by the plugin_dir system variable. For example:

    mysql> SHOW VARIABLES LIKE 'plugin_dir';
    +---------------+-----------------------------------+
    | Variable_name | Value                             |
    +---------------+-----------------------------------+
    | plugin_dir    | /usr/local/mysql/lib/mysql/plugin |
    +---------------+-----------------------------------+
    

    To install the plugin library, use make:

    shell> make install
    

    Verify that make install installed the plugin library in the proper directory. After installing it, make sure that the library permissions permit it to be executed by the server.

  8. Register the plugin with the server.

    The INSTALL PLUGIN statement causes the server to register the plugin in the mysql.plugin table and to load the plugin code from the library file. Use that statement to register simple_parser with the server, and then verify that the plugin is listed in the mysql.plugin table:

    mysql> INSTALL PLUGIN simple_parser SONAME 'libmypluglib.so';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM mysql.plugin;
    +---------------+-----------------+
    | name          | dl              |
    +---------------+-----------------+
    | simple_parser | libmypluglib.so |
    +---------------+-----------------+
    1 row in set (0.00 sec)
    

    For additional information about plugin loading, see Section 12.4.3.1, “Installing and Uninstalling Plugins”.

  9. Try the plugin.

    Create a table that contains a string column and associate the parser plugin with a FULLTEXT index on the column:

    mysql> CREATE TABLE t (c VARCHAR(255),
        ->   FULLTEXT (c) WITH PARSER simple_parser
        -> ) ENGINE=MyISAM;
    Query OK, 0 rows affected (0.01 sec)
    

    Insert some text into the table and try some searches. These should verify that the parser plugin treats all nonwhitespace characters as word characters:

    mysql> INSERT INTO t VALUES
        ->   ('latin1_general_cs is a case-sensitive collation'),
        ->   ('I\'d like a case of oranges'),
        ->   ('this is sensitive information'),
        ->   ('another row'),
        ->   ('yet another row');
    Query OK, 5 rows affected (0.02 sec)
    Records: 5  Duplicates: 0  Warnings: 0
    
    mysql> SELECT c FROM t;
    +-------------------------------------------------+
    | c                                               |
    +-------------------------------------------------+
    | latin1_general_cs is a case-sensitive collation |
    | I'd like a case of oranges                      |
    | this is sensitive information                   |
    | another row                                     |
    | yet another row                                 |
    +-------------------------------------------------+
    5 rows in set (0.00 sec)
    
    mysql> SELECT MATCH(c) AGAINST('case') FROM t;
    +--------------------------+
    | MATCH(c) AGAINST('case') |
    +--------------------------+
    |                        0 |
    |          1.2968142032623 |
    |                        0 |
    |                        0 |
    |                        0 |
    +--------------------------+
    5 rows in set (0.00 sec)
    
    mysql> SELECT MATCH(c) AGAINST('sensitive') FROM t;
    +-------------------------------+
    | MATCH(c) AGAINST('sensitive') |
    +-------------------------------+
    |                             0 |
    |                             0 |
    |               1.3253291845322 |
    |                             0 |
    |                             0 |
    +-------------------------------+
    5 rows in set (0.01 sec)
    
    mysql> SELECT MATCH(c) AGAINST('case-sensitive') FROM t;
    +------------------------------------+
    | MATCH(c) AGAINST('case-sensitive') |
    +------------------------------------+
    |                    1.3109166622162 |
    |                                  0 |
    |                                  0 |
    |                                  0 |
    |                                  0 |
    +------------------------------------+
    5 rows in set (0.01 sec)
    
    mysql> SELECT MATCH(c) AGAINST('I\'d') FROM t;
    +--------------------------+
    | MATCH(c) AGAINST('I\'d') |
    +--------------------------+
    |                        0 |
    |          1.2968142032623 |
    |                        0 |
    |                        0 |
    |                        0 |
    +--------------------------+
    5 rows in set (0.01 sec)
    

Note how neither “case” nor “insensitive” match “case-insensitive” the way that they would for the built-in parser.

Copyright © 2010-2020 Platon Technologies, s.r.o.           Home | Man pages | tLDP | Documents | Utilities | About
Design by styleshout