Mysql 51 Plugin Development Sergei Golubchik Andrew Hutchings
Mysql 51 Plugin Development Sergei Golubchik Andrew Hutchings
Mysql 51 Plugin Development Sergei Golubchik Andrew Hutchings
Mysql 51 Plugin Development Sergei Golubchik Andrew Hutchings
1. Mysql 51 Plugin Development Sergei Golubchik
Andrew Hutchings download
https://ebookbell.com/product/mysql-51-plugin-development-sergei-
golubchik-andrew-hutchings-4447232
Explore and download more ebooks at ebookbell.com
2. Here are some recommended products that we believe you will be
interested in. You can click the link to download.
Mysql 51 Plugin Development Sergei Golubchik Andrew Hutchings
https://ebookbell.com/product/mysql-51-plugin-development-sergei-
golubchik-andrew-hutchings-4111250
Php 53 Mysql 51 Florence Maurice
https://ebookbell.com/product/php-53-mysql-51-florence-maurice-1344064
Mysql 50 Certification Study Guide The Authoritative Study Guide To
Prepare For And Pass The Mysql Certified Developer And Mysql Certified
Dba Exams 2nd Ed Dubois
https://ebookbell.com/product/mysql-50-certification-study-guide-the-
authoritative-study-guide-to-prepare-for-and-pass-the-mysql-certified-
developer-and-mysql-certified-dba-exams-2nd-ed-dubois-22105896
Mysql 50 Reference Manual
https://ebookbell.com/product/mysql-50-reference-manual-1347860
3. Php 6 And Mysql 5 For Dynamic Web Sites Visual Quickpro Guide Ullman
https://ebookbell.com/product/php-6-and-mysql-5-for-dynamic-web-sites-
visual-quickpro-guide-ullman-21995298
Beginning Php And Mysql 5 From Novice To Professional 2nd Edition W
Jason Gilmore Auth
https://ebookbell.com/product/beginning-php-and-mysql-5-from-novice-
to-professional-2nd-edition-w-jason-gilmore-auth-4390806
The Definitive Guide To Mysql 5 3rd Edition Michael Kofler
https://ebookbell.com/product/the-definitive-guide-to-mysql-5-3rd-
edition-michael-kofler-5475416
Oracle Database 11g Mysql 56 Developer Handbook 1st Edition Michael
Mclaughlin
https://ebookbell.com/product/oracle-database-11g-mysql-56-developer-
handbook-1st-edition-michael-mclaughlin-46897768
Beginning Php 5 And Mysql From Novice To Professional 1st Edition W
Jason Gilmore
https://ebookbell.com/product/beginning-php-5-and-mysql-from-novice-
to-professional-1st-edition-w-jason-gilmore-49182964
6. MySQL 5.1 Plugin Development
Extend MySQL to suit your needs with this unique guide
into the world of MySQL plugins
Sergei Golubchik
Andrew Hutchings
BIRMINGHAM - MUMBAI
8. Credits
Authors
Sergei Golubchik
Andrew Hutchings
Reviewer
Giuseppe Maxia
Acquisition Editor
Sarah Cullington
Development Editor
Swapna Verlekar
Technical Editors
Priya Darwani
Chris Rodrigues
Indexer
Monica Ajmera Mehta
Editorial Team Leader
Akshara Aware
Project Team Leader
Ashwin Shetty
Project Coordinator
Zainab Bagasrawala
Proofreader
Kevin McGowan
Graphics
Geetanjali Sawant
Production Coordinator
Arvindkumar Gupta
Cover Work
Arvindkumar Gupta
9. About the Authors
Sergei Golubchik started modifying MySQL source code in 1998, and has
continued as a MySQL AB employee since 2000. Working professionally with MySQL
sources, he has had the opportunity to get to know and extend almost every part
of the server code—from the SQL core to the utility functions. He was one of the
primary architects of the Plugin API. After working for ten years in the ever-growing
MySQL AB, and later in Sun Microsystems as a Principal Software Developer, he
resigned to join a small startup company that works on a MariaDB—an extended
version of the MySQL server, where he continues to do what he likes most—hack on
MySQL, architecting, and developing MySQL/MariaDB Plugin API, making it even
more powerful, safe, and easy to use.
He works and lives in Germany, near Cologne, with his lovely wife and two kids.
Andrew Hutchings is currently one of the top MySQL Support Engineers
working at Oracle. He came from failing Computer Science at A-Level (British
exams for 17-18 year olds) to working on, pretty much, every field of computing.
His first development job was as an 8-bit assembly firmware developer for an
environment monitoring company. He then went on to become a senior PHP and C/
C++ developer as well as a DBA and system administrator for a large UK magazine
chain. From there he was snapped up by Sun Microsystems as a MySQL Support
Engineer specializing in MySQL Cluster and C/C++ APIs, much of this work
involving deep analysis of the MySQL source code. Sun has since been bought by
Oracle and Andrew is continuing his role there and was a tutorial speaker at the
2010 O’Reilly MySQL Conference & Expo. In his spare time Andrew is an active
community developer of MySQL, MySQL Cluster, Drizzle, and MySQL Data
Dumper (mydumper for short) as well as other small, related projects.
I’d like to thank my wife, Natalie, and my children, Tomos and
Oliver, for putting up with me while I was writing this book. I also
wish to thank my colleagues, ex-colleagues, and others in the MySQL
community (you know who you are) for their help and support in
getting started with writing a book. And, of course, my co-author
Sergei, without whom this whole book would not have been possible.
10. About the Reviewer
Giuseppe Maxia, a.k.a. The Data Charmer, is the MySQL Community Team Lead
at Sun Microsystems. He is an active member of the MySQL community and a long
time open source enthusiast. For the past 23 years he has worked in various IT
related fields, with focus on databases, object-oriented programming, and system
administration. He is fluent in Italian, English, Perl, SQL, Lua, C, Bash, and a good
speaker of C++, French, Spanish, and Java.
He works in cyberspace, with a virtual team.
12. Table of Contents
Preface 1
Chapter 1: Compiling and Using MySQL Plugins 9
UDF libraries 9
Linux 10
Mac OS X 10
Windows 10
Installing a UDF 15
Plugin libraries 15
Linux 15
Mac OS X 16
Windows 16
Installing a plugin 19
Automatic builds, packaging 19
UDFs and standalone plugins 19
Plugins that are built from the MySQL source tree 20
plug.in 21
Makefile.am 22
CMakeLists.txt 24
Summary 24
Chapter 2: User Defined Functions 25
Writing UDFs 25
Why write UDFs 26
Installing and using UDFs 27
Defining UDFs 31
Execution sequence of a UDF 37
UDF error handling 37
UDF security 38
Gotchas with UDFs 38
13. Table of Contents
[ ii ]
A constant integer output UDF 39
An integer echoing UDF 41
A simple static text output UDF 43
A summing aggregate UDF 46
Further reading 50
Summary 50
Chapter 3: Daemon Plugins 51
A look inside a Daemon plugin 51
Why write a Daemon plugin 51
Installing and using Daemon plugins 52
The role of a version 54
Defining Daemon plugins 54
Status variables 58
System variables 60
A Hello World! Daemon plugin 65
A system and status variables demo plugin 68
A simple monitoring plugin 72
System Status Variables plugin 76
Summary 81
Chapter 4: Information Schema Plugins 83
Why write Information Schema plugins 83
Installing and using Information Schema plugins 84
The creation of Information Schema plugins 85
Defining Information Schema plugins 86
A Static Table example 91
A System Information plugin 95
Summary 100
Chapter 5: Advanced Information Schema Plugins 101
Accessing MySQL internals 101
Condition pushdown 102
Using condition pushdown 103
A condition pushdown example 105
A User Variables Information Schema plugin 110
A Binary Logs Information Schema plugin 115
Summary 119
Chapter 6: Full-text Parser Plugins 121
The full-text parser plugin architecture 122
Three roles of a full-text parser plugin 122
Installing and using a full-text parser plugin 123
Structure of a full-text parser plugin 124
14. Table of Contents
[ iii ]
A PHP full-text parser 130
Summary 136
Chapter 7: Practical Full-text Parsers 137
Boolean parsers 137
A Boolean full-text parser 139
An Image Metadata processor 145
How to access Exif data 145
Writing the plugin 146
Test run 150
A Soundex full-text parser 152
The Soundex algorithm 153
The plugin 154
Trying it out 157
Summary 159
Chapter 8: Storage Engine Plugins 161
Introducing storage engines 161
A read-only storage engine 162
ha_text.cc 164
Summary 179
Chapter 9: HTML Storage Engine—Reads and Writes 181
An idea of the HTML engine 181
Flashback 182
Creating, opening, and closing the table 184
Reading data 187
Updating the table 190
Optimizing and analyzing 193
What's left 194
ha_html.h 195
htmlutils.cc 197
Compiling and linking 198
Putting it all together 199
Summary 201
Chapter 10: TOCAB Storage Engine—Implementing Indexes 203
B-tree library 203
Storage engine API for indexes 205
Describing the engine 208
Creating, opening, and closing the table 213
Searching in the index 217
Rows and keys 224
Table scan and random access 230
15. Table of Contents
[ iv ]
Inserting rows 232
What's left 234
Compiling and linking 236
Putting it all together 237
Possible extensions 239
Summary 239
Appendix: Beyond MySQL 5.1 241
Server services 241
my_snprintf 243
thd_alloc 243
Audit plugins 244
Authentication plugins 247
How it works 247
Authentication plugins—server side 248
Authentication plugins—client side 251
SQL extension by Storage Engine plugins 252
Putting it to use 254
Test drive 257
Summary 258
Index 259
16. Preface
Plugin based architecture is not something new, many popular software products
use it. It is good both for the software product itself—if done properly it forces
developers to structure the code and think about clean interfaces, which helps to
keep the code maintainable over years—and for the users—as they can extend it
without waiting for the vendor or choose from numerous third-party extensions.
History of the Plugin API
MySQL used to have "pluggable" extensions in a form of dynamically loaded
functions since version 3.21.24 released in February 1998. Despite being quite limited
in functionality, they were useful and people were using them. In early 2005, one of
the authors of this book together with another MySQL developer, Sergey Vojtovich,
were working on loadable parsers for MySQL full-text search, to be able to load a
very specialized parser that one of their customers wanted. And Brian Aker, who
was MySQL Director of Architecture at that time, suggested creating a unified
interface for loadable "modules". Based on this idea we developed the MySQL Plugin
API—a generic framework that allowed loading of any functionality in the server—
and Full-text Parser plugins were the first plugin type.
Storage Engine API already existed in MySQL at that time—Michael “Monty”
Widenius, the original author of MySQL, had it since the very first MySQL version,
although he only added the handler class few years later, in 1999. This made Storage
Engine plugins an easy target, and we added them as the next plugin type. Soon after
that I, and another MySQL developer, Antony Curtis, extended Plugin API with the
autotools support, the infamous plug.in file and MYSQL_PLUGIN_* macros that go
in it, and implemented support for server variables, MYSQL_SYSVAR_* and MYSQL_
THDVAR_* macros. Brian Aker added two more plugin types—Information Schema
Table plugins and Daemon plugins.
17. Preface
[ 2 ]
Life was going on even after MySQL 5.1 was released—Antony Curtis and
I have developed Audit plugins. And very recently I and an external contributor,
MIT student R.J. Silk, have completed the work on pluggable authentication and
Authentication plugins were born.
Meanwhile, Michael “Monty” Widenius had left MySQL and started a new company
to work on MySQL fork, that he named MariaDB. Another former MySQL developer,
Sanja Byelkin, and I have implemented the latest (at the time of writing) feature in the
Storage Engine API, the engine defined attributes in the CREATE TABLE statement.
Idea of this book
Today, the MySQL Plugin API is a robust and proven feature. There are many
third-party plugins both open and closed source, the most popular being Storage
Engines, often accompanied by Information Schema tables, and Full-text parsers.
However, the API documentation is not very helpful. If you are anything like me,
you prefer fiction to a dictionary and a few good examples to a grammar description
in the Backus-Naur form. The Plugin API documentation describes the functions and
the structures but does not show how to use them. Tutorials, on the other hand, help
the reader to understand how to use the API. Examples are important to illustrate
the concepts and to bootstrap a new plugin project easily.
This is where the idea of this book came from. We wanted to create a book that
would allow readers to start writing plugins right away. With detailed tutorials and
practical plugin examples, thoroughly explained line by line, highlighted common
mistakes and clarified design decisions. And with code samples that you can start
using in your projects. Not just the code you can copy, but more importantly, the
code you understand—every line, every variable—as if you had written it yourself.
But this book is not a reference manual. It does not contain an exhaustive list of all
functions, classes, and macros of the MySQL Plugin API. The API is documented in
the header files and in the MySQL manual. But to use it, you need to know what to
look for. It is often said that asking the right question is half the right answer. This
book teaches you to ask right questions. It gives detailed understanding—not just
knowledge—of the MySQL Plugin API, and even if you will not have every piece of
the puzzle, you will have most of them, you will know how they fit together, and
you will be able to see the whole picture.
18. Preface
[ 3 ]
What this book covers
The book encourages consecutive reading, but chapters can be read in any order
too. They are mostly independent, and, if needed, you can start reading from, for
example, storage engine chapters without reading about full-text search parsers or
UDFs. The book is structured as follows.
Chapter 1, Compiling and Using MySQL Plugins lays the necessary foundation for the
rest of the book, you will need it in all of the following chapters. It describes how
to compile, link, and install UDFs and plugins. Even if you are only interested in,
say, full-text parsers or storage engines, you may want to read this chapter first. It is
not called Read Me First!!! only because we suspected that the editor may not have
wanted a lot of exclamation marks in the chapter title.
Chapter 2, User Defined Functions deals with UDFs - these dynamically loaded server
extensions that first appeared in the server in 3.21.24, the great-grandparents of the
MySQL Plugin API. Although, strictly speaking, UDFs are not MySQL Plugins—not
part of the MySQL Plugin API—they are still used to load functionality in the server
at runtime, just like plugins are, and sometimes they are used to complement the
plugin functionality.
Chapter 3, Daemon Plugins introduces the reader to the MySQL Plugin API. It talks
about the most simple plugin type—Daemon plugins. It starts with the basic
structure of a plugin—what a plugin declaration should look like, what plugin types
are, and so on. Then it describes features common to all plugin types—initialization
and de-initialization callbacks, status variables, and configuration system variables.
After that it describes and analyzes line by line four Daemon plugin examples—from
a simple plugin that prints Hello World! when loaded, to a system monitoring plugin
that periodically logs the number of connections, to a system usage status plugin that
displays the memory and I/O usage of the MySQL server.
Chapter 4, Information Schema Plugins is dedicated to plugins that add tables to
INFORMATION_SCHEMA. It describes all of the necessary data structures and ends
with two plugin examples—a simple INFORMATION_SCHEMA table with versions
of different MySQL subsystems and system usage statistics presented as an
INFORMATION_SCHEMA table.
Chapter 5, Advanced Information Schema Plugins delves more into the topic started in
the previous chapter. It explains how to use condition pushdown and how to extract
and display information from the server internal data structures. It presents three
plugins that demonstrate condition pushdown, list all user variables, and all binary
log files.
19. Preface
[ 4 ]
Chapter 6, Full-text Parser Plugins is about plugins that extend the MySQL built-in
full-text search. It describes all of the data structures and the code execution flow
and illustrates all that with an example plugin that can parse PHP scripts.
Chapter 7, Practical Full-text Parsers is devoted to the advanced applications of the
plugins of this type. It explains how the search in Boolean mode works and contains
more plugin examples—an Exif parser that allows users to search within embedded
comments in image files, a Soundex parser that post-processes all words with a
Soundex algorithm making the search invulnerable to typos and misspelled words,
and a Boolean search parser plugin that supports AND and OR operators.
Chapter 8, Storage Engine Plugins starts the discussion about the most complex and
versatile plugin type in MySQL. It gives an overview of the main concepts of the
Storage Engine API and thoroughly analyzes sources of the very simple read-only
storage engine.
Chapter 9, HTML Storage Engine - Reads and Writes continues the Storage Engine
series. It presents a storage engine plugin that keeps table data in HTML tables and
uses it to explain how to implement an updatable data stores.
Chapter 10, TOCAB Storage Engine - Implementing Indexes concludes the Storage
Engine part of the book. In this chapter, we develop a storage engine that supports
indexes, using it to explain how the indexing part of the MySQL Storage Engine API
works, how to build an engine that uses an external indexing library, and how to
work around the incompatibilities of their APIs.
Appendix talks about new MySQL Plugin API features, those that did not make
it into MySQL 5.1. It describes Server Services, what they are and why they
were introduced, the Audit plugins, the example of a plugin that audits security
violations, Authentication plugins, with a plugin that uses USB devices to identify
users, and engine attributes in the CREATE TABLE, demonstrating the feature with the
help of the storage engine from Chapter 10.
What you need for this book
The book assumes basic knowledge of SQL and MySQL in particular, and until
MySQL developers implement support for plugins in scripting languages, which
would be great but can hardly happen any time soon, a certain level of familiarity
with C, and for storage engines C++, will be required.
20. Preface
[ 5 ]
Who this book is for
We wrote this book for people who want to create MySQL plugins. They could be
developers with a great idea for a new storage engine. But more often than not they
will be application developers that need to solve a specific problem, whether it is
searching text within Microsoft Word or Open Office documents, monitoring the
database server with their company-wide monitoring framework, querying with
SQL the multi-gigabyte files created with a 20 year old custom data storage library
and joining them with new relational data, or adding MySQL to the company-wide
single sign-on setup. All this and much more can be done with MySQL plugins.
Conventions
In this book, you will find a number of styles of text that distinguish between
different kinds of information. Here are some examples of these styles, and an
explanation of their meaning.
Code words in text are shown as follows: “The second argument of the name_init()
function is a pointer to the UDF_ARGS structure.”
A block of code is set as follows:
typedef struct st_field_info
{
const char* field_name;
uint field_length;
enum enum_field_types field_type;
int value;
uint field_flags;
const char* old_name;
uint open_method;
} ST_FIELD_INFO;
When we wish to draw your attention to a particular part of a code block, the
relevant lines or items are set in bold:
static int tocab_init(void *p)
{
handlerton *tocab_hton = (handlerton *)p;
tocab_hton->create = tocab_create_handler;
tocab_hton->table_options = table_option_list;
return 0;
}
21. Preface
[ 6 ]
Any command-line input or output is written as follows:
shell$ mysql_config --cflags
New terms and important words are shown in italics. Words that you see on
the screen, in menus or dialog boxes for example, appear in the text like this:
“Then in the C/C++ section we need to add the MySQL include path to
Additional Include Directories”.
Warnings or important notes appear in a box like this.
Tips and tricks appear like this.
Reader feedback
Feedback from our readers is always welcome. Let us know what you think about
this book—what you liked or may have disliked. Reader feedback is important for us
to develop titles that you really get the most out of.
To send us general feedback, simply send an e-mail to [email protected],
and mention the book title via the subject of your message.
If there is a book that you need and would like to see us publish, please send us a
note in the SUGGEST A TITLE form on www.packtpub.com or e-mail suggest@
packtpub.com.
If there is a topic that you have expertise in and you are interested in either writing
or contributing to a book, see our author guide on www.packtpub.com/authors.
Customer support
Now that you are the proud owner of a Packt book, we have a number of things to
help you to get the most from your purchase.
Downloading the example code for this book
You can download the example code files for all Packt books you have
purchased from your account at http://www.PacktPub.com. If you
purchased this book elsewhere, you can visit http://www.PacktPub.
com/support and register to have the files e-mailed directly to you.
22. Preface
[ 7 ]
Errata
Although we have taken every care to ensure the accuracy of our content, mistakes
do happen. If you find a mistake in one of our books—maybe a mistake in the text or
the code—we would be grateful if you would report this to us. By doing so, you can
save other readers from frustration and help us improve subsequent versions of this
book. If you find any errata, please report them by visiting http://www.packtpub.
com/support, selecting your book, clicking on the errata submission form link, and
entering the details of your errata. Once your errata are verified, your submission
will be accepted and the errata will be uploaded on our website, or added to any list
of existing errata, under the Errata section of that title. Any existing errata can be
viewed by selecting your title from http://www.packtpub.com/support.
Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media.
At Packt, we take the protection of our copyright and licenses very seriously. If you
come across any illegal copies of our works, in any form, on the Internet, please
provide us with the location address or website name immediately so that we can
pursue a remedy.
Please contact us at [email protected] with a link to the suspected
pirated material.
We appreciate your help in protecting our authors, and our ability to bring you
valuable content.
Questions
You can contact us at [email protected] if you are having a problem with
any aspect of the book, and we will do our best to address it.
24. Compiling and Using MySQL
Plugins
As you progress through this book you will see several examples of how to use
MySQL plugins. This chapter is designed to help you compile and install the UDFs
(User Defined Functions) and MySQL plugins that will be created in the following
chapters. Do not miss it; you will need this knowledge in every single chapter later on.
UDF libraries
MySQL comes with a small utility called mysql_config, which aids the supply of
some of the required options to your compiler. In most cases you need:
shell$ mysql_config --cflags
This will print something such as the following:
-I/opt/mysql-5.1/include/mysql -g -Wreturn-type -Wtrigraphs -W -Wformat
-Wsign-compare -Wunused-function -Wunused-value -Wunused-parameter -m64
-DUNIV_LINUX
Both MySQL plugins and UDFs need to be compiled as shared libraries. How this is
done depends on the platform.
25. Compiling and Using MySQL Plugins
[ 10 ]
Linux
Under Linux, UDFs should be compiled as follows:
gcc -o udf_library.so udf_library.c `mysql_config --cflags` -shared -fPIC
The mysql_config in backticks will apply the results for the command as switches
to gcc, so the include directories as well as other required build options are
automatically inserted. The -shared option tells the compiler that we are creating a
shared library and ‑fPIC enables Position Independent Code, which is required for
dynamic linking of this shared library.
Mac OS X
Compiling on Mac OS X is very much like compiling on Linux, but the way shared
libraries are defined is slightly different:
gcc -o udf_library.so udf_library.c `mysql_config --cflags` -bundle
A bundle is the Mac OS X equivalent of a shared library. If the UDF needs to call
functions in the server binary (for example, if it uses the DBUG debugging facility)
the command line will need to be:
gcc -o udf_library.so udf_library.c `mysql_config --cflags`
-bundle -Wl,-undefined -Wl,dynamic_lookup
Windows
Setting up for compiling UDFs in Windows is generally more involved than in other
operating systems.
As everywhere, we need to have the required libraries and include files installed. To
do this we run the MySQL installer. If you already have MySQL installed, you can
use this tool to modify your installation. The following screenshot shows that we have
selected Custom to do this, but a complete install will also give the required files:
26. Chapter 1
[ 11 ]
Now we need to select Developer Components and then C Include Files / Lib Files
to have them included in the installation. Once this is done the installer should look
similar to this:
27. Compiling and Using MySQL Plugins
[ 12 ]
Also, you need to have Microsoft Visual Studio installed. There are free express
editions available from the Microsoft website, which we can use.
In Visual Studio we need to create a new empty project to put our source code into
and set up the build environment:
Then we need to add a source file to this project. We can either create a new .cpp file
or add an existing one to a project:
28. Chapter 1
[ 13 ]
Now we need to modify the project properties to set up everything required to
compile the UDF. To start with, inside the General configuration section, we need
to set the Configuration Type to a .dll file (a Windows dynamic link library):
Then in the C/C++ section we need to add the MySQL include path to Additional
Include Directories:
29. Compiling and Using MySQL Plugins
[ 14 ]
Finally, we need to create a definitions file that lists the functions from this library
which we wish to export for MySQL to use. It may look as follows:
EXPORTS
udf_hello_world
udf_hello_world_init
udf_hello_world_deinit
This is then added to the Linker configuration in the Input section under Module
Definition File. This gives a hand-typed dialog, so we need to type in the full path to
the definitions file we just created:
We can then compile our UDF and, if successful, we will have a brand new .dll file:
30. Chapter 1
[ 15 ]
Installing a UDF
Now that we have our UDF, we need to install it in the MySQL server. For security
reasons MySQL will only load plugins and UDFs from the location defined in the
plugin_dir system variable. This variable can only be set during the startup of the
MySQL server. By default it is in the lib/mysql/plugin subdirectory inside the
directory where MySQL is installed. So we need to put our UDF library there.
We can then tell MySQL to load the library using:
CREATE FUNCTION my_udf_function RETURNS STRING SONAME 'my_udf_function.so'
More details on how to use this syntax and how to solve UDF loading errors are in
the UDF chapter of this book.
Plugin libraries
Building and installing plugin libraries is very much like building and installing
UDFs. The include and library paths are the same but some further build options
are needed. This is slightly complicated by the fact that some plugin types (namely
Information Schema and Storage Engine plugins) require the MySQL source to be
downloaded for the version of the MySQL server you have installed. This is so that
the plugin can have access to data and functions that are only "half-public" and are
not declared in the installed C header files.
Linux
When compiling on Linux and using just the normal plugin API we can compile in
the same way as with UDFs:
gcc -omy_plugin.so my_plugin.c `mysql_config --cflags` -shared -fPIC
-DMYSQL_DYNAMIC_PLUGIN
Notice that the main difference here is -DMYSQL_DYNAMIC_PLUGIN. This sets up the
necessary environment for the plugin at compile time.
For plugins that require access to the MySQL server source, compiling is slightly
different (suppose, the MySQL source tree is in /Sources/mysql‑5.1.35):
gcc omy_plugin.so my_plugin.cc `mysql_config cflags`
—I/Sources/mysql 5.1.35/include/ I/Sources/mysql 5.1.35/regex
—I/Sources/mysql 5.1.35/sql shared fPIC fno exceptions
—fno rtti DMYSQL_DYNAMIC_PLUGIN
31. Compiling and Using MySQL Plugins
[ 16 ]
Typically, such a plugin will be in C++, not C. It is compiled exactly the same way
the main server is—without exceptions or runtime type identification. Technically,
it could use exceptions, but then it may need to use g++ instead of gcc as a C++
compiler. Either way, it needs extra include paths that point to the include/,
regex/, and sql/ directories of the MySQL source tree.
Mac OS X
Just as in the UDF case, compiling plugins on Mac OS X is almost the same as on
Linux. You can use the same command line and only replace ‑shared ‑fPIC with
‑bundle or –bundle ‑Wl, ‑undefined ‑Wl,dynamic_lookup as explained before.
Windows
In Windows we can compile MySQL plugins that do not require the inclusion of the
MySQL source code (everything except Information Schema and Storage Engine
plugins) using a process very similar to compiling UDFs.
First, we need to create an empty project file to contain the source and
build environment:
32. Chapter 1
[ 17 ]
We can then add or create a .cpp file containing the source for our plugin:
This project needs to be a .dll, not an executable one. We can set this in the project's
Property Pages dialog:
33. Compiling and Using MySQL Plugins
[ 18 ]
We now need to set up the C/C++ include paths so that the MySQL include path is
in them:
This final step is different to compiling the UDFs. We need to add a C/C++
preprocessor definition so that the include files set up everything we need
for a MySQL plugin. To do this we simply add MYSQL_DYNAMIC_PLUGIN to the
definitions list:
34. Chapter 1
[ 19 ]
Installing a plugin
Just as with UDFs, our MySQL plugin needs to be in plugin_dir before it can be
added to MySQL. Once it is located there the syntax is very simple. All of the details
about how to use the plugin are in the plugin itself. So we simply need:
INSTALL PLUGIN my_plugin SONAME 'my_plugin.so'
Automatic builds, packaging
Specifying all compiler options manually, as we did in a previous section, gets more
complicated as the number of files grows. When a plugin consists of more than a
couple of files, an appropriate Makefile becomes almost a requirement. And it is
absolutely unavoidable if we want to distribute our great plugin, as we cannot expect
our users to copy and paste complex command lines from a README file. We want the
process of configuring and building a plugin to be as simple as possible. But first we
need to decide whether a plugin should be built from inside the MySQL source tree
or standalone.
UDFs and standalone plugins
UDFs and certain plugin types (for example, full-text parser plugins, some Daemon
plugins, or newer plugin types added after MySQL 5.1) do not require MySQL
sources for building; the API for them is complete and self-sufficient. These plugins
can be distributed and built independently from MySQL. Writing a Makefile or
configure.ac for such a plugin does not differ from writing them for any other
project—we only need to set the installation path correctly. When using automake
and libtool, a simple Makefile.am can look like this:
plugindir= $(libdir)/mysql/plugin
plugin_LTLIBRARIES= my_plugin.la
my_plugin_la_SOURCES= my_plugin.c
my_plugin_la_LDFLAGS= -module -rpath $(plugindir)
my_plugin_la_CFLAGS= -DMYSQL_DYNAMIC_PLUGIN
35. Compiling and Using MySQL Plugins
[ 20 ]
This file sets the installation directory to be mysql/plugin/ inside the library
path, which is usually /usr/lib. However, strictly speaking, the user has to use
the same library path that his MySQL installation uses. It specifies the build target
to be my_plugin.la—it is a libtool control file, a text file with information about
my_plugin.so. The latter will be built automatically. It tells the libtool that we are
building a library for dynamic loading with dlopen() (the -module option does that)
and where it will be installed. The last line adds ‑DMYSQL_DYNAMIC_PLUGIN to the
compiler command line. There is no need to specify ‑fPIC, ‑shared, or ‑bundle;
libtool will use them automatically, depending on the platform we are building
on. It knows a large number of operating systems, compilers, linkers, and their
corresponding command-line switches for building dynamically loaded modules.
In addition to Makefile.am, a complete project will need a configure.ac file,
AUTHORS, NEWS, ChangeLog, and README files. The last four files are required by
automake, but they can be empty. The configure.ac file is used by autoconf to
generate a configure script, which, in turn, will generate Makefile. A minimal
configure.ac could be as simple as:
AC_INIT(my_plugin, 0.1)
AM_INIT_AUTOMAKE
AC_PROG_LIBTOOL
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
It sets the name and version of our software package, initializes automake and
libtool, and specifies that the result of the configure script should be a Makefile.
Plugins that are built from the MySQL source
tree
If we need to have access to the MySQL source tree for our plugin, we can at least
do it with style. Plugins that are built from the MySQL source tree can be integrated
seamlessly into the MySQL build system. Additionally, we will get support for
Microsoft Windows builds and the ability to link the plugin statically into the server,
so that it becomes a part of the mysqld binary. Unlike standalone plugins, we will
only need three auxiliary files here.
On UNIX-like systems, MySQL 5.1 is built using autotools and make. A plug.in
file will be the source file for autoconf, and Makefile.am for automake. To build
MySQL on Windows one needs CMake, and thus our plugin should come with a
CMakeLists.txt file. All of these three files can use the full power of autotools or
CMake, if necessary, but for a minimal working plugin they only need to contain a
few simple lines.
36. Chapter 1
[ 21 ]
plug.in
The plug.in file describes the plugin to the MySQL configure script. A plugin
is detected automatically by autoconf as long as its plug.in file can be found in a
directory located in the plugin/ or storage/ subdirectory in the MySQL source tree
(in other words, it should be either plugin/*/plug.in or storage/*/plug.in).
A plug.in file can use all autoconf and m4 macros as usual. Additionally, MySQL
defines a few macros specifically for using in the plug.in files. They all are
documented in the config/ac-macros/plugin.m4 file in the MySQL source tree.
The most important of them are described as follows:
• MYSQL_PLUGIN([name],[long name], [description],
[group,group...])
This is usually the first line in any plug.in file. This macro is
mandatory. It declares a new plugin. The name will be used
in the configure options such as ‑‑with‑plugin‑foo and
‑‑without‑plugin‑foo. The long name and the description
will be printed in the ./configure ‑‑help output. "Groups"
are preset configuration names that one can specify in the
‑‑with‑plugin=group option. Any group name can be used, but
max, max‑no‑ndb, and default are commonly used. Most plugins
add themselves to the max and max‑no‑ndb groups.
• MYSQL_PLUGIN_STATIC([name],[libmyplugin.a])
This macro declares that a plugin name supports static builds, that is,
it can be built as a static library and linked statically into the server
binary. It specifies the name of this static library, which can be later
referred to in Makefile.am as @plugin_myplugin_static_target@.
It will be expanded to libmyplugin.a if a static build is selected,
otherwise it will be empty.
• MYSQL_PLUGIN_DYNAMIC([name],[myplugin.la])
Similarly, this macro declares that a plugin can be built as a shared
library and loaded into the server dynamically. It introduces a
Makefile.am substitution @plugin_myplugin_dynamic_target@,
which is myplugin.la if this shared library needs to be built, and
empty otherwise.
• MYSQL_PLUGIN_ACTIONS([name],[ ACTION-IF-SELECTED ])
The ACTION‑IF‑SELECTED code will be executed only if this plugin
is selected by configure either for static or dynamic builds. Here we
can check for system headers, libraries, and functions that are used by
the plugin. Normal AC_ macros can be used here freely.
37. Compiling and Using MySQL Plugins
[ 22 ]
An example of a plug.in file can look like
MYSQL_PLUGIN(my_plugin,[My Plugin Example],
[An example of My Plugin], [max,max-no-ndb])
MYSQL_PLUGIN_STATIC(my_plugin,[libmy_plugin.a])
MYSQL_PLUGIN_DYNAMIC(my_plugin,[my_plugin.la])
With such a file in place, say in plugin/my_plugin/plug.in, all we need to do
is to run autoreconf ‑f to recreate the configure script. After that, there is no
distinction between our plugin and official MySQL plugins:
$ ./configure --help
`configure' configures this package to adapt to many kinds of systems.
...
--with-plugins=PLUGIN[[[,PLUGIN..]]]
Plugins to include in mysqld. (default is: none)
Must be a configuration name or a comma separated
list of plugins.
Available configurations are: none max max-no-ndb
all.
Available plugins are: partition daemon_example
ftexample archive blackhole csv example federated
heap ibmdb2i innobase innodb_plugin myisam
myisammrg my_plugin ndbcluster.
...
=== My Plugin Example ===
Plugin Name: my_plugin
Description: An example of My Plugin
Supports build: static and dynamic
Configurations: max, max-no-ndb
A new plugin is mentioned in the "available plugins" list and described in detail at
the end of the configure ‑‑help output.
Makefile.am
As in the case of standalone plugins, we need a Makefile.am file. It will be
converted by automake and the configure script to a Makefile, and it defines how
the plugin, static or shared library, should be built. This file is more complex than
for standalone plugins because it needs to cover both static and dynamic builds. Of
course, when a plugin supports only one way of linking, only static or only dynamic,
Makefile.am gets much simpler. Let's analyze it line by line:
38. Chapter 1
[ 23 ]
pkgplugindir = $(pkglibdir)/plugin
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include
-I$(top_srcdir)/sql
pkgplugin_LTLIBRARIES = @plugin_my_plugin_shared_target@
my_plugin_la_LDFLAGS = -module -rpath $(pkgplugindir)
my_plugin_la_CXXFLAGS= -DMYSQL_DYNAMIC_PLUGIN
my_plugin_la_SOURCES = my_plugin.c
noinst_LIBRARIES = @plugin_my_plugin_static_target@
libmy_plugin_a_SOURCES= my_plugin.c
EXTRA_LTLIBRARIES = my_plugin.la
EXTRA_LIBRARIES = libmy_plugin.a
EXTRA_DIST = CMakeLists.txt plug.in
The file starts with defining pkgplugindir—a place where a plugin will be installed.
Then we set the search path for the #include directives; it needs to contain at least
the include/ directory where most of the headers are, and often the sql/ directory
too, especially when we need to access internal server structures.
Now we can specify automake build rules for the targets. A shared target is a libtool
library (LTLIBRARIES) that should be installed in pkgplugindir (because we used
the pkgplugin_ prefix). And we specify the source files, compiler and linker flags
that are needed to build the my_plugin.la library. If a user decides not to build
a dynamic version of our plugin, @plugin_my_plugin_shared_target@ will be
empty and no libtool library will be built.
Similarly, we specify rules for the static target. It is a library (LIBRARIES), and
it should not be installed (noinst_). Indeed, as it will be linked into the server
statically, becoming a part of the server binary, there is no need to install it
separately. In this case, we do not need any special compiler or linker flags, we
only specify the sources.
Because a user may decide to build either a static or a shared library, the name of the
build target is not known before the configure script is run. However, automake
needs to know all possible targets in advance, and we list them in the EXTRA_ variables.
We end the file by listing all remaining files that are part of the plugin source
distribution, but are not mentioned in other rules. The automake needs to know
about them, otherwise the make dist command will work incorrectly.
39. Compiling and Using MySQL Plugins
[ 24 ]
CMakeLists.txt
In MySQL 5.1, of all plugin types, only Storage Engine plugins can be integrated into
the MySQL build system on Windows. One does this by providing an appropriate
CMakeLists.txt file. All of the CMake power is available there, but a minimal
CMakeLists.txt file is as simple as this:
INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake")
SET(my_plugin_SOURCES my_plugin.c)
MYSQL_STORAGE_ENGINE(my_plugin)
We only specify the name of the storage engine, my_plugin, and the source files, and
include the file that does all of the heavy job.
Summary
Using the information in this chapter we should be able to compile all of the UDFs
and plugins for this book as well as any others. We should be able to prepare all of
the auxiliary files for plugins to be built with configure && make, as a standalone
project or as a part of MySQL, either dynamically or statically. We can package them
for distributing in the source form that allows the user to build and install the
plugin easily.
40. User Defined Functions
Way back in 1998, MySQL 3.21 introduced a framework that allowed users to
create new SQL functions easily. It made it possible for developers to write their
own functions in C/C++ and load them dynamically into the running server. The
functions loaded within this framework were called User Defined Functions or UDFs.
Today not much has changed with UDFs, they are more stable and slightly more
secure than they used to be, and they can be declared aggregate for use together with
GROUP BY queries. However, many UDFs that worked in 1998 with MySQL 3.21.24
would still work at the time of writing in 2010, with MySQL 5.1.47.
MySQL UDFs are not part of the newer MySQL Plugin API, but there are future
plans to make this happen. In the meantime, they can serve as an introduction
to MySQL plugins. And sometimes UDFs can be used together with plugins to
complement their functionality. In this chapter, we will cover creating User Defined
Functions and write several of our own UDFs, working up from a basic static output
to an aggregate summing function.
Writing UDFs
UDFs can be of two types, normal and aggregate. Normal UDFs take inputs and
deliver an output just like an ordinary function in most programming languages.
When run on a set of rows, they will return a result for every row. Aggregate UDFs
take a group of rows, process each row, and produce a result at the end. In other
words, they will return one result per group. Therefore, aggregate functions are
useful for tasks such as adding up a group of values or calculating an average.
41. User Defined Functions
[ 26 ]
Whether MySQL considers a given UDF as an aggregate or a normal type depends
on how it was installed. However, the API is somewhat different too, and we need to
take care to install UDFs that use the aggregate API as aggregate and normal UDFs
as normal. Otherwise they will not only fail to work correctly, but may as well crash
the whole server.
Why write UDFs
There are several advantages and disadvantages to UDFs that we should be aware
of. UDFs are much easier to develop than is hacking raw code into the MySQL
server. If our function were hacked into the server, we would need to change the
MySQL source every time we upgraded, which is never easy. MySQL code base
evolves quite rapidly and to implement the same function we would need different
code changes in every new version. UDFs, on the other hand, continue to work when
the server is upgraded. They will not even need to be recompiled.
Also, UDFs have many benefits as compared to Stored Functions written in SQL. For
example, UDFs are typically much faster and can be declared aggregate, while stored
functions cannot. There are few alternatives to UDFs when custom aggregation
functionality is needed.
UDFs are designed for development speed, the API is easy to access, and compilation
is much quicker than rebuilding the entire server just to add a tiny function. They are
also designed for portability between different MySQL versions.
Although UDFs are much faster than SQL stored functions, they are slightly
slower in execution when compared to built-in functions. When running the
udf_floatsum() function from the end of this chapter on a table with 10,000,000
rows, I got:
Query Execution time
SELECT UDF_FLOATSUM(a) FROM t1; 1.40 seconds
SELECT SUM(a) FROM t1; 1.36 seconds
As we can see, on my system, MySQL 5.1.47 runs this query about 3% slower when
using UDFs compared to native functions.
42. Chapter 2
[ 27 ]
While UDFs are easier to develop, they offer little extra safety over raw MySQL
server hacking. If your UDF code crashes, it will take the MySQL server with it. This
is due to the UDF code literally getting bolted onto the server code during runtime.
Installing and using UDFs
To install a normal function in MySQL we can use the following command:
CREATE FUNCTION my_func RETURNS INTEGER SONAME 'udf_my_func.so';
Whereas with an aggregate function we do it like this:
CREATE AGGREGATE FUNCTION my_total_func RETURNS INTEGER
SONAME 'udf_my_total_func.so';
Each shared library can contain multiple UDFs inside, which is useful when
installing a group of related functions. If we have multiple UDFs in one shared
library, we need to perform CREATE FUNCTION for every UDF contained in it. In
Windows, dynamic libraries have the .dll extension, so instead this would be:
CREATE FUNCTION my_func RETURNS INTEGER SONAME 'udf_my_func.dll';
Once installed, the details of a UDF get stored in the mysql.func table, so that
MySQL can automatically load the UDFs back in upon server startup. Once a few
UDFs are installed, the table may look as follows:
mysql> SELECT * FROM mysql.func;
+---------------------+-----+--------------------+-----------+
| name | ret | dl | type |
+---------------------+-----+--------------------+-----------+
| udf_staticexample | 2 | udf_static.so | function |
| udf_intexample | 2 | udf_integer.so | function |
| udf_textexample | 0 | udf_textexample.so | function |
| udf_textexample2 | 0 | udf_textexample.so | function |
| udf_floatsum | 1 | udf_floatsum.so | aggregate |
+---------------------+-----+--------------------+-----------+
5 rows in set (0.00 sec)
If at any point in time you wish to uninstall a UDF, there is a DROP FUNCTION
statement in MySQL. This is run as follows:
mysql> DROP FUNCTION my_func;
43. User Defined Functions
[ 28 ]
When installed, a UDF can be used just like any native MySQL function, for example,
in a SELECT statement. A normal UDF will process and return a result for every row:
mysql> SELECT my_example(my_ints) FROM my_table;
+-----------------------+
| my_example(my_ints) |
+-----------------------+
| 99 |
| 27 |
+-----------------------+
2 rows in set (0.00 sec)
Whereas aggregate functions will return a single result for an entire group of rows:
mysql> SELECT my_aggregate_example(my_ints) FROM my_table;
+---------------------------------+
| my_aggregate_example(my_ints) |
+---------------------------------+
| 126 |
+---------------------------------+
1 row in set (0.00 sec)
An attempt to install a UDF may fail, resulting in an error. In most cases, the causes
are easy to find and normally not too difficult to resolve. A few of the common ones
are as follows:
ERROR 1126 (HY000): Can't open shared library 'my_udf.so' (errno: X )
This error means that there is a problem opening the UDF shared library or one of
its dependencies. There are two things to look out for here. Firstly, the filename in
Windows will always be the UDF dynamic library filename, regardless of which
library cannot be opened. However, in Linux/Unix based operating systems it will
be the name of the library that is causing the problem.
The second thing to look at is the errno part of the error message. In Linux/Unix
this will show a number indicating the error that has occurred. MySQL cannot yet
interpret Windows error codes with regard to opening UDFs, but there is work in
progress to make this happen.
44. Chapter 2
[ 29 ]
When it comes to error codes, MySQL comes with a very useful utility called perror.
Now, say we get this error:
ERROR 1126 (HY000): Can't open shared library 'my_udf.so' (errno: 2 )
If we run perror on error code 2 we should see:
shell$ perror 2
OS error code 2: No such file or directory
So we can say that the operating system has reported to MySQL that it cannot find
my_udf.so. MySQL only loads UDFs from the path stored in the plugin_dir server
configuration variable, that is, the UDF shared library needs to be there and it needs to
be readable by the operating system user that MySQL is run under (usually mysql).
If the error is for another shared library, then the operating system cannot find that
library in the library path. We can check this by running ldd on the UDF shared
library to see what the UDF depends on and what it cannot find. For example:
shell$ ldd my_udf.so
linux-vdso.so.1 => (0x00007ffff22c9000)
my_dependency.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fb6a91dc000)
/lib64/ld-linux-x86-64.so.2 (0x00000035d5c00000)
Here we can see that my_dependency.so is not found. There are three ways we can
resolve this:
1. Find my_dependency.so and copy it to a known library path
(such as /usr/lib/)
2. Alter the list of known library paths to include the directory where
my_dependency.so is located
3. Embed the full path to my_dependency.so in my_udf.so
For this second option in Linux, we can do this by adding this directory to the
/etc/ld.so.conf file and then running ldconfig to update the library path cache.
For the third option in Linux we can do it by adding ‑Wl,‑rpath ‑Wl,/path/to/
my_dependency.so to the gcc command line when linking my_udf.so.
45. User Defined Functions
[ 30 ]
As stated earlier, Windows handles things a bit differently. For example, we do not
know the exact filename that caused the problem or the failure error code. Luckily,
Windows has a utility called Process Monitor, which can help us to find this out.
With this utility we can see file open calls as they happen and it will show real errors
when failures happen. You can download it from http://technet.microsoft.
com/en-us/sysinternals/bb896645.aspx. In the following example we try to
install a UDF called my_udf from my_udf.dll. As can be seen here, we get an error
because the file cannot be opened, but the error code is not very helpful. To resolve
the problem we will take a look at the file monitor inside the Process Monitor
window to see what has actually happened:
In the following screenshot there is a filter on the mysqld.exe process and with this
we can see that the file, my_udf.dll, was found and was perfectly readable. The
actual problem comes from a dependency library, libxml2.dll. Windows searches
for it in the predefined library paths, but fails. To fix this problem we simply need to
put libxml2.dll into one of the Windows library paths:
46. Chapter 2
[ 31 ]
There are other common errors we can get when installing UDFs, but they are
normally easier to diagnose:
ERROR 1127 (HY000): Can't find symbol 'my_udf' in library
This error shows that MySQL has loaded the library, but while searching it for the
functions inside, it could not find what it was expecting. This is usually due to the
wrong name for the UDF used in the CREATE FUNCTION command.
ERROR 1044 (42000): Access denied for user 'username'@'localhost' to
database 'mysql'
When installing a UDF, MySQL needs to insert an entry into the func table of the
mysql database because MySQL remembers all installed UDFs to be able to load
them automatically when restarted. To fix this permission error, we need to install
our UDF as a user who has INSERT privileges to the mysql database.
ERROR 1123 (HY000): Can't initialize function 'my_udf'; UDFs are
unavailable with the --skip-grant-tables option
With the --skip-grant-tables option, MySQL server ignores the privileges table,
including the func table required to install UDFs. Without access to this table, UDFs
are disabled. Simply restarting MySQL without this option or issuing the FLUSH
PRIVILEGES statement will let us install and use UDFs again.
ERROR 1146 (42S02): Table 'mysql.func' doesn't exist
Somehow the func table has been dropped, and without it we cannot install UDFs.
MySQL comes packaged with a utility called mysql_upgrade, which will create the
missing table for us.
ERROR 145 (HY000): Table './mysql/func' is marked as crashed and should
be repaired
The func table is a MyISAM table, and as such is not crash-resilient. We could
expect to see this error if the MySQL server crashes when installing or removing
a UDF. To fix this, follow the usual MyISAM repair instructions as found in the
MySQL manual.
Defining UDFs
All functions in the UDF API start with the name of the UDF itself. For example,
if the UDF is called name (that is, installed with CREATE FUNCTION name and
used like SELECT name()) the corresponding C functions may be called: name(),
name_init(), name_deinit(), and so on. The following table lists all UDF API
functions and their purpose:
47. User Defined Functions
[ 32 ]
Function Normal / Aggregate Description
name_init() Both Initialize the UDF for use. It is called for every
statement that uses the UDF name.
name_deinit() Both De-initialize the function and clean up memory
usage. It is called after the statement that used
the UDF name.
name() Both The main body of the UDF name, the function
that returns a result to the user. It is called for
every row (or for every group of rows if the
function is declared aggregate).
name_add() Aggregate Called for every row of the group.
name_clear() Aggregate Called before the first row of every group.
name_reset() Aggregate. Unused
since MySQL 4.1.1
Called for the first row of every group. This
function was doing the job of both name_clear
and name_add for the first row—starting a
new group and processing the first row in this
group. It was removed from the API in MySQL
4.1.1 and replaced with name_clear, because
it could not handle the case of the empty group.
However, you may see it in old UDF examples.
When calling a UDF inside MySQL, firstly name_init() is called with the
following prototype:
my_bool name_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)
Pay attention to the difference between UDF arguments, which are
arguments passed to the User Defined Function from the SQL (for
example, 1, 2, and "foo" in SELECT name(1,2,"foo")), and
arguments of C functions such as name_init(), name(), and others.
This function should be used to check the metadata of the UDF arguments, such as
their types, and prepare the UDF for execution, for example, allocate the memory.
Typically, for MySQL, the name_init() function should return 0 for success or 1 for
failure. In the latter case, an error message should be written into the buffer pointed
to by message, so that the user could see what went wrong. Two other arguments
of this function are args, which contains the metadata of the UDF arguments, and
initid, which is used to return the metadata of the result (see? argument metadata
are already known here, and MySQL wants to know the result metadata. The way
MySQL works, it needs to know all metadata before it starts executing the query,
that is, before it starts working with the data) and to preserve the internal UDF
context between the calls.
48. Chapter 2
[ 33 ]
A pointer to the UDF_INIT structure, initid, is passed as a first argument to all
C functions that implement the UDF, and it can be used to store the context of the
execution and share it between the different function calls. Typically, we would
allocate some memory for use in the UDF and store a pointer to it inside the
UDF_INIT structure. The second purpose of UDF_INIT is to allow us to tell MySQL
about the metadata of the UDF result. This structure has the following members:
Member Type Description
const_item my_bool Set to 1 if the UDF will always return the same result
in this query.
decimals unsigned int Number of digits after decimal point (this is often
called scale) in the UDF result. Setting it only makes
sense when the UDF is declared as RETURNS REAL.
max_length unsigned long The maximum length of the UDF return result
(length of its string representation if the result
is numeric).
maybe_null my_bool Set to 1 if the UDF can return NULL in this query.
ptr char * A free-to-use pointer. Not used by the server.
Reserved for internal UDF use.
• const_item: It is set by MySQL to 1 if all UDF arguments are constants, and
to 0 otherwise, which is a reasonable default for a well-behaving function
without side effects. Thus, we only need to set it to 1 if the UDF is a true
constant and always returns the same value independent of the arguments.
Similarly, we need to set const_item to 0 if the function is truly volatile, and
may return different results even if called with exactly the same arguments.
• decimals: It is set by MySQL to the largest scale of the UDF arguments.
The supported values are from 0 to 30. Anything larger than that (MySQL
internally uses 31 here) means that no scale was specified, and MySQL will
not limit the number of digits after a decimal point in the UDF return result.
• max_length: Its default value is set depending on the result type. For the
INTEGER return type it is 21, for the REAL return type it is usually 17 plus the
value in decimals, and for the STRING return type it is the largest value of
max_length of all UDF arguments.
• maybe_null: It is set to 0 by default unless any of the arguments may be
NULL, which usually works well for functions such as CONCAT() or FORMAT().
If the UDF can never return NULL (such as the ISNULL() function) this should
be set to 0. If the UDF can return NULL even when all arguments are not
NULL (such as the NULLIF() function) this should be set to 1.
49. User Defined Functions
[ 34 ]
• ptr: This is a char* pointer that MySQL never uses for anything. We can
store there any data we want. initid is passed to every function in the
UDF API, and if we put any value in initid->ptr in, say name_init(), we
will be able to use it in name(), name_add() or any other function. In other
words, this is an easy way to keep the execution context (such as allocated
memory buffer) around and preserve it between function calls. Unlike using
a global variable, if our UDF is invoked from many SQL statements in many
connections in parallel, every such invocation will have its own initid and
thus its own initid->ptr.
The second argument of the name_init() function is a pointer to the UDF_ARGS
structure. It contains the metadata of the arguments that have been passed from
MySQL to the UDF. It has pointers to the arguments' values too, but only the values
of the constant arguments are filled in at the time when name_init() is called.
In SELECT name(5, t1.c1) the value of 5 is, naturally, known, but the value of
column c1 in the table t1 is not. Later, when the same UDF_ARGS structure is passed
to the name() or name_add() functions all argument values will be filled in, so that
the function can compute the UDF return result. The UDF_ARGS structure has the
following members:
Member Type Description
arg_count unsigned int The number of arguments passed to
the UDF. All arrays below will be of
this length.
arg_type enum Item_result* An array of types of the arguments.
args char** An array of pointers to arguments'
values.
lengths unsigned long* An array of lengths of each
argument value.
maybe_null char* An array indicating whether each
argument can be NULL in this query.
attributes char** An array of names of the arguments.
attribute_lengths unsigned long* An array of the lengths of each
argument name
• arg_count: It should be used as a count for the number of array elements in
the rest of the members of the structure.
50. Chapter 2
[ 35 ]
• arg_type: This array contains the type of every argument passed to the
UDF. This can be one of STRING_RESULT, INT_RESULT, REAL_RESULT,
and DECIMAL_RESULT.
• args array: It contains pointers to the actual argument data. It is either a
char* pointer for the STRING_RESULT or DECIMAL_RESULT arguments,
a long long* for INT_RESULT, a double* for REAL_RESULT, or a null
pointer if the argument value is NULL or unknown. Note that string
values are not zero terminated!
• lengths: This array contains the length of each argument value in the args
member. This is most useful for string arguments. For integer and real types
it contains the maximum length of the argument.
• maybe_null: This array indicates whether or not each argument could
be NULL.
• attributes: This array contains names of each argument. This is either an
appropriate part of the SQL statement that corresponds to this particular
UDF argument, or an alias name, if it was specified. For example, for SELECT
name(1+log(2e3), 15 as bar) the name of the first argument will be
"1+log(2e3)" and the name of the second one will be "bar".
• attribute_lengths: It holds the length of each attribute name. Just as with
string attribute values, attribute name strings are not zero terminated.
The name_deinit() function should be used to clean up after the UDF execution.
Often this only entails freeing up any memory pointed to by initid->ptr. The
de-initialization function is called with the following prototype:
void name_deinit(UDF_INIT *initid)
This is the only function inside a UDF that cannot
return an error condition.
The function that does all the work for normal UDFs and the result function for
aggregate UDFs is the name() function.
51. User Defined Functions
[ 36 ]
We can see in the CREATE FUNCTION calls that a return type of the UDF is specified
when loading it into the server. This type should match the function prototype
chosen when writing the code of the UDF (or bad things could happen). The
following table outlines the different return types and the equivalent function
prototypes needed in C:
Type C function
STRING or DECIMAL char *name(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error)
INTEGER long long name(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error)
REAL double name(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
There is no check to see if the type in CREATE FUNCTION
matches the prototype. A user must ensure the type is correct
when installing a UDF or it may not function as desired.
With the STRING type we get a result buffer where we can store a UDF return
value, but it is only 766 bytes long. If a result longer than that is required we need
to use malloc() to allocate memory for it. As a UDF can return a binary string with
null bytes in it, we should always store the length of the returned string in *length,
so that MySQL knows how long the result should be.
Irrespective of the return type, all variants of the name() function get is_null and
error arguments. The *is_null should be set to 1 if we want our UDF to return
NULL in this particular invocation, and *error should be set to 1 if an error has
occurred during execution.
With aggregate functions MySQL repeatedly calls two additional functions,
name_add() and name_clear(). These are designed to perform one calculation
per row, while a result is returned only once per group of rows. For every row
in a group, MySQL calls name_add() and before each group starts, MySQL calls
name_clear(). These functions are declared as follows:
void name_add(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error)
void name_clear(UDF_INIT *initid, char *is_null, char *error)
52. Chapter 2
[ 37 ]
Execution sequence of a UDF
With a normal function MySQL simply calls three functions in sequence upon every
SQL statement that uses the UDF. With aggregate UDFs there are five functions to
call and rows and groups of rows to loop over. This creates the following execution
flow for each UDF type:
UDF error handling
Ideally most, if not all, possible error conditions should be detected in the
name_init() function of the UDF. In case of an error, this function needs to write a
zero-terminated error message string in the message buffer and return 1 to indicate
a failure. Error messages must be less than MYSQL_ERRMSG_SIZE bytes long (which
means less than 512 bytes long in MySQL 5.1) and should preferably be less than 80
characters to fit nicely within a terminal screen.
There is no error message facility in name(), name_add(), or name_clear()
functions; all you can do is set the *error argument to 1. This will return NULL
and if the UDF is to be run on subsequent rows, all further invocations will return
NULL too (in fact, the UDF will not be invoked for these rows).
53. User Defined Functions
[ 38 ]
UDF security
UDFs must be placed in the directory stored in the plugin_dir MySQL
configuration variable. Usually this is lib/mysql/plugin/ or lib/plugin/ relative
to the directory where MySQL is installed. We can find out where this is on our
particular installation by running:
mysql> SHOW VARIABLES LIKE 'plugin_dir';
+---------------+-----------------------------+
| Variable_name | Value |
+---------------+-----------------------------+
| plugin_dir | /opt/mysql/lib/mysql/plugin |
+---------------+-----------------------------+
1 row in set (0.00 sec)
When using MySQL 5.1 any attempt to explicitly specify a path in the CREATE
FUNCTION statement will result in an error.
Both the name_init() and name_deinit() functions are optional and can be omitted
if empty. However, at least one of the functions besides name(), that is name_init(),
name_deinit(), name_add(), or name_clear(), must be present in the UDF shared
library to reduce the chance of loading as a UDF something that was not meant to
be (tricks such as CREATE FUNCTION exit SONAME 'libc.so' were possible in the
past). It is possible to disable this protection by using the ‑‑allow‑suspicious‑udfs
command-line option, but this is not recommended.
Gotchas with UDFs
UDFs are a great way to add functionality, with more flexibility than MySQL Stored
Procedures. They do, however, have drawbacks that developers should be wary of:
• The mysql.func table needs to exist. If it does not, running the
mysql_upgrade command-line utility will create it for you.
• Running the MySQL server with the ‑‑skip‑grant‑tables option
disables UDFs.
• Imagine a UDF as a code that is part of the server. As such, if your UDF
crashes, for example with a Segmentation Fault, it will take out the MySQL
server with it.
• All functions in the UDF must be thread-safe. It is possible that the UDF
could be called multiple times simultaneously from different threads. You
need to keep this in mind when using global or static variables. Protect them
from concurrent modifications with mutexes, or avoid them altogether.
Allocate what you need in name_init() and free it in name_deinit().
54. Chapter 2
[ 39 ]
• The name() function cannot generate error messages. Hence, you must do all
your error checking in name_init().
• You need to have INSERT privileges on the MySQL database to be able to
install UDFs (and DELETE privileges to uninstall them).
• MySQL UDFs can only be installed from the plugin_dir path as described
in the UDF security section of this chapter.
A constant integer output UDF
To show the basic construction of a UDF we will start with a simple constant
function that returns an integer. The output of this UDF will be the same no matter
what arguments are passed to the UDF. To make the example a little more complex
and to demonstrate the initid->ptr usage we will allocate a small amount of
memory upon initialization to store our integer. This example will be called
udf_staticexample:
#include <stdlib.h>
#include <string.h>
#include <mysql.h>
These are the standard includes needed for this example. The header mysql.h
contains the structures and constants that we will use.
my_bool udf_staticexample_init(UDF_INIT *initid,
UDF_ARGS *args, char *message)
{
We are calling this UDF udf_staticexample so all functions need to be prefixed
with this. We start with the udf_staticexample_init() function, which as we have
seen before, prepares the UDF for execution in the context of an SQL statement.
long long *staticint = malloc(sizeof(long long));
if (staticint == NULL)
{
strcpy(message, "staticexample() allocation error");
return 1;
}
Here we declare a pointer to a type long long and allocate memory for it. If the
allocation fails, we put an error message into the message buffer and return 1 to
indicate that an error has occurred. This error will then be passed on to the client.
*staticint = 318749;
initid->ptr = (char*) staticint;
56. ANECDOTE OF A DOG.
The Lyons diligence was just going to start from Geneva. I climbed
on the roof, and chose my place next the postillion: there was still a
vacant seat, and the porter, after closing the door of the coupé,
called "Monsieur Dermann!" A tall young man, with a German style
of countenance, advanced, holding in his arms a large black
grayhound, which he vainly tried to place on the roof.
"Monsieur," said he, addressing me, "will you have the kindness to
take my dog?"
Bending over, I took hold of the animal, and placed him on the straw
at my feet. I observed that he wore a handsome silver collar, on
which the following words were tastefully engraved: "Bevis—I belong
to Sir Arthur Burnley, given him by Miss Clary."
His owner was, therefore, an Englishman; yet my fellow-traveler,
who had now taken his place by my side, was evidently either a
Swiss or a German, and his name was Dermann. Trifling as was the
mystery, it excited my curiosity, and, after two or three hours'
pleasant conversation had established a sort of intimacy between us,
I ventured to ask my companion for an explanation.
"It does not surprise me," he answered, "that this collar should
puzzle you; and I shall have great pleasure in telling you the story of
its wearer. Bevis belongs to me, but it is not many years since he
owned another master whose name is on his collar. You will see why
he still wears it. Here Bevis! speak to this gentleman."
The dog raised his head, opened his bright eyes, and laying back his
long ears, uttered a sound which might well pass for a salutation.
57. M. Dermann placed the animal's head on his knees, and began to
unfasten the collar.
Instantly Bevis drew back his head with a violent jerk, and darted
toward the luggage on the hinder part of the roof. There, growling
fiercely, he lay down, while his muscles were stiffened, and his eyes
glowing with fury.
"You see, Monsieur, how determined he is to guard his collar; I
should not like to be the man who would try to rob him of it. Here,
Bevis!" said he, in a soft, caressing tone, "I won't touch it again,
poor fellow! Come and make friends!"
The grayhound hesitated, still growling. At length he returned slowly
toward his master, and began to lick his hands; his muscles
gradually relaxed, and he trembled like a leaf.
"There, boy, there," said M. Dermann, caressing him. "We won't do
it again, lie down now, and be quiet."
The dog nestled between his master's feet, and went to sleep. My
fellow-traveler then turning toward me, began:
"I am a native of Suabia, but I live in a little village of the Sherland,
at the foot of the Grimsel. My father keeps an inn for the reception
of travelers going to St. Gothard.
"About two years since, there arrived at our house one evening a
young Englishman, with a pale, sad countenance; he traveled on
foot, and was followed by a large grayhound, this Bevis, whom you
see. He declined taking any refreshment, and asked to be shown to
his sleeping-room. We gave him one over the common hall, where
we were all seated round the fire. Presently we heard him pacing
rapidly up and down; from time to time uttering broken words,
addressed no doubt to his dog, for the animal moaned occasionally
as if replying to, and sympathizing with his master. At length we
heard the Englishman stop, and apparently strike the dog a violent
blow, for the poor beast gave a loud howl of agony, and seemed as
58. if he ran to take refuge under the bed. Then his master groaned
aloud. Soon afterward he lay down, and all was quiet for the night.
Early next morning he came down, looking still more pale than on
the previous evening, and having paid for his lodging, he took his
knapsack and resumed his journey, followed by the grayhound, who
had eaten nothing since their arrival, and whose master seemed to
take no further notice of him, than to frown when the creature
ventured to caress him.
"About noon, I happened to be standing at the door looking toward
the direction which the Englishman had taken when I perceived a
dark object moving slowly along. Presently I heard howls of distress,
proceeding from a wounded dog that was dragging himself toward
me. I ran to him, and recognized the Englishman's grayhound. His
head was torn, evidently by a bullet, and one of his paws broken. I
raised him in my arms, and carried him into the house. When I
crossed the threshold he made evident efforts to escape; so I placed
him on the ground. Then, in spite of the torture he was suffering,
which caused him to stagger every moment, he dragged himself up-
stairs, and began to scratch at the door of the room where his
master had slept, moaning at the same time so piteously, that I
could scarce help weeping myself. I opened the door and with a
great effort he got into the room, looked about, and not finding
whom he sought he fell down motionless.
"I called my father, and, perceiving that the dog was not dead, we
gave him all possible assistance, taking indeed as much care of him
as though he had been a child, so much did we feel for him. In two
months he was cured, and showed us much affection; we found it,
however, impossible to take off his collar, even for the purpose of
binding up his wounds. As soon as he was able to walk, he would
often go toward the mountain and be absent for hours. The second
time this occurred we followed him. He proceeded as far as a part of
the road where a narrow defile borders a precipice; there he
continued for a long time, smelling and scratching about. We
conjectured that the Englishman might have been attacked by
59. robbers on this spot, and his dog wounded in defending him.
However, no event of the kind had occurred in the country, and,
after the strictest search, no corpse was discovered. Recollecting,
therefore, the manner in which the traveler had treated his dog, I
came to the conclusion that he had tried to kill the faithful creature.
But wherefore? This was a mystery which I could not solve.
"Bevis remained with us, testifying the utmost gratitude for our
kindness. His intelligence and good-humor attracted the strangers
who frequented our inn, while the inscription on his collar, and the
tale we had to tell of him, failed not to excite their curiosity.
"One morning in autumn, I had been out to take a walk,
accompanied by Bevis. When I returned, I found seated by the fire,
in the common-hall, a newly-arrived traveler, who looked round as I
entered. As soon as he perceived Bevis, he started and called him.
The dog immediately darted toward him with frantic demonstrations
of joy. He ran round him, smelling his clothes and uttering the sort
of salutation with which he honored you just now, and finally placing
his fore-paws on the traveler's knees began to lick his face.
"'Where is your master, Bevis? Where is Sir Arthur?' said the
stranger, in English.
"The noble dog howled piteously, and lay down at the traveler's feet.
Then the latter begged us to explain his presence. I did so; and as
he listened, I saw a tear fall on the beautiful head of the grayhound,
whom he bent over to caress.
"'Monsieur,' said he, addressing me, 'from what you tell me, I
venture to hope that Sir Arthur still lives. We have been friends from
childhood. About three years since, he married a rich heiress, and
this dog was presented to him by her. Bevis was highly cherished for
his fidelity, a quality which unhappily was not possessed by his
mistress. She left her fond and loving husband, and eloped with
another man. Sir Arthur sued for a divorce and obtained it; then,
having arranged his affairs in England, he set out for the Continent,
60. followed only by his dog. His friends knew not whither he went; but
it now appears that he was here last spring. Doubtless, the presence
of Bevis, evermore recalling the memory of her who had so cruelly
wronged him, must have torn his heart, and at length impelled him
to destroy the faithful creature. But the shot not having been mortal,
the dog, I imagine, when he recovered consciousness, was led by
instinct to seek the house where his master had last slept. Now,
Monsieur, he is yours, and I heartily thank you for the kindness you
have shown him.'
"About ten o'clock the stranger retired to his room, after having
caressed Bevis, who escorted him to his door, and then returned to
his accustomed place before the fire. My parents and the servants
had retired to rest, and I prepared to follow their example, my bed
being placed at one end of the common-hall. While I was
undressing, I heard a storm rising in the mountains. Just then there
came a knocking at the door, and Bevis began to growl. I asked who
was there? A voice replied—'Two travelers, who want a night's
lodging.' I opened a small chink of the door to look out, and
perceived two ragged men, each leaning on a large club. I did not
like their look, and knowing that several robberies had been
committed in the neighborhood, I refused them admission, telling
them that in the next village they would readily find shelter. They
approached the door, as though they meant to force their way in;
but Bevis made his voice heard in so formidable a manner, that they
judged it prudent to retire. I bolted the door and went to bed. Bevis,
according to his custom, lay down near the threshold, but we neither
of us felt inclined to sleep.
"A quarter of an hour passed, when suddenly, above the wailing of
the wind, came the loud shrill cry of a human being in distress. Bevis
rushed against the door with a fearful howl; at the same moment
came the report of a gun, followed by another cry. Two minutes
afterward I was on the road, armed with a carbine, and holding a
dark lantern; my father and the stranger, also armed, accompanied
me. As for Bevis, he had darted out of the house, and disappeared.
61. "We approached the defile which I mentioned before, at the moment
when a flash of lightning illumined the scene. A hundred yards in
advance, we saw Bevis grasping a man by the throat. We hurried on,
but the dog had completed his work ere we reached him; for two
men, whom I recognized as those who had sought admittance at our
inn, lay dead, strangled by his powerful jaws. Farther on, we
discovered another man, whose bloody wounds the noble dog was
licking. The stranger approached him, and gave a convulsive cry: it
was Sir Arthur, the master of Bevis!"
Here M. Dermann paused; the recollection seemed to overcome him;
and he stooped to caress the sleeping grayhound, in order to hide
his emotion. After awhile, he finished his recital in a few words.
"Sir Arthur was mortally wounded, but he lived long enough to
recognize his dog, and to confess that, in a moment of desperation,
he had tried to kill the faithful creature, who now avenged his death,
by slaying the robbers who attacked him. He appointed the stranger
his executor, and settled a large pension on Bevis, to revert to the
family of the inn-keeper, wishing thus to testify his repentant love
toward his dog, and his gratitude to those who had succored him.
"The grief of Bevis was excessive; he watched by his master's couch,
covering his dead body with caresses, and for a long time lay
stretched on his grave, refusing to take nourishment; and it was not
until after the lapse of many months that the affection of his new
master seemed to console him for the death of Sir Arthur."
As my fellow-traveler finished his recital, the diligence stopped to
change horses at the little town of Mantua. Here M. Dermann's
journey ended, and having taken down his luggage, he asked me to
assist the descent of his dog. I shook hands with him cordially, and
then called Bevis, who, seeing me on such good terms with his
master, placed his large paws on my breast, and uttered a low,
friendly bark. Shortly afterward they both disappeared from my
sight, but not from my memory, as this little narrative has proved to
my readers.
63. THE DOMESTIC LIFE OF
ALEXANDER, EMPEROR OF RUSSIA.
BY ALEXANDRE DUMAS, TRANSLATED BY MISS
STRICKLAND.
The tragedy of which Paul I. was the victim, called Alexander to the
throne of all the Russias in the twenty-fourth year of his age. He had
been carefully educated under the eye of his grandmother, the able
Catharine. Her choice of a preceptor in La Harpe, a Swiss republican,
who had fraternized with the revolutionists of France, was a problem
the sovereigns of Europe could not solve; but after all, republicanism
can not be very far removed from despotism, if we may judge from
its consequences, since history shows us that republics end in
despotic sovereignties. Catharine was doubtless aware of this fact
when she gave La Harpe the direction of her grandson's education.
It was prudent to avoid Russian ascendency in a matter so important
to herself, for Catharine was a foreigner and a usurper, a fact of
which a native instructor might have availed himself to her
disadvantage. In educating her grandsons, the great empress
excluded the fine arts. She wished to make them rulers, not
professors of music and painting; and she was right; La Harpe
inspired, it is said, his imperial pupil with lessons of generosity and
truth it was no easy task to eradicate during his eventful life. The
policy of Catharine made her determine to give wives to her
grandsons as soon as they were marriageable. Her jealousy, or her
profound judgment, made her overlook Paul in the succession of
Russia, by a mental but not a public exclusion. Alexander was
destined by her to the throne of which she had robbed his father
Constantine, she proudly hoped to place on one she designed to win
from the Sultan, an ambitious desire which was never realized.
64. Three German princesses came to the court of St. Petersburg, in
order that Catharine might make choice of suitable brides for her
grandsons. The empress thoughtfully expected the arrival of her
guests, whose approach she watched from a window of her palace.
The empress, whose motions were dignified and graceful, attached
great importance to deportment; she formed her opinions of young
people by that standard. The destinies of these princesses were
decided the instant they alighted from their traveling carriage. The
first leaped down without availing herself of the step. The empress
shook her head, "She will never be empress of Russia, she is too
precipitate," was her internal remark. The second entangled her feet
in her dress, and with difficulty escaped a fall. "She is not the
empress, for she is too awkward," and Catharine again turned her
eyes on the carriage with anxious curiosity. The third princess
descended very gracefully; she was beautiful, majestic, and grave.
"Behold the future Empress of Russia," said Catharine. This princess
was Louisa of Baden.
Catharine introduced these ladies to her grandsons, as the children
of the Duchess of Baden-Durlack, born Princess of Darmstadt, her
early friend, whose education she wished to finish at her court, since
the possession of their country by the French had left them without
a home. The great dukes saw through this artifice, and upon their
return to their own palace talked much of Catharine's élèves.
"I think the eldest very pretty," said Alexander.
"For my part," rejoined Constantine, "I consider them neither pretty
nor plain. They ought to be sent to Riga to the princes of Courland;
they are really quite good enough for them."
The Empress Catharine was informed, that very day, of the opinion
of her grandsons. The admiration of Alexander for Louisa of Baden
sympathized with her intentions. The Grand Duke Constantine had
done the personal attractions of this young princess great injustice,
for Louisa of Baden, besides the freshness of her youth, had lovely
65. fair ringlets, hanging in rich profusion on her magnificent shoulders,
a form light and flexible as that of a fairy, and large blue eyes full of
sweetness and sensibility. The following day, the empress brought
the princesses to the palace of Prince Potemkin, which she had
appointed for their residence. While they were at their toilet, she
sent them dresses, jewels, and the cordon of St. Catharine. After
chatting with them upon the topics she considered suitable to their
age, she asked to see their wardrobe, which she examined, article
by article, with interest and curiosity. Having finished her scrutiny,
she kissed the princesses, and remarked, with an emphatic smile,
"My friends, I was not so rich as you when I came to St.
Petersburg." In fact, Catharine was very poor when she arrived in
Russia, but she left her adopted country a heritage in Poland and the
Crimea.
The predilection of Alexander for Louisa of Baden was responded to
by that lovely princess. The grand duke at that time was a charming
young man, full of benevolence and candor, with the best temper in
the world, and the young German did not attempt to disguise her
tenderness for him. Catharine, in announcing to them that they were
destined for each other, believed she was rendering them perfectly
happy.
The behavior of the bride was admirably adapted to the
circumstances in which she was placed. She acquired the Russian
language with grace and facility, and accepted a new name with the
tenets of the Greek religion. She received those of Elizabeth
Alexiowena, the same borne by the imperial daughter of Peter the
Great.
Notwithstanding the fortunate presages of the Empress Catharine,
this early marriage was not one of happiness. The inconstancy of
Alexander, indeed, withered the nuptial garland while yet green on
the brow of the bride, and made it for her a crown of thorns.
66. The tragedy that elevated Alexander to the throne, restored to the
devoted wife the wandering affections of her husband. His profound
grief made her sympathy necessary to him, and the young empress,
almost a stranger to Paul, wept for him like a true daughter. The
secret tears of Alexander were shed at night on the bosom of his
consort, whose tender concern for him consoled him for the restraint
he imposed upon his feelings during the day.
The regretful remembrance of Alexander for his father, outlasted the
reviving affection he had during that dolorous period felt for his wife.
The empress, still a young woman, was an old spouse, and the
emperor had inherited the passionate and inconstant temperament
of Catharine. But, gracious and smiling as he always was with the
ladies, or polite and friendly to the gentlemen, there crossed his
brow from time to time a gloomy shadow, the mute but terrible
memorial of that dreadful night, when he heard the death struggle
of his father, and was conscious of his agony without the power to
save him. His perpetual smile was the mask beneath which he
disguised the anguish of his mind, and as he advanced in life, this
profound melancholy threatened to deepen into malady. He did not
yield, however, without maintaining a warfare with his remorse. He
combated memory with action. His reforms, his long and laborious
journeys, had but one aim. In the course of his reign, he is supposed
to have traversed fifty thousand leagues. But, however rapidly he
performed these journeys, he never deviated from the time he fixed
for his setting off or return, even by an hour, and he undertook them
without guards and without an escort. He, of course, met with many
strange adventures, and was amused with rendering his personal
assistance whenever he met with accidents or encountered
difficulties by the wayside. In his journey to Finland in company with
Prince Pierre Volkouski, the imperial carriage in traversing a sandy
mountain rolled back, notwithstanding the efforts of the coachman,
upon which the emperor jumped out, and literally lent his shoulder
to the wheel, leaving his companion asleep.
67. The rough motion of the carriage disturbed the slumbers of the
prince, who found himself at the bottom of the carriage and alone.
He looked about him with astonishment, when he perceived the
emperor, with his brow bedewed with perspiration, from the effects
of his toil in assisting to drag him and the vehicle to the top of the
mountain, the precise point at which he had awakened from his
sleep.
At another time, while traversing Little Russia, while the horses were
changing at a certain station, the emperor expressed his
determination to travel on foot for a few miles, ordering his people
not to hasten their arrangements, but to let him walk forward.
Alone, with no mark of distinction, dressed in a military great-coat,
that gave no clew to the rank of the wearer, the emperor traversed
the town without attracting attention, till he arrived at two roads,
and found himself obliged to inquire his way of an individual who
was sitting before the door of the last house smoking a pipe. This
personage, like the emperor wore a military great-coat, and by his
pompous air seemed to entertain no small opinion of his own
consequence.
"My friend, can you tell me which of these roads will bring me to
——?" asked the emperor.
The man of the pipe scanned him from head to foot, apparently
surprised at the presumption of a pedestrian, in speaking to such a
dignitary as himself, and between two puffs of smoke he growled
out very disdainfully the ungracious reply, "The right."
"Thank you, sir," said the emperor, raising his hat with the respect
this uncivil personage seemed by his manner to command. "Will you
permit me to ask you another question?"
"What do you want to know?"
"Your rank in the army, if you please."
"Guess," returned he of the pipe.
68. "Lieutenant, perhaps?"
"Go higher."
"Captain?" rejoined the emperor.
"Much higher;" and the smoker gave a consequential puff.
"Major, I presume?"
"Go on," replied the officer.
"Lieutenant-colonel?"
"Yes, you have guessed it at last, but you have taken some trouble
to discover my rank."
The low bow of the emperor made the man with the pipe conclude
he was speaking to an inferior, so, without much ceremony, he said,
"Pray, who are you? for I conclude you are in the army."
"Guess," replied the emperor, much amused with the adventure.
"Lieutenant?"
"Go on."
"Captain?"
"Much higher."
"Major?"
"You must still go on."
"Lieutenant-colonel?"
"You have not yet arrived at my rank in the army."
The officer took his pipe out of his mouth. "Colonel, I presume."
"You have not yet reached my grade."
69. The officer assumed a more respectful attitude. "Your Excellency is
then Lieutenant-general?"
"You are getting nearer the mark."
The puzzled lieutenant-colonel kept his helmet in his hand, and
looked stupid and alarmed.
"Then it appears to me that your Highness is Field-Marshal?"
"Make another attempt, and perhaps you will discover my real
position."
"His Imperial Majesty!" exclaimed the officer, trembling with
apprehension, and dropping the pipe upon the ground, which was
broken into twenty pieces.
"The same, at your service," replied the emperor, laughing.
The poor lieutenant-colonel dropped upon his knees, uttering the
words in a pitiful tone, "Ah! sire, pardon me."
"What pardon do you require?" replied the emperor. "I asked my
way of you, and you pointed it out, and I thank you for that service.
—Good day."
The good-tempered prince then took the road to the right, leaving
the surly lieutenant-colonel ashamed and astonished at the colloquy
he had held with his sovereign.
He gave a proof of intrepidity and presence of mind during a
tempest which befell him on a lake near Archangel, when, perceiving
the pilot overwhelmed with the responsibility his imperial rank laid
upon him, he said, "My friend, more than eighteen hundred years
have elapsed, since a Roman general, placed in similar
circumstances, said to his pilot, 'Fear not, for thou hast with thee
Cæsar and his fortunes.' I am, however, less bold than Cæsar; I
therefore charge thee to think no more of the emperor than of
thyself or any other man, and do thy best to save us both." The pilot
70. took courage, and relieved from his burden by the wisdom of his
sovereign, guided the helm with a firm hand, and brought the
tempest-tossed skiff safely to the shore.
The Emperor Alexander was not always so fortunate. He met with
several dangerous accidents, and his last journey to the provinces of
the Don nearly cost him his life. A fall from his droski hurt his leg,
and left him incurably lame. This misfortune was aggravated by his
disregarding the advice of his medical attendant, who prescribed rest
for some days; but Alexander, who was a strict disciplinarian, did not
choose to delay his return to St. Petersburg an hour beyond the time
he had fixed. Erysipelas attacked the limb, and the emperor was
confined to his bed for many weeks, and never recovered his
lameness. The sight of his wife, pale and melancholy, whom his
infidelity had injured, increased his mental despondency. That
princess watched over him with the conjugal tenderness which no
neglect could extinguish, but her fair face had forever lost the smile
which once lighted up, like a sunbeam, every beautiful feature, and
he felt himself the cause of that secret sorrow which had banished
the bloom from her cheek and the smile from her lips. Elizabeth had
borne him two daughters, but her children had not survived their
fifth and seventh years. A childless mother and forsaken wife,
Elizabeth the Empress resembled no longer the bright Louisa of
Baden, the object of Alexander's first love, the princess who had
shed tears of happiness when the joyful start and impassioned look
of her lover had assured the Empress Catharine how willingly he
accepted the hand of the princess she had destined for him. The
heart of the wife had never swerved from her devotion; her love had
increased with time, but she knew not how to share his affections
with a rival.
Alexander was solitary in his habits; repose was necessary to a man
who loved privacy, and hated those prestiges of power which had
surrounded him from infancy. He had inherited his imperial
grandmother's love for Tzarsko Zelo, a palace situated between three
and four leagues from St. Petersburg. This palace stood upon the
71. site of a cottage formerly belonging to an old Dutch-woman named
Sarah, a person well known to Peter the Great, with whom that
mighty prince was accustomed to chat and drink milk.
The fruitful plains covered with grass and waving corn, lately
redeemed by the plow from their native sterility, pleased the
legislator who was an habitué at the abode of Sarah, and at the
death of the old woman, he presented the cottage to the Empress
Catharine, with the surrounding lands, as a suitable situation for a
farm-house. Catharine, as simple in her tastes as her imperial
consort, gave her architect proper directions respecting this grange.
He, however, thought fit to build her a fine mansion. Her daughter,
the Empress Elizabeth, found this house too costly for a farm-house,
and too mean for an imperial residence. She pulled it down and built
a magnificent palace after the design of Count Rastreti. This Russian
had the barbarous taste to gild the building within and without. The
bas-reliefs, statues, caryatides, roof and basement, glittered with a
waste of this precious metal. The count wished to make this palace
surpass Versailles, and so it did in wealth undoubtedly. The Empress
Elizabeth invited the French embassador to the fête she gave at the
inauguration of her golden house, which outshone even the
celebrated one built by Nero. The palace of Tzarsko Zelo, was
considered by the whole court the eighth wonder of the world.
The silence of the Marquis de Chetardie surprised her majesty, who
with some pique requested his opinion, adding, he appeared to think
something was wanting.
"I am seeking for the case of this jewel, Madam," dryly replied the
embassador; a bon mot which ought to have gained him a sitting in
the academy of St. Petersburg, where wit was a surer passport than
learning.
The golden roof of Tzarsko Zelo was ill-calculated to stand the rigor
of a Russian winter. The noble architect had built it for summer. Cold
had been forgotten in his calculation. The expensive repairs every
spring brought in its course, compelled Catharine the Great to
72. sacrifice the gilding. She had scarcely issued her orders, before a
customer appeared for the article she was excluding from her
palace, for which a speculator offered her an immense sum. The
empress thanked him for a liberal offer none but a Russian sovereign
would have declined, assuring him with a smile, "that she never sold
her old chattels."
This empress loved Tzarsko Zelo where she built the little palace for
her grandson Alexander, and surrounded it with spacious gardens,
which she was aware he loved. Bush, her architect, could discover
no supply from whence he could obtain water in the immediate
neighborhood, yet he prepared lakes, canals and fish-ponds, upon
the responsibility of the empress, being sure that his reservoirs
would not long be empty if she ordered water to come. His
successor Baner did not leave the empress to discover its source. He
cast his eyes upon the estate of Prince Demidoff, who possessed a
super-abundant quantity of the precious fluid the imperial gardens
wanted. He mentioned the aridity of Tzarsko Zelo, and the courteous
subject dutifully bestowed his superfluous moisture upon the
imperial gardens. In despite of nature, copious streams rushed
forward, and at the bidding of the architect rose into cascades, ran
into canals, filled fish-ponds, and spread in expansive lakes. The
Empress Consort Elizabeth, upon beholding these wonders, playfully
remarked, "We may fall out with all Europe, but we must take care
not to quarrel with Prince Demidoff." In fact, that obliging noble
could have killed the whole court with thirst, by stopping the supply
of water he allowed to the imperial family.
Educated at Tzarsko Zelo, Alexander was attached to a place filled
with the recollections of his infancy. He had learned there to walk; to
speak, to ride, to sail, to row. He had passed there the brightest and
happiest part of his life. He came with the first fine days, and only
left his favorite residence when the snows of winter compelled him
to take up his abode in the winter palace.
Even in this luxurious solitude, where the emperor wished to enjoy
the repose which affords to princes the same pleasure amusement
73. offers to persons of less exalted rank, Alexander found his privacy
invaded and his attention claimed by those who had the temerity to
break through the invisible circle with which Russian etiquette fenced
round a despotic sovereign.
A foreigner at St. Petersburg, in the summer of 1823, ventured to
seek the Emperor Alexander in the delicious gardens of Tzarsko Zelo,
in order to present a petition, with which delicate commission he had
been charged by a friend. He thus relates his adventure:
"After a bad breakfast at the Hotel de la Restauration, I entered the
park, into which the sentinels permitted every body to walk without
opposition. Respect alone prevented the Russian subject from
entering the gardens, I knew, yet I was about to break this
boundary and to intrude myself upon the emperor's notice. I was
told he passed a great deal of his time in the shady walks, and I
hoped chance would obtain for me the interview I sought.
Wandering about the grounds, I discovered the Chinese town, a
pretty group of five houses, each of which had its own ice-house and
garden. In the centre of this town, which is in the form of a star,
whose rays it terminates, stands a pavillion, which is used either for
a ball or concert-room, which surrounds a green court, at the four
corners of which are placed four mandarins, the size of life, smoking
their pipes. This Chinese town is inhabited by the aid-de-camps of
the sovereign. Catharine, attended by her court, was walking in this
part of her garden, when she beheld, to her surprise, the mandarins
puffing forth real smoke, while their eyes appeared to ogle her, and
their heads to bow in the most familiar manner in the world. She
approached in order to find out the cause of this sudden animation
on the part of these statues. Immediately the loyal mandarins
descended from their pedestals, and made Chinese prostrations at
her feet, reciting some complimentary verses to the imperial lady, to
please whom they had transformed themselves into the images of
the men with pig-tails. She smiled, and quickly recognized them for
the Prince de Ligne, Potemkin, Count Segur, and M. de Cobentzal.
74. "Leaving the Chinese town, I saw the huts of the lamas, where these
inhabitants of the south are kept and acclimated to a temperature
very different from that at the foot of the Cordilleras. These animals
were presented to the emperor by the Viceroy of Mexico, and their
original number of nine has been reduced, by the rigor of the
Russian winters, to five; from which, however, a numerous race have
succeeded, who bear the cold much better than the parent stock.
"In the middle of the French garden stands a pretty dining-room,
containing the celebrated table of Olympus, imitated from a whim
devised by the Regent Orleans; where the wishes of the guests are
supplied by invisible hands from beneath. They have only to place a
note in their plate expressive of their desire, when the plate
disappears, and in five minutes after reappears with the article
required. This magic originates in a forecast which anticipates every
possible want. A beautiful lady finding her hair out of dress, wished
for curling-irons, feeling assured that such an odd request would
defy even the enchantment of the Olympian table to procure. She
was astonished at finding her plate return with a dozen pair. I saw
the curious monument raised to commemorate three favorite
greyhounds, pets of the Empress Catharine. This pyramid, erected
by the French ambassador, Count Segur, contains two epitaphs: one,
by himself, is a sort of burlesque upon the old eulogistic style so
prevalent in the last century; the other is by Catharine, and may be
literally translated into English:—
75. "'Here lies the Duchess Anderson,
Who bit Mr. Rogerson.'
"I visited successively the column of Gregory Orloff, the pyramid
erected in honor of the conqueror of Tchesma, and the grotto of
Pausilippo, and passed four hours wandering along the borders of
lakes, and traversing the plains and forests inclosed in these
delicious gardens, when I met an officer in uniform, who courteously
raised his hat. I asked a lad employed in taking a walk 'the name of
this fine gentleman,' for such he appeared to me to be. 'It is the
emperor,' was his reply. I immediately took a path which intersected
that he had taken, yet, when I had advanced about twenty steps, I
stopped upon perceiving him near me.
"He divined, apparently, that respect to his person prevented me
from crossing his walk; he therefore kept on his way, while I awaited
him in the side walk, holding my hat in my hand. I perceived he
limped in his gait from the wound in his leg, which had lately re-
opened; and I remarked as he advanced the change that had taken
place in his appearance since I had seen him at Paris, nine years
before. His countenance, then so open and smiling, bore the
expression of that deep and devouring melancholy which it was said
continually oppressed his mind, yet his sorrowful features still were
impressed with a character of benevolence, which gave me courage
to attempt the performance of my hazardous commission. 'Sire,' said
I, advancing a single step toward him.
"'Put on your hat, sir,' was his kind and gracious reply; 'the air is too
keen for you to remain uncovered.'
"'Will your majesty permit—'
"'Cover your head, sir, then; cover your head;' but, perceiving my
respect rendered me disobedient to his commands, he took my hat
from my hand, and with his own imperial one replaced it on my
head. 'Now,' said he, 'what do you wish to say to me?'
76. "'Sire, this petition,' and I took the paper from my pocket, but the
action disturbed him, and I saw him frown.
"'Sir, why do you pursue me here with petitions? do you know that I
have left St. Petersburg to be free from such annoyances?'
"'Yes, sire, I am aware of it, nor dare I disguise the boldness of an
attempt for which I can only expect pardon from your benevolence.
This, however, seems to have some claim to your majesty's
consideration, since it is franked.'
"'By whom?' inquired the emperor, with some quickness in his
manner.
"'By his Imperial Highness the Grand Duke Constantine, your
majesty's august brother.'
"'Ah!' exclaimed the emperor, putting out his hand, but as quickly
withdrawing it again.
"'I hope your majesty will for once infringe your custom, and will
deign to accept this supplication.'
"'No, sir; I will not receive it; for to-morrow, I shall have a thousand,
and shall be compelled to desert these gardens, where it seems I
can no longer hope to enjoy privacy.' He perceived my
disappointment in my countenance, and his natural kindness would
not suffer him to dismiss me with a harsh refusal. Pointing with hand
toward the church of St. Sophia, he said—'Put that petition into the
post-office in the city, and I shall see it to-morrow, and the day after,
you will have an answer.'
"I expressed my gratitude in animated terms.
"'Prove it,' was his quick reply.
"I declared my willingness to do any thing he required, as the test of
that feeling.
77. "'Well, tell nobody that you have presented me a petition and got off
with impunity,' and he resumed his walk.
"I followed his advice, and posted my paper, and three days after
received a favorable reply to my petition."
[From Eliza Cook's Journal.]
78. Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com