|
|
|
|
27.2.5.3. Creating a Plugin Library
This section provides a step-by-step procedure for 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 non-empty runs of whitespace
characters.
Each plugin library has the following contents:
A plugin library 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.
Each plugin general declaration contains information that
is common to all types of plugin: A value that indicates
the plugin type; the plugin name, author, and description;
and pointers to the initialization and deinitialization
functions that the server invokes when it loads and
unloads the plugin.
The plugin general declaration also contains a pointer to
a type-specific plugin descriptor. The structure of these
descriptors can vary from one plugin type to another,
because each type of plugin can have its own API. A plugin
descriptor contains a type-specific 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 library descriptor and by the plugin
descriptors.
To create a plugin library, follow these steps:
-
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>
-
Set up the plugin library file descriptor.
Every plugin library must include a library 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
... one or more plugin declarations here ...
mysql_declare_plugin_end;
For example, the library descriptor for a library that
contains a single plugin named
simple_parser looks like this:
mysql_declare_plugin
{
MYSQL_FTPARSER_PLUGIN, /* type */
&simple_parser_descriptor, /* descriptor */
"simple_parser", /* name */
"MySQL AB", /* author */
"Simple Full-Text Parser", /* description */
simple_parser_plugin_init, /* init function (when loaded) */
simple_parser_plugin_deinit,/* deinit function (when unloaded) */
0x0001, /* version */
simple_status /* status variables */
}
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.)
The mysql_declare_plugin and
mysql_declare_plugin_end macros are
defined in plugin.h like this:
#define mysql_declare_plugin \
int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \
struct st_mysql_plugin _mysql_plugin_declarations_[]= {
#define mysql_declare_plugin_end ,{0,0,0,0,0,0,0,0,0}}
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;
struct st_mysql_plugin _mysql_plugin_declarations_[]= {
{
MYSQL_FTPARSER_PLUGIN, /* type */
&simple_parser_descriptor, /* descriptor */
"simple_parser", /* name */
"MySQL AB", /* author */
"Simple Full-Text Parser", /* description */
simple_parser_plugin_init, /* init function (when loaded) */
simple_parser_plugin_deinit,/* deinit function (when unloaded) */
0x0001, /* version */
simple_status /* status variables */
}
,{0,0,0,0,0,0,00,0}
};
The preceding example declares a single plugin in the
library 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++. One feature of
C++ is that you can use non-constant variables to
initialize global structures. However, if you write a C++
plugin, you should not use this feature. Members of
structures such as the st_mysql_plugin
structure should be initialized with constant variables.
See the discussion at the end of this section that
describes some legal and illegal initializers for plugins.
-
Set up the 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 */
};
-
Set up the plugin interface functions.
The general plugin declaration 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)
{
return(0);
}
static int simple_parser_plugin_deinit(void)
{
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)
{
return(0);
}
static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param)
{
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 non-empty 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 };
if (param->mode == MYSQL_FTPARSER_FULL_BOOLEAN_INFO)
param->mysql_add_word(param->mysql_ftparam, word, len, &bool_info);
else
param->mysql_add_word(param->mysql_ftparam, word, len, 0);
}
For boolean-mode parsing, add_word()
fills in the members of the bool_info
structure as described in
Section 27.2.5.2, “Type-Specific Plugin Structures and Functions”.
-
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 |
+----------------------+--------------------+
-
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 the GNU
autotools, libtool 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
filename might have a different extension on your system.)
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 |
+---------------+----------------------------+
When you install the plugin library, make sure that its
permissions allow it to be executed by the server.
-
Register the plugin with the server.
The INSTALL PLUGIN statement causes the
server to list the plugin in the 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
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)
-
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);
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
non-whitespace 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.
MySQL plugins can be written in C or C++. One feature of C++
is that you can use non-constant variables to initialize
global structures. However, if you write a C++ plugin, you
should not use this feature. Members of structures such as the
st_mysql_plugin structure should be
initialized with constant variables. The
simple_parser descriptor shown earlier is
allowable in a C++ plugin because it satisfies that
requirement:
mysql_declare_plugin
{
MYSQL_FTPARSER_PLUGIN, /* type */
&simple_parser_descriptor, /* descriptor */
"simple_parser", /* name */
"MySQL AB", /* author */
"Simple Full-Text Parser", /* description */
simple_parser_plugin_init, /* init function (when loaded) */
simple_parser_plugin_deinit,/* deinit function (when unloaded) */
0x0001, /* version */
simple_status /* status variables */
}
mysql_declare_plugin_end;
Here is another valid way to write the 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 = "MySQL AB";
const char *simple_parser_description = "Simple Full-Text Parser";
mysql_declare_plugin
{
MYSQL_FTPARSER_PLUGIN, /* type */
&simple_parser_descriptor, /* descriptor */
simple_parser_name, /* name */
simple_parser_author, /* author */
simple_parser_description, /* description */
simple_parser_plugin_init, /* init function (when loaded) */
simple_parser_plugin_deinit,/* deinit function (when unloaded) */
0x0001, /* version */
simple_status /* status variables */
}
mysql_declare_plugin_end;
However, the following 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",
"MySQL AB",
"Simple Full-Text Parser"
};
mysql_declare_plugin
{
MYSQL_FTPARSER_PLUGIN, /* type */
&simple_parser_descriptor, /* descriptor */
parser_info.name, /* name */
parser_info.author, /* author */
parser_info.description, /* description */
simple_parser_plugin_init, /* init function (when loaded) */
simple_parser_plugin_deinit,/* deinit function (when unloaded) */
0x0001, /* version */
simple_status /* status variables */
}
mysql_declare_plugin_end;
|
|
|