Add support for connect attributes, as of MySQL 5.6

This commit is contained in:
Andrey Hristov 2013-01-15 10:04:59 +01:00
parent be07f815f2
commit 1ff4352263
8 changed files with 171 additions and 8 deletions

View file

@ -95,6 +95,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_
mnd_pefree(conn->options->cfg_section, pers); mnd_pefree(conn->options->cfg_section, pers);
conn->options->cfg_section = NULL; conn->options->cfg_section = NULL;
} }
if (conn->options->connect_attr) {
zend_hash_destroy(conn->options->connect_attr);
mnd_pefree(conn->options->connect_attr, pers);
conn->options->connect_attr = NULL;
}
} }
/* }}} */ /* }}} */
@ -797,13 +802,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
goto err; goto err;
} }
conn->client_flag = mysql_flags;
conn->server_capabilities = greet_packet->server_capabilities;
if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len, if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
greet_packet, conn->options, mysql_flags TSRMLS_CC)) greet_packet, conn->options, mysql_flags TSRMLS_CC))
{ {
goto err; goto err;
} }
conn->client_flag = mysql_flags;
conn->server_capabilities = greet_packet->server_capabilities;
conn->upsert_status->warning_count = 0; conn->upsert_status->warning_count = 0;
conn->upsert_status->server_status = greet_packet->server_status; conn->upsert_status->server_status = greet_packet->server_status;
conn->upsert_status->affected_rows = 0; conn->upsert_status->affected_rows = 0;
@ -811,6 +817,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
PACKET_FREE(greet_packet); PACKET_FREE(greet_packet);
DBG_RETURN(PASS); DBG_RETURN(PASS);
err: err:
conn->client_flag = 0;
conn->server_capabilities = 0;
PACKET_FREE(greet_packet); PACKET_FREE(greet_packet);
DBG_RETURN(FAIL); DBG_RETURN(FAIL);
} }
@ -1086,6 +1094,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
DBG_ENTER("mysqlnd_conn::connect"); DBG_ENTER("mysqlnd_conn::connect");
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC); ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC); conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
@ -2375,6 +2384,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS; conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
} }
break; break;
case MYSQL_OPT_CONNECT_ATTR_RESET:
if (conn->options->connect_attr) {
DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
zend_hash_clean(conn->options->connect_attr);
}
break;
case MYSQL_OPT_CONNECT_ATTR_DELETE:
if (conn->options->connect_attr && value) {
DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
zend_hash_del(conn->options->connect_attr, value, strlen(value));
DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
}
break;
#ifdef WHEN_SUPPORTED_BY_MYSQLI #ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_SHARED_MEMORY_BASE_NAME: case MYSQL_SHARED_MEMORY_BASE_NAME:
case MYSQL_OPT_USE_RESULT: case MYSQL_OPT_USE_RESULT:
@ -2395,6 +2417,69 @@ end:
/* }}} */ /* }}} */
/* {{{ connect_attr_item_dtor */
static void
connect_attr_item_dtor(void * pDest)
{
#ifdef ZTS
TSRMLS_FETCH();
#endif
DBG_ENTER("connect_attr_item_dtor");
mnd_pefree(*(char **) pDest, 1);
DBG_VOID_RETURN;
}
/* }}} */
/* {{{ mysqlnd_conn_data::set_client_option_2d */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
enum mysqlnd_option option,
const char * const key,
const char * const value
TSRMLS_DC)
{
size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
goto end;
}
switch (option) {
case MYSQL_OPT_CONNECT_ATTR_ADD:
if (!conn->options->connect_attr) {
DBG_INF("Initializing connect_attr hash");
conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
if (!conn->options->connect_attr) {
goto oom;
}
zend_hash_init(conn->options->connect_attr, 0, NULL, NULL, conn->persistent);
}
DBG_INF_FMT("Adding [%s][%s]", key, value);
{
const char * copyv = mnd_pestrdup(value, 1);
if (!copyv) {
goto oom;
}
zend_hash_update(conn->options->connect_attr, key, strlen(key), &copyv, sizeof(char *), NULL);
}
break;
default:
ret = FAIL;
}
conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
DBG_RETURN(ret);
oom:
SET_OOM_ERROR(*conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
end:
DBG_RETURN(FAIL);
}
/* }}} */
/* {{{ mysqlnd_conn_data::use_result */ /* {{{ mysqlnd_conn_data::use_result */
static MYSQLND_RES * static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC) MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
@ -2662,7 +2747,9 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags), MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake), MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request), MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name) MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)
MYSQLND_CLASS_METHODS_END; MYSQLND_CLASS_METHODS_END;

View file

@ -207,6 +207,7 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND_CONN_DATA * const conn, con
#define mysqlnd_set_character_set(conn, cs) ((conn)->data)->m->set_charset((conn)->data, (cs) TSRMLS_CC) #define mysqlnd_set_character_set(conn, cs) ((conn)->data)->m->set_charset((conn)->data, (cs) TSRMLS_CC)
#define mysqlnd_stat(conn, msg, msg_len) ((conn)->data)->m->get_server_statistics(((conn)->data), (msg), (msg_len) TSRMLS_CC) #define mysqlnd_stat(conn, msg, msg_len) ((conn)->data)->m->get_server_statistics(((conn)->data), (msg), (msg_len) TSRMLS_CC)
#define mysqlnd_options(conn, opt, value) ((conn)->data)->m->set_client_option((conn)->data, (opt), (value) TSRMLS_CC) #define mysqlnd_options(conn, opt, value) ((conn)->data)->m->set_client_option((conn)->data, (opt), (value) TSRMLS_CC)
#define mysqlnd_options4(conn, opt, k, v) ((conn)->data)->m->set_client_option_2d((conn)->data, (opt), (k), (v) TSRMLS_CC)
#define mysqlnd_set_server_option(conn, op) ((conn)->data)->m->set_server_option((conn)->data, (op) TSRMLS_CC) #define mysqlnd_set_server_option(conn, op) ((conn)->data)->m->set_server_option((conn)->data, (op) TSRMLS_CC)
/* Escaping */ /* Escaping */

View file

@ -28,7 +28,6 @@
#include "mysqlnd_charset.h" #include "mysqlnd_charset.h"
#include "mysqlnd_debug.h" #include "mysqlnd_debug.h"
/* {{{ mysqlnd_auth_handshake */ /* {{{ mysqlnd_auth_handshake */
enum_func_status enum_func_status
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn, mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
@ -99,6 +98,10 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
auth_packet->auth_data = auth_plugin_data; auth_packet->auth_data = auth_plugin_data;
auth_packet->auth_data_len = auth_plugin_data_len; auth_packet->auth_data_len = auth_plugin_data_len;
auth_packet->auth_plugin_name = auth_protocol; auth_packet->auth_plugin_name = auth_protocol;
if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) {
auth_packet->connect_attr = conn->options->connect_attr;
}
if (!PACKET_WRITE(auth_packet, conn)) { if (!PACKET_WRITE(auth_packet, conn)) {
goto end; goto end;

View file

@ -168,6 +168,9 @@ typedef enum mysqlnd_option
MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
MYSQL_PLUGIN_DIR, MYSQL_PLUGIN_DIR,
MYSQL_DEFAULT_AUTH, MYSQL_DEFAULT_AUTH,
MYSQL_OPT_CONNECT_ATTR_RESET,
MYSQL_OPT_CONNECT_ATTR_ADD,
MYSQL_OPT_CONNECT_ATTR_DELETE,
MYSQL_SERVER_PUBLIC_KEY, MYSQL_SERVER_PUBLIC_KEY,
MYSQL_ENABLE_CLEARTEXT_PLUGIN, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,

View file

@ -106,7 +106,8 @@
#define mysql_stmt_more_results(s) mysqlnd_stmt_more_results((s)) #define mysql_stmt_more_results(s) mysqlnd_stmt_more_results((s))
#define mysql_thread_safe() mysqlnd_thread_safe() #define mysql_thread_safe() mysqlnd_thread_safe()
#define mysql_info(r) mysqlnd_info((r)) #define mysql_info(r) mysqlnd_info((r))
#define mysql_options(r,a,b) mysqlnd_options((r), (a), (b)) #define mysql_options(c,a,v) mysqlnd_options((c), (a), (v))
#define mysql_options4(c,a,k,v) mysqlnd_options4((c), (a), (k), (v))
#define mysql_stmt_init(r) mysqlnd_stmt_init((r)) #define mysql_stmt_init(r) mysqlnd_stmt_init((r))
#define mysql_free_result(r) mysqlnd_free_result((r), FALSE) #define mysql_free_result(r) mysqlnd_free_result((r), FALSE)
#define mysql_store_result(r) mysqlnd_store_result((r)) #define mysql_store_result(r) mysqlnd_store_result((r))

View file

@ -172,7 +172,7 @@ typedef struct st_mysqlnd_options
The ABI will be broken and the methods structure will be somewhere else The ABI will be broken and the methods structure will be somewhere else
in the memory which can crash external code. Feel free to reuse these. in the memory which can crash external code. Feel free to reuse these.
*/ */
char * unused2; HashTable * connect_attr;
char * unused3; char * unused3;
char * unused4; char * unused4;
char * unused5; char * unused5;
@ -489,6 +489,8 @@ typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CO
typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC);
typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC); typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value TSRMLS_DC);
struct st_mysqlnd_conn_data_methods struct st_mysqlnd_conn_data_methods
{ {
func_mysqlnd_conn_data__init init; func_mysqlnd_conn_data__init init;
@ -573,6 +575,8 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__connect_handshake connect_handshake; func_mysqlnd_conn_data__connect_handshake connect_handshake;
func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request; func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request;
func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name; func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name;
func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d;
}; };

View file

@ -212,6 +212,24 @@ php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
/* }}} */ /* }}} */
/* {{{ php_mysqlnd_net_store_length_size */
size_t
php_mysqlnd_net_store_length_size(uint64_t length)
{
if (length < (uint64_t) L64(251)) {
return 1;
}
if (length < (uint64_t) L64(65536)) {
return 3;
}
if (length < (uint64_t) L64(16777216)) {
return 4;
}
return 8;
}
/* }}} */
/* {{{ php_mysqlnd_read_error_from_line */ /* {{{ php_mysqlnd_read_error_from_line */
static enum_func_status static enum_func_status
php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
@ -459,7 +477,7 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation TSRML
/* }}} */ /* }}} */
#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 1024) #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 4096)
/* {{{ php_mysqlnd_auth_write */ /* {{{ php_mysqlnd_auth_write */
static static
@ -540,6 +558,52 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC
p+= len; p+= len;
*p++= '\0'; *p++= '\0';
} }
if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) {
HashPosition pos_value;
const char ** entry_value;
size_t ca_payload_len = 0;
zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
char *s_key;
unsigned int s_len;
unsigned long num_key;
size_t value_len = strlen(*entry_value);
if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) {
ca_payload_len += php_mysqlnd_net_store_length_size(s_len);
ca_payload_len += s_len;
ca_payload_len += php_mysqlnd_net_store_length_size(value_len);
ca_payload_len += value_len;
}
zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
}
if ((sizeof(buffer) - (p - buffer)) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len))) {
p = php_mysqlnd_net_store_length(p, ca_payload_len);
zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
char *s_key;
unsigned int s_len;
unsigned long num_key;
size_t value_len = strlen(*entry_value);
if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) {
/* copy key */
p = php_mysqlnd_net_store_length(p, s_len);
memcpy(p, s_key, s_len);
p+= s_len;
/* copy value */
p = php_mysqlnd_net_store_length(p, value_len);
memcpy(p, *entry_value, value_len);
p+= value_len;
}
zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
}
} else {
/* cannot put the data - skip */
}
}
} }
if (packet->is_change_user_packet) { if (packet->is_change_user_packet) {
if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE, if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE,

View file

@ -103,7 +103,7 @@ typedef struct st_mysqlnd_packet_auth {
zend_bool send_auth_data; zend_bool send_auth_data;
zend_bool is_change_user_packet; zend_bool is_change_user_packet;
zend_bool silent; zend_bool silent;
HashTable *connect_attr;
} MYSQLND_PACKET_AUTH; } MYSQLND_PACKET_AUTH;
/* Auth response packet */ /* Auth response packet */