MDEV-19275 Provide SQL service to plugins.

SQL service added.
It provides the limited set of client library functions
to be used by plugin.
This commit is contained in:
Alexey Botchkov 2021-09-06 22:34:35 +04:00 committed by Oleksandr Byelkin
parent 401ff6994d
commit 0a0dfd63d9
25 changed files with 701 additions and 253 deletions

View file

@ -77,7 +77,7 @@ typedef struct st_mysql_xid MYSQL_XID;
#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0104
/* MariaDB plugin interface version */
#define MARIA_PLUGIN_INTERFACE_VERSION 0x010e
#define MARIA_PLUGIN_INTERFACE_VERSION 0x010f
/*
The allowable types of plugins

View file

@ -0,0 +1,99 @@
/* 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 */
#if defined(MYSQL_SERVER) && !defined MYSQL_SERVICE_SQL
#define MYSQL_SERVICE_SQL
#include <mysql.h>
/**
@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)(MYSQL *mysql);
MYSQL *(*mysql_real_connect_local)(MYSQL *mysql,
const char *host, const char *user, const char *db,
unsigned long clientflag);
MYSQL *(STDCALL *mysql_real_connect)(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)(MYSQL *mysql);
const char *(STDCALL *mysql_error)(MYSQL *mysql);
int (STDCALL *mysql_real_query)(MYSQL *mysql, const char *q,
unsigned long length);
my_ulonglong (STDCALL *mysql_affected_rows)(MYSQL *mysql);
my_ulonglong (STDCALL *mysql_num_rows)(MYSQL_RES *res);
MYSQL_RES *(STDCALL *mysql_store_result)(MYSQL *mysql);
void (STDCALL *mysql_free_result)(MYSQL_RES *result);
MYSQL_ROW (STDCALL *mysql_fetch_row)(MYSQL_RES *result);
void (STDCALL *mysql_close)(MYSQL *sock);
} *sql_service;
#ifdef MYSQL_DYNAMIC_PLUGIN
#define mysql_init sql_service->mysql_init
#define mysql_real_connect_local sql_service->mysql_real_connect_local
#define mysql_real_connect sql_service->mysql_real_connect
#define mysql_errno(M) sql_service->mysql_errno(M)
#define mysql_error(M) sql_service->mysql_error(M)
#define mysql_real_query sql_service->mysql_real_query
#define mysql_affected_rows sql_service->mysql_affected_rows
#define mysql_num_rows sql_service->mysql_num_rows
#define mysql_store_result sql_service->mysql_store_result
#define mysql_free_result sql_service->mysql_free_result
#define mysql_fetch_row sql_service->mysql_fetch_row
#define mysql_close sql_service->mysql_close
#else
MYSQL *mysql_real_connect_local(MYSQL *mysql,
const char *host, const char *user, const char *db,
unsigned long clientflag);
/* The rest of the function declarations mest be taken from the mysql.h */
#endif /*MYSQL_DYNAMIC_PLUGIN*/
#ifdef __cplusplus
}
#endif
#endif /*MYSQL_SERVICE_SQL */

View file

@ -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
}

View file

@ -44,3 +44,4 @@
#define VERSION_wsrep 0x0500
#define VERSION_json 0x0100
#define VERSION_thd_mdl 0x0100
#define VERSION_sql_service 0x0100

View file

@ -61,13 +61,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);

View file

@ -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;
}
}
@ -430,7 +430,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);
@ -453,12 +453,23 @@ 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,
@ -468,12 +479,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

View file

@ -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})

19
libservices/sql_service.c Normal file
View file

@ -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;

View file

@ -5,7 +5,7 @@ plugin_version 1.0
plugin_status ACTIVE
plugin_type DAEMON
plugin_library handlersocket.so
plugin_library_version 1.14
plugin_library_version 1.15
plugin_author higuchi dot akira at dena dot jp
plugin_description Direct access into InnoDB
plugin_license BSD

View file

@ -12,7 +12,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE STORAGE ENGINE
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Brian Aker, MySQL AB
PLUGIN_DESCRIPTION Example storage engine
PLUGIN_LICENSE GPL
@ -25,7 +25,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE DAEMON
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Sergei Golubchik
PLUGIN_DESCRIPTION Unusable Daemon
PLUGIN_LICENSE GPL
@ -64,7 +64,7 @@ PLUGIN_STATUS DELETED
PLUGIN_TYPE STORAGE ENGINE
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Brian Aker, MySQL AB
PLUGIN_DESCRIPTION Example storage engine
PLUGIN_LICENSE GPL

View file

@ -27,7 +27,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE AUTHENTICATION
PLUGIN_TYPE_VERSION 2.2
PLUGIN_LIBRARY auth_ed25519.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Sergei Golubchik
PLUGIN_DESCRIPTION Elliptic curve ED25519 based authentication
PLUGIN_LICENSE GPL

View file

@ -6,7 +6,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE PASSWORD VALIDATION
PLUGIN_TYPE_VERSION 1.0
PLUGIN_LIBRARY cracklib_password_check.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Sergei Golubchik
PLUGIN_DESCRIPTION Password validation via CrackLib
PLUGIN_LICENSE GPL

View file

@ -4,8 +4,8 @@ Variable_name Value
Opened_plugin_libraries 0
select * from information_schema.all_plugins where plugin_library='ha_example.so';
PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS PLUGIN_TYPE PLUGIN_TYPE_VERSION PLUGIN_LIBRARY PLUGIN_LIBRARY_VERSION PLUGIN_AUTHOR PLUGIN_DESCRIPTION PLUGIN_LICENSE LOAD_OPTION PLUGIN_MATURITY PLUGIN_AUTH_VERSION
EXAMPLE 0.1 NOT INSTALLED STORAGE ENGINE MYSQL_VERSION_ID ha_example.so 1.14 Brian Aker, MySQL AB Example storage engine GPL OFF Experimental 0.1
UNUSABLE 3.14 NOT INSTALLED DAEMON MYSQL_VERSION_ID ha_example.so 1.14 Sergei Golubchik Unusable Daemon GPL OFF Experimental 3.14.15.926
EXAMPLE 0.1 NOT INSTALLED STORAGE ENGINE MYSQL_VERSION_ID ha_example.so 1.15 Brian Aker, MySQL AB Example storage engine GPL OFF Experimental 0.1
UNUSABLE 3.14 NOT INSTALLED DAEMON MYSQL_VERSION_ID ha_example.so 1.15 Sergei Golubchik Unusable Daemon GPL OFF Experimental 3.14.15.926
show status like '%libraries%';
Variable_name Value
Opened_plugin_libraries 1

View file

@ -6,7 +6,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE PASSWORD VALIDATION
PLUGIN_TYPE_VERSION 1.0
PLUGIN_LIBRARY simple_password_check.so
PLUGIN_LIBRARY_VERSION 1.14
PLUGIN_LIBRARY_VERSION 1.15
PLUGIN_AUTHOR Sergei Golubchik
PLUGIN_DESCRIPTION Simple password strength checks
PLUGIN_LICENSE GPL

View file

@ -1,8 +1,51 @@
install plugin test_sql_service soname 'test_sql_service';
set global test_sql_service_run_test= 1;
show status like 'test_sql_service%';
show status like 'test_sql_service_passed';
Variable_name Value
Test_sql_service_passed 0
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'
uninstall plugin test_sql_service;
Warnings:
Warning 1620 Plugin is busy and will be uninstalled on shutdown

View file

@ -9,9 +9,40 @@ 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%';
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';
uninstall plugin test_sql_service;

View file

@ -15,4 +15,5 @@
SET(SOURCES test_sql_service.c)
MYSQL_ADD_PLUGIN(test_sql_service ${SOURCES} MODULE_ONLY RECOMPILE_FOR_EMBEDDED)
ADD_DEFINITIONS(-DMYSQL_SERVER)
MYSQL_ADD_PLUGIN(test_sql_service ${SOURCES} MODULE_ONLY)

View file

@ -14,71 +14,113 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#define PLUGIN_VERSION 0x100
#define PLUGIN_STR_VERSION "1.0.0"
#define _my_thread_var loc_thread_var
#define PLUGIN_VERSION 0x20000
#define PLUGIN_STR_VERSION "2.0"
#include <my_config.h>
#include <assert.h>
#include <my_global.h>
#include <my_base.h>
#include <typelib.h>
//#include <mysql_com.h> /* for enum enum_server_command */
#include <mysql/plugin.h>
#include <mysql/plugin_audit.h>
//#include <string.h>
#include <mysql.h>
LEX_STRING * thd_query_string (MYSQL_THD thd);
unsigned long long thd_query_id(const MYSQL_THD thd);
size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
const char *thd_user_name(MYSQL_THD thd);
const char *thd_client_host(MYSQL_THD thd);
const char *thd_client_ip(MYSQL_THD thd);
LEX_CSTRING *thd_current_db(MYSQL_THD thd);
int thd_current_status(MYSQL_THD thd);
enum enum_server_command thd_current_command(MYSQL_THD thd);
int maria_compare_hostname(const char *wild_host, long wild_ip, long ip_mask,
const char *host, const char *ip);
void maria_update_hostname(const char **wild_host, long *wild_ip, long *ip_mask,
const char *host);
/* Status variables for SHOW STATUS */
static long test_passed= 0;
static 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= TRUE;
static void run_test(MYSQL_THD thd, struct st_mysql_sys_var *var,
void *var_ptr, const void *save);
static MYSQL_SYSVAR_BOOL(run_test, do_test, PLUGIN_VAR_OPCMDARG,
"Perform the test now.", NULL, run_test, FALSE);
static 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, FALSE);
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, FALSE);
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, FALSE);
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;
extern int execute_sql_command(const char *command,
char *hosts, char *names, char *filters);
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()
{
char plugins[1024];
char names[1024];
char dl[2048];
int result;
MYSQL *mysql;
int result= 1;
result= execute_sql_command("select 'plugin', name, dl from mysql.plugin",
plugins, names, dl);
mysql= mysql_init(NULL);
if (mysql_real_connect_local(mysql, NULL, NULL, NULL, 0) == NULL)
return 1;
if (run_queries(mysql))
goto exit;
if (run_queries(global_mysql))
goto exit;
result= 0;
exit:
mysql_close(mysql);
return result;
}
@ -89,12 +131,87 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev)
}
static void run_test(MYSQL_THD thd __attribute__((unused)),
struct st_mysql_sys_var *var __attribute__((unused)),
void *var_ptr __attribute__((unused)),
const void *save __attribute__((unused)))
static int run_test(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save,
struct st_mysql_value *value)
{
test_passed= do_tests();
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, NULL, NULL, 0) == 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);
}
@ -102,7 +219,16 @@ static int init_done= 0;
static int test_sql_service_plugin_init(void *p __attribute__((unused)))
{
global_mysql= mysql_init(NULL);
if (!global_mysql ||
mysql_real_connect_local(global_mysql, NULL, NULL, NULL, 0) == NULL)
return 1;
init_done= 1;
test_passed= (do_tests() == 0);
return 0;
}
@ -112,6 +238,8 @@ static int test_sql_service_plugin_deinit(void *p __attribute__((unused)))
if (!init_done)
return 0;
mysql_close(global_mysql);
return 0;
}

View file

@ -1712,14 +1712,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 */
@ -3319,10 +3319,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;
}
@ -3441,13 +3439,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);
}

View file

@ -1526,6 +1526,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
****************************************************************************/
@ -1757,7 +1767,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)
@ -1772,12 +1782,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
@ -5056,6 +5066,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()))
@ -5063,6 +5074,9 @@ static int init_server_components()
tc_log= 0; // ha_initialize_handlerton() needs that
if (ddl_log_initialize())
unireg_abort(1);
if (plugin_init(&remaining_argc, remaining_argv,
(opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
(opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0)))
@ -5304,9 +5318,6 @@ static int init_server_components()
}
#endif
if (ddl_log_initialize())
unireg_abort(1);
tc_log= get_tc_log_implementation();
if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
@ -5387,7 +5398,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)

View file

@ -1131,6 +1131,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--; }
};
@ -3918,6 +3919,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"
@ -3929,15 +3935,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;

View file

@ -230,6 +230,22 @@ static struct thd_mdl_service_st thd_mdl_handler=
thd_mdl_context
};
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 },
@ -254,5 +270,6 @@ static struct st_service_ref list_of_services[]=
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "wsrep_service", VERSION_wsrep, &wsrep_handler },
{ "json_service", VERSION_json, &json_handler },
{ "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler }
{ "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler },
{ "sql_service", VERSION_sql_service, &sql_service_handler },
};

View file

@ -133,6 +133,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8;
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
#include "xa.h" // xa_recover_get_fields
#include "sql_audit.h" // mysql_audit_release
/**
A result class used to send cursor rows using the binary protocol.
@ -4049,19 +4050,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);
@ -4079,17 +4083,23 @@ 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
****************************************************************************/
@ -4850,7 +4860,10 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
Statement stmt_backup;
bool error;
Query_arena *save_stmt_arena= thd->stmt_arena;
my_time_t save_query_start= thd->query_start();
ulong save_query_sec= thd->start_time_sec_part;
Item_change_list save_change_list;
thd->Item_change_list::move_elements_to(&save_change_list);
state= STMT_CONVENTIONAL_EXECUTION;
@ -4858,6 +4871,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
if (!(lex= new (mem_root) st_lex_local))
return TRUE;
thd->set_time();
thd->set_n_backup_statement(this, &stmt_backup);
thd->set_n_backup_active_arena(this, &stmt_backup);
thd->stmt_arena= this;
@ -4871,6 +4885,7 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
thd->stmt_arena= save_stmt_arena;
save_change_list.move_elements_to(thd);
thd->force_set_time(save_query_start, save_query_sec);
/* Items and memory will freed in destructor */
@ -5582,14 +5597,6 @@ Ed_connection::store_result_set()
return ed_result_set;
}
/*
MENT-56
Protocol_local and service_sql for plugins to enable 'local' SQL query execution.
*/
#ifndef EMBEDDED_LIBRARY
// This part is mostly copied from libmysqld/lib_sql.cc
// TODO: get rid of code duplications
#include <mysql.h>
#include "../libmysqld/embedded_priv.h"
@ -5605,11 +5612,13 @@ public:
char **next_field;
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
THD *new_thd;
Protocol_local(THD *thd_arg, ulong prealloc= 0) :
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)
{}
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);
@ -5681,6 +5690,20 @@ MYSQL_DATA *Protocol_local::alloc_new_dataset()
}
void Protocol_local::clear_data_list()
{
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;
}
static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
@ -5974,7 +5997,6 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
// Protocol_local prot(thd);
DBUG_ENTER("send_result_set_metadata");
// if (!thd->mysql) // bootstrap file handling
@ -5985,7 +6007,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
for (uint pos= 0 ; (item= it++); pos++)
{
if (/*prot.*/store_item_metadata(thd, item, pos))
if (store_item_metadata(thd, item, pos))
goto err;
}
@ -5999,6 +6021,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
DBUG_RETURN(1); /* purecov: inspected */
}
static void
list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos)
{
@ -6086,19 +6109,6 @@ bool Protocol_local::store_null()
#include <sql_common.h>
#include <errmsg.h>
struct local_results
{
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;
};
static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data)
{
NET *net= &mysql->net;
@ -6113,11 +6123,11 @@ static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data)
static my_bool loc_read_query_result(MYSQL *mysql)
{
local_results *thd= (local_results *) mysql->thd;
Protocol_local *p= (Protocol_local *) mysql->thd;
MYSQL_DATA *res= thd->first_data;
DBUG_ASSERT(!thd->cur_data);
thd->first_data= res->embedded_info->next;
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)
{
@ -6145,7 +6155,7 @@ static my_bool loc_read_query_result(MYSQL *mysql)
if (res->embedded_info->fields_list)
{
mysql->status=MYSQL_STATUS_GET_RESULT;
thd->cur_data= res;
p->cur_data= res;
}
else
my_free(res);
@ -6154,23 +6164,193 @@ static my_bool loc_read_query_result(MYSQL *mysql)
}
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)
{
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;
}
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;
}
/* 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
*/
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);
MYSQL_LEX_STRING sql_text;
DBUG_ASSERT(current_thd == p->thd);
sql_text.str= (char *) arg;
sql_text.length= arg_length;
result= con.execute_direct(p, sql_text);
}
if (skip_check)
result= 0;
p->cur_data= 0;
end:
return result;
}
/*
reads dataset from the next query result
SYNOPSIS
loc_read_rows()
mysql connection handle
other parameters are not used
NOTES
It just gets next MYSQL_DATA from the result's queue
RETURN
pointer to MYSQL_DATA with the coming recordset
*/
static MYSQL_DATA *
loc_read_rows(MYSQL *mysql, MYSQL_FIELD *mysql_fields __attribute__((unused)),
unsigned int fields __attribute__((unused)))
{
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;
}
/**************************************************************************
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.
**************************************************************************/
static void loc_fetch_lengths(ulong *to, MYSQL_ROW column,
unsigned int field_count)
{
MYSQL_ROW end;
for (end=column + field_count; column != end ; column++,to++)
*to= *column ? *(uint *)((*column) - sizeof(uint)) : 0;
}
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);
}
}
static void loc_on_close_free(MYSQL *mysql)
{
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_METHODS local_methods=
{
loc_read_query_result, /* read_query_result */
NULL/*loc_advanced_command*/, /* advanced_command */
NULL/*loc_read_rows*/, /* read_rows */
NULL/*loc_use_result*/, /* use_result */
NULL/*loc_fetch_lengths*/, /* fetch_lengths */
NULL/*loc_flush_use_result*/, /* flush_use_result */
NULL/*loc_read_change_user_result*/ /* read_change_user_result */
loc_advanced_command, /* advanced_command */
loc_read_rows, /* read_rows */
mysql_store_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
,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
};
extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
const char *host, const char *user, const char *passwd, const char *db)
{
//char name_buff[USERNAME_LENGTH];
Atomic_counter<uint32_t> local_connection_thread_count;
extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
const char *host, const char *user, const char *db,
unsigned long clientflag)
{
THD *thd_orig= current_thd;
THD *new_thd;
Protocol_local *p;
DBUG_ENTER("mysql_real_connect_local");
/* Test whether we're already connected */
@ -6191,137 +6371,50 @@ extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
if (!user || !user[0])
user=mysql->options.user;
mysql->user= my_strdup(PSI_INSTRUMENT_ME, user, MYF(0));
mysql->user= NULL;
mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME,
MYSQL_ERRMSG_SIZE, MYF(0));
//mysql->thd= create_embedded_thd(client_flag);
//init_embedded_mysql(mysql, client_flag);
//if (mysql_init_character_set(mysql))
// goto error;
//if (check_embedded_connection(mysql, db))
// goto error;
mysql->server_status= SERVER_STATUS_AUTOCOMMIT;
//if (mysql->options.init_commands)
//{
// DYNAMIC_ARRAY *init_commands= mysql->options.init_commands;
// char **ptr= (char**)init_commands->buffer;
// char **end= ptr + init_commands->elements;
//
// for (; ptr<end; ptr++)
// {
// MYSQL_RES *res;
// if (mysql_query(mysql,*ptr))
// goto error;
// if (mysql->fields)
// {
// if (!(res= (*mysql->methods->use_result)(mysql)))
// goto error;
// mysql_free_result(res);
// }
// }
//}
DBUG_PRINT("exit",("Mysql handler: %p", mysql));
DBUG_RETURN(mysql);
//error:
DBUG_PRINT("error",("message: %u (%s)",
mysql->net.last_errno,
mysql->net.last_error));
if (!thd_orig || thd_orig->lock)
{
/* Free alloced memory */
my_bool free_me=mysql->free_me;
free_old_query(mysql);
mysql->free_me=0;
mysql_close(mysql);
mysql->free_me=free_me;
}
DBUG_RETURN(0);
}
/*
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.
*/
extern "C" int execute_sql_command(const char *command,
char *hosts, char *names, char *filters)
{
MYSQL_LEX_STRING sql_text;
THD *thd= current_thd;
THD *new_thd= 0;
int result;
my_bool qc_save= 0;
Reprepare_observer *save_reprepare_observer= nullptr;
if (!thd)
{
new_thd= new THD(0);
new_thd->thread_stack= (char*) &sql_text;
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));
thd= new_thd;
set_current_thd(thd_orig);
thd_orig= new_thd;
}
else
{
if (thd->lock)
/* Doesn't work if the thread opened/locked tables already. */
return 2;
qc_save= thd->query_cache_is_applicable;
thd->query_cache_is_applicable= 0;
save_reprepare_observer= thd->m_reprepare_observer;
thd->m_reprepare_observer= nullptr;
}
sql_text.str= (char *) command;
sql_text.length= strlen(command);
{
Protocol_local p(thd);
Ed_connection con(thd);
result= con.execute_direct(&p, sql_text);
if (!result && p.first_data)
{
int nr= (int) p.first_data->rows;
MYSQL_ROWS *rows= p.first_data->data;
while (nr--)
{
strcpy(hosts, rows->data[0]);
hosts+= strlen(hosts) + 1;
strcpy(names, rows->data[1]);
names+= strlen(names) + 1;
if (filters)
{
strcpy(filters, rows->data[2]);
filters+= strlen(filters) + 1;
}
rows= rows->next;
}
}
if (p.first_data)
{
if (p.alloc)
free_root(p.alloc, MYF(0));
my_free(p.first_data);
}
}
new_thd= NULL;
p= new Protocol_local(thd_orig, new_thd, 0);
if (new_thd)
delete new_thd;
else
{
thd->query_cache_is_applicable= qc_save;
thd->m_reprepare_observer= save_reprepare_observer;
}
new_thd->protocol= p;
*hosts= 0;
return result;
mysql->thd= p;
mysql->server_status= SERVER_STATUS_AUTOCOMMIT;
DBUG_PRINT("exit",("Mysql handler: %p", mysql));
DBUG_RETURN(mysql);
}
#endif /*!EMBEDDED_LIBRARY*/

View file

@ -351,4 +351,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

View file

@ -14,9 +14,9 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
#include <mysql_version.h>
#include <mysql/plugin.h>
#include <my_global.h>
#include <mysql/plugin.h>
#include <sql_class.h>
#include <sql_i_s.h>
#include <mysql/plugin.h>