mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
9.0 KiB
209 lines
9.0 KiB
#ifndef PROXYSQL_PLUGIN_MANAGER_H
|
|
#define PROXYSQL_PLUGIN_MANAGER_H
|
|
|
|
// Plugin chassis is a v4.0 feature. Including this header from a v3.x
|
|
// (no PROXYSQL40) translation unit is a no-op: no class, no free
|
|
// functions, nothing. Callers that intend to use the plugin manager
|
|
// must guard their own code on PROXYSQL40 too.
|
|
#ifdef PROXYSQL40
|
|
|
|
#include "ProxySQL_Plugin.h"
|
|
|
|
#include <cstddef>
|
|
#include <deque>
|
|
#include <memory>
|
|
#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);
|
|
#ifdef PROXYSQL40
|
|
// Phase B of the four-phase plugin lifecycle: after all plugins have
|
|
// been dlopen'd but BEFORE admin module bootstrap. Invokes each
|
|
// plugin's optional register_schemas callback with a services struct
|
|
// that has register_table live but DB-handle getters stubbed to
|
|
// nullptr. Plugins that left the descriptor field null are skipped.
|
|
// Returns false on the first register_schemas callback that fails.
|
|
bool invoke_register_schemas_phase(std::string &err);
|
|
#endif /* PROXYSQL40 */
|
|
bool init_all(std::string &err);
|
|
bool start_all(std::string &err);
|
|
bool stop_all();
|
|
const std::vector<ProxySQL_PluginTableDef>& tables(ProxySQL_PluginDBKind kind) const;
|
|
bool dispatch_admin_command(const ProxySQL_PluginCommandContext& ctx, const std::string& sql, ProxySQL_PluginCommandResult& result) const;
|
|
|
|
void register_table_for_test(const ProxySQL_PluginTableDef& def);
|
|
bool register_command_for_test(const std::string& sql);
|
|
bool has_command_for_test(const std::string& sql) const;
|
|
bool register_table(const ProxySQL_PluginTableDef& def);
|
|
bool register_command(const char* sql, proxysql_plugin_admin_command_cb cb);
|
|
#ifdef PROXYSQL40
|
|
// Register an alternate spelling (alias) of an already-registered
|
|
// command. Returns true on successful registration, false if the
|
|
// canonical SQL isn't registered, if the alias is empty, or if the
|
|
// alias would collide with another (canonical or alias) of a
|
|
// different command. Duplicate registrations of the same
|
|
// (canonical, alias) pair are idempotent and return true.
|
|
bool register_command_alias(const char* canonical_sql, const char* alias_sql);
|
|
// Resolve an incoming admin-command spelling to its canonical form.
|
|
// Returns an owned copy of the canonical SQL if the query matches a
|
|
// registered command or any of its aliases; an empty string
|
|
// otherwise. Whitespace and case are normalized on both sides.
|
|
// Returns by value (not const char*) so callers can release the
|
|
// manager lock before dispatching without risking pointer
|
|
// invalidation on concurrent reload.
|
|
std::string resolve_alias_to_canonical(const std::string& sql) const;
|
|
bool register_query_hook(ProxySQL_PluginProtocol proto, proxysql_plugin_query_hook_cb cb);
|
|
bool has_query_hook(ProxySQL_PluginProtocol proto) const;
|
|
bool dispatch_query_hook(ProxySQL_PluginProtocol proto,
|
|
const ProxySQL_PluginQueryHookPayload& payload,
|
|
ProxySQL_PluginQueryHookResult& result) const;
|
|
|
|
// Runtime-view (admin-side projection of module state) plumbing.
|
|
// register_runtime_view returns false if the table is already
|
|
// registered or if the refresh callback is null.
|
|
bool register_runtime_view(const ProxySQL_PluginRuntimeView& view);
|
|
|
|
// Refresh every registered view whose table_name appears as a
|
|
// case-insensitive substring of `sql`. Each refresh callback is
|
|
// invoked exactly once per call, regardless of how many times its
|
|
// table is mentioned. Best-effort: a callback that throws or
|
|
// otherwise misbehaves is logged but does not stop other views from
|
|
// refreshing. Caller supplies admindb so the chassis does not have
|
|
// to reach into the global admin module.
|
|
void refresh_runtime_views_for_query(const std::string& sql, SQLite3DB* admindb) const;
|
|
#endif /* PROXYSQL40 */
|
|
|
|
size_t size() const;
|
|
|
|
private:
|
|
struct plugin_handle_t {
|
|
void *handle{nullptr};
|
|
const ProxySQL_PluginDescriptor *descriptor{nullptr};
|
|
std::string path {};
|
|
#ifdef PROXYSQL40
|
|
bool schemas_registered{false};
|
|
#endif /* PROXYSQL40 */
|
|
bool initialized{false};
|
|
bool started{false};
|
|
bool stopped{false};
|
|
};
|
|
|
|
struct registered_command_t {
|
|
std::string sql {};
|
|
proxysql_plugin_admin_command_cb cb { nullptr };
|
|
#ifdef PROXYSQL40
|
|
// User-friendly alternate spellings for this canonical command.
|
|
// Admin's dispatcher resolves any of these to `sql` before
|
|
// invoking `cb`. Normalized (case + whitespace) on insertion.
|
|
std::vector<std::string> aliases {};
|
|
#endif /* PROXYSQL40 */
|
|
};
|
|
|
|
struct registered_table_storage_t {
|
|
std::string table_name {};
|
|
std::string table_def {};
|
|
};
|
|
|
|
std::vector<plugin_handle_t> plugins_;
|
|
ProxySQL_PluginServices services_;
|
|
#ifdef PROXYSQL40
|
|
// Phase-B variant handed to register_schemas; DB-handle getters are
|
|
// stubbed, everything else mirrors services_. See the contract in
|
|
// ProxySQL_Plugin.h next to ProxySQL_PluginServices.
|
|
ProxySQL_PluginServices services_phase_b_;
|
|
#endif /* PROXYSQL40 */
|
|
std::vector<ProxySQL_PluginTableDef> tables_admin_;
|
|
std::vector<ProxySQL_PluginTableDef> tables_config_;
|
|
std::vector<ProxySQL_PluginTableDef> tables_stats_;
|
|
std::deque<registered_table_storage_t> table_storage_;
|
|
std::vector<registered_command_t> commands_;
|
|
#ifdef PROXYSQL40
|
|
// At most one hook per protocol; nullptr means "no hook".
|
|
proxysql_plugin_query_hook_cb mysql_query_hook_ { nullptr };
|
|
proxysql_plugin_query_hook_cb pgsql_query_hook_ { nullptr };
|
|
|
|
// Runtime-view registry: one entry per admin-side projection of
|
|
// module state. Stored alongside an owned table_name copy so
|
|
// callers may free the input string after registration. The
|
|
// refresh callback pointer and opaque are plugin-owned with
|
|
// static lifetime (the .so isn't unloaded while a view is live).
|
|
struct registered_runtime_view_t {
|
|
std::string table_name {};
|
|
void (*refresh)(SQLite3DB*, void*) { nullptr };
|
|
void* opaque { nullptr };
|
|
};
|
|
std::vector<registered_runtime_view_t> runtime_views_ {};
|
|
#endif /* PROXYSQL40 */
|
|
};
|
|
|
|
ProxySQL_PluginManager* proxysql_get_plugin_manager();
|
|
bool proxysql_dispatch_configured_plugin_admin_command(
|
|
const ProxySQL_PluginCommandContext& ctx,
|
|
const std::string& sql,
|
|
ProxySQL_PluginCommandResult& result
|
|
);
|
|
bool proxysql_dispatch_configured_plugin_query_hook(
|
|
ProxySQL_PluginProtocol proto,
|
|
const ProxySQL_PluginQueryHookPayload& payload,
|
|
ProxySQL_PluginQueryHookResult& result
|
|
);
|
|
// Fast path for hot code: returns true when the active manager has a hook
|
|
// registered for the given protocol. No locks taken. Callers should still
|
|
// invoke proxysql_dispatch_configured_plugin_query_hook to actually run the
|
|
// hook (which takes the manager lock). Use this to elide the dispatch call
|
|
// entirely on the no-plugin path.
|
|
bool proxysql_has_configured_plugin_query_hook(ProxySQL_PluginProtocol proto);
|
|
// Admin-side helper: consult the active plugin manager's command table and
|
|
// return the canonical spelling of `sql` if it's a registered command or
|
|
// alias, or an empty string otherwise. Returns by value so callers can
|
|
// release the manager lock before dispatching without risking pointer
|
|
// invalidation on concurrent reload.
|
|
std::string proxysql_resolve_configured_plugin_admin_alias(const std::string& sql);
|
|
|
|
// Admin-side helper: invoke every plugin runtime-view refresh callback
|
|
// whose registered table is referenced by `sql`. Used by Admin's
|
|
// pre-SELECT path, mirroring the way runtime_mysql_users is refreshed
|
|
// before its SELECTs. No-op if no plugin manager is active or no views
|
|
// match. Caller supplies admindb (typically the same handle Admin uses
|
|
// for its own runtime_mysql_users refresh).
|
|
void proxysql_refresh_configured_plugin_runtime_views(const std::string& sql, SQLite3DB* admindb);
|
|
// Phase A + B of the four-phase lifecycle: dlopen() each module, read its
|
|
// descriptor, then call register_schemas() on plugins that opted in. On
|
|
// success, `manager` is populated AND installed as the active manager so
|
|
// that ProxySQL_Admin::init() can see the declared tables and merge them
|
|
// into tables_defs_{admin,config,stats} for the existing
|
|
// check_and_build_standard_tables DDL pass. Phase D (init) must be
|
|
// invoked separately — after admin module bootstrap — via
|
|
// proxysql_init_configured_plugins.
|
|
bool proxysql_load_configured_plugins(
|
|
std::unique_ptr<ProxySQL_PluginManager>& manager,
|
|
const std::vector<std::string>& plugin_modules,
|
|
std::string& err
|
|
);
|
|
// Phase D: call each plugin's init() with full services (live DB handles).
|
|
// Must run after ProxySQL_Main_init_Admin_module so init() sees live
|
|
// admindb/configdb/statsdb with plugin-owned tables already materialized.
|
|
bool proxysql_init_configured_plugins(
|
|
ProxySQL_PluginManager* manager,
|
|
std::string& err
|
|
);
|
|
bool proxysql_start_configured_plugins(
|
|
ProxySQL_PluginManager* manager,
|
|
std::string& err
|
|
);
|
|
bool proxysql_stop_configured_plugins(
|
|
std::unique_ptr<ProxySQL_PluginManager>& manager,
|
|
std::string& err
|
|
);
|
|
|
|
#endif /* PROXYSQL40 */
|
|
#endif /* PROXYSQL_PLUGIN_MANAGER_H */
|