如何定制一个属于自己的MySQL扩展插件?

本文的目标是设置一个构建环境,编译自己的MySQL插件,以作为一个函数加载到MySQL中。我们的函数将使用ChrisBove/ulid的C++库实现一个ULID生成器。

1  下载源码,并解压

cd $HOME
wget https://downloads.percona.com/downloads/Percona-Server-8.0/Percona-Server-8.0.32-24/source/tarball/percona-server-8.0.32-24.tar.gz
tar -xvzf percona-server-8.0.32-24.tar.gz

2 在源代码树中增加插件位置

mkdir ~/percona-server-8.0.32-24/plugin/ulid/
cd ~/percona-server-8.0.32-24/plugin/ulid/
git clone https://github.com/ChrisBove/ulid

3 修改cmake文件

cd ~/percona-server-8.0.32-24/plugin/ulid/
echo "MYSQL_ADD_PLUGIN(ulid ulid_udf.cc MODULE_ONLY)" > CMakeLists.txt

MYSQL_ADD_PLUGIN是CMake的宏,用于定义插件名称、插件的主要源代码文件,并指定这是一个可加载的共享对象(.so)。

4 下载ulid代码

curl https://gist.githubusercontent.com/utdrmac/168c757144562408976854c50724fe75/raw/87f2401e422d90ec8e0da55cdbf42f1568c96a04/uild_udf.cc >ulid_udf.cc

5 准备编译

mkdir /tmp/BUILD_PS
cd /tmp/BUILD_PS
cmake ~/percona-server-8.0.32-24/ -DWITH_BOOST=/tmp/boost -DDOWNLOAD_BOOST=1 -DWITH_AUTHENTICATION_LDAP=OFF
...
...
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/BUILD_PS

## What I had to install; YMMV
apt install gcc-8 g++-8
apt install libkrb5-dev libsasl2-dev libsasl2-modules-gssapi-mit libldap-dev
apt install libncurses5-dev

6 编译插件

Cmake现在已经完成,并且所有的初始检查都很好。现在是时候只编译我们的插件了。我们不需要编译整个Percona MySQL服务器的代码库。

cd /tmp/BUILD_PS/plugin/ulid
make
...
...
Scanning dependencies of target ulid
[100%] Building CXX object plugin/ulid/CMakeFiles/ulid.dir/ulid_udf.cc.o
/home/jeves/percona-server-8.0.32-24/plugin/ulid/ulid_udf.cc: In function ‘void ulid_udf::ulid_deinit(UDF_INIT*)’:
/home/jeves/percona-server-8.0.32-24/plugin/ulid/ulid_udf.cc:132:39: warning: unused parameter ‘initid’ [-Wunused-parameter]
 extern "C" void ulid_deinit(UDF_INIT *initid) {}
                             ~~~~~~~~~~^~~~~~
[100%] Linking CXX shared module ../../plugin_output_directory/ulid.so
[100%] Built target ulid

7  验证

cp /tmp/BUILD_PS/plugin_output_directory/ulid.so /usr/lib/mysql/plugins/
mysql
mysql> CREATE FUNCTION ulid RETURNS STRING SONAME "ulid.so";
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT ULID();
+------------------------------------+
| ULID()                             |
+------------------------------------+
| 0x018931B0EA52D664C7CBE83255C3468D |
+------------------------------------+
1 row in set (0.00 sec)

本文提供了一种扩展MySQL的方法,大家感兴趣可以按照此方案实现自己的扩展插件。

附件 ulid函数源代码

#include <stdlib.h>
#include <ctype.h>
#include <mysql/plugin.h>

#define _MSC_VER 0  // Silence a VS Code warning in ulid.hh

#define ULID_BINARY_LENGTH 16

#include "ulid/src/ulid.hh"

namespace ulid_udf {

/**
 * https://dev.mysql.com/doc/extending-mysql/8.0/en/adding-loadable-function.html
 *
 * When an SQL statement invokes XXX(), MySQL calls the initialization function xxx_init() 
 * to let it perform any required setup, such as argument checking or memory allocation. 
 *
 * If xxx_init() returns an error, MySQL aborts the SQL statement with an error message 
 * and does not call the main or deinitialization functions. Otherwise, MySQL calls the 
 * main function xxx() once for each row.
 * 
 * After all rows have been processed, MySQL calls the deinitialization function xxx_deinit() 
 * so that it can perform any required cleanup.
 */

/*
 * Init
 */
extern "C" bool ulid_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {

 if (args->arg_count > 1) {
  strcpy(message, "ULID takes 0 or 1 arguments");
  return true;
 }

 if (args->arg_count == 1 && args->arg_type[0] != INT_RESULT) {
  strcpy(message, "Argument 1 must be an integer representing milliseconds");
  return true;
 }

 initid->max_length = ULID_BINARY_LENGTH;

 return false;
}


/*
 * Main - returns binary ULID
 */
extern "C" char *ulid(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, char *result,
  unsigned long *length, unsigned char *is_null,
  unsigned char *error) {

 *is_null = 0;

 ulid::ULID my_ulid = 0;

 if (args->arg_count == 0) {

  // Create a ULID using the current time (std::chrono) in nanoseconds
  ulid::EncodeTimeSystemClockNow(my_ulid);
 }
 else if (args->arg_count == 1) {

  // milliseconds
  long long ms_seed;
  ms_seed = *((long long*) args->args[0]);

  // Encode the provided timestamp
  std::chrono::system_clock::time_point tp{std::chrono::milliseconds{ms_seed}};
  ulid::EncodeTime(tp, my_ulid);
 }
 else {
  *error = 1;
  return result;
 }

 // Add randomness
 ulid::EncodeEntropyRand(my_ulid);

 // Marshal ulid to binary array
 uint8_t dst[16];
 ulid::MarshalBinaryTo(my_ulid, dst);

 // Copy binary array to mysql result pointer and confirm length
 memcpy(result, dst, ULID_BINARY_LENGTH);
 *length = ULID_BINARY_LENGTH;

 return result;
}


/**
 * De-init - cleanup memory allocated during init
 */
extern "C" void ulid_deinit(UDF_INIT *initid) {}

} // namespace ulid_udf








文章来源:https://www.percona.com/blog/extensibility-in-mysql-is-easy

欢迎关注公众号:DBA札记,一起交流数据库技术。后台回复“交流群”可添加技术交流群。欢迎觉得读完本文有收获,可以转发给其他朋友,大家一起学习进步!


免责声明:

1、本站资源由自动抓取工具收集整理于网络。

2、本站不承担由于内容的合法性及真实性所引起的一切争议和法律责任。

3、电子书、小说等仅供网友预览使用,书籍版权归作者或出版社所有。

4、如作者、出版社认为资源涉及侵权,请联系本站,本站将在收到通知书后尽快删除您认为侵权的作品。

5、如果您喜欢本资源,请您支持作者,购买正版内容。

6、资源失效,请下方留言,欢迎分享资源链接

文章评论

0条评论