You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-07 02:42:49 +03:00
session tracking implementation (10.2-integration):
- At the moment the following session tracking types are supported: SESSION_TRACK_SCHEMA SESSION_TRACK_SYSTEM_VARIABLES SESSION_TRACK_STATE_CHANGE SESSION_TRACK_TRANSACTION_CHARACTERISTICS - New API functions mysql_session_track_get_next mysql_session_track_get_first
This commit is contained in:
@@ -75,8 +75,15 @@ struct st_mariadb_net_extension {
|
||||
unsigned char *mbuff, *mbuff_end, *mbuff_pos;
|
||||
};
|
||||
|
||||
struct st_mariadb_session_state
|
||||
{
|
||||
LIST *list,
|
||||
*current;
|
||||
};
|
||||
|
||||
struct st_mariadb_extension {
|
||||
MA_CONNECTION_HANDLER *conn_hdlr;
|
||||
struct st_mariadb_session_state session_state[SESSION_TRACK_TYPES];
|
||||
};
|
||||
|
||||
#define OPT_HAS_EXT_VAL(a,key) \
|
||||
|
@@ -372,7 +372,7 @@ extern gptr _mymalloc(size_t uSize,const char *sFile,
|
||||
uint uLine, myf MyFlag);
|
||||
extern gptr _myrealloc(gptr pPtr,size_t uSize,const char *sFile,
|
||||
uint uLine, myf MyFlag);
|
||||
extern gptr my_multi_malloc _VARARGS((myf MyFlags, ...));
|
||||
extern void *ma_multi_malloc(myf MyFlags, ...);
|
||||
extern void _myfree(gptr pPtr,const char *sFile,uint uLine, myf MyFlag);
|
||||
extern int _sanity(const char *sFile,unsigned int uLine);
|
||||
#ifndef TERMINATE
|
||||
|
@@ -155,6 +155,7 @@ enum enum_server_command
|
||||
#define CLIENT_PS_MULTI_RESULTS (1UL << 18)
|
||||
#define CLIENT_PLUGIN_AUTH (1UL << 19)
|
||||
#define CLIENT_CONNECT_ATTRS (1UL << 20)
|
||||
#define CLIENT_SESSION_TRACKING (1UL << 23)
|
||||
#define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */
|
||||
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
|
||||
#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
|
||||
@@ -190,6 +191,7 @@ enum enum_server_command
|
||||
CLIENT_SSL_VERIFY_SERVER_CERT |\
|
||||
CLIENT_REMEMBER_OPTIONS |\
|
||||
CLIENT_PLUGIN_AUTH |\
|
||||
CLIENT_SESSION_TRACKING |\
|
||||
CLIENT_CONNECT_ATTRS)
|
||||
|
||||
#define CLIENT_CAPABILITIES (CLIENT_MYSQL | \
|
||||
@@ -200,6 +202,7 @@ enum enum_server_command
|
||||
CLIENT_PS_MULTI_RESULTS |\
|
||||
CLIENT_PROTOCOL_41 |\
|
||||
CLIENT_PLUGIN_AUTH |\
|
||||
CLIENT_SESSION_TRACKING |\
|
||||
CLIENT_CONNECT_ATTRS)
|
||||
|
||||
#define CLIENT_DEFAULT_FLAGS ((CLIENT_SUPPORTED_FLAGS & ~CLIENT_COMPRESS)\
|
||||
@@ -217,6 +220,7 @@ enum enum_server_command
|
||||
#define SERVER_STATUS_METADATA_CHANGED 1024
|
||||
#define SERVER_QUERY_WAS_SLOW 2048
|
||||
#define SERVER_PS_OUT_PARAMS 4096
|
||||
#define SERVER_SESSION_STATE_CHANGED (1UL << 14)
|
||||
|
||||
#define MYSQL_ERRMSG_SIZE 512
|
||||
#define NET_READ_TIMEOUT 30 /* Timeout on read */
|
||||
@@ -284,6 +288,22 @@ enum enum_mysql_set_option
|
||||
MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
||||
};
|
||||
|
||||
enum enum_session_state_type
|
||||
{
|
||||
SESSION_TRACK_SYSTEM_VARIABLES= 0,
|
||||
SESSION_TRACK_SCHEMA,
|
||||
SESSION_TRACK_STATE_CHANGE,
|
||||
/* currently not supported by MariaDB Server */
|
||||
SESSION_TRACK_GTIDS,
|
||||
SESSION_TRACK_TRANSACTION_CHARACTERISTICS,
|
||||
SESSION_TRACK_TRANSACTION_TYPE /* make sure that SESSION_TRACK_END always points
|
||||
to last element of enum !! */
|
||||
};
|
||||
|
||||
#define SESSION_TRACK_BEGIN 0
|
||||
#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_TYPE
|
||||
#define SESSION_TRACK_TYPES SESSION_TRACK_END + 1
|
||||
|
||||
enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
|
||||
MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
|
||||
MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
|
||||
|
@@ -660,6 +660,8 @@ int STDCALL mysql_read_query_result_start(my_bool *ret,
|
||||
MYSQL *mysql);
|
||||
int STDCALL mysql_read_query_result_cont(my_bool *ret,
|
||||
MYSQL *mysql, int status);
|
||||
int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type, const char **data, size_t *length);
|
||||
int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type, const char **data, size_t *length);
|
||||
int STDCALL mysql_stmt_prepare_start(int *ret, MYSQL_STMT *stmt,const char *query, size_t length);
|
||||
int STDCALL mysql_stmt_prepare_cont(int *ret, MYSQL_STMT *stmt, int status);
|
||||
int STDCALL mysql_stmt_execute_start(int *ret, MYSQL_STMT *stmt);
|
||||
|
@@ -91,6 +91,8 @@ SET(MARIADB_LIB_SYMBOLS
|
||||
mysql_send_query
|
||||
mysql_server_end
|
||||
mysql_server_init
|
||||
mysql_session_track_get_next
|
||||
mysql_session_track_get_first
|
||||
mysql_set_character_set
|
||||
mysql_set_local_infile_default
|
||||
mysql_set_local_infile_handler
|
||||
|
@@ -151,3 +151,33 @@ char *ma_memdup_root(MA_MEM_ROOT *root, const char *str, size_t len)
|
||||
memcpy(pos,str,len);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void *ma_multi_malloc(myf myFlags, ...)
|
||||
{
|
||||
va_list args;
|
||||
char **ptr,*start,*res;
|
||||
uint tot_length,length;
|
||||
|
||||
va_start(args,myFlags);
|
||||
tot_length=0;
|
||||
while ((ptr=va_arg(args, char **)))
|
||||
{
|
||||
length=va_arg(args,uint);
|
||||
tot_length+=ALIGN_SIZE(length);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
if (!(start=(char *)malloc(tot_length)))
|
||||
return 0;
|
||||
|
||||
va_start(args,myFlags);
|
||||
res=start;
|
||||
while ((ptr=va_arg(args, char **)))
|
||||
{
|
||||
*ptr=res;
|
||||
length=va_arg(args,uint);
|
||||
res+=ALIGN_SIZE(length);
|
||||
}
|
||||
va_end(args);
|
||||
return start;
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <mariadb_dyncol.h>
|
||||
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
@@ -1835,6 +1836,21 @@ void mysql_close_slow_part(MYSQL *mysql)
|
||||
}
|
||||
}
|
||||
|
||||
void ma_clear_session_state(MYSQL *mysql)
|
||||
{
|
||||
uint i;
|
||||
|
||||
if (!mysql || !mysql->extension)
|
||||
return;
|
||||
|
||||
for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
|
||||
{
|
||||
/* we acquired memory via ma_multi_alloc, so we don't need to free data */
|
||||
list_free(mysql->extension->session_state[i].list, 0);
|
||||
}
|
||||
memset(mysql->extension->session_state, 0, sizeof(struct st_mariadb_session_state) * SESSION_TRACK_TYPES);
|
||||
}
|
||||
|
||||
void STDCALL
|
||||
mysql_close(MYSQL *mysql)
|
||||
{
|
||||
@@ -1855,6 +1871,7 @@ mysql_close(MYSQL *mysql)
|
||||
|
||||
mysql_close_memory(mysql);
|
||||
mysql_close_options(mysql);
|
||||
ma_clear_session_state(mysql);
|
||||
|
||||
if (mysql->net.extension)
|
||||
free(mysql->net.extension);
|
||||
@@ -1914,14 +1931,124 @@ get_info:
|
||||
pos=(uchar*) mysql->net.read_pos;
|
||||
if ((field_count= net_field_length(&pos)) == 0)
|
||||
{
|
||||
size_t item_len;
|
||||
mysql->affected_rows= net_field_length_ll(&pos);
|
||||
mysql->insert_id= net_field_length_ll(&pos);
|
||||
mysql->server_status=uint2korr(pos);
|
||||
pos+=2;
|
||||
mysql->warning_count=uint2korr(pos);
|
||||
pos+=2;
|
||||
if (pos < mysql->net.read_pos+length && net_field_length(&pos))
|
||||
if (pos < mysql->net.read_pos+length && (item_len= net_field_length(&pos)))
|
||||
mysql->info=(char*) pos;
|
||||
|
||||
/* check if server supports session tracking */
|
||||
if (mysql->server_capabilities & CLIENT_SESSION_TRACKING)
|
||||
{
|
||||
ma_clear_session_state(mysql);
|
||||
pos+= item_len;
|
||||
|
||||
if (mysql->server_status & SERVER_SESSION_STATE_CHANGED)
|
||||
{
|
||||
int i;
|
||||
if (pos < mysql->net.read_pos + length)
|
||||
{
|
||||
LIST *session_item;
|
||||
MYSQL_LEX_STRING *str= NULL;
|
||||
enum enum_session_state_type si_type;
|
||||
uchar *old_pos= pos;
|
||||
size_t item_len= net_field_length(&pos); /* length for all items */
|
||||
|
||||
/* length was already set, so make sure that info will be zero terminated */
|
||||
if (mysql->info)
|
||||
*old_pos= 0;
|
||||
|
||||
while (item_len > 0)
|
||||
{
|
||||
size_t plen;
|
||||
char *data;
|
||||
old_pos= pos;
|
||||
si_type= (enum enum_session_state_type)net_field_length(&pos);
|
||||
switch(si_type) {
|
||||
case SESSION_TRACK_SCHEMA:
|
||||
case SESSION_TRACK_STATE_CHANGE:
|
||||
case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
|
||||
case SESSION_TRACK_SYSTEM_VARIABLES:
|
||||
net_field_length(&pos); /* ignore total length, item length will follow next */
|
||||
plen= net_field_length(&pos);
|
||||
if (!ma_multi_malloc(0,
|
||||
&session_item, sizeof(LIST),
|
||||
&str, sizeof(MYSQL_LEX_STRING),
|
||||
&data, plen,
|
||||
0))
|
||||
{
|
||||
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return -1;
|
||||
}
|
||||
str->length= plen;
|
||||
str->str= data;
|
||||
memcpy(str->str, (char *)pos, plen);
|
||||
pos+= plen;
|
||||
session_item->data= str;
|
||||
mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item);
|
||||
|
||||
/* in case schema has changed, we have to update mysql->db */
|
||||
if (si_type == SESSION_TRACK_SCHEMA)
|
||||
{
|
||||
free(mysql->db);
|
||||
mysql->db= malloc(plen + 1);
|
||||
memcpy(mysql->db, str->str, plen);
|
||||
mysql->db[plen]= 0;
|
||||
}
|
||||
else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES)
|
||||
{
|
||||
my_bool set_charset= 0;
|
||||
/* make sure that we update charset in case it has changed */
|
||||
if (!strncmp(str->str, "character_set_client", str->length))
|
||||
set_charset= 1;
|
||||
plen= net_field_length(&pos);
|
||||
if (!ma_multi_malloc(0,
|
||||
&session_item, sizeof(LIST),
|
||||
&str, sizeof(MYSQL_LEX_STRING),
|
||||
&data, plen,
|
||||
0))
|
||||
{
|
||||
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return -1;
|
||||
}
|
||||
str->length= plen;
|
||||
str->str= data;
|
||||
memcpy(str->str, (char *)pos, plen);
|
||||
pos+= plen;
|
||||
session_item->data= str;
|
||||
mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item);
|
||||
if (set_charset &&
|
||||
strncmp(mysql->charset->csname, str->str, str->length) != 0)
|
||||
{
|
||||
char cs_name[64];
|
||||
MARIADB_CHARSET_INFO *cs_info;
|
||||
memcpy(cs_name, str->str, str->length);
|
||||
cs_name[str->length]= 0;
|
||||
if ((cs_info = (MARIADB_CHARSET_INFO *)mysql_find_charset_name(cs_name)))
|
||||
mysql->charset= cs_info;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* not supported yet */
|
||||
plen= net_field_length(&pos);
|
||||
pos+= plen;
|
||||
break;
|
||||
}
|
||||
item_len-= (pos - old_pos);
|
||||
}
|
||||
}
|
||||
for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
|
||||
{
|
||||
mysql->extension->session_state[i].list= list_reverse(mysql->extension->session_state[i].list);
|
||||
mysql->extension->session_state[i].current= mysql->extension->session_state[i].list;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
|
||||
@@ -1948,6 +2075,28 @@ get_info:
|
||||
return(0);
|
||||
}
|
||||
|
||||
int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type,
|
||||
const char **data, size_t *length)
|
||||
{
|
||||
MYSQL_LEX_STRING *str;
|
||||
if (!mysql->extension->session_state[type].current)
|
||||
return 1;
|
||||
|
||||
str= (MYSQL_LEX_STRING *)mysql->extension->session_state[type].current->data;
|
||||
mysql->extension->session_state[type].current= mysql->extension->session_state[type].current->next;
|
||||
|
||||
*data= str->str ? str->str : NULL;
|
||||
*length= str->str ? str->length : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type,
|
||||
const char **data, size_t *length)
|
||||
{
|
||||
mysql->extension->session_state[type].current= mysql->extension->session_state[type].list;
|
||||
return mysql_session_track_get_next(mysql, type, data, length);
|
||||
}
|
||||
|
||||
my_bool STDCALL
|
||||
mysql_read_query_result(MYSQL *mysql)
|
||||
{
|
||||
|
@@ -312,9 +312,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
Cred.cCreds = 1;
|
||||
Cred.paCred = &sctx->client_cert_ctx;
|
||||
}
|
||||
Cred.grbitEnabledProtocols= 0;
|
||||
if (mysql->options.extension && mysql->options.extension->tls_version)
|
||||
{
|
||||
Cred.grbitEnabledProtocols= 0;
|
||||
if (strstr("TLSv1.0", mysql->options.extension->tls_version))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0;
|
||||
if (strstr("TLSv1.1", mysql->options.extension->tls_version))
|
||||
@@ -322,10 +322,6 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
if (strstr("TLSv1.2", mysql->options.extension->tls_version))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2;
|
||||
}
|
||||
else
|
||||
Cred.grbitEnabledProtocols = SP_PROT_TLS1_0 |
|
||||
SP_PROT_TLS1_1 |
|
||||
SP_PROT_TLS1_2;
|
||||
|
||||
if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
|
||||
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK)
|
||||
|
@@ -912,7 +912,70 @@ static int test_get_options(MYSQL *my)
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_sess_track_db(MYSQL *mysql)
|
||||
{
|
||||
int rc;
|
||||
const char *data;
|
||||
size_t len;
|
||||
|
||||
if (!(mysql->server_capabilities & CLIENT_SESSION_TRACKING))
|
||||
{
|
||||
diag("Server doesn't support session tracking (cap=%u)", mysql->server_capabilities);
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
rc= mysql_query(mysql, "USE mysql");
|
||||
check_mysql_rc(rc, mysql);
|
||||
FAIL_IF(strcmp(mysql->db, "mysql"), "Expected new schema 'mysql'");
|
||||
|
||||
FAIL_IF(mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len),
|
||||
"session_track_get_first failed");
|
||||
FAIL_IF(strncmp(data, "mysql", len), "Expected new schema 'mysql'");
|
||||
|
||||
rc= mysql_query(mysql, "USE testc");
|
||||
check_mysql_rc(rc, mysql);
|
||||
FAIL_IF(strcmp(mysql->db, "testc"), "Expected new schema 'testc'");
|
||||
|
||||
FAIL_IF(mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len),
|
||||
"session_track_get_first failed");
|
||||
FAIL_IF(strncmp(data, "testc", len), "Expected new schema 'testc'");
|
||||
|
||||
rc= mysql_query(mysql, "SET NAMES utf8");
|
||||
check_mysql_rc(rc, mysql);
|
||||
FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "Expected charset 'utf8'");
|
||||
if (!mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len))
|
||||
do {
|
||||
printf("# SESSION_TRACK_VARIABLES: %*.*s\n", len, len, data);
|
||||
} while (!mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len));
|
||||
|
||||
rc= mysql_query(mysql, "SET NAMES latin1");
|
||||
check_mysql_rc(rc, mysql);
|
||||
FAIL_IF(strcmp(mysql->charset->csname, "latin1"), "Expected charset 'latin1'");
|
||||
|
||||
rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
rc= mysql_query(mysql, "CREATE PROCEDURE p1() "
|
||||
"BEGIN "
|
||||
"SET @@autocommit=0; "
|
||||
"SET NAMES utf8; "
|
||||
"SET session auto_increment_increment=2; "
|
||||
"END ");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
rc= mysql_query(mysql, "CALL p1()");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
if (!mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len))
|
||||
do {
|
||||
printf("# SESSION_TRACK_VARIABLES: %*.*s\n", len, len, data);
|
||||
} while (!mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"test_sess_track_db", test_sess_track_db, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||
{"test_get_options", test_get_options, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||
{"test_wrong_bind_address", test_wrong_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||
{"test_bind_address", test_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||
|
Reference in New Issue
Block a user