]> granicus.if.org Git - php/commitdiff
Add support for connect attributes, as of MySQL 5.6
authorAndrey Hristov <andrey@php.net>
Tue, 15 Jan 2013 09:04:59 +0000 (10:04 +0100)
committerAndrey Hristov <andrey@php.net>
Tue, 15 Jan 2013 09:04:59 +0000 (10:04 +0100)
ext/mysqlnd/mysqlnd.c
ext/mysqlnd/mysqlnd.h
ext/mysqlnd/mysqlnd_auth.c
ext/mysqlnd/mysqlnd_enum_n_def.h
ext/mysqlnd/mysqlnd_libmysql_compat.h
ext/mysqlnd/mysqlnd_structs.h
ext/mysqlnd/mysqlnd_wireprotocol.c
ext/mysqlnd/mysqlnd_wireprotocol.h

index ebc4a774a65789400e1cdaff62dc228a353b304a..1023b3e5f449dd7fca45fd5e734ebf00b446a13c 100644 (file)
@@ -95,6 +95,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_
                mnd_pefree(conn->options->cfg_section, pers);
                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;
        }
 
+       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,
                                                                                                   greet_packet, conn->options, mysql_flags TSRMLS_CC))
        {
                goto err;
        }
-       conn->client_flag                       = mysql_flags;
-       conn->server_capabilities       = greet_packet->server_capabilities;
        conn->upsert_status->warning_count = 0;
        conn->upsert_status->server_status = greet_packet->server_status;
        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);
        DBG_RETURN(PASS);
 err:
+       conn->client_flag = 0;
+       conn->server_capabilities = 0;
        PACKET_FREE(greet_packet);
        DBG_RETURN(FAIL);
 }
@@ -1086,6 +1094,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
        DBG_ENTER("mysqlnd_conn::connect");
 
        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);
 
                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;
                        }
                        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
                case MYSQL_SHARED_MEMORY_BASE_NAME:
                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 */
 static MYSQLND_RES *
 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, connect_handshake),
        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;
 
 
index 7e02126f3b72ea871a4cdf11ad05da31a81b5c39..bf0f70b405c26d24b12cb948d3c02b29bf442924 100644 (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_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_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)
 
 /* Escaping */
index a3b4f36a2e2a3c504716c450c17bda1b90e24a20..8611d998647fa78d63da7cca96dc19aebaffee28 100644 (file)
@@ -28,7 +28,6 @@
 #include "mysqlnd_charset.h"
 #include "mysqlnd_debug.h"
 
-
 /* {{{ mysqlnd_auth_handshake */
 enum_func_status
 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_len = auth_plugin_data_len;
                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)) {
                        goto end;
index abaaf1f43e30e49c551c5b15be5f4c11a22dddea..cf5b02728badf0f7f3698d7788753e759762e0fd 100644 (file)
@@ -168,6 +168,9 @@ typedef enum mysqlnd_option
        MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
        MYSQL_PLUGIN_DIR,
        MYSQL_DEFAULT_AUTH,
+       MYSQL_OPT_CONNECT_ATTR_RESET,
+       MYSQL_OPT_CONNECT_ATTR_ADD,
+       MYSQL_OPT_CONNECT_ATTR_DELETE,
        MYSQL_SERVER_PUBLIC_KEY,
        MYSQL_ENABLE_CLEARTEXT_PLUGIN,
        MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
index c967fb3a9f0371ff9e794c51bc0d8c09c3c919de..e3ab9eefa99c5e6b8a4263e1d47de4909f7fe40a 100644 (file)
 #define mysql_stmt_more_results(s)             mysqlnd_stmt_more_results((s))
 #define mysql_thread_safe()                            mysqlnd_thread_safe()
 #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_free_result(r)                   mysqlnd_free_result((r), FALSE)
 #define mysql_store_result(r)                  mysqlnd_store_result((r))
index 6302c81c6aecface70609662cbf9c3e211c5af0c..ecb1d892169656104f5b09c7c9db9f2046d1ad94 100644 (file)
@@ -172,7 +172,7 @@ typedef struct st_mysqlnd_options
          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.
        */
-       char            * unused2;
+       HashTable       * connect_attr;
        char            * unused3;
        char            * unused4;
        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 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
 {
        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__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__set_client_option_2d set_client_option_2d;
 };
 
 
index 7c3bf1395fe17df4a7da40a3dbfc2b17b78e981b..669970789bac5ce7c06dbe6d7c66e373da3a47cd 100644 (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 */
 static enum_func_status
 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 */
 static
@@ -540,6 +558,52 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC
                        p+= len;
                        *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 (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE,
index e9c9024d049d5378be05b2ed5a3618e4ae3913e2..26dd4c65aecd82586247a0c84aab267bf315ee77 100644 (file)
@@ -103,7 +103,7 @@ typedef struct st_mysqlnd_packet_auth {
        zend_bool       send_auth_data;
        zend_bool       is_change_user_packet;
        zend_bool       silent;
-
+       HashTable       *connect_attr;
 } MYSQLND_PACKET_AUTH;
 
 /* Auth response packet */