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
status and session_tracik callback function:
This feature allows client applications to register a callback function, which is called as soon as the server status changes or session_track information was sent by the server. Registration is handled via mysql_optionsv() API function: mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, function, data) The callback function must be defined as follws: void status_callback(void *data, enum enum_mariadb_status_info type, ..) Parameters: - data Pointer passed with registration of callback function (usually a connection handle) - type Information type STATUS_TYPE or SESSION_TRACK_TYPE Variadic Parameters: if (type == STATUS_TYPE): - server status (unsigned int) if (type == SESSION_TRACK_TYPE) - enum enum_session_state_type track_type - session track type if (track_type == SESSION_TRACK_SYSTEM_VARIABLES) - MARIADB_CONST_STRING *key - MARIADB_CONST_STRING *value else - MARIADB_CONST_STRING *value An example can be found in connection.c (test_status_callback)
This commit is contained in:
@@ -84,6 +84,8 @@ struct st_mysql_options_extension {
|
|||||||
char *restricted_auth;
|
char *restricted_auth;
|
||||||
char *rpl_host;
|
char *rpl_host;
|
||||||
unsigned short rpl_port;
|
unsigned short rpl_port;
|
||||||
|
void (*status_callback)(void *ptr, enum enum_mariadb_status_info type, ...);
|
||||||
|
void *status_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct st_connection_handler
|
typedef struct st_connection_handler
|
||||||
|
@@ -306,6 +306,13 @@ enum enum_mysql_set_option
|
|||||||
MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* for status callback function */
|
||||||
|
enum enum_mariadb_status_info
|
||||||
|
{
|
||||||
|
STATUS_TYPE= 0,
|
||||||
|
SESSION_TRACK_TYPE
|
||||||
|
};
|
||||||
|
|
||||||
enum enum_session_state_type
|
enum enum_session_state_type
|
||||||
{
|
{
|
||||||
SESSION_TRACK_SYSTEM_VARIABLES= 0,
|
SESSION_TRACK_SYSTEM_VARIABLES= 0,
|
||||||
|
@@ -249,7 +249,8 @@ extern const char *SQLSTATE_UNKNOWN;
|
|||||||
MARIADB_OPT_IO_WAIT,
|
MARIADB_OPT_IO_WAIT,
|
||||||
MARIADB_OPT_SKIP_READ_RESPONSE,
|
MARIADB_OPT_SKIP_READ_RESPONSE,
|
||||||
MARIADB_OPT_RESTRICTED_AUTH,
|
MARIADB_OPT_RESTRICTED_AUTH,
|
||||||
MARIADB_OPT_RPL_REGISTER_REPLICA
|
MARIADB_OPT_RPL_REGISTER_REPLICA,
|
||||||
|
MARIADB_OPT_STATUS_CALLBACK
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mariadb_value {
|
enum mariadb_value {
|
||||||
|
@@ -1419,6 +1419,10 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
if (!mysql->methods)
|
if (!mysql->methods)
|
||||||
mysql->methods= &MARIADB_DEFAULT_METHODS;
|
mysql->methods= &MARIADB_DEFAULT_METHODS;
|
||||||
|
|
||||||
|
/* set default */
|
||||||
|
if (!mysql->options.extension || !mysql->options.extension->status_callback)
|
||||||
|
mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, NULL, NULL);
|
||||||
|
|
||||||
/* if host contains a semicolon, we need to parse connection string */
|
/* if host contains a semicolon, we need to parse connection string */
|
||||||
if (host && strchr(host, ';'))
|
if (host && strchr(host, ';'))
|
||||||
{
|
{
|
||||||
@@ -2430,19 +2434,97 @@ mysql_query(MYSQL *mysql, const char *query)
|
|||||||
finish processing it.
|
finish processing it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int STDCALL
|
int STDCALL
|
||||||
mysql_send_query(MYSQL* mysql, const char* query, unsigned long length)
|
mysql_send_query(MYSQL* mysql, const char* query, unsigned long length)
|
||||||
{
|
{
|
||||||
return ma_simple_command(mysql, COM_QUERY, query, length, 1,0);
|
return ma_simple_command(mysql, COM_QUERY, query, length, 1,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ma_save_session_track_info(void *ptr, enum enum_mariadb_status_info type, ...)
|
||||||
|
{
|
||||||
|
MYSQL *mysql= (MYSQL *)ptr;
|
||||||
|
enum enum_session_state_type track_type;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
DBUG_ASSERT(mysql != NULL);
|
||||||
|
|
||||||
|
/* We only handle SESSION_TRACK_TYPE here */
|
||||||
|
if (type != SESSION_TRACK_TYPE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, type);
|
||||||
|
|
||||||
|
track_type= va_arg(ap, enum enum_session_state_type);
|
||||||
|
|
||||||
|
switch (track_type) {
|
||||||
|
case SESSION_TRACK_SCHEMA:
|
||||||
|
case SESSION_TRACK_STATE_CHANGE:
|
||||||
|
case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
|
||||||
|
case SESSION_TRACK_TRANSACTION_STATE:
|
||||||
|
case SESSION_TRACK_GTIDS:
|
||||||
|
case SESSION_TRACK_SYSTEM_VARIABLES:
|
||||||
|
{
|
||||||
|
LIST *session_item;
|
||||||
|
MYSQL_LEX_STRING *str;
|
||||||
|
char *tmp;
|
||||||
|
MARIADB_CONST_STRING *data1= va_arg(ap, MARIADB_CONST_STRING *);
|
||||||
|
|
||||||
|
if (!(session_item= ma_multi_malloc(0,
|
||||||
|
&session_item, sizeof(LIST),
|
||||||
|
&str, sizeof(MYSQL_LEX_STRING),
|
||||||
|
&tmp, data1->length,
|
||||||
|
NULL)))
|
||||||
|
goto mem_error;
|
||||||
|
|
||||||
|
str->str= tmp;
|
||||||
|
memcpy(str->str, data1->str, data1->length);
|
||||||
|
str->length= data1->length;
|
||||||
|
session_item->data= str;
|
||||||
|
mysql->extension->session_state[track_type].list= list_add(mysql->extension->session_state[track_type].list,
|
||||||
|
session_item);
|
||||||
|
if (track_type == SESSION_TRACK_SYSTEM_VARIABLES)
|
||||||
|
{
|
||||||
|
MARIADB_CONST_STRING *data2= va_arg(ap, MARIADB_CONST_STRING *);
|
||||||
|
if (!(session_item= ma_multi_malloc(0,
|
||||||
|
&session_item, sizeof(LIST),
|
||||||
|
&str, sizeof(MYSQL_LEX_STRING),
|
||||||
|
&tmp, data2->length,
|
||||||
|
NULL)))
|
||||||
|
goto mem_error;
|
||||||
|
|
||||||
|
str->str= tmp;
|
||||||
|
memcpy(str->str, data2->str, data2->length);
|
||||||
|
str->length= data2->length;
|
||||||
|
session_item->data= str;
|
||||||
|
mysql->extension->session_state[track_type].list= list_add(mysql->extension->session_state[track_type].list,
|
||||||
|
session_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
mem_error:
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
|
int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
|
||||||
{
|
{
|
||||||
uchar *end= mysql->net.read_pos+length;
|
uchar *end= mysql->net.read_pos+length;
|
||||||
size_t item_len;
|
size_t item_len;
|
||||||
|
unsigned int last_server_status= mysql->server_status;
|
||||||
mysql->affected_rows= net_field_length_ll(&pos);
|
mysql->affected_rows= net_field_length_ll(&pos);
|
||||||
mysql->insert_id= net_field_length_ll(&pos);
|
mysql->insert_id= net_field_length_ll(&pos);
|
||||||
mysql->server_status=uint2korr(pos);
|
mysql->server_status=uint2korr(pos);
|
||||||
|
|
||||||
|
/* callback */
|
||||||
|
if (mysql->options.extension->status_callback &&
|
||||||
|
mysql->server_status != last_server_status)
|
||||||
|
mysql->options.extension->status_callback(mysql->options.extension->status_data,
|
||||||
|
STATUS_TYPE, mysql->server_status);
|
||||||
pos+=2;
|
pos+=2;
|
||||||
mysql->warning_count=uint2korr(pos);
|
mysql->warning_count=uint2korr(pos);
|
||||||
pos+=2;
|
pos+=2;
|
||||||
@@ -2466,8 +2548,6 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
|
|||||||
int i;
|
int i;
|
||||||
if (pos < end)
|
if (pos < end)
|
||||||
{
|
{
|
||||||
LIST *session_item;
|
|
||||||
MYSQL_LEX_STRING *str= NULL;
|
|
||||||
enum enum_session_state_type si_type;
|
enum enum_session_state_type si_type;
|
||||||
uchar *old_pos= pos;
|
uchar *old_pos= pos;
|
||||||
|
|
||||||
@@ -2483,7 +2563,7 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
|
|||||||
while (pos < end)
|
while (pos < end)
|
||||||
{
|
{
|
||||||
size_t plen;
|
size_t plen;
|
||||||
char *data;
|
MYSQL_LEX_STRING data1, data2;
|
||||||
si_type= (enum enum_session_state_type)net_field_length(&pos);
|
si_type= (enum enum_session_state_type)net_field_length(&pos);
|
||||||
|
|
||||||
switch(si_type) {
|
switch(si_type) {
|
||||||
@@ -2505,55 +2585,52 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
|
|||||||
plen= net_field_length(&pos);
|
plen= net_field_length(&pos);
|
||||||
if (pos + plen > end)
|
if (pos + plen > end)
|
||||||
goto corrupted;
|
goto corrupted;
|
||||||
if (!(session_item= ma_multi_malloc(0,
|
|
||||||
&session_item, sizeof(LIST),
|
|
||||||
&str, sizeof(MYSQL_LEX_STRING),
|
|
||||||
&data, plen,
|
|
||||||
NULL)))
|
|
||||||
goto oom;
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
data1.str= (char *)pos;
|
||||||
|
data1.length= plen;
|
||||||
|
if (si_type != SESSION_TRACK_SYSTEM_VARIABLES)
|
||||||
|
{
|
||||||
|
mysql->options.extension->status_callback(mysql->options.extension->status_data,
|
||||||
|
SESSION_TRACK_TYPE, si_type,
|
||||||
|
&data1);
|
||||||
|
if (mysql->net.last_errno)
|
||||||
|
goto oom;
|
||||||
|
}
|
||||||
|
pos+= plen;
|
||||||
/* in case schema has changed, we have to update mysql->db */
|
/* in case schema has changed, we have to update mysql->db */
|
||||||
if (si_type == SESSION_TRACK_SCHEMA)
|
if (si_type == SESSION_TRACK_SCHEMA)
|
||||||
{
|
{
|
||||||
free(mysql->db);
|
free(mysql->db);
|
||||||
mysql->db= malloc(plen + 1);
|
mysql->db= malloc(plen + 1);
|
||||||
memcpy(mysql->db, str->str, plen);
|
memcpy(mysql->db, data1.str, data1.length);
|
||||||
mysql->db[plen]= 0;
|
mysql->db[data1.length]= 0;
|
||||||
}
|
}
|
||||||
else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES)
|
else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES)
|
||||||
{
|
{
|
||||||
my_bool set_charset= 0;
|
my_bool set_charset= 0;
|
||||||
/* make sure that we update charset in case it has changed */
|
/* make sure that we update charset in case it has changed */
|
||||||
if (!strncmp(str->str, "character_set_client", str->length))
|
if (!strncmp(data1.str, "character_set_client", plen))
|
||||||
set_charset= 1;
|
set_charset= 1;
|
||||||
plen= net_field_length(&pos);
|
plen= net_field_length(&pos);
|
||||||
if (pos + plen > end)
|
if (pos + plen > end)
|
||||||
goto corrupted;
|
goto corrupted;
|
||||||
if (!(session_item= ma_multi_malloc(0,
|
data2.str= (char *)pos;
|
||||||
&session_item, sizeof(LIST),
|
data2.length= plen;
|
||||||
&str, sizeof(MYSQL_LEX_STRING),
|
|
||||||
&data, plen,
|
mysql->options.extension->status_callback(mysql->options.extension->status_data,
|
||||||
NULL)))
|
SESSION_TRACK_TYPE, si_type,
|
||||||
goto oom;
|
&data1, &data2);
|
||||||
str->length= plen;
|
if (mysql->net.last_errno)
|
||||||
str->str= data;
|
goto oom;
|
||||||
memcpy(str->str, (char *)pos, plen);
|
|
||||||
pos+= plen;
|
pos+= plen;
|
||||||
session_item->data= str;
|
if (set_charset && plen < CHARSET_NAME_LEN &&
|
||||||
mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item);
|
strncmp(mysql->charset->csname, data2.str, data2.length) != 0)
|
||||||
if (set_charset && str->length < CHARSET_NAME_LEN &&
|
|
||||||
strncmp(mysql->charset->csname, str->str, str->length) != 0)
|
|
||||||
{
|
{
|
||||||
char cs_name[CHARSET_NAME_LEN];
|
char cs_name[CHARSET_NAME_LEN];
|
||||||
const MARIADB_CHARSET_INFO *cs_info;
|
const MARIADB_CHARSET_INFO *cs_info;
|
||||||
memcpy(cs_name, str->str, str->length);
|
memcpy(cs_name, data2.str, data2.length);
|
||||||
cs_name[str->length]= 0;
|
cs_name[plen]= 0;
|
||||||
if ((cs_info = mysql_find_charset_name(cs_name)))
|
if ((cs_info = mysql_find_charset_name(cs_name)))
|
||||||
mysql->charset= cs_info;
|
mysql->charset= cs_info;
|
||||||
}
|
}
|
||||||
@@ -3663,6 +3740,19 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
|||||||
OPT_SET_EXTENDED_VALUE(&mysql->options, rpl_port, (ushort)arg2);
|
OPT_SET_EXTENDED_VALUE(&mysql->options, rpl_port, (ushort)arg2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MARIADB_OPT_STATUS_CALLBACK:
|
||||||
|
{
|
||||||
|
void *arg2= va_arg(ap, void *);
|
||||||
|
if (arg1)
|
||||||
|
{
|
||||||
|
OPT_SET_EXTENDED_VALUE(&mysql->options, status_callback, arg1);
|
||||||
|
OPT_SET_EXTENDED_VALUE(&mysql->options, status_data, arg2);
|
||||||
|
} else {
|
||||||
|
OPT_SET_EXTENDED_VALUE(&mysql->options, status_callback, ma_save_session_track_info);
|
||||||
|
OPT_SET_EXTENDED_VALUE(&mysql->options, status_data, mysql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
|
SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
|
||||||
|
@@ -2151,7 +2151,109 @@ static int test_conc365_reconnect(MYSQL *my)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct st_callback {
|
||||||
|
char autocommit;
|
||||||
|
char database[64];
|
||||||
|
char charset[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
void my_status_callback(void *ptr, enum enum_mariadb_status_info type, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
struct st_callback *data= (struct st_callback *)ptr;
|
||||||
|
va_start(ap, type);
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case STATUS_TYPE:
|
||||||
|
{
|
||||||
|
int status= va_arg(ap, int);
|
||||||
|
data->autocommit= status & SERVER_STATUS_AUTOCOMMIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SESSION_TRACK_TYPE:
|
||||||
|
{
|
||||||
|
enum enum_session_state_type track_type= va_arg(ap, enum enum_session_state_type);
|
||||||
|
switch (track_type) {
|
||||||
|
case SESSION_TRACK_SCHEMA:
|
||||||
|
{
|
||||||
|
MARIADB_CONST_STRING *str= va_arg(ap, MARIADB_CONST_STRING *);
|
||||||
|
strncpy(data->database, str->str, str->length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SESSION_TRACK_SYSTEM_VARIABLES:
|
||||||
|
{
|
||||||
|
MARIADB_CONST_STRING *key= va_arg(ap, MARIADB_CONST_STRING *);
|
||||||
|
MARIADB_CONST_STRING *val= va_arg(ap, MARIADB_CONST_STRING *);
|
||||||
|
|
||||||
|
if (!strncmp(key->str, "character_set_client", key->length))
|
||||||
|
{
|
||||||
|
strncpy(data->charset, val->str, val->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_status_callback(MYSQL *my __attribute__((unused)))
|
||||||
|
{
|
||||||
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
char tmp[64];
|
||||||
|
int rc;
|
||||||
|
struct st_callback data= {0,"", ""};
|
||||||
|
|
||||||
|
rc= mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, my_status_callback, &data);
|
||||||
|
|
||||||
|
if (!my_test_connect(mysql, hostname, username,
|
||||||
|
password, NULL, port, socketname, 0))
|
||||||
|
{
|
||||||
|
diag("error1: %s", mysql_error(mysql));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc= mysql_autocommit(mysql, 0);
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
rc= mysql_autocommit(mysql, 1);
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
if (!data.autocommit)
|
||||||
|
{
|
||||||
|
diag("autocommit not set");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
diag("-------------------------");
|
||||||
|
|
||||||
|
sprintf(tmp, "USE %s", schema);
|
||||||
|
rc= mysql_query(mysql, tmp);
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
if (strcmp(data.database, schema))
|
||||||
|
{
|
||||||
|
diag("Expected database: %s instead of %s", schema, data.database);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "SET NAMES latin1");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
if (strcmp(data.charset, "latin1"))
|
||||||
|
{
|
||||||
|
diag("Expected charset latin1 instead of %s", data.charset);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"test_conc365_reconnect", test_conc365_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc365_reconnect", test_conc365_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
|
Reference in New Issue
Block a user