feat: add generic plugin ABI and loader

mysqlx-plugin-impl
Rene Cannao 2 months ago
parent 8cecd064fa
commit 7e1a12b8f7

@ -0,0 +1,82 @@
#ifndef PROXYSQL_PLUGIN_H
#define PROXYSQL_PLUGIN_H
#include <cstdint>
#include <string>
#include <vector>
enum ProxySQL_PluginDBKind {
admin_db,
config_db,
stats_db,
};
struct ProxySQL_PluginTableDef {
ProxySQL_PluginDBKind db_kind{admin_db};
const char *schema_name{nullptr};
const char *table_name{nullptr};
const char *create_statement{nullptr};
};
struct ProxySQL_PluginCommandContext {
const char *command_name{nullptr};
std::vector<std::string> arguments;
};
struct ProxySQL_PluginCommandResult {
bool success{true};
std::string message;
std::vector<std::vector<std::string>> rows;
};
class ProxySQL_PluginServices;
using proxysql_plugin_admin_command_cb =
bool (*)(const ProxySQL_PluginCommandContext &, ProxySQL_PluginCommandResult &, void *);
using proxysql_plugin_register_table_cb =
bool (*)(void *, const ProxySQL_PluginTableDef &);
using proxysql_plugin_register_command_cb =
bool (*)(void *, const char *, proxysql_plugin_admin_command_cb, void *);
using proxysql_plugin_snapshot_cb =
bool (*)(void *, std::vector<std::vector<std::string>> &);
using proxysql_plugin_log_message_cb =
void (*)(void *, int, const char *);
struct ProxySQL_PluginServices {
proxysql_plugin_register_table_cb register_table{nullptr};
proxysql_plugin_register_command_cb register_command{nullptr};
proxysql_plugin_snapshot_cb get_mysql_users_snapshot{nullptr};
proxysql_plugin_snapshot_cb get_mysql_servers_snapshot{nullptr};
proxysql_plugin_snapshot_cb get_mysql_group_replication_hostgroups_snapshot{nullptr};
proxysql_plugin_log_message_cb log_message{nullptr};
void *context{nullptr};
};
using proxysql_plugin_init_cb =
bool (*)(ProxySQL_PluginServices *, std::string &);
using proxysql_plugin_start_cb =
bool (*)(std::string &);
using proxysql_plugin_stop_cb =
bool (*)();
using proxysql_plugin_status_cb =
bool (*)(std::string &);
struct ProxySQL_PluginDescriptor {
uint32_t abi_version{0};
const char *name{nullptr};
proxysql_plugin_init_cb init{nullptr};
proxysql_plugin_start_cb start{nullptr};
proxysql_plugin_stop_cb stop{nullptr};
proxysql_plugin_status_cb status{nullptr};
};
using proxysql_plugin_descriptor_v1_t = const ProxySQL_PluginDescriptor *(*)();
#endif /* PROXYSQL_PLUGIN_H */

@ -0,0 +1,38 @@
#ifndef PROXYSQL_PLUGIN_MANAGER_H
#define PROXYSQL_PLUGIN_MANAGER_H
#include "ProxySQL_Plugin.h"
#include <cstddef>
#include <string>
#include <vector>
class ProxySQL_PluginManager {
public:
ProxySQL_PluginManager();
~ProxySQL_PluginManager();
ProxySQL_PluginManager(const ProxySQL_PluginManager &) = delete;
ProxySQL_PluginManager &operator=(const ProxySQL_PluginManager &) = delete;
bool load(const std::string &path, std::string &err);
bool init_all(std::string &err);
bool start_all(std::string &err);
bool stop_all();
size_t size() const;
private:
struct plugin_handle_t {
void *handle{nullptr};
const ProxySQL_PluginDescriptor *descriptor{nullptr};
bool initialized{false};
bool started{false};
bool stopped{false};
};
std::vector<plugin_handle_t> plugins_;
ProxySQL_PluginServices services_;
};
#endif /* PROXYSQL_PLUGIN_MANAGER_H */

@ -90,6 +90,7 @@ default: libproxysql.a
_OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo MySQL_Query_Processor.oo PgSQL_Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo log_utils.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \
sha256crypt.oo \
ProxySQL_PluginManager.oo \
BaseSrvList.oo BaseHGC.oo Base_HostGroups_Manager.oo \
QP_rule_text.oo QP_query_digest_stats.oo \
GTID_Server_Data.oo MyHGC.oo MySrvConnList.oo MySrvC.oo \

@ -0,0 +1,131 @@
#include "ProxySQL_PluginManager.h"
#include <dlfcn.h>
#include <cstring>
namespace {
std::string format_dl_error(const char *prefix) {
const char *dl_err = dlerror();
if (dl_err == nullptr) {
return prefix;
}
return std::string(prefix) + dl_err;
}
} // namespace
ProxySQL_PluginManager::ProxySQL_PluginManager() : services_{} {}
ProxySQL_PluginManager::~ProxySQL_PluginManager() {
stop_all();
for (auto it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
if (it->handle != nullptr) {
dlclose(it->handle);
it->handle = nullptr;
}
}
}
bool ProxySQL_PluginManager::load(const std::string &path, std::string &err) {
err.clear();
void *handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
if (handle == nullptr) {
err = format_dl_error("dlopen failed: ");
return false;
}
dlerror();
auto descriptor_fn = reinterpret_cast<proxysql_plugin_descriptor_v1_t>(
dlsym(handle, "proxysql_plugin_descriptor_v1"));
const char *dlsym_err = dlerror();
if (dlsym_err != nullptr || descriptor_fn == nullptr) {
err = dlsym_err != nullptr ? dlsym_err : "missing proxysql_plugin_descriptor_v1";
dlclose(handle);
return false;
}
const ProxySQL_PluginDescriptor *descriptor = descriptor_fn();
if (descriptor == nullptr) {
err = "proxysql_plugin_descriptor_v1 returned null";
dlclose(handle);
return false;
}
if (descriptor->abi_version != 1) {
err = "unsupported plugin ABI version";
dlclose(handle);
return false;
}
plugin_handle_t plugin;
plugin.handle = handle;
plugin.descriptor = descriptor;
plugins_.push_back(plugin);
return true;
}
bool ProxySQL_PluginManager::init_all(std::string &err) {
err.clear();
for (auto &plugin : plugins_) {
if (plugin.initialized || plugin.stopped) {
continue;
}
if (plugin.descriptor == nullptr || plugin.descriptor->init == nullptr) {
plugin.initialized = true;
continue;
}
if (!plugin.descriptor->init(&services_, err)) {
return false;
}
plugin.initialized = true;
}
return true;
}
bool ProxySQL_PluginManager::start_all(std::string &err) {
err.clear();
for (auto &plugin : plugins_) {
if (plugin.started || plugin.stopped) {
continue;
}
if (plugin.descriptor == nullptr || plugin.descriptor->start == nullptr) {
plugin.started = true;
continue;
}
if (!plugin.descriptor->start(err)) {
return false;
}
plugin.started = true;
}
return true;
}
bool ProxySQL_PluginManager::stop_all() {
bool ok = true;
for (auto it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
if (!it->initialized && !it->started) {
continue;
}
if (it->stopped) {
continue;
}
if (it->descriptor != nullptr && it->descriptor->stop != nullptr) {
ok = it->descriptor->stop() && ok;
}
it->stopped = true;
}
return ok;
}
size_t ProxySQL_PluginManager::size() const {
return plugins_.size();
}

@ -0,0 +1,36 @@
#include "ProxySQL_Plugin.h"
#include <string>
namespace {
bool fake_init(ProxySQL_PluginServices *, std::string &) {
return true;
}
bool fake_start(std::string &) {
return true;
}
bool fake_stop() {
return true;
}
bool fake_status(std::string &) {
return true;
}
const ProxySQL_PluginDescriptor fake_descriptor = {
1,
"fake_plugin",
&fake_init,
&fake_start,
&fake_stop,
&fake_status,
};
} // namespace
extern "C" const ProxySQL_PluginDescriptor *proxysql_plugin_descriptor_v1() {
return &fake_descriptor;
}

@ -221,6 +221,7 @@ endif
# ===========================================================================
TEST_HELPERS_DIR := $(PROXYSQL_PATH)/test/tap/test_helpers
FAKE_PLUGIN_SO := $(TEST_HELPERS_DIR)/libproxysql_fake_plugin.so
ODIR := obj
TEST_HELPERS_OBJ := $(ODIR)/test_globals.o $(ODIR)/test_init.o $(ODIR)/tap.o
@ -236,11 +237,22 @@ $(ODIR)/tap.o: $(TAP_SRC) | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -w
$(ODIR)/test_globals.o: $(TEST_HELPERS_DIR)/test_globals.cpp | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -Wall
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -I$(PROXYSQL_PATH)/test -Wall
$(ODIR)/test_init.o: $(TEST_HELPERS_DIR)/test_init.cpp | $(ODIR)
$(CXX) -c -o $@ $< $(OPT) $(IDIRS) -Wall
$(FAKE_PLUGIN_SO): $(TEST_HELPERS_DIR)/fake_plugin.cpp | $(TEST_HELPERS_DIR)
$(CXX) -shared -fPIC -o $@ $< $(STDCPP) $(IDIRS) -ldl
# Keep on-demand unit-test library rebuilds aligned with the default top-level
# feature set so incremental rebuilds do not mix incompatible objects.
$(LIBPROXYSQLAR):
$(MAKE) -C $(PROXYSQL_PATH)/lib libproxysql.a \
PROXYSQLCLICKHOUSE=1 PROXYSQLGENAI=$(PROXYSQLGENAI) \
PROXYSQLFFTO=$(PROXYSQLFFTO) PROXYSQLTSDB=$(PROXYSQLTSDB) \
PROXYSQL31=$(PROXYSQL31) CC=$(CC) CXX=$(CXX)
# ===========================================================================
# Unit test targets
@ -288,7 +300,8 @@ UNIT_TESTS := smoke_test-t query_cache_unit-t query_processor_unit-t \
gtid_utils_unit-t \
genai_mysql_catalog_unit-t \
admin_disk_upgrade_unit-t \
glovars_unit-t
glovars_unit-t \
plugin_manager_unit-t
.PHONY: all
all: $(UNIT_TESTS)
@ -312,6 +325,11 @@ ezoption_parser_unit-t: ezoption_parser_unit-t.cpp $(ODIR)/tap.o $(ODIR)/tap_noi
-I$(TAP_IDIR) -I$(PROXYSQL_PATH)/include \
$(STDCPP) -O0 -ggdb $(WGCOV) $(LWGCOV) -lpthread -o $@
plugin_manager_unit-t: plugin_manager_unit-t.cpp $(FAKE_PLUGIN_SO) $(ODIR)/tap.o $(ODIR)/test_globals.o $(ODIR)/test_init.o $(LIBPROXYSQLAR)
$(CXX) $< $(ODIR)/tap.o $(ODIR)/test_globals.o $(ODIR)/test_init.o \
$(IDIRS) $(LDIRS) $(OPT) $(LIBPROXYSQLAR_FULL) $(STATIC_LIBS) \
$(MYLIBS) -ldl $(ALLOW_MULTI_DEF) -o $@
# Pattern rule: all unit tests use the same compile + link flags.
# Each test binary is built from its .cpp source, linked against
# the test harness objects and libproxysql.a with all dependencies.

@ -0,0 +1,24 @@
#include "tap.h"
#include "ProxySQL_PluginManager.h"
#include <string>
static void test_loader_round_trip() {
ProxySQL_PluginManager mgr;
std::string err;
ok(mgr.load("../../test_helpers/libproxysql_fake_plugin.so", err),
"load fake plugin succeeds");
ok(mgr.size() == 1, "exactly one plugin is loaded");
ok(mgr.init_all(err), "init_all succeeds");
ok(mgr.start_all(err), "start_all succeeds");
ok(mgr.stop_all(), "stop_all succeeds");
}
int main() {
plan(5);
test_loader_round_trip();
return exit_status();
}
Loading…
Cancel
Save