Commit 0ccdf8b1 authored by Alexey Botchkov's avatar Alexey Botchkov

MDEV-19275 Provide SQL service to plugins.

test_sql_service plugin added and employed in test_sql_service.test.
parent 82bc007f
install plugin test_sql_service soname 'test_sql_service';
set global test_sql_service_run_test= 1;
show status like 'test_sql_service%';
Variable_name Value
Test_sql_service_passed 0
uninstall plugin test_sql_service;
Warnings:
Warning 1620 Plugin is busy and will be uninstalled on shutdown
--thread_handling='one-thread-per-connection'
--source include/not_embedded.inc
if (!$TEST_SQL_SERVICE_SO) {
skip No TEST_SQL_SERVICE plugin;
}
# An unfortunate wait for check-testcase.test to complete disconnect.
let count_sessions= 1;
source include/wait_until_count_sessions.inc;
install plugin test_sql_service soname 'test_sql_service';
set global test_sql_service_run_test= 1;
show status like 'test_sql_service%';
uninstall plugin test_sql_service;
# Copyright (C) 2013 Alexey Botchkov and SkySQL Ab
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
SET(SOURCES test_sql_service.c)
MYSQL_ADD_PLUGIN(test_sql_service ${SOURCES} MODULE_ONLY RECOMPILE_FOR_EMBEDDED)
This diff is collapsed.
/* Copyright (C) 2019, Alexey Botchkov and MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#define PLUGIN_VERSION 0x100
#define PLUGIN_STR_VERSION "1.0.0"
#define _my_thread_var loc_thread_var
#include <my_config.h>
#include <assert.h>
#include <my_global.h>
#include <my_base.h>
#include <typelib.h>
//#include <mysql_com.h> /* for enum enum_server_command */
#include <mysql/plugin.h>
#include <mysql/plugin_audit.h>
//#include <string.h>
LEX_STRING * thd_query_string (MYSQL_THD thd);
unsigned long long thd_query_id(const MYSQL_THD thd);
size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
const char *thd_user_name(MYSQL_THD thd);
const char *thd_client_host(MYSQL_THD thd);
const char *thd_client_ip(MYSQL_THD thd);
LEX_CSTRING *thd_current_db(MYSQL_THD thd);
int thd_current_status(MYSQL_THD thd);
enum enum_server_command thd_current_command(MYSQL_THD thd);
int maria_compare_hostname(const char *wild_host, long wild_ip, long ip_mask,
const char *host, const char *ip);
void maria_update_hostname(const char **wild_host, long *wild_ip, long *ip_mask,
const char *host);
/* Status variables for SHOW STATUS */
static long test_passed= 0;
static struct st_mysql_show_var test_sql_status[]=
{
{"test_sql_service_passed", (char *)&test_passed, SHOW_LONG},
{0,0,0}
};
static my_bool do_test= TRUE;
static void run_test(MYSQL_THD thd, struct st_mysql_sys_var *var,
void *var_ptr, const void *save);
static MYSQL_SYSVAR_BOOL(run_test, do_test, PLUGIN_VAR_OPCMDARG,
"Perform the test now.", NULL, run_test, FALSE);
static struct st_mysql_sys_var* test_sql_vars[]=
{
MYSQL_SYSVAR(run_test),
NULL
};
extern int execute_sql_command(const char *command,
char *hosts, char *names, char *filters);
static int do_tests()
{
char plugins[1024];
char names[1024];
char dl[2048];
int result;
result= execute_sql_command("select 'plugin', name, dl from mysql.plugin",
plugins, names, dl);
return result;
}
void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
{
}
static void run_test(MYSQL_THD thd __attribute__((unused)),
struct st_mysql_sys_var *var __attribute__((unused)),
void *var_ptr __attribute__((unused)),
const void *save __attribute__((unused)))
{
test_passed= do_tests();
}
static int init_done= 0;
static int test_sql_service_plugin_init(void *p __attribute__((unused)))
{
init_done= 1;
return 0;
}
static int test_sql_service_plugin_deinit(void *p __attribute__((unused)))
{
if (!init_done)
return 0;
return 0;
}
static struct st_mysql_audit maria_descriptor =
{
MYSQL_AUDIT_INTERFACE_VERSION,
NULL,
auditing,
{ MYSQL_AUDIT_GENERAL_CLASSMASK |
MYSQL_AUDIT_TABLE_CLASSMASK |
MYSQL_AUDIT_CONNECTION_CLASSMASK }
};
maria_declare_plugin(test_sql_service)
{
MYSQL_AUDIT_PLUGIN,
&maria_descriptor,
"TEST_SQL_SERVICE",
"Alexey Botchkov (MariaDB Corporation)",
"Test SQL service",
PLUGIN_LICENSE_GPL,
test_sql_service_plugin_init,
test_sql_service_plugin_deinit,
PLUGIN_VERSION,
test_sql_status,
test_sql_vars,
PLUGIN_STR_VERSION,
MariaDB_PLUGIN_MATURITY_STABLE
}
maria_declare_plugin_end;
...@@ -1355,7 +1355,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length, ...@@ -1355,7 +1355,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length,
my_hrtime_t current_time= { hrtime_from_time(thd->start_time) + my_hrtime_t current_time= { hrtime_from_time(thd->start_time) +
thd->start_time_sec_part + query_utime }; thd->start_time_sec_part + query_utime };
if (!query) if (!query || thd->get_command() == COM_STMT_PREPARE)
{ {
is_command= TRUE; is_command= TRUE;
query= command_name[thd->get_command()].str; query= command_name[thd->get_command()].str;
......
...@@ -1211,6 +1211,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) ...@@ -1211,6 +1211,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP), Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP),
backup_arena; backup_arena;
query_id_t old_query_id; query_id_t old_query_id;
CSET_STRING old_query;
TABLE *old_derived_tables; TABLE *old_derived_tables;
TABLE *old_rec_tables; TABLE *old_rec_tables;
LEX *old_lex; LEX *old_lex;
...@@ -1291,6 +1292,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) ...@@ -1291,6 +1292,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
be able properly do close_thread_tables() in instructions. be able properly do close_thread_tables() in instructions.
*/ */
old_query_id= thd->query_id; old_query_id= thd->query_id;
old_query= thd->query_string;
old_derived_tables= thd->derived_tables; old_derived_tables= thd->derived_tables;
thd->derived_tables= 0; thd->derived_tables= 0;
old_rec_tables= thd->rec_tables; old_rec_tables= thd->rec_tables;
...@@ -1567,6 +1569,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) ...@@ -1567,6 +1569,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
old_change_list.move_elements_to(thd); old_change_list.move_elements_to(thd);
thd->lex= old_lex; thd->lex= old_lex;
thd->set_query_id(old_query_id); thd->set_query_id(old_query_id);
thd->set_query_inner(old_query);
DBUG_ASSERT(!thd->derived_tables); DBUG_ASSERT(!thd->derived_tables);
thd->derived_tables= old_derived_tables; thd->derived_tables= old_derived_tables;
thd->rec_tables= old_rec_tables; thd->rec_tables= old_rec_tables;
......
...@@ -14679,3 +14679,40 @@ maria_declare_plugin(mysql_password) ...@@ -14679,3 +14679,40 @@ maria_declare_plugin(mysql_password)
MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */ MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
} }
maria_declare_plugin_end; maria_declare_plugin_end;
/*
Exporting functions that allow plugins to do server-style
host/user matching. Used in server_audit2 plugin.
*/
extern "C" int maria_compare_hostname(
const char *wild_host, long wild_ip, long ip_mask,
const char *host, const char *ip)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_host_and_ip h;
h.hostname= (char *) wild_host;
h.ip= wild_ip;
h.ip_mask= ip_mask;
return compare_hostname(&h, host, ip);
#else
return 0;
#endif
}
extern "C" void maria_update_hostname(
const char **wild_host, long *wild_ip, long *ip_mask,
const char *host)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_host_and_ip h;
update_hostname(&h, host);
*wild_host= h.hostname;
*wild_ip= h.ip;
*ip_mask= h.ip_mask;
#endif
}
...@@ -2618,6 +2618,15 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -2618,6 +2618,15 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
if (stmt->prepare(packet, packet_length)) if (stmt->prepare(packet, packet_length))
{ {
/*
Prepare failed and stmt will be freed.
Now we have to save the query_string in the so the
audit plugin later gets the meaningful notification.
*/
if (alloc_query(thd, stmt->query_string.str(), stmt->query_string.length()))
{
thd->set_query(0, 0);
}
/* Statement map deletes statement on erase */ /* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
thd->clear_last_stmt(); thd->clear_last_stmt();
...@@ -2752,6 +2761,7 @@ bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd, ...@@ -2752,6 +2761,7 @@ bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd,
void mysql_sql_stmt_prepare(THD *thd) void mysql_sql_stmt_prepare(THD *thd)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
CSET_STRING orig_query= thd->query_string;
const LEX_CSTRING *name= &lex->prepared_stmt.name(); const LEX_CSTRING *name= &lex->prepared_stmt.name();
Prepared_statement *stmt; Prepared_statement *stmt;
LEX_CSTRING query; LEX_CSTRING query;
...@@ -2822,7 +2832,16 @@ void mysql_sql_stmt_prepare(THD *thd) ...@@ -2822,7 +2832,16 @@ void mysql_sql_stmt_prepare(THD *thd)
thd->m_statement_psi, thd->m_statement_psi,
stmt->name.str, stmt->name.length); stmt->name.str, stmt->name.length);
if (stmt->prepare(query.str, (uint) query.length)) bool res= stmt->prepare(query.str, (uint) query.length);
/*
stmt->prepare() sets thd->query_string with the prepared
query, so the audit plugin gets adequate notification with the
mysqld_stmt_* set of functions.
But here we should restore the original query so it's mentioned in
logs properly.
*/
thd->set_query(orig_query);
if (res)
{ {
/* Statement map deletes the statement on erase */ /* Statement map deletes the statement on erase */
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
...@@ -2841,6 +2860,7 @@ void mysql_sql_stmt_prepare(THD *thd) ...@@ -2841,6 +2860,7 @@ void mysql_sql_stmt_prepare(THD *thd)
void mysql_sql_stmt_execute_immediate(THD *thd) void mysql_sql_stmt_execute_immediate(THD *thd)
{ {
LEX *lex= thd->lex; LEX *lex= thd->lex;
CSET_STRING orig_query= thd->query_string;
Prepared_statement *stmt; Prepared_statement *stmt;
LEX_CSTRING query; LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_execute_immediate"); DBUG_ENTER("mysql_sql_stmt_execute_immediate");
...@@ -2889,6 +2909,14 @@ void mysql_sql_stmt_execute_immediate(THD *thd) ...@@ -2889,6 +2909,14 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
thd->free_items(); thd->free_items();
thd->free_list= free_list_backup; thd->free_list= free_list_backup;
/*
stmt->execute_immediately() sets thd->query_string with the executed
query, so the audit plugin gets adequate notification with the
mysqld_stmt_* set of functions.
But here we should restore the original query so it's mentioned in
logs properly.
*/
thd->set_query_inner(orig_query);
stmt->lex->restore_set_statement_var(); stmt->lex->restore_set_statement_var();
delete stmt; delete stmt;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -3205,6 +3233,13 @@ static void mysql_stmt_execute_common(THD *thd, ...@@ -3205,6 +3233,13 @@ static void mysql_stmt_execute_common(THD *thd,
if (!(stmt= find_prepared_statement(thd, stmt_id))) if (!(stmt= find_prepared_statement(thd, stmt_id)))
{ {
char llbuf[22]; char llbuf[22];
/*
Did not find the statement with the provided stmt_id.
Set thd->query_string with the stmt_id so the
audit plugin gets the meaningful notification.
*/
if (alloc_query(thd, llbuf, strlen(llbuf)))
thd->set_query(0, 0);
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)), my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
llstr(stmt_id, llbuf), "mysqld_stmt_execute"); llstr(stmt_id, llbuf), "mysqld_stmt_execute");
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -3969,6 +4004,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) ...@@ -3969,6 +4004,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/*
We'd like to have thd->query to be set to the actual query
after the function ends.
This value will be sent to audit plugins later.
As the statement is created, the query will be stored
in statement's arena. Normally the statement lives longer than
the end of this query, so we can just set thd->query_string to
be the stmt->query_string.
Though errors can result in statement to be freed. These cases
should be handled appropriately.
*/
stmt_backup.query_string= thd->query_string;
old_stmt_arena= thd->stmt_arena; old_stmt_arena= thd->stmt_arena;
thd->stmt_arena= this; thd->stmt_arena= this;
...@@ -4504,6 +4552,15 @@ Prepared_statement::reprepare() ...@@ -4504,6 +4552,15 @@ Prepared_statement::reprepare()
*/ */
thd->get_stmt_da()->clear_warning_info(thd->query_id); thd->get_stmt_da()->clear_warning_info(thd->query_id);
} }
else
{
/*
Prepare failed and the 'copy' will be freed.
Now we have to restore the query_string in the so the
audit plugin later gets the meaningful notification.
*/
thd->set_query(query(), query_length());
}
return error; return error;
} }
...@@ -5127,7 +5184,7 @@ class Protocol_local : public Protocol_text ...@@ -5127,7 +5184,7 @@ class Protocol_local : public Protocol_text
Protocol_local(THD *thd_arg, ulong prealloc= 0) : Protocol_local(THD *thd_arg, ulong prealloc= 0) :
Protocol_text(thd_arg, prealloc), Protocol_text(thd_arg, prealloc),
cur_data(0), first_data(0), data_tail(&first_data) cur_data(0), first_data(0), data_tail(&first_data), alloc(0)
{} {}
protected: protected:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment