diff options
author | Alexey Botchkov <holyfoot@askmonty.org> | 2022-01-04 14:59:45 +0400 |
---|---|---|
committer | Alexey Botchkov <holyfoot@askmonty.org> | 2022-01-04 14:59:45 +0400 |
commit | 6137a97653a8962e8876cb8ad1da0cd9c3b3b425 (patch) | |
tree | 6b81d29c0ddbe5b86e0c72d0a112c27e070e8812 | |
parent | a48d2ec866751e9da76066bf3a30f99da9031ab0 (diff) | |
download | mariadb-git-6137a97653a8962e8876cb8ad1da0cd9c3b3b425.tar.gz |
The SQL SERVICE backported into the 10.4 to be tested.
30 files changed, 1780 insertions, 347 deletions
diff --git a/include/mysql.h b/include/mysql.h index ec49ca0482a..abfbb2ec7c0 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -329,7 +329,7 @@ typedef struct st_mysql_res { } MYSQL_RES; -#if !defined(MYSQL_SERVER) && !defined(MYSQL_CLIENT) +#if !defined(MYSQL_SERVICE_SQL) && !defined(MYSQL_CLIENT) #define MYSQL_CLIENT #endif @@ -361,7 +361,7 @@ typedef struct st_mysql_parameters */ #define MYSQL_WAIT_TIMEOUT 8 -#if !defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY) +#if !defined(MYSQL_SERVICE_SQL) #define max_allowed_packet (*mysql_get_parameters()->p_max_allowed_packet) #define net_buffer_length (*mysql_get_parameters()->p_net_buffer_length) #endif diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp index c5ae678e82a..15b11002b96 100644 --- a/include/mysql/plugin_audit.h.pp +++ b/include/mysql/plugin_audit.h.pp @@ -419,6 +419,24 @@ int json_escape_string(const char *str,const char *str_end, char *json, char *json_end); int json_unescape_json(const char *json_str, const char *json_end, char *res, char *res_end); +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; +MYSQL *mysql_real_connect_local(MYSQL *mysql); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp index 41cb7d075c4..7bfc249afa7 100644 --- a/include/mysql/plugin_auth.h.pp +++ b/include/mysql/plugin_auth.h.pp @@ -419,6 +419,24 @@ int json_escape_string(const char *str,const char *str_end, char *json, char *json_end); int json_unescape_json(const char *json_str, const char *json_end, char *res, char *res_end); +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; +MYSQL *mysql_real_connect_local(MYSQL *mysql); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/plugin_encryption.h.pp b/include/mysql/plugin_encryption.h.pp index 6597decfbef..808d4a799fa 100644 --- a/include/mysql/plugin_encryption.h.pp +++ b/include/mysql/plugin_encryption.h.pp @@ -419,6 +419,24 @@ int json_escape_string(const char *str,const char *str_end, char *json, char *json_end); int json_unescape_json(const char *json_str, const char *json_end, char *res, char *res_end); +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; +MYSQL *mysql_real_connect_local(MYSQL *mysql); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp index bd1cfc7b68b..737214dfef3 100644 --- a/include/mysql/plugin_ftparser.h.pp +++ b/include/mysql/plugin_ftparser.h.pp @@ -419,6 +419,24 @@ int json_escape_string(const char *str,const char *str_end, char *json, char *json_end); int json_unescape_json(const char *json_str, const char *json_end, char *res, char *res_end); +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; +MYSQL *mysql_real_connect_local(MYSQL *mysql); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/plugin_password_validation.h.pp b/include/mysql/plugin_password_validation.h.pp index 2f9d2299c1f..f7840bfa364 100644 --- a/include/mysql/plugin_password_validation.h.pp +++ b/include/mysql/plugin_password_validation.h.pp @@ -419,6 +419,24 @@ int json_escape_string(const char *str,const char *str_end, char *json, char *json_end); int json_unescape_json(const char *json_str, const char *json_end, char *res, char *res_end); +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; +MYSQL *mysql_real_connect_local(MYSQL *mysql); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/service_sql.h b/include/mysql/service_sql.h new file mode 100644 index 00000000000..ef6de8b34ef --- /dev/null +++ b/include/mysql/service_sql.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2021 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 02111-1301 USA */ + +#ifndef MYSQL_SERVICE_SQL +#define MYSQL_SERVICE_SQL + +#ifndef MYSQL_ABI_CHECK +#include <mysql.h> +#endif + +/** + @file + SQL service + + Interface for plugins to execute SQL queries on the local server. + + Functions of the service are the 'server-limited' client library: + mysql_init + mysql_real_connect_local + mysql_real_connect + mysql_errno + mysql_error + mysql_real_query + mysql_affected_rows + mysql_num_rows + mysql_store_result + mysql_free_result + mysql_fetch_row + mysql_close +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct sql_service_st { + MYSQL *(STDCALL *mysql_init_func)(MYSQL *mysql); + MYSQL *(*mysql_real_connect_local_func)(MYSQL *mysql); + MYSQL *(STDCALL *mysql_real_connect_func)(MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, unsigned int port, + const char *unix_socket, unsigned long clientflag); + unsigned int(STDCALL *mysql_errno_func)(MYSQL *mysql); + const char *(STDCALL *mysql_error_func)(MYSQL *mysql); + int (STDCALL *mysql_real_query_func)(MYSQL *mysql, const char *q, + unsigned long length); + my_ulonglong (STDCALL *mysql_affected_rows_func)(MYSQL *mysql); + my_ulonglong (STDCALL *mysql_num_rows_func)(MYSQL_RES *res); + MYSQL_RES *(STDCALL *mysql_store_result_func)(MYSQL *mysql); + void (STDCALL *mysql_free_result_func)(MYSQL_RES *result); + MYSQL_ROW (STDCALL *mysql_fetch_row_func)(MYSQL_RES *result); + void (STDCALL *mysql_close_func)(MYSQL *mysql); +} *sql_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define mysql_init(M) sql_service->mysql_init_func(M) +#define mysql_real_connect_local(M) sql_service->mysql_real_connect_local_func(M) +#define mysql_real_connect(M,H,U,PW,D,P,S,F) sql_service->mysql_real_connect_func(M,H,U,PW,D,P,S,F) +#define mysql_errno(M) sql_service->mysql_errno_func(M) +#define mysql_error(M) sql_service->mysql_error_func(M) +#define mysql_real_query sql_service->mysql_real_query_func +#define mysql_affected_rows(M) sql_service->mysql_affected_rows_func(M) +#define mysql_num_rows(R) sql_service->mysql_num_rows_func(R) +#define mysql_store_result(M) sql_service->mysql_store_result_func(M) +#define mysql_free_result(R) sql_service->mysql_free_result_func(R) +#define mysql_fetch_row(R) sql_service->mysql_fetch_row_func(R) +#define mysql_close(M) sql_service->mysql_close_func(M) + +#else + +/* + Establishes the connection to the 'local' server that started the plugin. + Like the mysql_real_connect() does for the remote server. + The established connection has no user/host associated to it, + neither it has the current db, so the queries should have + database/table name specified. +*/ +MYSQL *mysql_real_connect_local(MYSQL *mysql); + +/* The rest of the function declarations mest be taken from the mysql.h */ + +#endif /*MYSQL_DYNAMIC_PLUGIN*/ + + +#ifdef __cplusplus +} +#endif + +#endif /*MYSQL_SERVICE_SQL */ + + diff --git a/include/mysql/services.h b/include/mysql/services.h index 2c3a0ae421b..94f7bb3b2da 100644 --- a/include/mysql/services.h +++ b/include/mysql/services.h @@ -41,6 +41,7 @@ extern "C" { #include <mysql/service_thd_wait.h> #include <mysql/service_json.h> /*#include <mysql/service_wsrep.h>*/ +#include <mysql/service_sql.h> #ifdef __cplusplus } diff --git a/include/service_versions.h b/include/service_versions.h index fb7e715f858..fc31583b14a 100644 --- a/include/service_versions.h +++ b/include/service_versions.h @@ -43,3 +43,4 @@ #define VERSION_thd_wait 0x0100 #define VERSION_wsrep 0x0400 #define VERSION_json 0x0100 +#define VERSION_sql_service 0x0100 diff --git a/include/sql_common.h b/include/sql_common.h index 9836d0c1cdc..791cdbaddf0 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -64,13 +64,13 @@ typedef struct st_mysql_methods MYSQL_ROW column, unsigned int field_count); void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); int (*read_change_user_result)(MYSQL *mysql); + void (*on_close_free)(MYSQL *mysql); #if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) MYSQL_FIELD * (*list_fields)(MYSQL *mysql); my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); int (*stmt_execute)(MYSQL_STMT *stmt); int (*read_binary_rows)(MYSQL_STMT *stmt); int (*unbuffered_fetch)(MYSQL *mysql, char **row); - void (*free_embedded_thd)(MYSQL *mysql); const char *(*read_statistics)(MYSQL *mysql); my_bool (*next_result)(MYSQL *mysql); int (*read_rows_from_cursor)(MYSQL_STMT *stmt); diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 42a93f9a38b..56a6b877937 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -43,7 +43,7 @@ C_MODE_START extern unsigned int mysql_server_last_errno; extern char mysql_server_last_error[MYSQL_ERRMSG_SIZE]; static my_bool emb_read_query_result(MYSQL *mysql); -static void emb_free_embedded_thd(MYSQL *mysql); +static void free_embedded_thd(MYSQL *mysql); static bool embedded_print_errors= 0; extern "C" void unireg_clear(int exit_code) @@ -121,7 +121,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, thd->killed= NOT_KILLED; else { - emb_free_embedded_thd(mysql); + free_embedded_thd(mysql); thd= 0; } } @@ -431,7 +431,7 @@ int emb_unbuffered_fetch(MYSQL *mysql, char **row) return 0; } -static void emb_free_embedded_thd(MYSQL *mysql) +static void free_embedded_thd(MYSQL *mysql) { THD *thd= (THD*)mysql->thd; server_threads.erase(thd); @@ -454,12 +454,25 @@ static MYSQL_RES * emb_store_result(MYSQL *mysql) return mysql_store_result(mysql); } -int emb_read_change_user_result(MYSQL *mysql) +static int emb_read_change_user_result(MYSQL *mysql) { mysql->net.read_pos= (uchar*)""; // fake an OK packet return mysql_errno(mysql) ? (int)packet_error : 1 /* length of the OK packet */; } + +static void emb_on_close_free(MYSQL *mysql) +{ + my_free(mysql->info_buffer); + mysql->info_buffer= 0; + if (mysql->thd) + { + free_embedded_thd(mysql); + mysql->thd= 0; + } +} + + MYSQL_METHODS embedded_methods= { emb_read_query_result, @@ -469,12 +482,12 @@ MYSQL_METHODS embedded_methods= emb_fetch_lengths, emb_flush_use_result, emb_read_change_user_result, + emb_on_close_free, emb_list_fields, emb_read_prepare_result, emb_stmt_execute, emb_read_binary_rows, emb_unbuffered_fetch, - emb_free_embedded_thd, emb_read_statistics, emb_read_query_result, emb_read_rows_from_cursor diff --git a/libmysqld/libmysql.c b/libmysqld/libmysql.c index 1ebcae0d8d5..04fea18ee52 100644 --- a/libmysqld/libmysql.c +++ b/libmysqld/libmysql.c @@ -1064,11 +1064,6 @@ unsigned int STDCALL mysql_field_count(MYSQL *mysql) return mysql->field_count; } -my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) -{ - return mysql->affected_rows; -} - my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) { return mysql->insert_id; diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt index 274c8ce6dac..6b47bb53fdb 100644 --- a/libservices/CMakeLists.txt +++ b/libservices/CMakeLists.txt @@ -38,6 +38,7 @@ SET(MYSQLSERVICES_SOURCES thd_wait_service.c wsrep_service.c json_service.c + sql_service.c ) ADD_CONVENIENCE_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES}) diff --git a/libservices/sql_service.c b/libservices/sql_service.c new file mode 100644 index 00000000000..5c0102bfadf --- /dev/null +++ b/libservices/sql_service.c @@ -0,0 +1,19 @@ + +/* Copyright (c) 2018, Monty Program 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 St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include <service_versions.h> +SERVICE_VERSION sql_service= (void*)VERSION_sql_service; diff --git a/mysql-test/suite/plugins/r/test_sql_service.result b/mysql-test/suite/plugins/r/test_sql_service.result new file mode 100644 index 00000000000..00f0411b665 --- /dev/null +++ b/mysql-test/suite/plugins/r/test_sql_service.result @@ -0,0 +1,70 @@ +install plugin test_sql_service soname 'test_sql_service'; +show status like 'test_sql_service_passed'; +Variable_name Value +Test_sql_service_passed 1 +set global test_sql_service_run_test= 1; +show status like 'test_sql_service_passed'; +Variable_name Value +Test_sql_service_passed 1 +set global test_sql_service_execute_sql_local= 'create table test.t1(id int)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 0 rows. +set global test_sql_service_execute_sql_local= 'insert into test.t1 values (1), (2)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 2 rows. +set global test_sql_service_execute_sql_local= 'select * from test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 2 rows. +set global test_sql_service_execute_sql_local= 'drop table test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 0 rows. +set global test_sql_service_execute_sql_local= 'drop table test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Error 1051 returned. Unknown table 'test.t1' +set global test_sql_service_execute_sql_global= 'create table test.t1(id int)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 0 rows. +set global test_sql_service_execute_sql_global= 'insert into test.t1 values (1), (2)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 2 rows. +set global test_sql_service_execute_sql_global= 'select * from test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 2 rows. +set global test_sql_service_execute_sql_global= 'drop table test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query affected 0 rows. +set global test_sql_service_execute_sql_global= 'drop table test.t1'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Error 1051 returned. Unknown table 'test.t1' +create table t1 (id int, time timestamp); +insert into t1 values (1, NULL), (2, NULL), (3, NULL), (4, NULL), (5, NULL); +set global test_sql_service_execute_sql_global= 'select * FROM test.t1 WHERE time < DATE_SUB(NOW(), interval 5 minute)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 0 rows. +set global test_sql_service_execute_sql_global= 'select * FROM test.t1 WHERE time <= NOW()'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 5 rows. +set global test_sql_service_execute_sql_local= 'select * FROM test.t1 WHERE time < DATE_SUB(NOW(), interval 5 minute)'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 0 rows. +set global test_sql_service_execute_sql_local= 'select * FROM test.t1 WHERE time <= NOW()'; +show status like 'test_sql_query_result'; +Variable_name Value +Test_sql_query_result Query returned 5 rows. +drop table t1; +uninstall plugin test_sql_service; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/mysql-test/suite/plugins/t/test_sql_service.test b/mysql-test/suite/plugins/t/test_sql_service.test new file mode 100644 index 00000000000..b80d78fe6e5 --- /dev/null +++ b/mysql-test/suite/plugins/t/test_sql_service.test @@ -0,0 +1,60 @@ +--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'; +show status like 'test_sql_service_passed'; + +set global test_sql_service_run_test= 1; +show status like 'test_sql_service_passed'; + +set global test_sql_service_execute_sql_local= 'create table test.t1(id int)'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_local= 'insert into test.t1 values (1), (2)'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_local= 'select * from test.t1'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_local= 'drop table test.t1'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_local= 'drop table test.t1'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_global= 'create table test.t1(id int)'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_global= 'insert into test.t1 values (1), (2)'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_global= 'select * from test.t1'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_global= 'drop table test.t1'; +show status like 'test_sql_query_result'; + +set global test_sql_service_execute_sql_global= 'drop table test.t1'; +show status like 'test_sql_query_result'; + +create table t1 (id int, time timestamp); +insert into t1 values (1, NULL), (2, NULL), (3, NULL), (4, NULL), (5, NULL); +set global test_sql_service_execute_sql_global= 'select * FROM test.t1 WHERE time < DATE_SUB(NOW(), interval 5 minute)'; +show status like 'test_sql_query_result'; +set global test_sql_service_execute_sql_global= 'select * FROM test.t1 WHERE time <= NOW()'; +show status like 'test_sql_query_result'; +set global test_sql_service_execute_sql_local= 'select * FROM test.t1 WHERE time < DATE_SUB(NOW(), interval 5 minute)'; +show status like 'test_sql_query_result'; +set global test_sql_service_execute_sql_local= 'select * FROM test.t1 WHERE time <= NOW()'; +show status like 'test_sql_query_result'; +drop table t1; + +uninstall plugin test_sql_service; + diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 7a170b13b27..0e08a490870 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -1133,7 +1133,6 @@ static void setup_connection_simple(struct connection_info *ci) #define MAX_HOSTNAME 61 -#define USERNAME_LENGTH 384 static void setup_connection_connect(struct connection_info *cn, const struct mysql_event_connection *event) diff --git a/plugin/test_sql_service/CMakeLists.txt b/plugin/test_sql_service/CMakeLists.txt new file mode 100644 index 00000000000..615508bdc4e --- /dev/null +++ b/plugin/test_sql_service/CMakeLists.txt @@ -0,0 +1,18 @@ +# 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) diff --git a/plugin/test_sql_service/COPYING b/plugin/test_sql_service/COPYING new file mode 100644 index 00000000000..6e475df5526 --- /dev/null +++ b/plugin/test_sql_service/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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; either version 2 of the License, or + (at your option) any later version. + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/plugin/test_sql_service/test_sql_service.c b/plugin/test_sql_service/test_sql_service.c new file mode 100644 index 00000000000..4ea6533af84 --- /dev/null +++ b/plugin/test_sql_service/test_sql_service.c @@ -0,0 +1,270 @@ +/* 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 0x200 + +#include <mysql/plugin_audit.h> +#define STRING_WITH_LEN(X) (X), ((size_t) (sizeof(X) - 1)) + +/* Status variables for SHOW STATUS */ +static long test_passed= 0; +static char *sql_text_local, *sql_text_global; +static char qwe_res[1024]= ""; + +static struct st_mysql_show_var test_sql_status[]= +{ + {"test_sql_service_passed", (char *)&test_passed, SHOW_LONG}, + {"test_sql_query_result", qwe_res, SHOW_CHAR}, + {0,0,0} +}; + +static my_bool do_test= 1; +static int run_test(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value); +static int run_sql_local(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value); +static int run_sql_global(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value); + +static void noop_update(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.", + run_test, NULL, 0); + +static MYSQL_SYSVAR_STR(execute_sql_local, sql_text_local, + PLUGIN_VAR_OPCMDARG, + "Create the new local connection, execute SQL statement with it.", + run_sql_local, noop_update, 0); + +static MYSQL_SYSVAR_STR(execute_sql_global, sql_text_global, + PLUGIN_VAR_OPCMDARG, + "Execute SQL statement using the global connection.", + run_sql_global, noop_update, 0); + +static struct st_mysql_sys_var* test_sql_vars[]= +{ + MYSQL_SYSVAR(run_test), + MYSQL_SYSVAR(execute_sql_local), + MYSQL_SYSVAR(execute_sql_global), + NULL +}; + +static MYSQL *global_mysql; + + +static int run_queries(MYSQL *mysql) +{ + MYSQL_RES *res; + + if (mysql_real_query(mysql, + STRING_WITH_LEN("CREATE TABLE test.ts_table" + " ( hash varbinary(512)," + " time timestamp default current_time," + " primary key (hash), index tm (time) )"))) + return 1; + + if (mysql_real_query(mysql, + STRING_WITH_LEN("INSERT INTO test.ts_table VALUES('1234567890', NULL)"))) + return 1; + + if (mysql_real_query(mysql, STRING_WITH_LEN("select * from test.ts_table"))) + return 1; + + if (!(res= mysql_store_result(mysql))) + return 1; + + mysql_free_result(res); + + if (mysql_real_query(mysql, STRING_WITH_LEN("DROP TABLE test.ts_table"))) + return 1; + + return 0; +} + + +static int do_tests() +{ + MYSQL *mysql; + int result= 1; + + mysql= mysql_init(NULL); + if (mysql_real_connect_local(mysql) == NULL) + return 1; + + if (run_queries(mysql)) + goto exit; + + if (run_queries(global_mysql)) + goto exit; + + result= 0; +exit: + mysql_close(mysql); + + return result; +} + + +void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) +{ +} + + +static int run_test(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value) +{ + return (test_passed= (do_tests() == 0)) == 0; +} + + +static int run_sql(MYSQL *mysql, void *save, struct st_mysql_value *value) +{ + const char *str; + int len= 0; + MYSQL_RES *res; + + str= value->val_str(value, NULL, &len); + + if (mysql_real_query(mysql, str, len)) + { + if (mysql_error(mysql)[0]) + { + my_snprintf(qwe_res, sizeof(qwe_res), "Error %d returned. %s", + mysql_errno(mysql), mysql_error(mysql)); + return 0; + } + + return 1; + } + + if ((res= mysql_store_result(mysql))) + { + my_snprintf(qwe_res, sizeof(qwe_res), "Query returned %lld rows.", + mysql_num_rows(res)); + mysql_free_result(res); + } + else + { + if (mysql_error(mysql)[0]) + { + my_snprintf(qwe_res, sizeof(qwe_res), "Error %d returned. %s", + mysql_errno(mysql), mysql_error(mysql)); + } + else + my_snprintf(qwe_res, sizeof(qwe_res), "Query affected %lld rows.", + mysql_affected_rows(mysql)); + } + + return 0; +} + + +static void noop_update(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + sql_text_local= sql_text_global= qwe_res; +} + +static int run_sql_local(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value) +{ + MYSQL *mysql; + int result= 1; + + mysql= mysql_init(NULL); + if (mysql_real_connect_local(mysql) == NULL) + return 1; + + if (run_sql(mysql, save, value)) + goto exit; + + result= 0; + +exit: + mysql_close(mysql); + + return result; +} + + +static int run_sql_global(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value) +{ + return run_sql(global_mysql, save, value); +} + + +static int init_done= 0; + +static int test_sql_service_plugin_init(void *p) +{ + (void) p; + global_mysql= mysql_init(NULL); + + if (!global_mysql || + mysql_real_connect_local(global_mysql) == NULL) + return 1; + + init_done= 1; + + test_passed= (do_tests() == 0); + + return 0; +} + + +static int test_sql_service_plugin_deinit(void *p) +{ + (void) p; + if (!init_done) + return 0; + + mysql_close(global_mysql); + + 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, + NULL, + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; + diff --git a/sql-common/client.c b/sql-common/client.c index 04ee00d6bea..d49194c817c 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1670,14 +1670,14 @@ static MYSQL_METHODS client_methods= cli_use_result, /* use_result */ cli_fetch_lengths, /* fetch_lengths */ cli_flush_use_result, /* flush_use_result */ - cli_read_change_user_result /* read_change_user_result */ + cli_read_change_user_result, /* read_change_user_result */ + NULL /* on_close_free */ #ifndef MYSQL_SERVER ,cli_list_fields, /* list_fields */ cli_read_prepare_result, /* read_prepare_result */ cli_stmt_execute, /* stmt_execute */ cli_read_binary_rows, /* read_binary_rows */ cli_unbuffered_fetch, /* unbuffered_fetch */ - NULL, /* free_embedded_thd */ cli_read_statistics, /* read_statistics */ cli_read_query_result, /* next_result */ cli_read_binary_rows /* read_rows_from_cursor */ @@ -3349,10 +3349,8 @@ static void mysql_close_free(MYSQL *mysql) my_free(mysql->user); my_free(mysql->passwd); my_free(mysql->db); -#if defined(EMBEDDED_LIBRARY) || MYSQL_VERSION_ID >= 50100 - my_free(mysql->info_buffer); - mysql->info_buffer= 0; -#endif + if (mysql->methods && mysql->methods->on_close_free) + (*mysql->methods->on_close_free)(mysql); /* Clear pointers for better safety */ mysql->host_info= mysql->user= mysql->passwd= mysql->db= 0; } @@ -3471,13 +3469,6 @@ void STDCALL mysql_close(MYSQL *mysql) mysql_close_free_options(mysql); mysql_close_free(mysql); mysql_detach_stmt_list(&mysql->stmts, "mysql_close"); -#ifndef MYSQL_SERVER - if (mysql->thd) - { - (*mysql->methods->free_embedded_thd)(mysql); - mysql->thd= 0; - } -#endif if (mysql->free_me) my_free(mysql); } @@ -4297,3 +4288,9 @@ int STDCALL mysql_cancel(MYSQL *mysql) return vio_shutdown(mysql->net.vio, SHUT_RDWR); return -1; } + +my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) +{ + return mysql->affected_rows; +} + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 310dcada671..c17016448b6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1508,6 +1508,16 @@ static void end_ssl(); #ifndef EMBEDDED_LIBRARY +extern Atomic_counter<uint32_t> local_connection_thread_count; + +uint THD_count::connection_thd_count() +{ + return value() - + binlog_dump_thread_count - + local_connection_thread_count; +} + + /**************************************************************************** ** Code to end mysqld ****************************************************************************/ @@ -1752,7 +1762,7 @@ static void close_connections(void) */ DBUG_PRINT("info", ("THD_count: %u", THD_count::value())); - for (int i= 0; (THD_count::value() - binlog_dump_thread_count) && i < 1000; i++) + for (int i= 0; (THD_count::connection_thd_count()) && i < 1000; i++) my_sleep(20000); if (global_system_variables.log_warnings) @@ -1767,12 +1777,12 @@ static void close_connections(void) /* All threads has now been aborted */ DBUG_PRINT("quit", ("Waiting for threads to die (count=%u)", THD_count::value())); - while (THD_count::value() - binlog_dump_thread_count) + while (THD_count::connection_thd_count()) my_sleep(1000); /* Kill phase 2 */ server_threads.iterate(kill_thread_phase_2); - for (uint64 i= 0; THD_count::value(); i++) + for (uint64 i= 0; THD_count::connection_thd_count(); i++) { /* This time the warnings are emitted within the loop to provide a @@ -2535,7 +2545,7 @@ void close_connection(THD *thd, uint sql_errno) if (sql_errno) { - net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL); + thd->protocol->net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL); thd->print_aborted_warning(lvl, ER_DEFAULT(sql_errno)); } else @@ -5181,6 +5191,7 @@ static int init_server_components() init_global_table_stats(); init_global_index_stats(); + init_update_queries(); /* Allow storage engine to give real error messages */ if (unlikely(ha_init_errors())) @@ -5424,7 +5435,6 @@ static int init_server_components() ft_init_stopwords(); init_max_user_conn(); - init_update_queries(); init_global_user_stats(); init_global_client_stats(); if (!opt_bootstrap) diff --git a/sql/protocol.cc b/sql/protocol.cc index 1eb7a096ca9..de5e8ad7a92 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -33,11 +33,6 @@ static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; /* Declared non-static only because of the embedded library. */ -bool net_send_error_packet(THD *, uint, const char *, const char *); -/* Declared non-static only because of the embedded library. */ -bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, - bool, bool); -/* Declared non-static only because of the embedded library. */ bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count); #ifndef EMBEDDED_LIBRARY static bool write_eof_packet(THD *, NET *, uint, uint); @@ -147,7 +142,7 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length, @retval TRUE An error occurred and the message wasn't sent properly */ -bool net_send_error(THD *thd, uint sql_errno, const char *err, +bool Protocol::net_send_error(THD *thd, uint sql_errno, const char *err, const char* sqlstate) { bool error; @@ -209,7 +204,7 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, #ifndef EMBEDDED_LIBRARY bool -net_send_ok(THD *thd, +Protocol::net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, bool is_eof, @@ -321,7 +316,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ */ bool -net_send_eof(THD *thd, uint server_status, uint statement_warn_count) +Protocol::net_send_eof(THD *thd, uint server_status, uint statement_warn_count) { NET *net= &thd->net; bool error= FALSE; @@ -412,7 +407,7 @@ static bool write_eof_packet(THD *thd, NET *net, @retval TRUE An error occurred and the messages wasn't sent properly */ -bool net_send_error_packet(THD *thd, uint sql_errno, const char *err, +bool Protocol::net_send_error_packet(THD *thd, uint sql_errno, const char *err, const char* sqlstate) { diff --git a/sql/protocol.h b/sql/protocol.h index 3b2c905ed9e..bcd98388f66 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -49,14 +49,13 @@ protected: } #endif uint field_count; -#ifndef EMBEDDED_LIBRARY - bool net_store_data(const uchar *from, size_t length); - bool net_store_data_cs(const uchar *from, size_t length, - CHARSET_INFO *fromcs, CHARSET_INFO *tocs); -#else virtual bool net_store_data(const uchar *from, size_t length); virtual bool net_store_data_cs(const uchar *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); + virtual bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, + bool, bool); + virtual bool net_send_error_packet(THD *, uint, const char *, const char *); +#ifdef EMBEDDED_LIBRARY char **next_field; MYSQL_FIELD *next_mysql_field; MEM_ROOT *alloc; @@ -156,6 +155,9 @@ public: }; virtual enum enum_protocol_type type()= 0; + virtual bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count); + bool net_send_error(THD *thd, uint sql_errno, const char *err, + const char* sqlstate); void end_statement(); friend int send_answer_1(Protocol *protocol, String *s1, String *s2, @@ -289,8 +291,6 @@ public: void send_warning(THD *thd, uint sql_errno, const char *err=0); -bool net_send_error(THD *thd, uint sql_errno, const char *err, - const char* sqlstate); void net_send_progress_packet(THD *thd); uchar *net_store_data(uchar *to,const uchar *from, size_t length); uchar *net_store_data(uchar *to,int32 from); diff --git a/sql/sql_class.h b/sql/sql_class.h index b39be36a9dd..7e4fd7749f4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1006,6 +1006,7 @@ struct THD_count { static Atomic_counter<uint32_t> count; static uint value() { return static_cast<uint>(count); } + static uint connection_thd_count(); THD_count() { count++; } ~THD_count() { count--; } }; @@ -3518,6 +3519,11 @@ public: user_time= t; set_time(); } + inline void force_set_time(my_time_t t, ulong sec_part) + { + start_time= system_time.sec= t; + start_time_sec_part= system_time.sec_part= sec_part; + } /* this is only used by replication and BINLOG command. usecs > TIME_MAX_SECOND_PART means "was not in binlog" @@ -3529,15 +3535,9 @@ public: else { if (sec_part <= TIME_MAX_SECOND_PART) - { - start_time= system_time.sec= t; - start_time_sec_part= system_time.sec_part= sec_part; - } + force_set_time(t, sec_part); else if (t != system_time.sec) - { - start_time= system_time.sec= t; - start_time_sec_part= system_time.sec_part= 0; - } + force_set_time(t, 0); else { start_time= t; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 733d281efd5..5837a4a0d5b 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1477,7 +1477,7 @@ void CONNECT::close_with_error(uint sql_errno, if (thd) { if (sql_errno) - net_send_error(thd, sql_errno, message, NULL); + thd->protocol->net_send_error(thd, sql_errno, message, NULL); close_connection(thd, close_error); delete thd; set_current_thd(0); diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 60ba38eae61..93412d527b8 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -222,6 +222,22 @@ struct json_service_st json_handler= json_unescape_json }; +struct sql_service_st sql_service_handler= +{ + mysql_init, + mysql_real_connect_local, + mysql_real_connect, + mysql_errno, + mysql_error, + mysql_real_query, + mysql_affected_rows, + mysql_num_rows, + mysql_store_result, + mysql_free_result, + mysql_fetch_row, + mysql_close, +}; + static struct st_service_ref list_of_services[]= { { "base64_service", VERSION_base64, &base64_handler }, @@ -245,5 +261,6 @@ static struct st_service_ref list_of_services[]= { "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler }, { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler }, { "wsrep_service", VERSION_wsrep, &wsrep_handler }, - { "json_service", VERSION_json, &json_handler } + { "json_service", VERSION_json, &json_handler }, + { "sql_service", VERSION_sql_service, &sql_service_handler }, }; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 980e37a7686..3d2c0e76ab3 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -129,6 +129,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8; #include "wsrep_mysqld.h" #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ +#include "sql_audit.h" // mysql_audit_release /** A result class used to send cursor rows using the binary protocol. @@ -248,63 +249,6 @@ private: class Ed_connection; -/** - Protocol_local: a helper class to intercept the result - of the data written to the network. -*/ - -class Protocol_local :public Protocol -{ -public: - Protocol_local(THD *thd, Ed_connection *ed_connection); - ~Protocol_local() { free_root(&m_rset_root, MYF(0)); } -protected: - virtual void prepare_for_resend(); - virtual bool write(); - virtual bool store_null(); - virtual bool store_tiny(longlong from); - virtual bool store_short(longlong from); - virtual bool store_long(longlong from); - virtual bool store_longlong(longlong from, bool unsigned_flag); - virtual bool store_decimal(const my_decimal *); - virtual bool store(const char *from, size_t length, CHARSET_INFO *cs); - virtual bool store(const char *from, size_t length, - CHARSET_INFO *fromcs, CHARSET_INFO *tocs); - virtual bool store(MYSQL_TIME *time, int decimals); - virtual bool store_date(MYSQL_TIME *time); - virtual bool store_time(MYSQL_TIME *time, int decimals); - virtual bool store(float value, uint32 decimals, String *buffer); - virtual bool store(double value, uint32 decimals, String *buffer); - virtual bool store(Field *field); - - virtual bool send_result_set_metadata(List<Item> *list, uint flags); - virtual bool send_out_parameters(List<Item_param> *sp_params); -#ifdef EMBEDDED_LIBRARY - void remove_last_row(); -#endif - virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; }; - - virtual bool send_ok(uint server_status, uint statement_warn_count, - ulonglong affected_rows, ulonglong last_insert_id, - const char *message, bool skip_flush); - - virtual bool send_eof(uint server_status, uint statement_warn_count); - virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate); -private: - bool store_string(const char *str, size_t length, - CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs); - - bool store_column(const void *data, size_t length); - void opt_add_row_to_rset(); -private: - Ed_connection *m_connection; - MEM_ROOT m_rset_root; - List<Ed_row> *m_rset; - size_t m_column_count; - Ed_column *m_current_row; - Ed_column *m_current_column; -}; - /****************************************************************************** Implementation ******************************************************************************/ @@ -3939,19 +3883,22 @@ Execute_sql_statement(LEX_STRING sql_text) executions without having to cleanup/reset THD in between. */ -bool -Execute_sql_statement::execute_server_code(THD *thd) +static bool execute_server_code(THD *thd, + const char *sql_text, size_t sql_len) { PSI_statement_locker *parent_locker; bool error; + query_id_t save_query_id= thd->query_id; + query_id_t next_id= next_query_id(); - if (alloc_query(thd, m_sql_text.str, m_sql_text.length)) + if (alloc_query(thd, sql_text, sql_len)) return TRUE; Parser_state parser_state; if (parser_state.init(thd, thd->query(), thd->query_length())) return TRUE; + thd->query_id= next_id; parser_state.m_lip.multi_statements= FALSE; lex_start(thd); @@ -3969,16 +3916,26 @@ Execute_sql_statement::execute_server_code(THD *thd) /* report error issued during command execution */ if (likely(error == 0) && thd->spcont == NULL) - general_log_write(thd, COM_STMT_EXECUTE, + general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); end: thd->lex->restore_set_statement_var(); + thd->query_id= save_query_id; + delete_explain_query(thd->lex); + lex_end(thd->lex); return error; } + +bool Execute_sql_statement::execute_server_code(THD *thd) +{ + return ::execute_server_code(thd, m_sql_text.str, m_sql_text.length); +} + + /*************************************************************************** Prepared_statement ****************************************************************************/ @@ -5255,12 +5212,12 @@ Ed_connection::free_old_result() */ bool -Ed_connection::execute_direct(LEX_STRING sql_text) +Ed_connection::execute_direct(Protocol *p, LEX_STRING sql_text) { Execute_sql_statement execute_sql_statement(sql_text); DBUG_PRINT("ed_query", ("%s", sql_text.str)); - return execute_direct(&execute_sql_statement); + return execute_direct(p, &execute_sql_statement); } @@ -5277,10 +5234,9 @@ Ed_connection::execute_direct(LEX_STRING sql_text) @param server_runnable A code fragment to execute. */ -bool Ed_connection::execute_direct(Server_runnable *server_runnable) +bool Ed_connection::execute_direct(Protocol *p, Server_runnable *server_runnable) { bool rc= FALSE; - Protocol_local protocol_local(m_thd, this); Prepared_statement stmt(m_thd); Protocol *save_protocol= m_thd->protocol; Diagnostics_area *save_diagnostics_area= m_thd->get_stmt_da(); @@ -5289,7 +5245,7 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable) free_old_result(); /* Delete all data from previous execution, if any */ - m_thd->protocol= &protocol_local; + m_thd->protocol= p; m_thd->set_stmt_da(&m_diagnostics_area); rc= stmt.execute_server_runnable(server_runnable); @@ -5376,354 +5332,831 @@ Ed_connection::store_result_set() return ed_result_set; } -/************************************************************************* -* Protocol_local -**************************************************************************/ +#include <mysql.h> +#include "../libmysqld/embedded_priv.h" -Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection) - :Protocol(thd), - m_connection(ed_connection), - m_rset(NULL), - m_column_count(0), - m_current_row(NULL), - m_current_column(NULL) +class Protocol_local : public Protocol_text { - clear_alloc_root(&m_rset_root); -} +public: + struct st_mysql_data *cur_data; + struct st_mysql_data *first_data; + struct st_mysql_data **data_tail; + void clear_data_list(); + struct st_mysql_data *alloc_new_dataset(); + char **next_field; + MYSQL_FIELD *next_mysql_field; + MEM_ROOT *alloc; + THD *new_thd; + Security_context empty_ctx; + + Protocol_local(THD *thd_arg, THD *new_thd_arg, ulong prealloc) : + Protocol_text(thd_arg, prealloc), + cur_data(0), first_data(0), data_tail(&first_data), alloc(0), + new_thd(new_thd_arg) + {} + +protected: + bool net_store_data(const uchar *from, size_t length); + bool net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); + bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count); + bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, + bool, bool); + bool net_send_error_packet(THD *, uint, const char *, const char *); + bool begin_dataset(); + bool begin_dataset(THD *thd, uint numfields); + + bool write(); + bool flush(); + + bool store_field_metadata(const THD *thd, const Send_field &field, + CHARSET_INFO *charset_for_protocol, + uint pos); + bool send_result_set_metadata(List<Item> *list, uint flags); + void remove_last_row(); + bool store_null(); + void prepare_for_resend(); + bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list); + + enum enum_protocol_type type() { return PROTOCOL_LOCAL; }; +}; -/** - Called between two result set rows. +static +bool +write_eof_packet_local(THD *thd, + Protocol_local *p, uint server_status, uint statement_warn_count) +{ +// if (!thd->mysql) // bootstrap file handling +// return FALSE; + /* + The following test should never be true, but it's better to do it + because if 'is_fatal_error' is set the server is not going to execute + other queries (see the if test in dispatch_command / COM_QUERY) + */ + if (thd->is_fatal_error) + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; + p->cur_data->embedded_info->server_status= server_status; + /* + Don't send warn count during SP execution, as the warn_list + is cleared between substatements, and mysqltest gets confused + */ + p->cur_data->embedded_info->warning_count= + (thd->spcont ? 0 : MY_MIN(statement_warn_count, 65535)); + return FALSE; +} - Prepare structures to fill result set rows. - Unfortunately, we can't return an error here. If memory allocation - fails, we'll have to return an error later. And so is done - in methods such as @sa store_column(). -*/ -void Protocol_local::prepare_for_resend() +MYSQL_DATA *Protocol_local::alloc_new_dataset() { - DBUG_ASSERT(alloc_root_inited(&m_rset_root)); + MYSQL_DATA *data; + struct embedded_query_result *emb_data; + if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &data, sizeof(*data), + &emb_data, sizeof(*emb_data), + NULL)) + return NULL; - opt_add_row_to_rset(); - /* Start a new row. */ - m_current_row= (Ed_column *) alloc_root(&m_rset_root, - sizeof(Ed_column) * m_column_count); - m_current_column= m_current_row; + emb_data->prev_ptr= &data->data; + cur_data= data; + *data_tail= data; + data_tail= &emb_data->next; + data->embedded_info= emb_data; + return data; } -/** - In "real" protocols this is called to finish a result set row. - Unused in the local implementation. -*/ - -bool Protocol_local::write() +void Protocol_local::clear_data_list() { - return FALSE; + while (first_data) + { + MYSQL_DATA *data= first_data; + first_data= data->embedded_info->next; + free_rows(data); + } + data_tail= &first_data; + free_rows(cur_data); + cur_data= 0; } -/** - A helper function to add the current row to the current result - set. Called in @sa prepare_for_resend(), when a new row is started, - and in send_eof(), when the result set is finished. -*/ -void Protocol_local::opt_add_row_to_rset() +static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { - if (m_current_row) + uint32 dummy32; + uint dummy_err; + char *result; + + /* 'tocs' is set 0 when client issues SET character_set_results=NULL */ + if (tocs && String::needs_conversion(0, fromcs, tocs, &dummy32)) { - /* Add the old row to the result set */ - Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count); - if (ed_row) - m_rset->push_back(ed_row, &m_rset_root); + uint new_len= (tocs->mbmaxlen * length) / fromcs->mbminlen + 1; + result= (char *)alloc_root(root, new_len); + length= copy_and_convert(result, new_len, + tocs, from, length, fromcs, &dummy_err); + } + else + { + result= (char *)alloc_root(root, length + 1); + memcpy(result, from, length); } -} + result[length]= 0; + return result; +} -/** - Add a NULL column to the current row. -*/ -bool Protocol_local::store_null() +static char *dup_str_aux(MEM_ROOT *root, const LEX_CSTRING &from, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { - if (m_current_column == NULL) - return TRUE; /* prepare_for_resend() failed to allocate memory. */ - - bzero(m_current_column, sizeof(*m_current_column)); - ++m_current_column; - return FALSE; + return dup_str_aux(root, from.str, (uint) from.length, fromcs, tocs); } -/** - A helper method to add any column to the current row - in its binary form. +static char *dup_str_aux(MEM_ROOT *root, const char *from, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs) +{ + return dup_str_aux(root, from, from ? strlen(from) : 0 , fromcs, tocs); +} - Allocates memory for the data in the result set memory root. -*/ -bool Protocol_local::store_column(const void *data, size_t length) +bool Protocol_local::net_store_data(const uchar *from, size_t length) { - if (m_current_column == NULL) - return TRUE; /* prepare_for_resend() failed to allocate memory. */ - /* - alloc_root() automatically aligns memory, so we don't need to - do any extra alignment if we're pointing to, say, an integer. - */ - m_current_column->str= (char*) memdup_root(&m_rset_root, - data, - length + 1 /* Safety */); - if (! m_current_column->str) + char *field_buf; +// if (!thd->mysql) // bootstrap file handling +// return FALSE; + + if (!(field_buf= (char*) alloc_root(alloc, length + sizeof(uint) + 1))) return TRUE; - m_current_column->str[length]= '\0'; /* Safety */ - m_current_column->length= length; - ++m_current_column; + *(uint *)field_buf= (uint) length; + *next_field= field_buf + sizeof(uint); + memcpy((uchar*) *next_field, from, length); + (*next_field)[length]= 0; + if (next_mysql_field->max_length < length) + next_mysql_field->max_length= (unsigned long) length; + ++next_field; + ++next_mysql_field; return FALSE; } +bool Protocol_local::net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) +{ + uint conv_length= (uint) (to_cs->mbmaxlen * length / from_cs->mbminlen); + uint dummy_error; + char *field_buf; +// if (!thd->mysql) // bootstrap file handling +// return false; + + if (!(field_buf= (char*) alloc_root(alloc, conv_length + sizeof(uint) + 1))) + return true; + *next_field= field_buf + sizeof(uint); + length= copy_and_convert(*next_field, conv_length, to_cs, + (const char*) from, length, from_cs, &dummy_error); + *(uint *) field_buf= (uint) length; + (*next_field)[length]= 0; + if (next_mysql_field->max_length < length) + next_mysql_field->max_length= (unsigned long) length; + ++next_field; + ++next_mysql_field; + return false; +} + + /** - Store a string value in a result set column, optionally - having converted it to character_set_results. + Embedded library implementation of OK response. + + This function is used by the server to write 'OK' packet to + the "network" when the server is compiled as an embedded library. + Since there is no network in the embedded configuration, + a different implementation is necessary. + Instead of marshalling response parameters to a network representation + and then writing it to the socket, here we simply copy the data to the + corresponding client-side connection structures. + + @sa Server implementation of net_send_ok in protocol.cc for + description of the arguments. + + @return + @retval TRUE An error occurred + @retval FALSE Success */ bool -Protocol_local::store_string(const char *str, size_t length, - CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs) +Protocol_local::net_send_ok(THD *thd, + uint server_status, uint statement_warn_count, + ulonglong affected_rows, ulonglong id, const char *message, bool, bool) { - /* Store with conversion */ - uint error_unused; + DBUG_ENTER("emb_net_send_ok"); + MYSQL_DATA *data; +// MYSQL *mysql= thd->mysql; - if (dst_cs && !my_charset_same(src_cs, dst_cs) && - src_cs != &my_charset_bin && - dst_cs != &my_charset_bin) - { - if (unlikely(convert->copy(str, length, src_cs, dst_cs, &error_unused))) - return TRUE; - str= convert->ptr(); - length= convert->length(); - } - return store_column(str, length); +// if (!mysql) // bootstrap file handling +// DBUG_RETURN(FALSE); + if (!(data= alloc_new_dataset())) + DBUG_RETURN(TRUE); + data->embedded_info->affected_rows= affected_rows; + data->embedded_info->insert_id= id; + if (message) + strmake_buf(data->embedded_info->info, message); + + bool error= write_eof_packet_local(thd, this, + server_status, statement_warn_count); + cur_data= 0; + DBUG_RETURN(error); } -/** Store a tiny int as is (1 byte) in a result set column. */ +/** + Embedded library implementation of EOF response. + + @sa net_send_ok -bool Protocol_local::store_tiny(longlong value) + @return + @retval TRUE An error occurred + @retval FALSE Success +*/ + +bool +Protocol_local::net_send_eof(THD *thd, uint server_status, + uint statement_warn_count) { - char v= (char) value; - return store_column(&v, 1); + bool error= write_eof_packet_local(thd, this, server_status, + statement_warn_count); + cur_data= 0; + return error; } -/** Store a short as is (2 bytes, host order) in a result set column. */ +bool Protocol_local::net_send_error_packet(THD *thd, uint sql_errno, + const char *err, const char *sqlstate) +{ + uint error; + char converted_err[MYSQL_ERRMSG_SIZE]; + MYSQL_DATA *data= cur_data; + struct embedded_query_result *ei; + +// if (!thd->mysql) // bootstrap file handling +// { +// fprintf(stderr, "ERROR: %d %s\n", sql_errno, err); +// return TRUE; +// } + if (!data) + data= alloc_new_dataset(); + + ei= data->embedded_info; + ei->last_errno= sql_errno; + convert_error_message(converted_err, sizeof(converted_err), + thd->variables.character_set_results, + err, strlen(err), + system_charset_info, &error); + /* Converted error message is always null-terminated. */ + strmake_buf(ei->info, converted_err); + strmov(ei->sqlstate, sqlstate); + ei->server_status= thd->server_status; + cur_data= 0; + return FALSE; +} + -bool Protocol_local::store_short(longlong value) +bool Protocol_local::begin_dataset() { - int16 v= (int16) value; - return store_column(&v, 2); + MYSQL_DATA *data= alloc_new_dataset(); + if (!data) + return 1; + alloc= &data->alloc; + /* Assume rowlength < 8192 */ + init_alloc_root(alloc, "dataset", 8192, 0, MYF(0)); + alloc->min_malloc= sizeof(MYSQL_ROWS); + return 0; } -/** Store a "long" as is (4 bytes, host order) in a result set column. */ - -bool Protocol_local::store_long(longlong value) +bool Protocol_local::begin_dataset(THD *thd, uint numfields) { - int32 v= (int32) value; - return store_column(&v, 4); + if (begin_dataset()) + return true; + MYSQL_DATA *data= cur_data; + data->fields= field_count= numfields; + if (!(data->embedded_info->fields_list= + (MYSQL_FIELD*)alloc_root(&data->alloc, sizeof(MYSQL_FIELD)*field_count))) + return true; + return false; } -/** Store a "longlong" as is (8 bytes, host order) in a result set column. */ - -bool Protocol_local::store_longlong(longlong value, bool unsigned_flag) +bool Protocol_local::write() { - int64 v= (int64) value; - return store_column(&v, 8); -} +// if (!thd->mysql) // bootstrap file handling +// return false; + *next_field= 0; + return false; +} -/** Store a decimal in string format in a result set column */ -bool Protocol_local::store_decimal(const my_decimal *value) +bool Protocol_local::flush() { - DBUG_ASSERT(0); // This method is not used yet - StringBuffer<DECIMAL_MAX_STR_LENGTH> str; - return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true; + return 0; } -/** Convert to cs_results and store a string. */ +bool Protocol_local::store_field_metadata(const THD * thd, + const Send_field &server_field, + CHARSET_INFO *charset_for_protocol, + uint pos) +{ + CHARSET_INFO *cs= system_charset_info; + CHARSET_INFO *thd_cs= thd->variables.character_set_results; + MYSQL_DATA *data= cur_data; + MEM_ROOT *field_alloc= &data->alloc; + MYSQL_FIELD *client_field= &cur_data->embedded_info->fields_list[pos]; + DBUG_ASSERT(server_field.is_sane()); + + client_field->db= dup_str_aux(field_alloc, server_field.db_name, + cs, thd_cs); + client_field->table= dup_str_aux(field_alloc, server_field.table_name, + cs, thd_cs); + client_field->name= dup_str_aux(field_alloc, server_field.col_name, + cs, thd_cs); + client_field->org_table= dup_str_aux(field_alloc, server_field.org_table_name, + cs, thd_cs); + client_field->org_name= dup_str_aux(field_alloc, server_field.org_col_name, + cs, thd_cs); + if (charset_for_protocol == &my_charset_bin || thd_cs == NULL) + { + /* No conversion */ + client_field->charsetnr= charset_for_protocol->number; + client_field->length= server_field.length; + } + else + { + /* With conversion */ + client_field->charsetnr= thd_cs->number; + client_field->length= server_field.max_octet_length(charset_for_protocol, + thd_cs); + } + client_field->type= server_field.type_handler()->type_code_for_protocol(); + client_field->flags= (uint16) server_field.flags; + client_field->decimals= server_field.decimals; -bool Protocol_local::store(const char *str, size_t length, - CHARSET_INFO *src_cs) -{ - CHARSET_INFO *dst_cs; + client_field->db_length= (unsigned int) strlen(client_field->db); + client_field->table_length= (unsigned int) strlen(client_field->table); + client_field->name_length= (unsigned int) strlen(client_field->name); + client_field->org_name_length= (unsigned int) strlen(client_field->org_name); + client_field->org_table_length= (unsigned int) strlen(client_field->org_table); - dst_cs= m_connection->m_thd->variables.character_set_results; - return store_string(str, length, src_cs, dst_cs); -} + client_field->catalog= dup_str_aux(field_alloc, "def", 3, cs, thd_cs); + client_field->catalog_length= 3; + if (IS_NUM(client_field->type)) + client_field->flags|= NUM_FLAG; -/** Store a string. */ + client_field->max_length= 0; + client_field->def= 0; + return false; +} -bool Protocol_local::store(const char *str, size_t length, - CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs) + +void Protocol_local::remove_last_row() { - return store_string(str, length, src_cs, dst_cs); -} + MYSQL_DATA *data= cur_data; + MYSQL_ROWS **last_row_hook= &data->data; + my_ulonglong count= data->rows; + DBUG_ENTER("Protocol_text::remove_last_row"); + while (--count) + last_row_hook= &(*last_row_hook)->next; + + *last_row_hook= 0; + data->embedded_info->prev_ptr= last_row_hook; + data->rows--; + DBUG_VOID_RETURN; +} -/* Store MYSQL_TIME (in binary format) */ -bool Protocol_local::store(MYSQL_TIME *time, int decimals) +bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags) { - if (decimals != AUTO_SEC_PART_DIGITS) - my_datetime_trunc(time, decimals); - return store_column(time, sizeof(MYSQL_TIME)); -} + List_iterator_fast<Item> it(*list); + Item *item; + DBUG_ENTER("send_result_set_metadata"); +// if (!thd->mysql) // bootstrap file handling +// DBUG_RETURN(0); -/** Store MYSQL_TIME (in binary format) */ + if (begin_dataset(thd, list->elements)) + goto err; -bool Protocol_local::store_date(MYSQL_TIME *time) -{ - return store_column(time, sizeof(MYSQL_TIME)); -} + for (uint pos= 0 ; (item= it++); pos++) + { + Send_field sf(thd, item); + if (store_field_metadata(thd, sf, item->charset_for_protocol(), pos)) + goto err; + } + if (flags & SEND_EOF) + write_eof_packet_local(thd, this, thd->server_status, + thd->get_stmt_da()->current_statement_warn_count()); + + DBUG_RETURN(prepare_for_send(list->elements)); + err: + my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ +} -/** Store MYSQL_TIME (in binary format) */ -bool Protocol_local::store_time(MYSQL_TIME *time, int decimals) +static void +list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos) { - if (decimals != AUTO_SEC_PART_DIGITS) - my_time_trunc(time, decimals); - return store_column(time, sizeof(MYSQL_TIME)); -} + char buff[80]; + String tmp(buff, sizeof(buff), default_charset_info), *res; + MYSQL_FIELD *client_field= &p->cur_data->embedded_info->fields_list[pos]; + if (fld->is_null() || !(res= fld->val_str(&tmp))) + { + client_field->def_length= 0; + client_field->def= strmake_root(&p->cur_data->alloc, "", 0); + } + else + { + client_field->def_length= res->length(); + client_field->def= strmake_root(&p->cur_data->alloc, res->ptr(), + client_field->def_length); + } +} -/* Store a floating point number, as is. */ -bool Protocol_local::store(float value, uint32 decimals, String *buffer) +bool Protocol_local::send_list_fields(List<Field> *list, const TABLE_LIST *table_list) { - return store_column(&value, sizeof(float)); + DBUG_ENTER("send_result_set_metadata"); + Protocol_text prot(thd); + List_iterator_fast<Field> it(*list); + Field *fld; + +// if (!thd->mysql) // bootstrap file handling +// DBUG_RETURN(0); + + if (begin_dataset(thd, list->elements)) + goto err; + + for (uint pos= 0 ; (fld= it++); pos++) + { + if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos)) + goto err; + list_fields_send_default(thd, this, fld, pos); + } + + DBUG_RETURN(prepare_for_send(list->elements)); +err: + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + DBUG_RETURN(1); } -/* Store a double precision number, as is. */ +void Protocol_local::prepare_for_resend() +{ + MYSQL_ROWS *cur; + MYSQL_DATA *data= cur_data; + DBUG_ENTER("send_data"); + +// if (!thd->mysql) // bootstrap file handling +// DBUG_VOID_RETURN; -bool Protocol_local::store(double value, uint32 decimals, String *buffer) + data->rows++; + if (!(cur= (MYSQL_ROWS *)alloc_root(alloc, sizeof(MYSQL_ROWS)+(field_count + 1) * sizeof(char *)))) + { + my_error(ER_OUT_OF_RESOURCES,MYF(0)); + DBUG_VOID_RETURN; + } + cur->data= (MYSQL_ROW)(((char *)cur) + sizeof(MYSQL_ROWS)); + + *data->embedded_info->prev_ptr= cur; + data->embedded_info->prev_ptr= &cur->next; + next_field=cur->data; + next_mysql_field= data->embedded_info->fields_list; +#ifndef DBUG_OFF + field_pos= 0; +#endif + + DBUG_VOID_RETURN; +} + +bool Protocol_local::store_null() { - return store_column(&value, sizeof (double)); + *(next_field++)= NULL; + ++next_mysql_field; + return false; } -/* Store a Field. */ +#include <sql_common.h> +#include <errmsg.h> -bool Protocol_local::store(Field *field) +static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data) { - if (field->is_null()) - return store_null(); - return field->send_binary(this); + NET *net= &mysql->net; + struct embedded_query_result *ei= data->embedded_info; + net->last_errno= ei->last_errno; + strmake_buf(net->last_error, ei->info); + memcpy(net->sqlstate, ei->sqlstate, sizeof(net->sqlstate)); + mysql->server_status= ei->server_status; + my_free(data); } -/** Called to start a new result set. */ - -bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint) +static my_bool loc_read_query_result(MYSQL *mysql) { - DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root)); + Protocol_local *p= (Protocol_local *) mysql->thd; - init_sql_alloc(&m_rset_root, "send_result_set_metadata", - MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC)); + MYSQL_DATA *res= p->first_data; + DBUG_ASSERT(!p->cur_data); + p->first_data= res->embedded_info->next; + if (res->embedded_info->last_errno && + !res->embedded_info->fields_list) + { + embedded_get_error(mysql, res); + return 1; + } - if (! (m_rset= new (&m_rset_root) List<Ed_row>)) - return TRUE; + mysql->warning_count= res->embedded_info->warning_count; + mysql->server_status= res->embedded_info->server_status; + mysql->field_count= res->fields; + if (!(mysql->fields= res->embedded_info->fields_list)) + { + mysql->affected_rows= res->embedded_info->affected_rows; + mysql->insert_id= res->embedded_info->insert_id; + } + net_clear_error(&mysql->net); + mysql->info= 0; - m_column_count= columns->elements; + if (res->embedded_info->info[0]) + { + strmake(mysql->info_buffer, res->embedded_info->info, MYSQL_ERRMSG_SIZE-1); + mysql->info= mysql->info_buffer; + } - return FALSE; -} + if (res->embedded_info->fields_list) + { + mysql->status=MYSQL_STATUS_GET_RESULT; + p->cur_data= res; + } + else + my_free(res); + return 0; +} -/** - Normally this is a separate result set with OUT parameters - of stored procedures. Currently unsupported for the local - version. -*/ -bool Protocol_local::send_out_parameters(List<Item_param> *sp_params) +static my_bool +loc_advanced_command(MYSQL *mysql, enum enum_server_command command, + const uchar *header, ulong header_length, + const uchar *arg, ulong arg_length, my_bool skip_check, + MYSQL_STMT *stmt) { - return FALSE; -} + my_bool result= 1; + Protocol_local *p= (Protocol_local *) mysql->thd; + NET *net= &mysql->net; + if (p->thd && p->thd->killed != NOT_KILLED) + { + if (p->thd->killed < KILL_CONNECTION) + p->thd->killed= NOT_KILLED; + else + return 1; + } -/** Called for statements that don't have a result set, at statement end. */ + p->clear_data_list(); + /* Check that we are calling the client functions in right order */ + if (mysql->status != MYSQL_STATUS_READY) + { + set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + goto end; + } -bool -Protocol_local::send_ok(uint server_status, uint statement_warn_count, - ulonglong affected_rows, ulonglong last_insert_id, - const char *message, bool skip_flush) -{ - /* - Just make sure nothing is sent to the client, we have grabbed - the status information in the connection diagnostics area. + /* Clear result variables */ + p->thd->clear_error(1); + mysql->affected_rows= ~(my_ulonglong) 0; + mysql->field_count= 0; + net_clear_error(net); + + /* + We have to call free_old_query before we start to fill mysql->fields + for new query. In the case of embedded server we collect field data + during query execution (not during data retrieval as it is in remote + client). So we have to call free_old_query here */ - return FALSE; + free_old_query(mysql); + + if (header) + { + arg= header; + arg_length= header_length; + } + + if (p->new_thd) + { + THD *thd_orig= current_thd; + set_current_thd(p->thd); + p->thd->thread_stack= (char*) &result; + p->thd->set_time(); + result= execute_server_code(p->thd, (const char *)arg, arg_length); + p->thd->cleanup_after_query(); + mysql_audit_release(p->thd); + p->end_statement(); + set_current_thd(thd_orig); + } + else + { + Ed_connection con(p->thd); + Security_context *ctx_orig= p->thd->security_ctx; + MYSQL_LEX_STRING sql_text; + DBUG_ASSERT(current_thd == p->thd); + sql_text.str= (char *) arg; + sql_text.length= arg_length; + p->thd->security_ctx= &p->empty_ctx; + result= con.execute_direct(p, sql_text); + p->thd->security_ctx= ctx_orig; + } + if (skip_check) + result= 0; + p->cur_data= 0; + +end: + return result; } -/** - Called at the end of a result set. Append a complete - result set to the list in Ed_connection. +/* + reads dataset from the next query result + + SYNOPSIS + loc_read_rows() + mysql connection handle + other parameters are not used - Don't send anything to the client, but instead finish - building of the result set at hand. + NOTES + It just gets next MYSQL_DATA from the result's queue + + RETURN + pointer to MYSQL_DATA with the coming recordset */ -bool Protocol_local::send_eof(uint server_status, uint statement_warn_count) +static MYSQL_DATA * +loc_read_rows(MYSQL *mysql, MYSQL_FIELD *mysql_fields __attribute__((unused)), + unsigned int fields __attribute__((unused))) { - Ed_result_set *ed_result_set; - - DBUG_ASSERT(m_rset); + MYSQL_DATA *result= ((Protocol_local *)mysql->thd)->cur_data; + ((Protocol_local *)mysql->thd)->cur_data= 0; + if (result->embedded_info->last_errno) + { + embedded_get_error(mysql, result); + return NULL; + } + *result->embedded_info->prev_ptr= NULL; + return result; +} - opt_add_row_to_rset(); - m_current_row= 0; - ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count, - &m_rset_root); +/************************************************************************** + Get column lengths of the current row + If one uses mysql_use_result, res->lengths contains the length information, + else the lengths are calculated from the offset between pointers. +**************************************************************************/ - m_rset= NULL; +static void loc_fetch_lengths(ulong *to, MYSQL_ROW column, + unsigned int field_count) +{ + MYSQL_ROW end; - if (! ed_result_set) - return TRUE; + for (end=column + field_count; column != end ; column++,to++) + *to= *column ? *(uint *)((*column) - sizeof(uint)) : 0; +} - /* In case of successful allocation memory ownership was transferred. */ - DBUG_ASSERT(!alloc_root_inited(&m_rset_root)); - /* - Link the created Ed_result_set instance into the list of connection - result sets. Never fails. - */ - m_connection->add_result_set(ed_result_set); - return FALSE; +static void loc_flush_use_result(MYSQL *mysql, my_bool) +{ + Protocol_local *p= (Protocol_local *) mysql->thd; + if (p->cur_data) + { + free_rows(p->cur_data); + p->cur_data= 0; + } + else if (p->first_data) + { + MYSQL_DATA *data= p->first_data; + p->first_data= data->embedded_info->next; + free_rows(data); + } } -/** Called to send an error to the client at the end of a statement. */ - -bool -Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*) +static void loc_on_close_free(MYSQL *mysql) { - /* - Just make sure that nothing is sent to the client (default - implementation). - */ - return FALSE; + Protocol_local *p= (Protocol_local *) mysql->thd; + THD *thd= p->new_thd; + delete p; + if (thd) + { + delete thd; + local_connection_thread_count--; + } + my_free(mysql->info_buffer); + mysql->info_buffer= 0; } +static MYSQL_RES *loc_use_result(MYSQL *mysql) +{ + return mysql_store_result(mysql); +} +static MYSQL_METHODS local_methods= +{ + loc_read_query_result, /* read_query_result */ + loc_advanced_command, /* advanced_command */ + loc_read_rows, /* read_rows */ + loc_use_result, /* use_result */ + loc_fetch_lengths, /* fetch_lengths */ + loc_flush_use_result, /* flush_use_result */ + NULL, /* read_change_user_result */ + loc_on_close_free /* on_close_free */ #ifdef EMBEDDED_LIBRARY -void Protocol_local::remove_last_row() -{ } + ,NULL, /* list_fields */ + NULL, /* read_prepare_result */ + NULL, /* stmt_execute */ + NULL, /* read_binary_rows */ + NULL, /* unbuffered_fetch */ + NULL, /* read_statistics */ + NULL, /* next_result */ + NULL /* read_rows_from_cursor */ #endif +}; + + +Atomic_counter<uint32_t> local_connection_thread_count; + +extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql) +{ + THD *thd_orig= current_thd; + THD *new_thd; + Protocol_local *p; + DBUG_ENTER("mysql_real_connect_local"); + + /* Test whether we're already connected */ + if (mysql->server_version) + { + set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate); + DBUG_RETURN(0); + } + + mysql->methods= &local_methods; + mysql->user= NULL; + + mysql->info_buffer= (char *) my_malloc(MYSQL_ERRMSG_SIZE, MYF(0)); + if (!thd_orig || thd_orig->lock) + { + /* + When we start with the empty current_thd (that happens when plugins + are loaded during the server start) or when some tables are locked + with the current_thd already (that happens when INSTALL PLUGIN + calls the plugin_init or with queries), we create the new THD for + the local connection. So queries with this MYSQL will be run with + it rather than the current THD. + */ + + new_thd= new THD(0); + local_connection_thread_count++; + new_thd->thread_stack= (char*) &thd_orig; + new_thd->store_globals(); + new_thd->security_ctx->skip_grants(); + new_thd->query_cache_is_applicable= 0; + new_thd->variables.wsrep_on= 0; + /* + TOSO: decide if we should turn the auditing off + for such threads. + We can do it like this: + new_thd->audit_class_mask[0]= ~0; + */ + bzero((char*) &new_thd->net, sizeof(new_thd->net)); + set_current_thd(thd_orig); + thd_orig= new_thd; + } + else + new_thd= NULL; + + p= new Protocol_local(thd_orig, new_thd, 0); + if (new_thd) + new_thd->protocol= p; + else + { + p->empty_ctx.init(); + p->empty_ctx.skip_grants(); + } + + mysql->thd= p; + mysql->server_status= SERVER_STATUS_AUTOCOMMIT; + + + DBUG_PRINT("exit",("Mysql handler: %p", mysql)); + DBUG_RETURN(mysql); +} + diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h index acdaa9a67a7..1a96df85a19 100644 --- a/sql/sql_prepare.h +++ b/sql/sql_prepare.h @@ -202,7 +202,7 @@ public: @retval TRUE error, use get_last_error() to see the error number. */ - bool execute_direct(LEX_STRING sql_text); + bool execute_direct(Protocol *p, LEX_STRING sql_text); /** Same as the previous, but takes an instance of Server_runnable @@ -215,7 +215,7 @@ public: return a result set @retval TRUE failure */ - bool execute_direct(Server_runnable *server_runnable); + bool execute_direct(Protocol *p, Server_runnable *server_runnable); /** Get the number of affected (deleted, updated) @@ -311,7 +311,6 @@ private: THD *m_thd; Ed_result_set *m_rsets; Ed_result_set *m_current_rset; - friend class Protocol_local; private: void free_old_result(); void add_result_set(Ed_result_set *ed_result_set); @@ -354,4 +353,6 @@ private: size_t m_column_count; /* TODO: change to point to metadata */ }; +extern Atomic_counter<uint32_t> local_connection_thread_count; + #endif // SQL_PREPARE_H diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index e873837694b..d68c408403b 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -98,7 +98,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_config.h> +#include <my_global.h> #include <mysql/plugin.h> #include "ha_example.h" #include "sql_class.h" |