summaryrefslogtreecommitdiff
path: root/ext/mysqlnd
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /ext/mysqlnd
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r--ext/mysqlnd/CREDITS2
-rw-r--r--ext/mysqlnd/config-win.h115
-rw-r--r--ext/mysqlnd/config.w3238
-rw-r--r--ext/mysqlnd/config9.m453
-rw-r--r--ext/mysqlnd/mysqlnd.c2764
-rw-r--r--ext/mysqlnd/mysqlnd.h301
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.c796
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.h100
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c477
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.c194
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.h39
-rw-r--r--ext/mysqlnd/mysqlnd_bt.c484
-rw-r--r--ext/mysqlnd/mysqlnd_charset.c864
-rw-r--r--ext/mysqlnd/mysqlnd_charset.h53
-rw-r--r--ext/mysqlnd/mysqlnd_debug.c811
-rw-r--r--ext/mysqlnd/mysqlnd_debug.h193
-rw-r--r--ext/mysqlnd/mysqlnd_driver.c306
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h605
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c226
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h72
-rw-r--r--ext/mysqlnd/mysqlnd_libmysql_compat.h135
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c249
-rw-r--r--ext/mysqlnd/mysqlnd_net.c1069
-rw-r--r--ext/mysqlnd/mysqlnd_net.h38
-rw-r--r--ext/mysqlnd/mysqlnd_plugin.c209
-rw-r--r--ext/mysqlnd/mysqlnd_portability.h506
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h273
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c2374
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c952
-rw-r--r--ext/mysqlnd/mysqlnd_result.c1702
-rw-r--r--ext/mysqlnd/mysqlnd_result.h39
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c532
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.h39
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.c100
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.h50
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.c317
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.h177
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h1085
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c2416
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h321
-rw-r--r--ext/mysqlnd/php_mysqlnd.c389
-rw-r--r--ext/mysqlnd/php_mysqlnd.h38
42 files changed, 21503 insertions, 0 deletions
diff --git a/ext/mysqlnd/CREDITS b/ext/mysqlnd/CREDITS
new file mode 100644
index 0000000..5aed316
--- /dev/null
+++ b/ext/mysqlnd/CREDITS
@@ -0,0 +1,2 @@
+MySQLnd
+Andrey Hristov, Ulf Wendel, Georg Richter
diff --git a/ext/mysqlnd/config-win.h b/ext/mysqlnd/config-win.h
new file mode 100644
index 0000000..bc44050
--- /dev/null
+++ b/ext/mysqlnd/config-win.h
@@ -0,0 +1,115 @@
+/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
+This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Defines for Win32 to make it compatible for MySQL */
+
+#ifndef _MYSQLND_CONFIG_WIN_H
+#define _MYSQLND_CONFIG_WIN_H
+
+#include <sys/locking.h>
+#include <windows.h>
+#include <math.h> /* Because of rint() */
+#include <fcntl.h>
+#include <io.h>
+#include <malloc.h>
+
+#include <win32/php_stdint.h>
+
+#ifndef HAVE_INT8_T
+#define HAVE_INT8_T
+#endif
+#ifndef HAVE_UINT8_T
+#define HAVE_UINT8_T
+#endif
+#ifndef HAVE_INT16_T
+#define HAVE_INT16_T
+#endif
+#ifndef HAVE_UINT16_T
+#define HAVE_UINT16_T
+#endif
+#ifndef HAVE_INT32_T
+#define HAVE_INT32_T
+#endif
+#ifndef HAVE_UINT32_T
+#define HAVE_UINT32_T
+#endif
+#ifndef HAVE_INT64_T
+#define HAVE_INT64_T
+#endif
+#ifndef HAVE_UINT64_T
+#define HAVE_UINT64_T
+#endif
+
+
+#ifndef _WIN64
+#ifndef _WIN32
+#define _WIN32 /* Compatible with old source */
+#endif
+#ifndef __WIN32__
+#define __WIN32__
+#endif
+#endif /* _WIN64 */
+#ifndef __WIN__
+#define __WIN__ /* To make it easier in VC++ */
+#endif
+
+/* Type information */
+
+#define SIZEOF_CHAR 1
+#define SIZEOF_LONG 4
+#define SIZEOF_LONG_LONG 8
+
+
+#ifndef _WIN64
+/* Optimized store functions for Intel x86 */
+
+#define sint2korr(A) (*((int16_t *) (A)))
+#define sint3korr(A) ((int32_t) ((((zend_uchar) (A)[2]) & 128) ? \
+ (((uint32_t) 255L << 24) | \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])) : \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])))
+#define sint4korr(A) (*((int32_t *) (A)))
+#define uint2korr(A) (*((uint16_t *) (A)))
+#define uint3korr(A) (int32_t) (*((uint32_t *) (A)) & 0xFFFFFF)
+#define uint4korr(A) (*((uint32_t *) (A)))
+#define uint5korr(A) ((uint64_t)(((uint32_t) ((zend_uchar) (A)[0])) +\
+ (((uint32_t) ((zend_uchar) (A)[1])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[2])) << 16) +\
+ (((uint32_t) ((zend_uchar) (A)[3])) << 24)) +\
+ (((uint64_t) ((zend_uchar) (A)[4])) << 32))
+#define uint8korr(A) (*((uint64_t *) (A)))
+#define sint8korr(A) (*((int64_t *) (A)))
+#define int2store(T,A) *((uint16_t*) (T))= (uint16_t) (A)
+#define int3store(T,A) { *(T)= (zend_uchar) ((A));\
+ *(T+1)=(zend_uchar) (((uint32_t) (A) >> 8));\
+ *(T+2)=(zend_uchar) (((A) >> 16)); }
+#define int4store(T,A) *((int32_t *) (T))= (int32_t) (A)
+#define int5store(T,A) { *(T)= (zend_uchar)((A));\
+ *((T)+1)=(zend_uchar) (((A) >> 8));\
+ *((T)+2)=(zend_uchar) (((A) >> 16));\
+ *((T)+3)=(zend_uchar) (((A) >> 24)); \
+ *((T)+4)=(zend_uchar) (((A) >> 32)); }
+#define int8store(T,A) *((uint64_t *) (T))= (uint64_t) (A)
+
+#define float8get(V,M) { *((int32_t *) &V) = *((int32_t*) M); \
+ *(((int32_t *) &V)+1) = *(((int32_t*) M)+1); }
+#define float8store(T,V) { *((int32_t *) T) = *((int32_t*) &V); \
+ *(((int32_t *) T)+1) = *(((int32_t*) &V)+1); }
+#define float4get(V,M) { *((int32_t *) &(V)) = *((int32_t*) (M)); }
+
+#endif /* _WIN64 */
+
+#endif /* _MYSQLND_CONFIG_WIN_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32
new file mode 100644
index 0000000..a910a0e
--- /dev/null
+++ b/ext/mysqlnd/config.w32
@@ -0,0 +1,38 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("mysqlnd", "Mysql Native Client Driver", "yes");
+if (PHP_MYSQLND != "no") {
+
+ if (CHECK_LIB("ws2_32.lib", "mysqlnd")) {
+ mysqlnd_source =
+ "mysqlnd.c " +
+ "mysqlnd_alloc.c " +
+ "mysqlnd_auth.c " +
+ "mysqlnd_block_alloc.c " +
+ "mysqlnd_bt.c " +
+ "mysqlnd_charset.c " +
+ "mysqlnd_debug.c " +
+ "mysqlnd_driver.c " +
+ "mysqlnd_ext_plugin.c " +
+ "mysqlnd_loaddata.c " +
+ "mysqlnd_reverse_api.c " +
+ "mysqlnd_net.c " +
+ "mysqlnd_plugin.c " +
+ "mysqlnd_ps.c " +
+ "mysqlnd_ps_codec.c " +
+ "mysqlnd_result.c " +
+ "mysqlnd_result_meta.c " +
+ "mysqlnd_statistics.c " +
+ "mysqlnd_wireprotocol.c " +
+ "php_mysqlnd.c ";
+ EXTENSION("mysqlnd", mysqlnd_source, false);
+ if (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "mysqlnd", PHP_MYSQLND))) ||
+ (PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "mysqlnd", PHP_MYSQLND)) || (PHP_ZLIB == "yes" && (!PHP_ZLIB_SHARED)))
+ {
+ AC_DEFINE("MYSQLND_COMPRESSION_ENABLED", 1, "Compression support");
+ AC_DEFINE("MYSQLND_SSL_SUPPORTED", 1, "SSL support");
+ }
+ PHP_INSTALL_HEADERS("", "ext/mysqlnd");
+ }
+}
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4
new file mode 100644
index 0000000..2c15c34
--- /dev/null
+++ b/ext/mysqlnd/config9.m4
@@ -0,0 +1,53 @@
+dnl
+dnl $Id$
+dnl config.m4 for mysqlnd driver
+
+PHP_ARG_ENABLE(mysqlnd, whether to enable mysqlnd,
+ [ --enable-mysqlnd Enable mysqlnd explicitly, will be done implicitly
+ when required by other extensions], no, yes)
+
+PHP_ARG_ENABLE(mysqlnd_compression_support, whether to disable compressed protocol support in mysqlnd,
+[ --disable-mysqlnd-compression-support
+ Disable support for the MySQL compressed protocol in mysqlnd], yes, no)
+
+if test -z "$PHP_ZLIB_DIR"; then
+ PHP_ARG_WITH(zlib-dir, for the location of libz,
+ [ --with-zlib-dir[=DIR] mysqlnd: Set the path to libz install prefix], no, no)
+fi
+
+dnl If some extension uses mysqlnd it will get compiled in PHP core
+if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes"; then
+ mysqlnd_ps_sources="mysqlnd_ps.c mysqlnd_ps_codec.c"
+ mysqlnd_base_sources="mysqlnd.c mysqlnd_alloc.c mysqlnd_bt.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
+ mysqlnd_loaddata.c mysqlnd_reverse_api.c mysqlnd_net.c \
+ mysqlnd_statistics.c mysqlnd_driver.c mysqlnd_ext_plugin.c mysqlnd_auth.c \
+ mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c\
+ mysqlnd_block_alloc.c mysqlnd_plugin.c php_mysqlnd.c"
+
+
+ if test "$PHP_MYSQLND_COMPRESSION_SUPPORT" != "no"; then
+ AC_DEFINE([MYSQLND_COMPRESSION_WANTED], 1, [Enable compressed protocol support])
+ fi
+ AC_DEFINE([MYSQLND_SSL_SUPPORTED], 1, [Enable SSL support])
+
+ mysqlnd_sources="$mysqlnd_base_sources $mysqlnd_ps_sources"
+ PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, $ext_shared)
+ PHP_ADD_BUILD_DIR([ext/mysqlnd], 1)
+ PHP_INSTALL_HEADERS([ext/mysqlnd/])
+fi
+
+if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then
+ PHP_ADD_BUILD_DIR([ext/mysqlnd], 1)
+
+ dnl This creates a file so it has to be after above macros
+ PHP_CHECK_TYPES([int8 uint8 int16 uint16 int32 uint32 uchar ulong int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t], [
+ ext/mysqlnd/php_mysqlnd_config.h
+ ],[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+ ])
+fi
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
new file mode 100644
index 0000000..90c2b73
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd.c
@@ -0,0 +1,2764 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+
+/*
+ TODO :
+ - Don't bind so tightly the metadata with the result set. This means
+ that the metadata reading should not expect a MYSQLND_RES pointer, it
+ does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
+ For normal statements we will then just assign it to a member of
+ MYSQLND_RES. For PS statements, it will stay as part of the statement
+ (MYSQLND_STMT) between prepare and execute. At execute the new metadata
+ will be sent by the server, so we will discard the old one and then
+ finally attach it to the result set. This will make the code more clean,
+ as a prepared statement won't have anymore stmt->result != NULL, as it
+ is now, just to have where to store the metadata.
+
+ - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
+ terminated by a string with ptr being NULL. Thus, multi-part messages can be
+ sent to the network like writev() and this can save at least for
+ mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
+ code in few other places cleaner.
+*/
+
+extern MYSQLND_CHARSET *mysqlnd_charsets;
+
+
+
+PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
+"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
+"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
+"flag from your my.cnf file";
+
+PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
+PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
+PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
+
+PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
+
+
+/* {{{ mysqlnd_conn_data::free_options */
+static void
+MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_bool pers = conn->persistent;
+
+ if (conn->options->charset_name) {
+ mnd_pefree(conn->options->charset_name, pers);
+ conn->options->charset_name = NULL;
+ }
+ if (conn->options->auth_protocol) {
+ mnd_pefree(conn->options->auth_protocol, pers);
+ conn->options->auth_protocol = NULL;
+ }
+ if (conn->options->num_commands) {
+ unsigned int i;
+ for (i = 0; i < conn->options->num_commands; i++) {
+ /* allocated with pestrdup */
+ mnd_pefree(conn->options->init_commands[i], pers);
+ }
+ mnd_pefree(conn->options->init_commands, pers);
+ conn->options->init_commands = NULL;
+ }
+ if (conn->options->cfg_file) {
+ mnd_pefree(conn->options->cfg_file, pers);
+ conn->options->cfg_file = NULL;
+ }
+ if (conn->options->cfg_section) {
+ mnd_pefree(conn->options->cfg_section, pers);
+ conn->options->cfg_section = NULL;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_bool pers = conn->persistent;
+
+ DBG_ENTER("mysqlnd_conn_data::free_contents");
+
+ mysqlnd_local_infile_default(conn);
+ if (conn->current_result) {
+ conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
+ conn->current_result = NULL;
+ }
+
+ if (conn->net) {
+ conn->net->m.free_contents(conn->net TSRMLS_CC);
+ }
+
+ DBG_INF("Freeing memory of members");
+
+ if (conn->host) {
+ mnd_pefree(conn->host, pers);
+ conn->host = NULL;
+ }
+ if (conn->user) {
+ mnd_pefree(conn->user, pers);
+ conn->user = NULL;
+ }
+ if (conn->passwd) {
+ mnd_pefree(conn->passwd, pers);
+ conn->passwd = NULL;
+ }
+ if (conn->connect_or_select_db) {
+ mnd_pefree(conn->connect_or_select_db, pers);
+ conn->connect_or_select_db = NULL;
+ }
+ if (conn->unix_socket) {
+ mnd_pefree(conn->unix_socket, pers);
+ conn->unix_socket = NULL;
+ }
+ DBG_INF_FMT("scheme=%s", conn->scheme);
+ if (conn->scheme) {
+ mnd_pefree(conn->scheme, pers);
+ conn->scheme = NULL;
+ }
+ if (conn->server_version) {
+ mnd_pefree(conn->server_version, pers);
+ conn->server_version = NULL;
+ }
+ if (conn->host_info) {
+ mnd_pefree(conn->host_info, pers);
+ conn->host_info = NULL;
+ }
+ if (conn->auth_plugin_data) {
+ mnd_pefree(conn->auth_plugin_data, pers);
+ conn->auth_plugin_data = NULL;
+ }
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, pers);
+ conn->last_message = NULL;
+ }
+ if (conn->error_info->error_list) {
+ zend_llist_clean(conn->error_info->error_list);
+ mnd_pefree(conn->error_info->error_list, pers);
+ conn->error_info->error_list = NULL;
+ }
+ conn->charset = NULL;
+ conn->greet_charset = NULL;
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::dtor */
+static void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::dtor");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ conn->m->free_contents(conn TSRMLS_CC);
+ conn->m->free_options(conn TSRMLS_CC);
+
+ if (conn->net) {
+ mysqlnd_net_free(conn->net, conn->stats, conn->error_info TSRMLS_CC);
+ conn->net = NULL;
+ }
+
+ if (conn->protocol) {
+ mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
+ conn->protocol = NULL;
+ }
+
+ if (conn->stats) {
+ mysqlnd_stats_end(conn->stats);
+ }
+
+ mnd_pefree(conn, conn->persistent);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::simple_command_handle_response */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet,
+ zend_bool silent, enum php_mysqlnd_server_command command,
+ zend_bool ignore_upsert_status TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
+ DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
+
+ switch (ok_packet) {
+ case PROT_OK_PACKET:{
+ MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!ok_response) {
+ SET_OOM_ERROR(*conn->error_info);
+ break;
+ }
+ if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
+ if (!silent) {
+ DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u",
+ mysqlnd_command_to_text[command], getpid());
+ }
+ } else {
+ DBG_INF_FMT("OK from server");
+ if (0xFF == ok_response->field_count) {
+ /* The server signalled error. Set the error */
+ SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
+ ret = FAIL;
+ /*
+ Cover a protocol design error: error packet does not
+ contain the server status. Therefore, the client has no way
+ to find out whether there are more result sets of
+ a multiple-result-set statement pending. Luckily, in 5.0 an
+ error always aborts execution of a statement, wherever it is
+ a multi-statement or a stored procedure, so it should be
+ safe to unconditionally turn off the flag here.
+ */
+ conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ SET_ERROR_AFF_ROWS(conn);
+ } else {
+ SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
+ ok_response->message, ok_response->message_len,
+ conn->persistent);
+
+ if (!ignore_upsert_status) {
+ conn->upsert_status->warning_count = ok_response->warning_count;
+ conn->upsert_status->server_status = ok_response->server_status;
+ conn->upsert_status->affected_rows = ok_response->affected_rows;
+ conn->upsert_status->last_insert_id = ok_response->last_insert_id;
+ }
+ }
+ }
+ PACKET_FREE(ok_response);
+ break;
+ }
+ case PROT_EOF_PACKET:{
+ MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!ok_response) {
+ SET_OOM_ERROR(*conn->error_info);
+ break;
+ }
+ if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
+ "Malformed packet");
+ if (!silent) {
+ DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
+ mysqlnd_command_to_text[command], getpid());
+ }
+ } else if (0xFF == ok_response->field_count) {
+ /* The server signalled error. Set the error */
+ SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
+ SET_ERROR_AFF_ROWS(conn);
+ } else if (0xFE != ok_response->field_count) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ if (!silent) {
+ DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
+ ok_response->field_count);
+ }
+ } else {
+ DBG_INF_FMT("OK from server");
+ }
+ PACKET_FREE(ok_response);
+ break;
+ }
+ default:
+ SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
+ break;
+ }
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::simple_command */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
+ const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
+ zend_bool ignore_upsert_status TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ MYSQLND_PACKET_COMMAND * cmd_packet;
+
+ DBG_ENTER("mysqlnd_conn_data::simple_command");
+ DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent);
+
+ switch (CONN_GET_STATE(conn)) {
+ case CONN_READY:
+ break;
+ case CONN_QUIT_SENT:
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ DBG_ERR("Server is gone");
+ DBG_RETURN(FAIL);
+ default:
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
+ DBG_RETURN(FAIL);
+ }
+
+ /* clean UPSERT info */
+ if (!ignore_upsert_status) {
+ memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ }
+ SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(*conn->error_info);
+
+ cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!cmd_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ cmd_packet->command = command;
+ if (arg && arg_len) {
+ cmd_packet->argument = arg;
+ cmd_packet->arg_len = arg_len;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
+
+ if (! PACKET_WRITE(cmd_packet, conn)) {
+ if (!silent) {
+ DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
+ php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
+ }
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ DBG_ERR("Server is gone");
+ ret = FAIL;
+ } else if (ok_packet != PROT_LAST) {
+ ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
+ }
+
+ PACKET_FREE(cmd_packet);
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::set_server_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
+ zend_uchar buffer[2];
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::set_server_option");
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+
+ int2store(buffer, (unsigned int) option);
+ ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::restart_psession */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::restart_psession");
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
+ /* Free here what should not be seen by the next script */
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, conn->persistent);
+ conn->last_message = NULL;
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::end_psession */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::end_psession");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_switch_to_ssl_if_needed */
+static enum_func_status
+mysqlnd_switch_to_ssl_if_needed(
+ MYSQLND_CONN_DATA * conn,
+ const MYSQLND_PACKET_GREET * const greet_packet,
+ const MYSQLND_OPTIONS * const options,
+ unsigned long mysql_flags
+ TSRMLS_DC
+ )
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset;
+ MYSQLND_PACKET_AUTH * auth_packet;
+ DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
+
+ auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!auth_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+ auth_packet->client_flags = mysql_flags;
+ auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+
+ if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
+ auth_packet->charset_no = charset->nr;
+ } else {
+#if MYSQLND_UNICODE
+ auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
+#else
+ auth_packet->charset_no = greet_packet->charset_no;
+#endif
+ }
+
+#ifdef MYSQLND_SSL_SUPPORTED
+ if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) {
+ zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE;
+ DBG_INF("Switching to SSL");
+ if (!PACKET_WRITE(auth_packet, conn)) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ goto end;
+ }
+
+ conn->net->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
+
+ if (FAIL == conn->net->m.enable_ssl(conn->net TSRMLS_CC)) {
+ goto end;
+ }
+ }
+#endif
+ ret = PASS;
+end:
+ PACKET_FREE(auth_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect_run_authentication */
+static enum_func_status
+mysqlnd_connect_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ size_t db_len,
+ size_t passwd_len,
+ const MYSQLND_PACKET_GREET * const greet_packet,
+ const MYSQLND_OPTIONS * const options,
+ unsigned long mysql_flags
+ TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_connect_run_authentication");
+
+ ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
+ if (PASS == ret) {
+ zend_bool first_call = TRUE;
+
+ char * switch_to_auth_protocol = NULL;
+ size_t switch_to_auth_protocol_len = 0;
+ char * requested_protocol = NULL;
+ zend_uchar * plugin_data;
+ size_t plugin_data_len;
+
+ plugin_data_len = greet_packet->auth_plugin_data_len;
+ plugin_data = mnd_emalloc(plugin_data_len + 1);
+ if (!plugin_data) {
+ ret = FAIL;
+ goto end;
+ }
+ memcpy(plugin_data, greet_packet->auth_plugin_data, plugin_data_len);
+ plugin_data[plugin_data_len] = '\0';
+
+ requested_protocol = mnd_pestrdup(greet_packet->auth_protocol? greet_packet->auth_protocol: "mysql_native_password", FALSE);
+ if (!requested_protocol) {
+ ret = FAIL;
+ goto end;
+ }
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin;
+ {
+ char * plugin_name = NULL;
+
+ mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
+
+ DBG_INF_FMT("looking for %s auth plugin", plugin_name);
+ auth_plugin = mysqlnd_plugin_find(plugin_name);
+ mnd_sprintf_free(plugin_name);
+
+ if (!auth_plugin) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
+ SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method umknown to the client");
+ break;
+ }
+ }
+ DBG_INF("plugin found");
+
+ {
+ zend_uchar * switch_to_auth_protocol_data = NULL;
+ size_t switch_to_auth_protocol_data_len = 0;
+ zend_uchar * scrambled_data = NULL;
+ size_t scrambled_data_len = 0;
+
+ switch_to_auth_protocol = NULL;
+ switch_to_auth_protocol_len = 0;
+
+ if (conn->auth_plugin_data) {
+ mnd_pefree(conn->auth_plugin_data, conn->persistent);
+ conn->auth_plugin_data = NULL;
+ }
+ conn->auth_plugin_data_len = plugin_data_len;
+ conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
+ if (!conn->auth_plugin_data) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+ memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
+
+ DBG_INF_FMT("salt=[%*s]", plugin_data_len - 1, plugin_data);
+ /* The data should be allocated with malloc() */
+ scrambled_data =
+ auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
+ plugin_data, plugin_data_len, options, mysql_flags TSRMLS_CC);
+
+
+ ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
+ greet_packet->charset_no,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ TSRMLS_CC);
+ first_call = FALSE;
+ free(scrambled_data);
+
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ if (requested_protocol && switch_to_auth_protocol) {
+ mnd_efree(requested_protocol);
+ requested_protocol = switch_to_auth_protocol;
+ }
+
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ plugin_data_len = switch_to_auth_protocol_data_len;
+ plugin_data = switch_to_auth_protocol_data;
+ }
+ DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
+ } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+
+ if (ret == PASS) {
+ DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
+ }
+
+ if (requested_protocol) {
+ mnd_efree(requested_protocol);
+ }
+ }
+end:
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::connect */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
+ const char *host, const char *user,
+ const char *passwd, unsigned int passwd_len,
+ const char *db, unsigned int db_len,
+ unsigned int port,
+ const char *socket_or_pipe,
+ unsigned int mysql_flags
+ TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
+ size_t host_len;
+ zend_bool unix_socket = FALSE;
+ zend_bool named_pipe = FALSE;
+ zend_bool reconnect = FALSE;
+ zend_bool saved_compression = FALSE;
+ zend_bool local_tx_started = FALSE;
+
+ MYSQLND_PACKET_GREET * greet_packet = NULL;
+
+ DBG_ENTER("mysqlnd_conn_data::connect");
+
+ if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ goto err;
+ }
+ local_tx_started = TRUE;
+
+ SET_EMPTY_ERROR(*conn->error_info);
+ SET_ERROR_AFF_ROWS(conn);
+
+ DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
+ host?host:"", user?user:"", db?db:"", port, mysql_flags,
+ conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
+
+ if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
+ DBG_INF("Connecting on a connected handle.");
+
+ if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
+ reconnect = TRUE;
+ conn->m->send_close(conn TSRMLS_CC);
+ }
+
+ conn->m->free_contents(conn TSRMLS_CC);
+ MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
+ if (conn->persistent) {
+ MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ }
+ /* Now reconnect using the same handle */
+ if (conn->net->compressed) {
+ /*
+ we need to save the state. As we will re-connect, net->compressed should be off, or
+ we will look for a compression header as part of the greet message, but there will
+ be none.
+ */
+ saved_compression = TRUE;
+ conn->net->compressed = FALSE;
+ }
+ } else {
+ unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+ conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size TSRMLS_CC);
+ }
+
+ if (!host || !host[0]) {
+ host = "localhost";
+ }
+ if (!user) {
+ DBG_INF_FMT("no user given, using empty string");
+ user = "";
+ }
+ if (!passwd) {
+ DBG_INF_FMT("no password given, using empty string");
+ passwd = "";
+ passwd_len = 0;
+ }
+ if (!db) {
+ DBG_INF_FMT("no db given, using empty string");
+ db = "";
+ db_len = 0;
+ }
+
+ host_len = strlen(host);
+ {
+ char * transport = NULL;
+ int transport_len;
+#ifndef PHP_WIN32
+ if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
+ DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
+ if (!socket_or_pipe) {
+ socket_or_pipe = "/tmp/mysql.sock";
+ }
+ transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
+ unix_socket = TRUE;
+#else
+ if (host_len == sizeof(".") - 1 && host[0] == '.') {
+ /* named pipe in socket */
+ if (!socket_or_pipe) {
+ socket_or_pipe = "\\\\.\\pipe\\MySQL";
+ }
+ transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
+ named_pipe = TRUE;
+#endif
+ } else {
+ if (!port) {
+ port = 3306;
+ }
+ transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
+ }
+ if (!transport) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
+ conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
+ conn->scheme_len = transport_len;
+ mnd_sprintf_free(transport);
+ transport = NULL;
+ if (!conn->scheme) {
+ goto err; /* OOM */
+ }
+ }
+
+ greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!greet_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+
+ if (FAIL == conn->net->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
+ conn->stats, conn->error_info TSRMLS_CC))
+ {
+ goto err;
+ }
+
+ DBG_INF_FMT("stream=%p", conn->net->stream);
+
+ if (FAIL == PACKET_READ(greet_packet, conn)) {
+ DBG_ERR("Error while reading greeting packet");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
+ goto err;
+ } else if (greet_packet->error_no) {
+ DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
+ SET_CLIENT_ERROR(*conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
+ goto err;
+ } else if (greet_packet->pre41) {
+ DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
+ " is not supported. Server is %-.32s", greet_packet->server_version);
+ SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
+ goto err;
+ }
+
+ conn->thread_id = greet_packet->thread_id;
+ conn->protocol_version = greet_packet->protocol_version;
+ conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
+
+ conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
+ if (!conn->greet_charset) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
+ SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Server sent charset unknown to the client. Please, report to the developers");
+ goto err;
+ }
+ /* we allow load data local infile by default */
+ mysql_flags |= MYSQLND_CAPABILITIES;
+
+ mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */
+
+ if (db) {
+ mysql_flags |= CLIENT_CONNECT_WITH_DB;
+ }
+
+ if (PG(open_basedir) && strlen(PG(open_basedir))) {
+ mysql_flags ^= CLIENT_LOCAL_FILES;
+ }
+
+#ifndef MYSQLND_COMPRESSION_ENABLED
+ if (mysql_flags & CLIENT_COMPRESS) {
+ mysql_flags &= ~CLIENT_COMPRESS;
+ }
+#else
+ if (conn->net->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
+ mysql_flags |= CLIENT_COMPRESS;
+ }
+#endif
+#ifndef MYSQLND_SSL_SUPPORTED
+ if (mysql_flags & CLIENT_SSL) {
+ mysql_flags &= ~CLIENT_SSL;
+ }
+#else
+ if (conn->net->options.ssl_key || conn->net->options.ssl_cert ||
+ conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher)
+ {
+ mysql_flags |= CLIENT_SSL;
+ }
+#endif
+
+ 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_SET_STATE(conn, CONN_READY);
+
+ if (saved_compression) {
+ conn->net->compressed = TRUE;
+ }
+ /*
+ If a connect on a existing handle is performed and mysql_flags is
+ passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
+ which we set based on saved_compression.
+ */
+ conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
+
+ conn->user = mnd_pestrdup(user, conn->persistent);
+ conn->user_len = strlen(conn->user);
+ conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
+ conn->passwd_len = passwd_len;
+ conn->port = port;
+ conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
+ conn->connect_or_select_db_len = db_len;
+
+ if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+
+ if (!unix_socket && !named_pipe) {
+ conn->host = mnd_pestrdup(host, conn->persistent);
+ if (!conn->host) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ conn->host_len = strlen(conn->host);
+ {
+ char *p;
+ mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
+ if (!p) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ conn->host_info = mnd_pestrdup(p, conn->persistent);
+ mnd_sprintf_free(p);
+ if (!conn->host_info) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ }
+ } else {
+ conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
+ if (unix_socket) {
+ conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
+ } else if (named_pipe) {
+ char *p;
+ mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
+ if (!p) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ conn->host_info = mnd_pestrdup(p, conn->persistent);
+ mnd_sprintf_free(p);
+ if (!conn->host_info) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
+ }
+ if (!conn->unix_socket || !conn->host_info) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto err; /* OOM */
+ }
+ conn->unix_socket_len = strlen(conn->unix_socket);
+ }
+ conn->client_flag = mysql_flags;
+ conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+ /* todo: check if charset is available */
+ 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;
+
+ SET_EMPTY_ERROR(*conn->error_info);
+
+ mysqlnd_local_infile_default(conn);
+
+#if MYSQLND_UNICODE
+ {
+ unsigned int as_unicode = 1;
+ conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC);
+ DBG_INF("unicode set");
+ }
+#endif
+ if (conn->options->init_commands) {
+ unsigned int current_command = 0;
+ for (; current_command < conn->options->num_commands; ++current_command) {
+ const char * const command = conn->options->init_commands[current_command];
+ if (command) {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
+ if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
+ goto err;
+ }
+ if (conn->last_query_type == QUERY_SELECT) {
+ MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC);
+ if (result) {
+ result->m.free_result(result, TRUE TSRMLS_CC);
+ }
+ }
+ }
+ }
+ }
+
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
+ if (reconnect) {
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
+ }
+ if (conn->persistent) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
+ }
+
+ DBG_INF_FMT("connection_id=%llu", conn->thread_id);
+
+ PACKET_FREE(greet_packet);
+
+ conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+err:
+ PACKET_FREE(greet_packet);
+
+ DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ if (!conn->error_info->error_no) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)",
+ conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ }
+
+ conn->m->free_contents(conn TSRMLS_CC);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
+ if (TRUE == local_tx_started) {
+ conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
+ }
+
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::connect */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
+ const char * host, const char * user,
+ const char * passwd, unsigned int passwd_len,
+ const char * db, unsigned int db_len,
+ unsigned int port,
+ const char * socket_or_pipe,
+ unsigned int mysql_flags
+ TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = conn_handle->data;
+
+ DBG_ENTER("mysqlnd_conn::connect");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func 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);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/* {{{ mysqlnd_connect */
+PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
+ const char * host, const char * user,
+ const char * passwd, unsigned int passwd_len,
+ const char * db, unsigned int db_len,
+ unsigned int port,
+ const char * socket_or_pipe,
+ unsigned int mysql_flags
+ TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ zend_bool self_alloced = FALSE;
+
+ DBG_ENTER("mysqlnd_connect");
+ DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
+
+ if (!conn_handle) {
+ self_alloced = TRUE;
+ if (!(conn_handle = mysqlnd_init(FALSE))) {
+ /* OOM */
+ DBG_RETURN(NULL);
+ }
+ }
+
+ ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
+
+ if (ret == FAIL) {
+ if (self_alloced) {
+ /*
+ We have alloced, thus there are no references to this
+ object - we are free to kill it!
+ */
+ conn_handle->m->dtor(conn_handle TSRMLS_CC);
+ }
+ DBG_RETURN(NULL);
+ }
+ DBG_RETURN(conn_handle);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::query */
+/*
+ If conn->error_info->error_no is not zero, then we had an error.
+ Still the result from the query is PASS
+*/
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::query");
+ DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
+ PASS == conn->m->reap_query(conn TSRMLS_CC))
+ {
+ ret = PASS;
+ if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ }
+ }
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::send_query */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
+ enum_func_status ret;
+ DBG_ENTER("mysqlnd_conn_data::send_query");
+ DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE, FALSE TSRMLS_CC);
+ if (PASS == ret) {
+ CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ }
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::reap_query */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
+ enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::reap_query");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ if (state <= CONN_READY || state == CONN_QUIT_SENT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
+ DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
+ DBG_RETURN(ret);
+ }
+ ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+#include "php_network.h"
+
+MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
+{
+ int cnt = 0;
+ MYSQLND **p = conn_array, **p_p;
+ MYSQLND **ret = NULL;
+
+ while (*p) {
+ if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
+ cnt++;
+ }
+ p++;
+ }
+ if (cnt) {
+ MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
+ p_p = p = conn_array;
+ while (*p) {
+ if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
+ *ret_p = *p;
+ *p = NULL;
+ ret_p++;
+ } else {
+ *p_p = *p;
+ p_p++;
+ }
+ p++;
+ }
+ *ret_p = NULL;
+ }
+ return ret;
+}
+
+
+/* {{{ stream_select mysqlnd_stream_array_to_fd_set functions */
+static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd TSRMLS_DC)
+{
+ php_socket_t this_fd;
+ int cnt = 0;
+ MYSQLND **p = conn_array;
+
+ while (*p) {
+ /* get the fd.
+ * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+ * when casting. It is only used here so that the buffered data warning
+ * is not displayed.
+ * */
+ if (SUCCESS == php_stream_cast((*p)->data->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && this_fd >= 0) {
+
+ PHP_SAFE_FD_SET(this_fd, fds);
+
+ if (this_fd > *max_fd) {
+ *max_fd = this_fd;
+ }
+ cnt++;
+ }
+ p++;
+ }
+ return cnt ? 1 : 0;
+}
+
+static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds TSRMLS_DC)
+{
+ php_socket_t this_fd;
+ int ret = 0;
+ zend_bool disproportion = FALSE;
+
+
+ MYSQLND **fwd = conn_array, **bckwd = conn_array;
+
+ while (*fwd) {
+ if (SUCCESS == php_stream_cast((*fwd)->data->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && this_fd >= 0) {
+ if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
+ if (disproportion) {
+ *bckwd = *fwd;
+ }
+ bckwd++;
+ fwd++;
+ ret++;
+ continue;
+ }
+ }
+ disproportion = TRUE;
+ fwd++;
+ }
+ *bckwd = NULL;/* NULL-terminate the list */
+
+ return ret;
+}
+/* }}} */
+
+
+#ifndef PHP_WIN32
+#define php_select(m, r, w, e, t) select(m, r, w, e, t)
+#else
+#include "win32/select.h"
+#endif
+
+
+/* {{{ _mysqlnd_poll */
+PHPAPI enum_func_status
+_mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC)
+{
+ struct timeval tv;
+ struct timeval *tv_p = NULL;
+ fd_set rfds, wfds, efds;
+ php_socket_t max_fd = 0;
+ int retval, sets = 0;
+ int set_count, max_set_count = 0;
+
+ DBG_ENTER("_mysqlnd_poll");
+ if (sec < 0 || usec < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
+ DBG_RETURN(FAIL);
+ }
+
+ *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ if (r_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (e_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (!sets) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_RETURN(FAIL);
+ }
+
+ PHP_SAFE_MAX_FD(max_fd, max_set_count);
+
+ /* Solaris + BSD do not like microsecond values which are >= 1 sec */
+ if (usec > 999999) {
+ tv.tv_sec = sec + (usec / 1000000);
+ tv.tv_usec = usec % 1000000;
+ } else {
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ }
+
+ tv_p = &tv;
+
+ retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
+
+ if (retval == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
+ errno, strerror(errno), max_fd);
+ DBG_RETURN(FAIL);
+ }
+
+ if (r_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
+ }
+ if (e_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
+ }
+
+ *desc_num = retval;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/*
+ COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
+ - There is no result set header - status from the command, which
+ impacts us to allocate big chunk of memory for reading the metadata.
+ - The EOF packet is consumed by the metadata packet reader.
+*/
+
+/* {{{ mysqlnd_conn_data::list_fields */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
+ /* db + \0 + wild + \0 (for wild) */
+ zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
+ size_t table_len, wild_len;
+ MYSQLND_RES * result = NULL;
+ DBG_ENTER("mysqlnd_conn_data::list_fields");
+ DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ do {
+ p = buff;
+ if (table && (table_len = strlen(table))) {
+ size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
+ memcpy(p, table, to_copy);
+ p += to_copy;
+ *p++ = '\0';
+ }
+
+ if (achtung_wild && (wild_len = strlen(achtung_wild))) {
+ size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
+ memcpy(p, achtung_wild, to_copy);
+ p += to_copy;
+ *p++ = '\0';
+ }
+
+ if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE, TRUE TSRMLS_CC)) {
+ conn->m->local_tx_end(conn, 0, FAIL TSRMLS_CC);
+ break;
+ }
+
+ /*
+ Prepare for the worst case.
+ MyISAM goes to 2500 BIT columns, double it for safety.
+ */
+ result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
+ if (!result) {
+ break;
+ }
+
+ if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
+ DBG_ERR("Error ocurred while reading metadata");
+ result->m.free_result(result, TRUE TSRMLS_CC);
+ result = NULL;
+ break;
+ }
+
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ if (!result->unbuf) {
+ /* OOM */
+ SET_OOM_ERROR(*conn->error_info);
+ result->m.free_result(result, TRUE TSRMLS_CC);
+ result = NULL;
+ break;
+ }
+ result->unbuf->eof_reached = TRUE;
+ } while (0);
+ conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
+ }
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::list_method */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
+ char * show_query = NULL;
+ size_t show_query_len;
+ MYSQLND_RES * result = NULL;
+
+ DBG_ENTER("mysqlnd_conn_data::list_method");
+ DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ if (par1) {
+ if (achtung_wild) {
+ show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
+ } else {
+ show_query_len = mnd_sprintf(&show_query, 0, query, par1);
+ }
+ } else {
+ if (achtung_wild) {
+ show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
+ } else {
+ show_query_len = strlen(show_query = (char *)query);
+ }
+ }
+
+ if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
+ result = conn->m->store_result(conn TSRMLS_CC);
+ }
+ if (show_query != query) {
+ mnd_sprintf_free(show_query);
+ }
+ conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
+ }
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::errno */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->error_info->error_no;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::error */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->error_info->error;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::sqlstate */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_old_escape_string */
+PHPAPI ulong
+mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_old_escape_string");
+ DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::ssl_set */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
+ const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::ssl_set");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = (PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
+ PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
+ PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
+ PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
+ PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::escape_string */
+static ulong
+MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
+ ulong ret;
+ DBG_ENTER("mysqlnd_conn_data::escape_string");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
+ ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
+ } else {
+ ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
+ }
+ conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::dump_debug_info */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::select_db */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_conn_data::select_db");
+ DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ SET_ERROR_AFF_ROWS(conn);
+ if (ret == PASS) {
+ if (conn->connect_or_select_db) {
+ mnd_pefree(conn->connect_or_select_db, conn->persistent);
+ }
+ conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
+ conn->connect_or_select_db_len = db_len;
+ if (!conn->connect_or_select_db) {
+ /* OOM */
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ }
+ }
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::ping */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_conn_data::ping");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ SET_ERROR_AFF_ROWS(conn);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_INF_FMT("ret=%u", ret);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::statistic */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, char **message, unsigned int * message_len TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_STATS * stats_header;
+
+ DBG_ENTER("mysqlnd_conn_data::statistic");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ do {
+ ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
+ if (FAIL == ret) {
+ break;
+ }
+ stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!stats_header) {
+ SET_OOM_ERROR(*conn->error_info);
+ break;
+ }
+
+ if (PASS == (ret = PACKET_READ(stats_header, conn))) {
+ /* will be freed by Zend, thus don't use the mnd_ allocator */
+ *message = estrndup(stats_header->message, stats_header->message_len);
+ *message_len = stats_header->message_len;
+ DBG_INF(*message);
+ }
+ PACKET_FREE(stats_header);
+ } while (0);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::kill */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
+ enum_func_status ret = FAIL;
+ zend_uchar buff[4];
+
+ DBG_ENTER("mysqlnd_conn_data::kill");
+ DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ int4store(buff, pid);
+
+ /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
+ if (pid != conn->thread_id) {
+ ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ SET_ERROR_AFF_ROWS(conn);
+ } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ }
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::set_charset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
+
+ DBG_ENTER("mysqlnd_conn_data::set_charset");
+ DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
+
+ if (!charset) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
+ "Invalid characterset or character set not supported");
+ DBG_RETURN(ret);
+ }
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ char * query;
+ size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
+
+ if (FAIL == (ret = conn->m->query(conn, query, query_len TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
+ } else if (conn->error_info->error_no) {
+ ret = FAIL;
+ } else {
+ conn->charset = charset;
+ }
+ mnd_sprintf_free(query);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::refresh */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
+ enum_func_status ret = FAIL;
+ zend_uchar bits[1];
+ DBG_ENTER("mysqlnd_conn_data::refresh");
+ DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ int1store(bits, options);
+
+ ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::shutdown */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
+ enum_func_status ret = FAIL;
+ zend_uchar bits[1];
+ DBG_ENTER("mysqlnd_conn_data::shutdown");
+ DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ int1store(bits, level);
+
+ ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
+
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_send_close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+
+ DBG_ENTER("mysqlnd_send_close");
+ DBG_INF_FMT("conn=%llu conn->net->stream->abstract=%p",
+ conn->thread_id, conn->net->stream? conn->net->stream->abstract:NULL);
+
+ if (CONN_GET_STATE(conn) >= CONN_READY) {
+ MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
+ if (conn->persistent) {
+ MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ }
+ }
+ switch (CONN_GET_STATE(conn)) {
+ case CONN_READY:
+ DBG_INF("Connection clean, sending COM_QUIT");
+ if (conn->net->stream) {
+ ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
+ }
+ /* Do nothing */
+ break;
+ case CONN_SENDING_LOAD_DATA:
+ /*
+ Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
+ will crash (assert) a debug server.
+ */
+ case CONN_NEXT_RESULT_PENDING:
+ case CONN_QUERY_SENT:
+ case CONN_FETCHING_DATA:
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
+ DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
+ /*
+ Do nothing, the connection will be brutally closed
+ and the server will catch it and free close from its side.
+ */
+ case CONN_ALLOCED:
+ /*
+ Allocated but not connected or there was failure when trying
+ to connect with pre-allocated connect.
+
+ Fall-through
+ */
+ case CONN_QUIT_SENT:
+ /* The user has killed its own connection */
+ break;
+ }
+ /*
+ We hold one reference, and every other object which needs the
+ connection does increase it by 1.
+ */
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_reference */
+static MYSQLND_CONN_DATA *
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::get_reference");
+ ++conn->refcount;
+ DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
+ DBG_RETURN(conn);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::free_reference */
+static enum_func_status
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ DBG_ENTER("mysqlnd_conn_data::free_reference");
+ DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
+ if (!(--conn->refcount)) {
+ /*
+ No multithreading issues as we don't share the connection :)
+ This will free the object too, of course because references has
+ reached zero.
+ */
+ ret = conn->m->send_close(conn TSRMLS_CC);
+ conn->m->dtor(conn TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_state */
+static enum mysqlnd_connection_state
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::get_state");
+ DBG_RETURN(conn->state);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::set_state */
+static void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::set_state");
+ DBG_INF_FMT("New state=%u", new_state);
+ conn->state = new_state;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::field_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::server_status */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->upsert_status->server_status;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::insert_id */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->upsert_status->last_insert_id;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::affected_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->upsert_status->affected_rows;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::warning_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->upsert_status->warning_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->last_message;
+}
+/* }}} */
+
+/* {{{ mysqlnd_get_client_info */
+PHPAPI const char * mysqlnd_get_client_info()
+{
+ return MYSQLND_VERSION;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_get_client_version */
+PHPAPI unsigned int mysqlnd_get_client_version()
+{
+ return MYSQLND_VERSION_ID;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_server_info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->server_version;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_host_info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->host_info;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_proto_info */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->protocol_version;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::charset_name */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->charset->name;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::thread_id */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ return conn->thread_id;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_server_version */
+static unsigned long
+MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ long major, minor, patch;
+ char *p;
+
+ if (!(p = conn->server_version)) {
+ return 0;
+ }
+
+ major = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ minor = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ patch = strtol(p, &p, 10);
+
+ return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::more_results */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::more_results");
+ /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
+ DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::next_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_conn_data::next_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ do {
+ if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
+ break;
+ }
+
+ SET_EMPTY_ERROR(*conn->error_info);
+ SET_ERROR_AFF_ROWS(conn);
+ /*
+ We are sure that there is a result set, since conn->state is set accordingly
+ in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
+ */
+ if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
+ /*
+ There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
+ So there are no more results and we should just return FALSE, error_no has been set
+ */
+ if (!conn->error_info->error_no) {
+ DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ } else {
+ DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
+ }
+ break;
+ }
+ if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ }
+ } while (0);
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_field_type_name */
+PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
+{
+ switch(field_type) {
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_VAR_STRING:
+ return "string";
+ case FIELD_TYPE_TINY:
+ case FIELD_TYPE_SHORT:
+ case FIELD_TYPE_LONG:
+ case FIELD_TYPE_LONGLONG:
+ case FIELD_TYPE_INT24:
+ return "int";
+ case FIELD_TYPE_FLOAT:
+ case FIELD_TYPE_DOUBLE:
+ case FIELD_TYPE_DECIMAL:
+ case FIELD_TYPE_NEWDECIMAL:
+ return "real";
+ case FIELD_TYPE_TIMESTAMP:
+ return "timestamp";
+ case FIELD_TYPE_YEAR:
+ return "year";
+ case FIELD_TYPE_DATE:
+ case FIELD_TYPE_NEWDATE:
+ return "date";
+ case FIELD_TYPE_TIME:
+ return "time";
+ case FIELD_TYPE_SET:
+ return "set";
+ case FIELD_TYPE_ENUM:
+ return "enum";
+ case FIELD_TYPE_GEOMETRY:
+ return "geometry";
+ case FIELD_TYPE_DATETIME:
+ return "datetime";
+ case FIELD_TYPE_TINY_BLOB:
+ case FIELD_TYPE_MEDIUM_BLOB:
+ case FIELD_TYPE_LONG_BLOB:
+ case FIELD_TYPE_BLOB:
+ return "blob";
+ case FIELD_TYPE_NULL:
+ return "null";
+ case FIELD_TYPE_BIT:
+ return "bit";
+ default:
+ return "unknown";
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::change_user */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
+ const char * user,
+ const char * passwd,
+ const char * db,
+ zend_bool silent,
+ size_t passwd_len
+ TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
+ /*
+ User could be max 16 * 3 (utf8), pass is 20 usually, db is up to 64*3
+ Stack space is not that expensive, so use a bit more to be protected against
+ buffer overflows.
+ */
+ enum_func_status ret = FAIL;
+ zend_bool local_tx_started = FALSE;
+
+ DBG_ENTER("mysqlnd_conn_data::change_user");
+ DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
+ conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
+
+ if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ goto end;
+ }
+ local_tx_started = TRUE;
+
+ SET_EMPTY_ERROR(*conn->error_info);
+ SET_ERROR_AFF_ROWS(conn);
+
+ if (!user) {
+ user = "";
+ }
+ if (!passwd) {
+ passwd = "";
+ }
+ if (!db) {
+ db = "";
+ }
+
+ {
+ zend_bool first_call = TRUE;
+ char * switch_to_auth_protocol = NULL;
+ size_t switch_to_auth_protocol_len = 0;
+ char * requested_protocol = NULL;
+ zend_uchar * plugin_data;
+ size_t plugin_data_len;
+
+ plugin_data_len = conn->auth_plugin_data_len;
+ plugin_data = mnd_emalloc(plugin_data_len);
+ if (!plugin_data) {
+ ret = FAIL;
+ goto end;
+ }
+ memcpy(plugin_data, conn->auth_plugin_data, plugin_data_len);
+
+ requested_protocol = mnd_pestrdup(conn->options->auth_protocol? conn->options->auth_protocol:"mysql_native_password", FALSE);
+ if (!requested_protocol) {
+ ret = FAIL;
+ goto end;
+ }
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin;
+ {
+ char * plugin_name = NULL;
+
+ mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
+
+ DBG_INF_FMT("looking for %s auth plugin", plugin_name);
+ auth_plugin = mysqlnd_plugin_find(plugin_name);
+ mnd_sprintf_free(plugin_name);
+
+ if (!auth_plugin) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
+ SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method umknown to the client");
+ break;
+ }
+ }
+ DBG_INF("plugin found");
+
+ {
+ zend_uchar * switch_to_auth_protocol_data = NULL;
+ size_t switch_to_auth_protocol_data_len = 0;
+ zend_uchar * scrambled_data = NULL;
+ size_t scrambled_data_len = 0;
+
+ switch_to_auth_protocol = NULL;
+ switch_to_auth_protocol_len = 0;
+
+ if (conn->auth_plugin_data) {
+ mnd_pefree(conn->auth_plugin_data, conn->persistent);
+ conn->auth_plugin_data = NULL;
+ }
+ conn->auth_plugin_data_len = plugin_data_len;
+ conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
+ if (!conn->auth_plugin_data) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
+
+ DBG_INF_FMT("salt=[%*.s]", plugin_data_len - 1, plugin_data);
+
+ /* The data should be allocated with malloc() */
+ scrambled_data =
+ auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
+ plugin_data, plugin_data_len, 0, conn->server_capabilities TSRMLS_CC);
+
+
+ ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, strlen(db), silent,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ TSRMLS_CC);
+
+ first_call = FALSE;
+ free(scrambled_data);
+
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ if (requested_protocol) {
+ mnd_efree(requested_protocol);
+ }
+ requested_protocol = switch_to_auth_protocol;
+
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ plugin_data_len = switch_to_auth_protocol_data_len;
+ plugin_data = switch_to_auth_protocol_data;
+ }
+ DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
+ } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ if (ret == PASS) {
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
+ }
+ if (requested_protocol) {
+ mnd_efree(requested_protocol);
+ }
+ }
+ /*
+ Here we should close all statements. Unbuffered queries should not be a
+ problem as we won't allow sending COM_CHANGE_USER.
+ */
+end:
+ if (TRUE == local_tx_started) {
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
+ enum mysqlnd_option option,
+ const char * const value
+ TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
+ enum_func_status ret = PASS;
+ DBG_ENTER("mysqlnd_conn_data::set_client_option");
+ 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_COMPRESS:
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_READ_TIMEOUT:
+ case MYSQL_OPT_WRITE_TIMEOUT:
+#endif
+ case MYSQLND_OPT_SSL_KEY:
+ case MYSQLND_OPT_SSL_CERT:
+ case MYSQLND_OPT_SSL_CA:
+ case MYSQLND_OPT_SSL_CAPATH:
+ case MYSQLND_OPT_SSL_CIPHER:
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC);
+ break;
+#if MYSQLND_UNICODE
+ case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
+ conn->options->numeric_and_datetime_as_unicode = *(unsigned int*) value;
+ break;
+#endif
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
+ conn->options->int_and_float_native = *(unsigned int*) value;
+ break;
+#endif
+ case MYSQL_OPT_LOCAL_INFILE:
+ if (value && (*(unsigned int*) value) ? 1 : 0) {
+ conn->options->flags |= CLIENT_LOCAL_FILES;
+ } else {
+ conn->options->flags &= ~CLIENT_LOCAL_FILES;
+ }
+ break;
+ case MYSQL_INIT_COMMAND:
+ {
+ char ** new_init_commands;
+ char * new_command;
+ /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
+ /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
+ new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
+ if (!new_init_commands) {
+ goto oom;
+ }
+ conn->options->init_commands = new_init_commands;
+ new_command = mnd_pestrdup(value, conn->persistent);
+ if (!new_command) {
+ goto oom;
+ }
+ conn->options->init_commands[conn->options->num_commands] = new_command;
+ ++conn->options->num_commands;
+ break;
+ }
+ case MYSQL_READ_DEFAULT_FILE:
+ case MYSQL_READ_DEFAULT_GROUP:
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_SET_CLIENT_IP:
+ case MYSQL_REPORT_DATA_TRUNCATION:
+#endif
+ /* currently not supported. Todo!! */
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ {
+ char * new_charset_name;
+ if (!mysqlnd_find_charset_name(value)) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
+ ret = FAIL;
+ break;
+ }
+
+ new_charset_name = mnd_pestrdup(value, conn->persistent);
+ if (!new_charset_name) {
+ goto oom;
+ }
+ if (conn->options->charset_name) {
+ mnd_pefree(conn->options->charset_name, conn->persistent);
+ }
+ conn->options->charset_name = new_charset_name;
+ DBG_INF_FMT("charset=%s", conn->options->charset_name);
+ break;
+ }
+ case MYSQL_OPT_NAMED_PIPE:
+ conn->options->protocol = MYSQL_PROTOCOL_PIPE;
+ break;
+ case MYSQL_OPT_PROTOCOL:
+ if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
+ conn->options->protocol = *(unsigned int*) value;
+ }
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_SET_CHARSET_DIR:
+ case MYSQL_OPT_RECONNECT:
+ /* we don't need external character sets, all character sets are
+ compiled in. For compatibility we just ignore this setting.
+ Same for protocol, we don't support old protocol */
+ case MYSQL_OPT_USE_REMOTE_CONNECTION:
+ case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
+ case MYSQL_OPT_GUESS_CONNECTION:
+ /* todo: throw an error, we don't support embedded */
+ break;
+#endif
+ case MYSQLND_OPT_MAX_ALLOWED_PACKET:
+ if (*(unsigned int*) value > (1<<16)) {
+ conn->options->max_allowed_packet = *(unsigned int*) value;
+ }
+ break;
+ case MYSQLND_OPT_AUTH_PROTOCOL:
+ {
+ char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
+ if (value && !new_auth_protocol) {
+ goto oom;
+ }
+ if (conn->options->auth_protocol) {
+ mnd_pefree(conn->options->auth_protocol, conn->persistent);
+ }
+ conn->options->auth_protocol = new_auth_protocol;
+ DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
+ break;
+ }
+ case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
+ if (value && (*(unsigned int*) value) ? 1 : 0) {
+ conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
+ } else {
+ conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
+ }
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_SHARED_MEMORY_BASE_NAME:
+ case MYSQL_OPT_USE_RESULT:
+ case MYSQL_SECURE_AUTH:
+ /* not sure, todo ? */
+#endif
+ 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)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
+ MYSQLND_RES * result = NULL;
+
+ DBG_ENTER("mysqlnd_conn_data::use_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ do {
+ if (!conn->current_result) {
+ break;
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA */
+ if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR("Command out of sync");
+ break;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
+
+ conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
+ result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
+
+ if (!result) {
+ conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
+ }
+ conn->current_result = NULL;
+ } while (0);
+
+ conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
+ }
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::store_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
+ MYSQLND_RES * result = NULL;
+
+ DBG_ENTER("mysqlnd_conn_data::store_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ do {
+ if (!conn->current_result) {
+ break;
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR("Command out of sync");
+ break;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
+
+ result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC);
+ if (!result) {
+ conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
+ }
+ conn->current_result = NULL;
+ } while (0);
+
+ conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
+ }
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::get_connection_stats */
+static void
+MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
+ zval * return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
+ mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::set_autocommit */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::set_autocommit");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::tx_commit */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::tx_commit");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->query(conn, "COMMIT", sizeof("COMMIT") - 1 TSRMLS_CC);
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::tx_rollback */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_rollback);
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_conn_data::tx_rollback");
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ ret = conn->m->query(conn, "ROLLBACK", sizeof("ROLLBACK") - 1 TSRMLS_CC);
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::local_tx_start */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ DBG_ENTER("mysqlnd_conn_data::local_tx_start");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::local_tx_end */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::local_tx_end");
+ DBG_RETURN(status);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn_data::init");
+ mysqlnd_stats_init(&conn->stats, STAT_LAST);
+ SET_ERROR_AFF_ROWS(conn);
+
+ conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info TSRMLS_CC);
+ conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
+
+ DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
+}
+/* }}} */
+
+
+MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
+ MYSQLND_METHOD(mysqlnd_conn_data, init),
+ MYSQLND_METHOD(mysqlnd_conn_data, connect),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
+ MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
+ MYSQLND_METHOD(mysqlnd_conn_data, query),
+ MYSQLND_METHOD(mysqlnd_conn_data, send_query),
+ MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
+ MYSQLND_METHOD(mysqlnd_conn_data, use_result),
+ MYSQLND_METHOD(mysqlnd_conn_data, store_result),
+ MYSQLND_METHOD(mysqlnd_conn_data, next_result),
+ MYSQLND_METHOD(mysqlnd_conn_data, more_results),
+
+ _mysqlnd_stmt_init,
+
+ MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
+ MYSQLND_METHOD(mysqlnd_conn_data, refresh),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, ping),
+ MYSQLND_METHOD(mysqlnd_conn_data, kill),
+ MYSQLND_METHOD(mysqlnd_conn_data, select_db),
+ MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
+ MYSQLND_METHOD(mysqlnd_conn_data, change_user),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, errno),
+ MYSQLND_METHOD(mysqlnd_conn_data, error),
+ MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
+ MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
+ MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
+ MYSQLND_METHOD(mysqlnd_conn_data, statistic),
+ MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
+ MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
+ MYSQLND_METHOD(mysqlnd_conn_data, info),
+ MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
+ MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
+ MYSQLND_METHOD(mysqlnd_conn_data, list_method),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
+ MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
+ MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
+ MYSQLND_METHOD(mysqlnd_conn_data, field_count),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, server_status),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
+ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
+ MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
+ MYSQLND_METHOD(mysqlnd_conn_data, free_options),
+
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
+
+ mysqlnd_query_read_result_set_header,
+
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
+ MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
+ MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
+ MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
+ MYSQLND_METHOD(mysqlnd_conn_data, send_close),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
+ mysqlnd_result_init,
+ MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
+ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
+ MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
+ MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
+ MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_conn::get_reference */
+static MYSQLND *
+MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND * ret;
+ DBG_ENTER("mysqlnd_conn::get_reference");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::dtor */
+static void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::dtor");
+ DBG_INF_FMT("conn=%llu", conn->data->thread_id);
+
+ conn->data->m->free_reference(conn->data TSRMLS_CC);
+
+ mnd_pefree(conn, conn->persistent);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_data::close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type TSRMLS_DC)
+{
+ size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
+ MYSQLND_CONN_DATA * conn = conn_handle->data;
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_conn::close");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
+ if (CONN_GET_STATE(conn) >= CONN_READY) {
+ static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
+ STAT_CLOSE_EXPLICIT,
+ STAT_CLOSE_IMPLICIT,
+ STAT_CLOSE_DISCONNECT
+ };
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
+ }
+
+ /*
+ Close now, free_reference will try,
+ if we are last, but that's not a problem.
+ */
+ ret = conn->m->send_close(conn TSRMLS_CC);
+
+ /* do it after free_reference/dtor and we might crash */
+ conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
+
+ conn_handle->m->dtor(conn_handle TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
+ MYSQLND_METHOD(mysqlnd_conn, connect),
+ MYSQLND_METHOD(mysqlnd_conn, clone_object),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
+ MYSQLND_METHOD(mysqlnd_conn, close)
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ _mysqlnd_init */
+PHPAPI MYSQLND *
+_mysqlnd_init(zend_bool persistent TSRMLS_DC)
+{
+ MYSQLND * ret;
+ DBG_ENTER("mysqlnd_init");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
new file mode 100644
index 0000000..494fbcb
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd.h
@@ -0,0 +1,301 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+/* $Id: e707c415db32080b3752b232487a435ee0372157 $ */
+
+#ifndef MYSQLND_H
+#define MYSQLND_H
+
+#define MYSQLND_VERSION "mysqlnd 5.0.10 - 20111026 - $Id: e707c415db32080b3752b232487a435ee0372157 $"
+#define MYSQLND_VERSION_ID 50010
+
+#define MYSQLND_PLUGIN_API_VERSION 1
+
+#define MYSQLND_STRING_TO_INT_CONVERSION
+/*
+ This force mysqlnd to do a single (or more depending on ammount of data)
+ non-blocking read() calls before sending a command to the server. Useful
+ for debugging, if previous function hasn't consumed all the output sent
+ to it - like stmt_send_long_data() error because the data was larger that
+ max_allowed_packet_size, and COM_STMT_SEND_LONG_DATA by protocol doesn't
+ use response packets, thus letting the next command to fail miserably, if
+ the connector implementor is not aware of this deficiency. Should be off
+ on production systems, if of course measured performance degradation is not
+ minimal.
+*/
+#if A0 && PHP_DEBUG
+#define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1
+#endif
+
+#if PHP_DEBUG
+#define MYSQLND_DBG_ENABLED 1
+#else
+#define MYSQLND_DBG_ENABLED 0
+#endif
+
+#if defined(MYSQLND_COMPRESSION_WANTED) && defined(HAVE_ZLIB)
+#define MYSQLND_COMPRESSION_ENABLED 1
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#include "mysqlnd_portability.h"
+#include "mysqlnd_enum_n_def.h"
+#include "mysqlnd_structs.h"
+
+
+/* Library related */
+PHPAPI void mysqlnd_library_init(TSRMLS_D);
+PHPAPI void mysqlnd_library_end(TSRMLS_D);
+
+PHPAPI unsigned int mysqlnd_plugin_register();
+PHPAPI unsigned int mysqlnd_plugin_register_ex(struct st_mysqlnd_plugin_header * plugin TSRMLS_DC);
+PHPAPI unsigned int mysqlnd_plugin_count();
+PHPAPI void * _mysqlnd_plugin_find(const char * const name TSRMLS_DC);
+#define mysqlnd_plugin_find(name) _mysqlnd_plugin_find((name) TSRMLS_CC);
+
+PHPAPI void _mysqlnd_plugin_apply_with_argument(apply_func_arg_t apply_func, void * argument TSRMLS_DC);
+#define mysqlnd_plugin_apply_with_argument(func, argument) _mysqlnd_plugin_apply_with_argument((func), (argument) TSRMLS_CC);
+
+#define mysqlnd_restart_psession(conn) ((conn)->data)->m->restart_psession((conn)->data TSRMLS_CC)
+#define mysqlnd_end_psession(conn) ((conn)->data)->m->end_psession((conn)->data TSRMLS_CC)
+PHPAPI void mysqlnd_minfo_print_hash(zval *values);
+#define mysqlnd_thread_safe() TRUE
+
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(unsigned int charsetno);
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const charsetname);
+
+
+/* Connect */
+#define mysqlnd_init(persistent) _mysqlnd_init((persistent) TSRMLS_CC)
+PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC);
+PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
+ const char * host, const char * user,
+ const char * passwd, unsigned int passwd_len,
+ const char * db, unsigned int db_len,
+ unsigned int port,
+ const char * socket_or_pipe,
+ unsigned int mysql_flags
+ TSRMLS_DC);
+
+#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)) TSRMLS_CC)
+#define mysqlnd_change_user_ex(conn, user, passwd, db, silent, passwd_len) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), (passwd_len) TSRMLS_CC)
+
+#define mysqlnd_debug(x) _mysqlnd_debug((x) TSRMLS_CC)
+PHPAPI void _mysqlnd_debug(const char *mode TSRMLS_DC);
+
+/* Query */
+#define mysqlnd_fetch_into(result, flags, ret_val, ext) (result)->m.fetch_into((result), (flags), (ret_val), (ext) TSRMLS_CC ZEND_FILE_LINE_CC)
+#define mysqlnd_fetch_row_c(result) (result)->m.fetch_row_c((result) TSRMLS_CC)
+#define mysqlnd_fetch_all(result, flags, return_value) (result)->m.fetch_all((result), (flags), (return_value) TSRMLS_CC ZEND_FILE_LINE_CC)
+#define mysqlnd_result_fetch_field_data(res,offset,ret) (res)->m.fetch_field_data((res), (offset), (ret) TSRMLS_CC)
+#define mysqlnd_get_connection_stats(conn, values) ((conn)->data)->m->get_statistics((conn)->data, (values) TSRMLS_CC ZEND_FILE_LINE_CC)
+#define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats((values) TSRMLS_CC ZEND_FILE_LINE_CC)
+
+#define mysqlnd_close(conn,is_forced) (conn)->m->close((conn), (is_forced) TSRMLS_CC)
+#define mysqlnd_query(conn, query_str, query_len) ((conn)->data)->m->query((conn)->data, (query_str), (query_len) TSRMLS_CC)
+#define mysqlnd_async_query(conn, query_str, query_len) ((conn)->data)->m->send_query((conn)->data, (query_str), (query_len) TSRMLS_CC)
+#define mysqlnd_poll(r, err, d_pull,sec,usec,desc_num) _mysqlnd_poll((r), (err), (d_pull), (sec), (usec), (desc_num) TSRMLS_CC)
+#define mysqlnd_reap_async_query(conn) ((conn)->data)->m->reap_query((conn)->data TSRMLS_CC)
+#define mysqlnd_unbuffered_skip_result(result) (result)->m.skip_result((result) TSRMLS_CC)
+
+PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC);
+
+#define mysqlnd_use_result(conn) ((conn)->data)->m->use_result((conn)->data TSRMLS_CC)
+#define mysqlnd_store_result(conn) ((conn)->data)->m->store_result((conn)->data TSRMLS_CC)
+#define mysqlnd_next_result(conn) ((conn)->data)->m->next_result((conn)->data TSRMLS_CC)
+#define mysqlnd_more_results(conn) ((conn)->data)->m->more_results((conn)->data TSRMLS_CC)
+#define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC)
+#define mysqlnd_data_seek(result, row) (result)->m.seek_data((result), (row) TSRMLS_CC)
+
+/* Errors */
+#define mysqlnd_errno(conn) ((conn)->data)->m->get_error_no((conn)->data TSRMLS_CC)
+#define mysqlnd_error(conn) ((conn)->data)->m->get_error_str((conn)->data TSRMLS_CC)
+#define mysqlnd_sqlstate(conn) ((conn)->data)->m->get_sqlstate((conn)->data TSRMLS_CC)
+
+/* Charset */
+#define mysqlnd_character_set_name(conn) ((conn)->data)->m->charset_name((conn)->data TSRMLS_CC)
+
+/* Simple metadata */
+#define mysqlnd_field_count(conn) ((conn)->data)->m->get_field_count((conn)->data TSRMLS_CC)
+#define mysqlnd_insert_id(conn) ((conn)->data)->m->get_last_insert_id((conn)->data TSRMLS_CC)
+#define mysqlnd_affected_rows(conn) ((conn)->data)->m->get_affected_rows((conn)->data TSRMLS_CC)
+#define mysqlnd_warning_count(conn) ((conn)->data)->m->get_warning_count((conn)->data TSRMLS_CC)
+#define mysqlnd_info(conn) ((conn)->data)->m->get_last_message((conn)->data TSRMLS_CC)
+#define mysqlnd_get_server_info(conn) ((conn)->data)->m->get_server_information((conn)->data TSRMLS_CC)
+#define mysqlnd_get_server_version(conn) ((conn)->data)->m->get_server_version((conn)->data TSRMLS_CC)
+#define mysqlnd_get_host_info(conn) ((conn)->data)->m->get_host_information((conn)->data TSRMLS_CC)
+#define mysqlnd_get_proto_info(conn) ((conn)->data)->m->get_protocol_information((conn)->data TSRMLS_CC)
+#define mysqlnd_thread_id(conn) ((conn)->data)->m->get_thread_id((conn)->data TSRMLS_CC)
+#define mysqlnd_get_server_status(conn) ((conn)->data)->m->get_server_status((conn)->data TSRMLS_CC)
+
+#define mysqlnd_num_rows(result) (result)->m.num_rows((result) TSRMLS_CC)
+#define mysqlnd_num_fields(result) (result)->m.num_fields((result) TSRMLS_CC)
+
+#define mysqlnd_fetch_lengths(result) _mysqlnd_fetch_lengths((result) TSRMLS_CC)
+PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC);
+
+#define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs) TSRMLS_CC)
+#define mysqlnd_field_tell(result) (result)->m.field_tell((result) TSRMLS_CC)
+#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result) TSRMLS_CC)
+#define mysqlnd_fetch_field_direct(result,fnr) (result)->m.fetch_field_direct((result), (fnr) TSRMLS_CC)
+#define mysqlnd_fetch_fields(result) (result)->m.fetch_fields((result) TSRMLS_CC)
+
+/* mysqlnd metadata */
+PHPAPI const char * mysqlnd_get_client_info();
+PHPAPI unsigned int mysqlnd_get_client_version();
+
+#define mysqlnd_ssl_set(conn, key, cert, ca, capath, cipher) ((conn)->data)->m->ssl_set((conn)->data, (key), (cert), (ca), (capath), (cipher) TSRMLS_CC)
+
+/* PS */
+#define mysqlnd_stmt_insert_id(stmt) (stmt)->m->get_last_insert_id((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_affected_rows(stmt) (stmt)->m->get_affected_rows((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_num_rows(stmt) (stmt)->m->get_num_rows((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_param_count(stmt) (stmt)->m->get_param_count((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_field_count(stmt) (stmt)->m->get_field_count((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_warning_count(stmt) (stmt)->m->get_warning_count((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_server_status(stmt) (stmt)->m->get_server_status((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_errno(stmt) (stmt)->m->get_error_no((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_error(stmt) (stmt)->m->get_error_str((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_sqlstate(stmt) (stmt)->m->get_sqlstate((stmt) TSRMLS_CC)
+
+
+PHPAPI void mysqlnd_efree_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC);
+PHPAPI void mysqlnd_efree_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC);
+PHPAPI void mysqlnd_free_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC);
+PHPAPI void mysqlnd_free_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC);
+
+
+PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type);
+
+/* LOAD DATA LOCAL */
+PHPAPI void mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn);
+PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND_CONN_DATA * const conn, const char * const funcname);
+
+/* Simple commands */
+#define mysqlnd_autocommit(conn, mode) ((conn)->data)->m->set_autocommit((conn)->data, (mode) TSRMLS_CC)
+#define mysqlnd_commit(conn) ((conn)->data)->m->tx_commit((conn)->data TSRMLS_CC)
+#define mysqlnd_rollback(conn) ((conn)->data)->m->tx_rollback((conn)->data TSRMLS_CC)
+#define mysqlnd_list_dbs(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL TSRMLS_CC)
+#define mysqlnd_list_fields(conn, tab,wild) ((conn)->data)->m->list_fields((conn)->data, (tab), (wild) TSRMLS_CC)
+#define mysqlnd_list_processes(conn) ((conn)->data)->m->list_method((conn)->data, "SHOW PROCESSLIST", NULL, NULL TSRMLS_CC)
+#define mysqlnd_list_tables(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL TSRMLS_CC)
+#define mysqlnd_dump_debug_info(conn) ((conn)->data)->m->server_dump_debug_information((conn)->data TSRMLS_CC)
+#define mysqlnd_select_db(conn, db, db_len) ((conn)->data)->m->select_db((conn)->data, (db), (db_len) TSRMLS_CC)
+#define mysqlnd_ping(conn) ((conn)->data)->m->ping((conn)->data TSRMLS_CC)
+#define mysqlnd_kill(conn, pid) ((conn)->data)->m->kill_connection((conn)->data, (pid) TSRMLS_CC)
+#define mysqlnd_refresh(conn, options) ((conn)->data)->m->refresh_server((conn)->data, (options) TSRMLS_CC)
+#define mysqlnd_shutdown(conn, level) ((conn)->data)->m->shutdown_server((conn)->data, (level) 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_options(conn, opt, value) ((conn)->data)->m->set_client_option((conn)->data, (opt), (value) TSRMLS_CC)
+#define mysqlnd_set_server_option(conn, op) ((conn)->data)->m->set_server_option((conn)->data, (op) TSRMLS_CC)
+
+/* Escaping */
+#define mysqlnd_real_escape_string(conn, newstr, escapestr, escapestr_len) \
+ ((conn)->data)->m->escape_string((conn)->data, (newstr), (escapestr), (escapestr_len) TSRMLS_CC)
+#define mysqlnd_escape_string(newstr, escapestr, escapestr_len) \
+ mysqlnd_old_escape_string((newstr), (escapestr), (escapestr_len) TSRMLS_CC)
+
+PHPAPI ulong mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC);
+
+
+/* PS */
+#define mysqlnd_stmt_init(conn) ((conn)->data)->m->stmt_init(((conn)->data) TSRMLS_CC)
+#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt) TSRMLS_CC)? PASS:FAIL))
+#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row) TSRMLS_CC)
+#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen) TSRMLS_CC)
+#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_send_long_data(stmt,p,d,l) (stmt)->m->send_long_data((stmt), (p), (d), (l) TSRMLS_CC)
+#define mysqlnd_stmt_alloc_param_bind(stmt) (stmt)->m->alloc_parameter_bind((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_free_param_bind(stmt,bind) (stmt)->m->free_parameter_bind((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_parameters((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_one_param(stmt,n,z,t) (stmt)->m->bind_one_parameter((stmt), (n), (z), (t) TSRMLS_CC)
+#define mysqlnd_stmt_refresh_bind_param(s) (s)->m->refresh_bind_param((s) TSRMLS_CC)
+#define mysqlnd_stmt_alloc_result_bind(stmt) (stmt)->m->alloc_result_bind((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_free_result_bind(stmt,bind) (stmt)->m->free_result_bind((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_one_result(s,no) (s)->m->bind_one_result((s), (no) TSRMLS_CC)
+#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt))
+#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt) TSRMLS_CC)
+
+#define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit) TSRMLS_CC)
+#define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt) TSRMLS_CC)
+#define mysqlnd_stmt_flush(stmt) (stmt)->m->flush((stmt) TSRMLS_CC)
+
+
+#define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value) TSRMLS_CC)
+#define mysqlnd_stmt_attr_set(stmt, attr, value) (stmt)->m->set_attribute((stmt), (attr), (value) TSRMLS_CC)
+
+#define mysqlnd_stmt_fetch(stmt, fetched) (stmt)->m->fetch((stmt), (fetched) TSRMLS_CC)
+
+
+/* Performance statistics */
+PHPAPI void _mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+
+/* double check the class name to avoid naming conflicts when using these: */
+#define MYSQLND_METHOD(class, method) php_##class##_##method##_pub
+#define MYSQLND_METHOD_PRIVATE(class, method) php_##class##_##method##_priv
+
+ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
+ zend_bool collect_statistics;
+ zend_bool collect_memory_statistics;
+ char* debug; /* The actual string */
+ MYSQLND_DEBUG *dbg; /* The DBG object */
+ long net_cmd_buffer_size;
+ long net_read_buffer_size;
+ long log_mask;
+ long net_read_timeout;
+ long mempool_default_size;
+ long debug_emalloc_fail_threshold;
+ long debug_ecalloc_fail_threshold;
+ long debug_erealloc_fail_threshold;
+ long debug_malloc_fail_threshold;
+ long debug_calloc_fail_threshold;
+ long debug_realloc_fail_threshold;
+ZEND_END_MODULE_GLOBALS(mysqlnd)
+
+PHPAPI ZEND_EXTERN_MODULE_GLOBALS(mysqlnd)
+
+#ifdef ZTS
+#define MYSQLND_G(v) TSRMG(mysqlnd_globals_id, zend_mysqlnd_globals *, v)
+#else
+#define MYSQLND_G(v) (mysqlnd_globals.v)
+#endif
+
+
+PHPAPI void mysqlnd_minfo_print_hash(zval *values);
+
+#endif /* MYSQLND_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c
new file mode 100644
index 0000000..39c1711
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_alloc.c
@@ -0,0 +1,796 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+
+
+static const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
+static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
+static const char mysqlnd_ecalloc_name[] = "_mysqlnd_ecalloc";
+static const char mysqlnd_pecalloc_name[] = "_mysqlnd_pecalloc";
+static const char mysqlnd_erealloc_name[] = "_mysqlnd_erealloc";
+static const char mysqlnd_perealloc_name[] = "_mysqlnd_perealloc";
+static const char mysqlnd_efree_name[] = "_mysqlnd_efree";
+static const char mysqlnd_pefree_name[] = "_mysqlnd_pefree";
+static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc";
+static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc";
+static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc";
+static const char mysqlnd_free_name[] = "_mysqlnd_free";
+static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
+static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
+
+PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
+{
+ mysqlnd_emalloc_name,
+ mysqlnd_ecalloc_name,
+ mysqlnd_efree_name,
+ mysqlnd_erealloc_name,
+ mysqlnd_pemalloc_name,
+ mysqlnd_pecalloc_name,
+ mysqlnd_pefree_name,
+ mysqlnd_perealloc_name,
+ mysqlnd_malloc_name,
+ mysqlnd_calloc_name,
+ mysqlnd_realloc_name,
+ mysqlnd_free_name,
+ mysqlnd_pestrndup_name,
+ mysqlnd_read_header_name,
+ mysqlnd_read_body_name,
+ NULL /* must be always last */
+};
+
+
+#if ZEND_DEBUG
+#else
+#define __zend_filename "/unknown/unknown"
+#define __zend_lineno 0
+#endif
+
+#define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
+#define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p))
+#define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
+
+/* {{{ _mysqlnd_emalloc */
+void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_emalloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_emalloc_name);
+
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = emalloc(REAL_SIZE(size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pemalloc */
+void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = persistent? &MYSQLND_G(debug_malloc_fail_threshold):&MYSQLND_G(debug_emalloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_pemalloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = pemalloc(REAL_SIZE(size), persistent);
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("size=%lu ptr=%p persistent=%u", size, ret, persistent);
+
+ if (ret && collect_memory_statistics) {
+ enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
+ enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
+ }
+
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_ecalloc */
+void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_ecalloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_ecalloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = ecalloc(nmemb, REAL_SIZE(size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pecalloc */
+void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = persistent? &MYSQLND_G(debug_calloc_fail_threshold):&MYSQLND_G(debug_ecalloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_pecalloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+
+ if (ret && collect_memory_statistics) {
+ enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
+ enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
+ }
+
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_erealloc */
+void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_erealloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_erealloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size);
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("new_ptr=%p", (char*)ret);
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = new_size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_perealloc */
+void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
+#if PHP_DEBUG
+ long * threshold = persistent? &MYSQLND_G(debug_realloc_fail_threshold):&MYSQLND_G(debug_erealloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_perealloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent);
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("new_ptr=%p", (char*)ret);
+
+ if (ret && collect_memory_statistics) {
+ enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
+ enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
+ *(size_t *) ret = new_size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_efree */
+void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
+{
+ size_t free_amount = 0;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ DBG_ENTER(mysqlnd_efree_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p", ptr);
+
+ if (ptr) {
+ if (collect_memory_statistics) {
+ free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
+ DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
+ }
+ efree(REAL_PTR(ptr));
+ }
+
+ if (collect_memory_statistics) {
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pefree */
+void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
+{
+ size_t free_amount = 0;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ DBG_ENTER(mysqlnd_pefree_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p persistent=%u", ptr, persistent);
+
+ if (ptr) {
+ if (collect_memory_statistics) {
+ free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
+ DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
+ }
+ pefree(REAL_PTR(ptr), persistent);
+ }
+
+ if (collect_memory_statistics) {
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
+ persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
+ }
+ DBG_VOID_RETURN;
+}
+
+
+/* {{{ _mysqlnd_malloc */
+void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_malloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_malloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = malloc(REAL_SIZE(size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_calloc */
+void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_calloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_calloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = calloc(nmemb, REAL_SIZE(size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_realloc */
+void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+{
+ void *ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+#if PHP_DEBUG
+ long * threshold = &MYSQLND_G(debug_realloc_fail_threshold);
+#endif
+ DBG_ENTER(mysqlnd_realloc_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
+
+#if PHP_DEBUG
+ /* -1 is also "true" */
+ if (*threshold) {
+#endif
+ ret = realloc(REAL_PTR(ptr), REAL_SIZE(new_size));
+#if PHP_DEBUG
+ --*threshold;
+ } else if (*threshold == 0) {
+ ret = NULL;
+ }
+#endif
+
+ DBG_INF_FMT("new_ptr=%p", (char*)ret);
+
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = new_size;
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size);
+ }
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_free */
+void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
+{
+ size_t free_amount = 0;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ DBG_ENTER(mysqlnd_free_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p", ptr);
+
+ if (ptr) {
+ if (collect_memory_statistics) {
+ free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
+ DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
+ }
+ free(REAL_PTR(ptr));
+ }
+
+ if (collect_memory_statistics) {
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+#define SMART_STR_START_SIZE 2048
+#define SMART_STR_PREALLOC 512
+#include "ext/standard/php_smart_str.h"
+
+
+/* {{{ _mysqlnd_pestrndup */
+char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ DBG_ENTER(mysqlnd_pestrndup_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p", ptr);
+
+ ret = pemalloc(REAL_SIZE(length) + 1, persistent);
+ {
+ size_t l = length;
+ char * p = (char *) ptr;
+ char * dest = (char *) FAKE_PTR(ret);
+ while (*p && l--) {
+ *dest++ = *p++;
+ }
+ *dest = '\0';
+ }
+
+ if (collect_memory_statistics) {
+ *(size_t *) ret = length;
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
+ }
+
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pestrdup */
+char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * ret;
+ smart_str tmp_str = {0, 0, 0};
+ const char * p = ptr;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ DBG_ENTER(mysqlnd_pestrdup_name);
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ DBG_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ DBG_INF_FMT("ptr=%p", ptr);
+ do {
+ smart_str_appendc(&tmp_str, *p);
+ } while (*p++);
+
+ ret = pemalloc(tmp_str.len + sizeof(size_t), persistent);
+ memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len);
+
+ if (ret && collect_memory_statistics) {
+ *(size_t *) ret = tmp_str.len;
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
+ }
+ smart_str_free(&tmp_str);
+
+ DBG_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_sprintf */
+PHPAPI int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ...)
+{
+ int len;
+ va_list ap;
+ va_start(ap, format);
+ len = vspprintf(pbuf, max_len, format, ap);
+ va_end(ap);
+ return len;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_sprintf_free */
+PHPAPI void _mysqlnd_sprintf_free(char * p)
+{
+ efree(p);
+}
+/* }}} */
+
+
+PHPAPI int _mysqlnd_vsprintf(char ** pbuf, size_t max_len, const char * format, va_list ap)
+{
+ return vspprintf(pbuf, max_len, format, ap);
+}
+/* }}} */
+
+
+#define MYSQLND_DEBUG_MEMORY 1
+
+#if MYSQLND_DEBUG_MEMORY == 0
+
+/* {{{ mysqlnd_zend_mm_emalloc */
+static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
+{
+ return emalloc(size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_pemalloc */
+static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ return pemalloc(size, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_ecalloc */
+static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+{
+ return ecalloc(nmemb, size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_pecalloc */
+static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ return pecalloc(nmemb, size, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_erealloc */
+static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+{
+ return erealloc(ptr, new_size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_perealloc */
+static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
+{
+ return perealloc(ptr, new_size, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_efree */
+static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
+{
+ efree(ptr);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_pefree */
+static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
+{
+ pefree(ptr, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_malloc */
+static void * mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D)
+{
+ return malloc(size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_calloc */
+static void * mysqlnd_zend_mm_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+{
+ return calloc(nmemb, size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_realloc */
+static void * mysqlnd_zend_mm_realloc(void * ptr, size_t new_size MYSQLND_MEM_D)
+{
+ return realloc(ptr, new_size);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_free */
+static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
+{
+ free(ptr);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_pestrndup */
+static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ return pestrndup(ptr, length, persistent);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zend_mm_pestrdup */
+static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
+{
+ return pestrdup(ptr, persistent);
+}
+/* }}} */
+
+#endif
+
+
+PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
+{
+#if MYSQLND_DEBUG_MEMORY
+ _mysqlnd_emalloc,
+ _mysqlnd_pemalloc,
+ _mysqlnd_ecalloc,
+ _mysqlnd_pecalloc,
+ _mysqlnd_erealloc,
+ _mysqlnd_perealloc,
+ _mysqlnd_efree,
+ _mysqlnd_pefree,
+ _mysqlnd_malloc,
+ _mysqlnd_calloc,
+ _mysqlnd_realloc,
+ _mysqlnd_free,
+ _mysqlnd_pestrndup,
+ _mysqlnd_pestrdup,
+ _mysqlnd_sprintf,
+ _mysqlnd_vsprintf,
+ _mysqlnd_sprintf_free
+#else
+ mysqlnd_zend_mm_emalloc,
+ mysqlnd_zend_mm_pemalloc,
+ mysqlnd_zend_mm_ecalloc,
+ mysqlnd_zend_mm_pecalloc,
+ mysqlnd_zend_mm_erealloc,
+ mysqlnd_zend_mm_perealloc,
+ mysqlnd_zend_mm_efree,
+ mysqlnd_zend_mm_pefree,
+ mysqlnd_zend_mm_malloc,
+ mysqlnd_zend_mm_calloc,
+ mysqlnd_zend_mm_realloc,
+ mysqlnd_zend_mm_free,
+ mysqlnd_zend_mm_pestrndup,
+ mysqlnd_zend_mm_pestrdup
+ sprintf,
+ mysqlnd_zend_mm_efree,
+#endif
+};
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_alloc.h b/ext/mysqlnd/mysqlnd_alloc.h
new file mode 100644
index 0000000..33c0070
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_alloc.h
@@ -0,0 +1,100 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd_debug.h 306938 2011-01-01 02:17:06Z felipe $ */
+/* $Id: mysqlnd_debug.h 306938 2011-01-01 02:17:06Z felipe $ */
+
+#ifndef MYSQLND_ALLOC_H
+#define MYSQLND_ALLOC_H
+
+PHPAPI extern const char * mysqlnd_debug_std_no_trace_funcs[];
+
+#define MYSQLND_MEM_D TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC
+#define MYSQLND_MEM_C TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC
+
+struct st_mysqlnd_allocator_methods
+{
+ void * (*m_emalloc)(size_t size MYSQLND_MEM_D);
+ void * (*m_pemalloc)(size_t size, zend_bool persistent MYSQLND_MEM_D);
+ void * (*m_ecalloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D);
+ void * (*m_pecalloc)(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D);
+ void * (*m_erealloc)(void *ptr, size_t new_size MYSQLND_MEM_D);
+ void * (*m_perealloc)(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D);
+ void (*m_efree)(void *ptr MYSQLND_MEM_D);
+ void (*m_pefree)(void *ptr, zend_bool persistent MYSQLND_MEM_D);
+ void * (*m_malloc)(size_t size MYSQLND_MEM_D);
+ void * (*m_calloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D);
+ void * (*m_realloc)(void *ptr, size_t new_size MYSQLND_MEM_D);
+ void (*m_free)(void *ptr MYSQLND_MEM_D);
+ char * (*m_pestrndup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
+ char * (*m_pestrdup)(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
+ int (*m_sprintf)(char **pbuf, size_t max_len, const char *format, ...);
+ int (*m_vsprintf)(char **pbuf, size_t max_len, const char *format, va_list ap);
+ void (*m_sprintf_free)(char * p);
+};
+
+PHPAPI extern struct st_mysqlnd_allocator_methods mysqlnd_allocator;
+
+PHPAPI void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI void _mysqlnd_efree(void *ptr MYSQLND_MEM_D);
+PHPAPI void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
+PHPAPI void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D);
+PHPAPI void _mysqlnd_free(void *ptr MYSQLND_MEM_D);
+PHPAPI char * _mysqlnd_pestrndup(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
+PHPAPI int _mysqlnd_sprintf(char **pbuf, size_t max_len, const char *format, ...);
+PHPAPI void _mysqlnd_sprintf_free(char * p);
+PHPAPI int _mysqlnd_vsprintf(char **pbuf, size_t max_len, const char *format, va_list ap);
+
+#define mnd_emalloc(size) mysqlnd_allocator.m_emalloc((size) MYSQLND_MEM_C)
+#define mnd_pemalloc(size, pers) mysqlnd_allocator.m_pemalloc((size), (pers) MYSQLND_MEM_C)
+#define mnd_ecalloc(nmemb, size) mysqlnd_allocator.m_ecalloc((nmemb), (size) MYSQLND_MEM_C)
+#define mnd_pecalloc(nmemb, size, p) mysqlnd_allocator.m_pecalloc((nmemb), (size), (p) MYSQLND_MEM_C)
+#define mnd_erealloc(ptr, new_size) mysqlnd_allocator.m_erealloc((ptr), (new_size) MYSQLND_MEM_C)
+#define mnd_perealloc(ptr, new_size, p) mysqlnd_allocator.m_perealloc((ptr), (new_size), (p) MYSQLND_MEM_C)
+#define mnd_efree(ptr) mysqlnd_allocator.m_efree((ptr) MYSQLND_MEM_C)
+#define mnd_pefree(ptr, pers) mysqlnd_allocator.m_pefree((ptr), (pers) MYSQLND_MEM_C)
+#define mnd_malloc(size) mysqlnd_allocator.m_malloc((size) MYSQLND_MEM_C)
+#define mnd_calloc(nmemb, size) mysqlnd_allocator.m_calloc((nmemb), (size) MYSQLND_MEM_C)
+#define mnd_realloc(ptr, new_size) mysqlnd_allocator.m_realloc((ptr), (new_size) MYSQLND_MEM_C)
+#define mnd_free(ptr) mysqlnd_allocator.m_free((ptr) MYSQLND_MEM_C)
+#define mnd_pestrndup(ptr, size, pers) mysqlnd_allocator.m_pestrndup((ptr), (size), (pers) MYSQLND_MEM_C)
+#define mnd_pestrdup(ptr, pers) mysqlnd_allocator.m_pestrdup((ptr), (pers) MYSQLND_MEM_C)
+#define mnd_sprintf(p, mx_len, fmt,...) mysqlnd_allocator.m_sprintf((p), (mx_len), (fmt), __VA_ARGS__)
+#define mnd_vsprintf(p, mx_len, fmt,ap) mysqlnd_allocator.m_vsprintf((p), (mx_len), (fmt), (ap))
+#define mnd_sprintf_free(p) mysqlnd_allocator.m_sprintf_free((p))
+
+#endif /* MYSQLND_ALLOC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c
new file mode 100644
index 0000000..918697a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_auth.c
@@ -0,0 +1,477 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_structs.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+
+
+/* {{{ mysqlnd_auth_handshake */
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_OPTIONS * const options,
+ unsigned long mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset = NULL;
+ MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
+ MYSQLND_PACKET_AUTH_RESPONSE * auth_resp_packet = NULL;
+ MYSQLND_PACKET_AUTH * auth_packet = NULL;
+
+ DBG_ENTER("mysqlnd_auth_handshake");
+
+ auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ if (!auth_resp_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+
+ if (use_full_blown_auth_packet != TRUE) {
+ change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!change_auth_resp_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+
+ change_auth_resp_packet->auth_data = auth_plugin_data;
+ change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
+
+ if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ goto end;
+ }
+ } else {
+ auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ auth_packet->client_flags = mysql_flags;
+ auth_packet->max_packet_size = options->max_allowed_packet;
+ if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
+ auth_packet->charset_no = charset->nr;
+ } else {
+#if MYSQLND_UNICODE
+ auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
+#else
+ auth_packet->charset_no = server_charset_no;
+#endif
+ }
+
+ auth_packet->send_auth_data = TRUE;
+ auth_packet->user = user;
+ auth_packet->db = db;
+ auth_packet->db_len = db_len;
+
+ auth_packet->auth_data = auth_plugin_data;
+ auth_packet->auth_data_len = auth_plugin_data_len;
+ auth_packet->auth_plugin_name = auth_protocol;
+
+ if (!PACKET_WRITE(auth_packet, conn)) {
+ goto end;
+ }
+ }
+ if (use_full_blown_auth_packet == TRUE) {
+ conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
+ }
+
+ if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
+ if (auth_resp_packet->response_code == 0xFE) {
+ /* old authentication with new server !*/
+ if (!auth_resp_packet->new_auth_protocol) {
+ DBG_ERR(mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ } else {
+ *switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
+ *switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
+ if (auth_resp_packet->new_auth_protocol_data) {
+ *switch_to_auth_protocol_data_len = auth_resp_packet->new_auth_protocol_data_len;
+ *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
+ memcpy(*switch_to_auth_protocol_data, auth_resp_packet->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
+ } else {
+ *switch_to_auth_protocol_data = NULL;
+ *switch_to_auth_protocol_data_len = 0;
+ }
+ }
+ } else if (auth_resp_packet->response_code == 0xFF) {
+ if (auth_resp_packet->sqlstate[0]) {
+ strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
+ DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
+ }
+ SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
+ }
+ goto end;
+ }
+
+ SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
+ ret = PASS;
+end:
+ PACKET_FREE(change_auth_resp_packet);
+ PACKET_FREE(auth_packet);
+ PACKET_FREE(auth_resp_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_auth_change_user */
+enum_func_status
+mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
+ const char * const user,
+ const size_t user_len,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const zend_bool silent,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ zend_uchar * auth_plugin_data,
+ size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * old_cs = conn->charset;
+ MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
+ MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
+ MYSQLND_PACKET_AUTH * auth_packet = NULL;
+
+ DBG_ENTER("mysqlnd_auth_change_user");
+
+ chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ if (!chg_user_resp) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+
+ if (use_full_blown_auth_packet != TRUE) {
+ change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!change_auth_resp_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+
+ change_auth_resp_packet->auth_data = auth_plugin_data;
+ change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
+
+ if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ goto end;
+ }
+ } else {
+ auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
+
+ if (!auth_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ goto end;
+ }
+
+ auth_packet->is_change_user_packet = TRUE;
+ auth_packet->user = user;
+ auth_packet->db = db;
+ auth_packet->db_len = db_len;
+ auth_packet->silent = silent;
+
+ 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->m->get_server_version(conn TSRMLS_CC) >= 50123) {
+ auth_packet->charset_no = conn->charset->nr;
+ }
+
+ if (!PACKET_WRITE(auth_packet, conn)) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ goto end;
+ }
+ }
+
+ ret = PACKET_READ(chg_user_resp, conn);
+ COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
+
+ if (0xFE == chg_user_resp->response_code) {
+ ret = FAIL;
+ if (!chg_user_resp->new_auth_protocol) {
+ DBG_ERR(mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ } else {
+ *switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
+ *switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
+ if (chg_user_resp->new_auth_protocol_data) {
+ *switch_to_auth_protocol_data_len = chg_user_resp->new_auth_protocol_data_len;
+ *switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
+ memcpy(*switch_to_auth_protocol_data, chg_user_resp->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
+ } else {
+ *switch_to_auth_protocol_data = NULL;
+ *switch_to_auth_protocol_data_len = 0;
+ }
+ }
+ }
+
+ if (conn->error_info->error_no) {
+ ret = FAIL;
+ /*
+ COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
+ bug#25371 mysql_change_user() triggers "packets out of sync"
+ When it gets fixed, there should be one more check here
+ */
+ if (conn->m->get_server_version(conn TSRMLS_CC) > 50113L &&conn->m->get_server_version(conn TSRMLS_CC) < 50118L) {
+ MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (redundant_error_packet) {
+ PACKET_READ(redundant_error_packet, conn);
+ PACKET_FREE(redundant_error_packet);
+ DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn TSRMLS_CC));
+ } else {
+ SET_OOM_ERROR(*conn->error_info);
+ }
+ }
+ }
+ if (ret == PASS) {
+ char * tmp = NULL;
+ /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
+ tmp = mnd_pestrndup(user, user_len, conn->persistent);
+ if (conn->user) {
+ mnd_pefree(conn->user, conn->persistent);
+ }
+ conn->user = tmp;
+
+ tmp = mnd_pestrdup(passwd, conn->persistent);
+ if (conn->passwd) {
+ mnd_pefree(conn->passwd, conn->persistent);
+ }
+ conn->passwd = tmp;
+
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, conn->persistent);
+ conn->last_message = NULL;
+ }
+ memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ /* set charset for old servers */
+ if (conn->m->get_server_version(conn TSRMLS_CC) < 50123) {
+ ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
+ }
+ } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
+ /* old authentication with new server !*/
+ DBG_ERR(mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ }
+end:
+ PACKET_FREE(change_auth_resp_packet);
+ PACKET_FREE(auth_packet);
+ PACKET_FREE(chg_user_resp);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/******************************************* MySQL Native Password ***********************************/
+
+#include "ext/standard/sha1.h"
+
+/* {{{ php_mysqlnd_crypt */
+static void
+php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
+{
+ const zend_uchar *s1_end = s1 + len;
+ while (s1 < s1_end) {
+ *buffer++= *s1++ ^ *s2++;
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_scramble */
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, size_t password_len)
+{
+ PHP_SHA1_CTX context;
+ zend_uchar sha1[SHA1_MAX_LENGTH];
+ zend_uchar sha2[SHA1_MAX_LENGTH];
+
+ /* Phase 1: hash password */
+ PHP_SHA1Init(&context);
+ PHP_SHA1Update(&context, password, password_len);
+ PHP_SHA1Final(sha1, &context);
+
+ /* Phase 2: hash sha1 */
+ PHP_SHA1Init(&context);
+ PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
+ PHP_SHA1Final(sha2, &context);
+
+ /* Phase 3: hash scramble + sha2 */
+ PHP_SHA1Init(&context);
+ PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
+ PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
+ PHP_SHA1Final(buffer, &context);
+
+ /* let's crypt buffer now */
+ php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_native_auth_get_auth_data */
+static zend_uchar *
+mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
+ size_t * auth_data_len,
+ MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
+ const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
+ const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
+ TSRMLS_DC)
+{
+ zend_uchar * ret = NULL;
+ DBG_ENTER("mysqlnd_native_auth_get_auth_data");
+ *auth_data_len = 0;
+
+ /* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
+ if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
+ /* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
+ SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
+ DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
+ DBG_RETURN(NULL);
+ }
+
+ /* copy scrambled pass*/
+ if (passwd && passwd_len) {
+ ret = malloc(SCRAMBLE_LENGTH);
+ *auth_data_len = SCRAMBLE_LENGTH;
+ /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
+ php_mysqlnd_scramble((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "auth_plugin_mysql_native_password",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* no statistics , will be filled later if there are some */
+ NULL, /* no statistics */
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ },
+ {/* methods */
+ mysqlnd_native_auth_get_auth_data
+ }
+};
+
+
+/******************************************* PAM Authentication ***********************************/
+
+/* {{{ mysqlnd_pam_auth_get_auth_data */
+static zend_uchar *
+mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
+ size_t * auth_data_len,
+ MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
+ const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
+ const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
+ TSRMLS_DC)
+{
+ zend_uchar * ret = NULL;
+
+ /* copy pass*/
+ if (passwd && passwd_len) {
+ ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
+ }
+ *auth_data_len = passwd_len;
+
+ return ret;
+}
+/* }}} */
+
+
+static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "auth_plugin_mysql_clear_password",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* no statistics , will be filled later if there are some */
+ NULL, /* no statistics */
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ },
+ {/* methods */
+ mysqlnd_pam_auth_get_auth_data
+ }
+};
+
+
+/* {{{ mysqlnd_register_builtin_authentication_plugins */
+void
+mysqlnd_register_builtin_authentication_plugins(TSRMLS_D)
+{
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin TSRMLS_CC);
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin TSRMLS_CC);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.c b/ext/mysqlnd/mysqlnd_block_alloc.c
new file mode 100644
index 0000000..b9d7887
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_block_alloc.c
@@ -0,0 +1,194 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_block_alloc.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_priv.h"
+
+
+/* {{{ mysqlnd_mempool_free_chunk */
+static void
+mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk TSRMLS_DC)
+{
+ MYSQLND_MEMORY_POOL * pool = chunk->pool;
+ DBG_ENTER("mysqlnd_mempool_free_chunk");
+ if (chunk->from_pool) {
+ /* Try to back-off and guess if this is the last block allocated */
+ if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
+ /*
+ This was the last allocation. Lucky us, we can free
+ a bit of memory from the pool. Next time we will return from the same ptr.
+ */
+ pool->free_size += chunk->size;
+ }
+ pool->refcount--;
+ } else {
+ mnd_free(chunk->ptr);
+ }
+ mnd_free(chunk);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_mempool_resize_chunk */
+static enum_func_status
+mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_mempool_resize_chunk");
+ if (chunk->from_pool) {
+ MYSQLND_MEMORY_POOL * pool = chunk->pool;
+ /* Try to back-off and guess if this is the last block allocated */
+ if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
+ /*
+ This was the last allocation. Lucky us, we can free
+ a bit of memory from the pool. Next time we will return from the same ptr.
+ */
+ if ((chunk->size + pool->free_size) < size) {
+ zend_uchar *new_ptr;
+ new_ptr = mnd_malloc(size);
+ if (!new_ptr) {
+ DBG_RETURN(FAIL);
+ }
+ memcpy(new_ptr, chunk->ptr, chunk->size);
+ chunk->ptr = new_ptr;
+ pool->free_size += chunk->size;
+ chunk->size = size;
+ chunk->pool = NULL; /* now we have no pool memory */
+ pool->refcount--;
+ } else {
+ /* If the chunk is > than asked size then free_memory increases, otherwise decreases*/
+ pool->free_size += (chunk->size - size);
+ }
+ } else {
+ /* Not last chunk, if the user asks for less, give it to him */
+ if (chunk->size >= size) {
+ ; /* nop */
+ } else {
+ zend_uchar *new_ptr;
+ new_ptr = mnd_malloc(size);
+ if (!new_ptr) {
+ DBG_RETURN(FAIL);
+ }
+ memcpy(new_ptr, chunk->ptr, chunk->size);
+ chunk->ptr = new_ptr;
+ chunk->size = size;
+ chunk->pool = NULL; /* now we have non-pool memory */
+ pool->refcount--;
+ }
+ }
+ } else {
+ zend_uchar *new_ptr = mnd_realloc(chunk->ptr, size);
+ if (!new_ptr) {
+ DBG_RETURN(FAIL);
+ }
+ chunk->ptr = new_ptr;
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_mempool_get_chunk */
+static
+MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool, unsigned int size TSRMLS_DC)
+{
+ MYSQLND_MEMORY_POOL_CHUNK *chunk = NULL;
+ DBG_ENTER("mysqlnd_mempool_get_chunk");
+
+ chunk = mnd_malloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK));
+ if (chunk) {
+ chunk->free_chunk = mysqlnd_mempool_free_chunk;
+ chunk->resize_chunk = mysqlnd_mempool_resize_chunk;
+ chunk->size = size;
+ /*
+ Should not go over MYSQLND_MAX_PACKET_SIZE, since we
+ expect non-arena memory in mysqlnd_wireprotocol.c . We
+ realloc the non-arena memory.
+ */
+ chunk->pool = pool;
+ if (size > pool->free_size) {
+ chunk->from_pool = FALSE;
+ chunk->ptr = mnd_malloc(size);
+ if (!chunk->ptr) {
+ chunk->free_chunk(chunk TSRMLS_CC);
+ chunk = NULL;
+ }
+ } else {
+ chunk->from_pool = TRUE;
+ ++pool->refcount;
+ chunk->ptr = pool->arena + (pool->arena_size - pool->free_size);
+ /* Last step, update free_size */
+ pool->free_size -= size;
+ }
+ }
+ DBG_RETURN(chunk);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_mempool_create */
+PHPAPI MYSQLND_MEMORY_POOL *
+mysqlnd_mempool_create(size_t arena_size TSRMLS_DC)
+{
+ /* We calloc, because we free(). We don't mnd_calloc() for a reason. */
+ MYSQLND_MEMORY_POOL * ret = mnd_calloc(1, sizeof(MYSQLND_MEMORY_POOL));
+ DBG_ENTER("mysqlnd_mempool_create");
+ if (ret) {
+ ret->get_chunk = mysqlnd_mempool_get_chunk;
+ ret->free_size = ret->arena_size = arena_size ? arena_size : 0;
+ ret->refcount = 0;
+ /* OOM ? */
+ ret->arena = mnd_malloc(ret->arena_size);
+ if (!ret->arena) {
+ mysqlnd_mempool_destroy(ret TSRMLS_CC);
+ ret = NULL;
+ }
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_mempool_destroy */
+PHPAPI void
+mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_mempool_destroy");
+ /* mnd_free will reference LOCK_access and might crash, depending on the caller...*/
+ mnd_free(pool->arena);
+ mnd_free(pool);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.h b/ext/mysqlnd/mysqlnd_block_alloc.h
new file mode 100644
index 0000000..39ccbdc
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_block_alloc.h
@@ -0,0 +1,39 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_BLOCK_ALLOC_H
+#define MYSQLND_BLOCK_ALLOC_H
+
+PHPAPI MYSQLND_MEMORY_POOL * mysqlnd_mempool_create(size_t arena_size TSRMLS_DC);
+PHPAPI void mysqlnd_mempool_destroy(MYSQLND_MEMORY_POOL * pool TSRMLS_DC);
+
+#endif /* MYSQLND_BLOCK_ALLOC_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_bt.c b/ext/mysqlnd/mysqlnd_bt.c
new file mode 100644
index 0000000..3aeb8f2
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_bt.c
@@ -0,0 +1,484 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */
+
+#include "php.h"
+#include "Zend/zend_builtin_functions.h"
+
+/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
+
+#if MYSQLND_UNICODE
+/* {{{ gettraceasstring() macros */
+#define TRACE_APPEND_CHR(chr) \
+ *str = (char*)erealloc(*str, *len + 1 + 1); \
+ (*str)[(*len)++] = chr
+
+#define TRACE_APPEND_STRL(val, vallen) \
+ { \
+ int l = vallen; \
+ *str = (char*)erealloc(*str, *len + l + 1); \
+ memcpy((*str) + *len, val, l); \
+ *len += l; \
+ }
+
+#define TRACE_APPEND_USTRL(val, vallen) \
+ { \
+ zval tmp, copy; \
+ int use_copy; \
+ ZVAL_UNICODEL(&tmp, val, vallen, 1); \
+ zend_make_printable_zval(&tmp, &copy, &use_copy); \
+ TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
+ zval_dtor(&copy); \
+ zval_dtor(&tmp); \
+ }
+
+#define TRACE_APPEND_ZVAL(zv) \
+ if (Z_TYPE_P((zv)) == IS_UNICODE) { \
+ zval copy; \
+ int use_copy; \
+ zend_make_printable_zval((zv), &copy, &use_copy); \
+ TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
+ zval_dtor(&copy); \
+ } else { \
+ TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
+ }
+
+#define TRACE_APPEND_STR(val) \
+ TRACE_APPEND_STRL(val, sizeof(val)-1)
+
+#define TRACE_APPEND_KEY(key) \
+ if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
+ if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
+ zval copy; \
+ int use_copy; \
+ zend_make_printable_zval(*tmp, &copy, &use_copy); \
+ TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
+ zval_dtor(&copy); \
+ } else { \
+ TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
+ } \
+ }
+/* }}} */
+
+
+/* {{{ mysqlnd_build_trace_args */
+static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
+{
+ char **str;
+ int *len;
+
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+
+ /* the trivial way would be to do:
+ * conver_to_string_ex(arg);
+ * append it and kill the now tmp arg.
+ * but that could cause some E_NOTICE and also damn long lines.
+ */
+
+ switch (Z_TYPE_PP(arg)) {
+ case IS_NULL:
+ TRACE_APPEND_STR("NULL, ");
+ break;
+ case IS_STRING: {
+ int l_added;
+ TRACE_APPEND_CHR('\'');
+ if (Z_STRLEN_PP(arg) > 15) {
+ TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
+ TRACE_APPEND_STR("...', ");
+ l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
+ } else {
+ l_added = Z_STRLEN_PP(arg);
+ TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
+ TRACE_APPEND_STR("', ");
+ l_added += 3 + 1;
+ }
+ while (--l_added) {
+ if ((unsigned char)(*str)[*len - l_added] < 32) {
+ (*str)[*len - l_added] = '?';
+ }
+ }
+ break;
+ }
+ case IS_UNICODE: {
+ int l_added;
+
+ /*
+ * We do not want to apply current error mode here, since
+ * zend_make_printable_zval() uses output encoding converter.
+ * Temporarily set output encoding converter to escape offending
+ * chars with \uXXXX notation.
+ */
+ zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
+ TRACE_APPEND_CHR('\'');
+ if (Z_USTRLEN_PP(arg) > 15) {
+ TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
+ TRACE_APPEND_STR("...', ");
+ l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
+ } else {
+ l_added = Z_USTRLEN_PP(arg);
+ TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
+ TRACE_APPEND_STR("', ");
+ l_added += 3 + 1;
+ }
+ /*
+ * Reset output encoding converter error mode.
+ */
+ zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
+ while (--l_added) {
+ if ((unsigned char)(*str)[*len - l_added] < 32) {
+ (*str)[*len - l_added] = '?';
+ }
+ }
+ break;
+ }
+ case IS_BOOL:
+ if (Z_LVAL_PP(arg)) {
+ TRACE_APPEND_STR("true, ");
+ } else {
+ TRACE_APPEND_STR("false, ");
+ }
+ break;
+ case IS_RESOURCE:
+ TRACE_APPEND_STR("Resource id #");
+ /* break; */
+ case IS_LONG: {
+ long lval = Z_LVAL_PP(arg);
+ char s_tmp[MAX_LENGTH_OF_LONG + 1];
+ int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
+ TRACE_APPEND_STRL(s_tmp, l_tmp);
+ TRACE_APPEND_STR(", ");
+ break;
+ }
+ case IS_DOUBLE: {
+ double dval = Z_DVAL_PP(arg);
+ char *s_tmp;
+ int l_tmp;
+
+ s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
+ l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
+ TRACE_APPEND_STRL(s_tmp, l_tmp);
+ /* %G already handles removing trailing zeros from the fractional part, yay */
+ efree(s_tmp);
+ TRACE_APPEND_STR(", ");
+ break;
+ }
+ case IS_ARRAY:
+ TRACE_APPEND_STR("Array, ");
+ break;
+ case IS_OBJECT: {
+ zval tmp;
+ zstr class_name;
+ zend_uint class_name_len;
+ int dup;
+
+ TRACE_APPEND_STR("Object(");
+
+ dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
+
+ ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
+ convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
+ TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
+ zval_dtor(&tmp);
+
+ if(!dup) {
+ efree(class_name.v);
+ }
+
+ TRACE_APPEND_STR("), ");
+ break;
+ }
+ default:
+ break;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+
+static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
+{
+ char *s_tmp, **str;
+ int *len, *num;
+ long line;
+ HashTable *ht = Z_ARRVAL_PP(frame);
+ zval **file, **tmp;
+ uint * level;
+
+ level = va_arg(args, uint *);
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+ num = va_arg(args, int*);
+
+ if (!*level) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+ --*level;
+
+ s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
+ sprintf(s_tmp, "#%d ", (*num)++);
+ TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
+ efree(s_tmp);
+ if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
+ if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
+ line = Z_LVAL_PP(tmp);
+ } else {
+ line = 0;
+ }
+ TRACE_APPEND_ZVAL(*file);
+ s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
+ sprintf(s_tmp, "(%ld): ", line);
+ TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
+ efree(s_tmp);
+ } else {
+ TRACE_APPEND_STR("[internal function]: ");
+ }
+ TRACE_APPEND_KEY("class");
+ TRACE_APPEND_KEY("type");
+ TRACE_APPEND_KEY("function");
+ TRACE_APPEND_CHR('(');
+ if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
+ int last_len = *len;
+ zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
+ if (last_len != *len) {
+ *len -= 2; /* remove last ', ' */
+ }
+ }
+ TRACE_APPEND_STR(")\n");
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+
+#else /* PHP 5*/
+
+
+/* {{{ gettraceasstring() macros */
+#define TRACE_APPEND_CHR(chr) \
+ *str = (char*)erealloc(*str, *len + 1 + 1); \
+ (*str)[(*len)++] = chr
+
+#define TRACE_APPEND_STRL(val, vallen) \
+ { \
+ int l = vallen; \
+ *str = (char*)erealloc(*str, *len + l + 1); \
+ memcpy((*str) + *len, val, l); \
+ *len += l; \
+ }
+
+#define TRACE_APPEND_STR(val) \
+ TRACE_APPEND_STRL(val, sizeof(val)-1)
+
+#define TRACE_APPEND_KEY(key) \
+ if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
+ TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
+ }
+
+/* }}} */
+
+
+static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
+{
+ char **str;
+ int *len;
+
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+
+ /* the trivial way would be to do:
+ * conver_to_string_ex(arg);
+ * append it and kill the now tmp arg.
+ * but that could cause some E_NOTICE and also damn long lines.
+ */
+
+ switch (Z_TYPE_PP(arg)) {
+ case IS_NULL:
+ TRACE_APPEND_STR("NULL, ");
+ break;
+ case IS_STRING: {
+ int l_added;
+ TRACE_APPEND_CHR('\'');
+ if (Z_STRLEN_PP(arg) > 15) {
+ TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
+ TRACE_APPEND_STR("...', ");
+ l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
+ } else {
+ l_added = Z_STRLEN_PP(arg);
+ TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
+ TRACE_APPEND_STR("', ");
+ l_added += 3 + 1;
+ }
+ while (--l_added) {
+ if ((*str)[*len - l_added] < 32) {
+ (*str)[*len - l_added] = '?';
+ }
+ }
+ break;
+ }
+ case IS_BOOL:
+ if (Z_LVAL_PP(arg)) {
+ TRACE_APPEND_STR("true, ");
+ } else {
+ TRACE_APPEND_STR("false, ");
+ }
+ break;
+ case IS_RESOURCE:
+ TRACE_APPEND_STR("Resource id #");
+ /* break; */
+ case IS_LONG: {
+ long lval = Z_LVAL_PP(arg);
+ char s_tmp[MAX_LENGTH_OF_LONG + 1];
+ int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
+ TRACE_APPEND_STRL(s_tmp, l_tmp);
+ TRACE_APPEND_STR(", ");
+ break;
+ }
+ case IS_DOUBLE: {
+ double dval = Z_DVAL_PP(arg);
+ char *s_tmp;
+ int l_tmp;
+
+ s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
+ l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
+ TRACE_APPEND_STRL(s_tmp, l_tmp);
+ /* %G already handles removing trailing zeros from the fractional part, yay */
+ efree(s_tmp);
+ TRACE_APPEND_STR(", ");
+ break;
+ }
+ case IS_ARRAY:
+ TRACE_APPEND_STR("Array, ");
+ break;
+ case IS_OBJECT: {
+ char *class_name;
+ zend_uint class_name_len;
+ int dupl;
+
+ TRACE_APPEND_STR("Object(");
+
+ dupl = zend_get_object_classname(*arg, (const char **)&class_name, &class_name_len TSRMLS_CC);
+
+ TRACE_APPEND_STRL(class_name, class_name_len);
+ if (!dupl) {
+ efree(class_name);
+ }
+
+ TRACE_APPEND_STR("), ");
+ break;
+ }
+ default:
+ break;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
+{
+ char *s_tmp, **str;
+ int *len, *num;
+ long line;
+ HashTable *ht = Z_ARRVAL_PP(frame);
+ zval **file, **tmp;
+ uint * level;
+
+ level = va_arg(args, uint *);
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+ num = va_arg(args, int*);
+
+ if (!*level) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+ --*level;
+
+ s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
+ sprintf(s_tmp, "#%d ", (*num)++);
+ TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
+ efree(s_tmp);
+ if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
+ if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
+ line = Z_LVAL_PP(tmp);
+ } else {
+ line = 0;
+ }
+ s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
+ sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
+ TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
+ efree(s_tmp);
+ } else {
+ TRACE_APPEND_STR("[internal function]: ");
+ }
+ TRACE_APPEND_KEY("class");
+ TRACE_APPEND_KEY("type");
+ TRACE_APPEND_KEY("function");
+ TRACE_APPEND_CHR('(');
+ if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
+ int last_len = *len;
+ zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
+ if (last_len != *len) {
+ *len -= 2; /* remove last ', ' */
+ }
+ }
+ TRACE_APPEND_STR(")\n");
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+#endif
+
+
+PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
+{
+ zval *trace;
+ char *res = estrdup(""), **str = &res, *s_tmp;
+ int res_len = 0, *len = &res_len, num = 0;
+ if (max_levels == 0) {
+ max_levels = 99999;
+ }
+
+ MAKE_STD_ZVAL(trace);
+ zend_fetch_debug_backtrace(trace, 0, 0, 0 TSRMLS_CC);
+
+ zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_string, 4, &max_levels, str, len, &num);
+ zval_ptr_dtor(&trace);
+
+ if (max_levels) {
+ s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
+ sprintf(s_tmp, "#%d {main}", num);
+ TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
+ efree(s_tmp);
+ }
+
+ res[res_len] = '\0';
+ *length = res_len;
+
+ return res;
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c
new file mode 100644
index 0000000..8766a4b
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_charset.c
@@ -0,0 +1,864 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+#include "php.h"
+#include "php_globals.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_charset.h"
+
+/* {{{ utf8 functions */
+static unsigned int check_mb_utf8mb3_sequence(const char *start, const char *end)
+{
+ zend_uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (zend_uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((zend_uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((zend_uchar)start[1] ^ 0x80) < 0x40 && ((zend_uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (zend_uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int check_mb_utf8_sequence(const char *start, const char *end)
+{
+ zend_uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (zend_uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((zend_uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((zend_uchar)start[1] ^ 0x80) < 0x40 && ((zend_uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (zend_uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ if (c < 0xF5) {
+ if (start + 4 > end) { /* We need 4 characters */
+ return 0; /* too small */
+ }
+
+ /*
+ UTF-8 quick four-byte mask:
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ Encoding allows to encode U+00010000..U+001FFFFF
+
+ The maximum character defined in the Unicode standard is U+0010FFFF.
+ Higher characters U+00110000..U+001FFFFF are not used.
+
+ 11110000.10010000.10xxxxxx.10xxxxxx == F0.90.80.80 == U+00010000 (min)
+ 11110100.10001111.10111111.10111111 == F4.8F.BF.BF == U+0010FFFF (max)
+
+ Valid codes:
+ [F0][90..BF][80..BF][80..BF]
+ [F1][80..BF][80..BF][80..BF]
+ [F2][80..BF][80..BF][80..BF]
+ [F3][80..BF][80..BF][80..BF]
+ [F4][80..8F][80..BF][80..BF]
+ */
+
+ if (!(((zend_uchar)start[1] ^ 0x80) < 0x40 &&
+ ((zend_uchar)start[2] ^ 0x80) < 0x40 &&
+ ((zend_uchar)start[3] ^ 0x80) < 0x40 &&
+ (c >= 0xf1 || (zend_uchar)start[1] >= 0x90) &&
+ (c <= 0xf3 || (zend_uchar)start[1] <= 0x8F)))
+ {
+ return 0; /* invalid utf8 character */
+ }
+ return 4;
+ }
+ return 0;
+}
+
+static unsigned int check_mb_utf8mb3_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8mb3_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+static unsigned int check_mb_utf8_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_utf8mb3(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ return 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_utf8(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ if (utf8 < 0xF8) {
+ return 4; /* four byte character */
+ }
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ big5 functions */
+#define valid_big5head(c) (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xF9)
+#define valid_big5tail(c) ((0x40 <= (unsigned int)(c) && (unsigned int)(c) <= 0x7E) || \
+ (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xFE))
+
+#define isbig5code(c,d) (isbig5head(c) && isbig5tail(d))
+
+static unsigned int check_mb_big5(const char *start, const char *end)
+{
+ return (valid_big5head(*(start)) && (end - start) > 1 && valid_big5tail(*(start + 1)) ? 2 : 0);
+}
+
+
+static unsigned int mysqlnd_mbcharlen_big5(unsigned int big5)
+{
+ return (valid_big5head(big5)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ cp932 functions */
+#define valid_cp932head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && c <= 0xFC))
+#define valid_cp932tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && c <= 0xFC))
+
+
+static unsigned int check_mb_cp932(const char *start, const char *end)
+{
+ return (valid_cp932head((zend_uchar)start[0]) && (end - start > 1) &&
+ valid_cp932tail((zend_uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_cp932(unsigned int cp932)
+{
+ return (valid_cp932head((zend_uchar)cp932)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ euckr functions */
+#define valid_euckr(c) ((0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE))
+
+static unsigned int check_mb_euckr(const char *start, const char *end)
+{
+ if (end - start <= 1) {
+ return 0; /* invalid length */
+ }
+ if (*(zend_uchar *)start < 0x80) {
+ return 0; /* invalid euckr character */
+ }
+ if (valid_euckr(start[1])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_euckr(unsigned int kr)
+{
+ return (valid_euckr(kr)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ eucjpms functions */
+#define valid_eucjpms(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xFE)
+#define valid_eucjpms_kata(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xDF)
+#define valid_eucjpms_ss2(c) (((c) & 0xFF) == 0x8E)
+#define valid_eucjpms_ss3(c) (((c) & 0xFF) == 0x8F)
+
+static unsigned int check_mb_eucjpms(const char *start, const char *end)
+{
+ if (*((zend_uchar *)start) < 0x80) {
+ return 0; /* invalid eucjpms character */
+ }
+ if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss2(start[0]) && (end - start) > 1 && valid_eucjpms_kata(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) &&
+ valid_eucjpms(start[2])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_eucjpms(unsigned int jpms)
+{
+ if (valid_eucjpms(jpms) || valid_eucjpms_ss2(jpms)) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(jpms)) {
+ return 3;
+ }
+ return 1;
+}
+/* }}} */
+
+
+/* {{{ gb2312 functions */
+#define valid_gb2312_head(c) (0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xF7)
+#define valid_gb2312_tail(c) (0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE)
+
+
+static unsigned int check_mb_gb2312(const char *start, const char *end)
+{
+ return (valid_gb2312_head((unsigned int)start[0]) && end - start > 1 &&
+ valid_gb2312_tail((unsigned int)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_gb2312(unsigned int gb)
+{
+ return (valid_gb2312_head(gb)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ gbk functions */
+#define valid_gbk_head(c) (0x81<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE)
+#define valid_gbk_tail(c) ((0x40<=(zend_uchar)(c) && (zend_uchar)(c)<=0x7E) || (0x80<=(zend_uchar)(c) && (zend_uchar)(c)<=0xFE))
+
+static unsigned int check_mb_gbk(const char *start, const char *end)
+{
+ return (valid_gbk_head(start[0]) && (end) - (start) > 1 && valid_gbk_tail(start[1])) ? 2 : 0;
+}
+
+static unsigned int mysqlnd_mbcharlen_gbk(unsigned int gbk)
+{
+ return (valid_gbk_head(gbk) ? 2 : 1);
+}
+/* }}} */
+
+
+/* {{{ sjis functions */
+#define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && (c) <= 0xFC))
+#define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && (c) <= 0xFC))
+
+
+static unsigned int check_mb_sjis(const char *start, const char *end)
+{
+ return (valid_sjis_head((zend_uchar)start[0]) && (end - start) > 1 && valid_sjis_tail((zend_uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_sjis(unsigned int sjis)
+{
+ return (valid_sjis_head((zend_uchar)sjis)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ ucs2 functions */
+static unsigned int check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+
+static unsigned int mysqlnd_mbcharlen_ucs2(unsigned int ucs2 __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+/* }}} */
+
+
+/* {{{ ujis functions */
+#define valid_ujis(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xFE))
+#define valid_ujis_kata(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xDF))
+#define valid_ujis_ss2(c) (((c)&0xFF) == 0x8E)
+#define valid_ujis_ss3(c) (((c)&0xFF) == 0x8F)
+
+static unsigned int check_mb_ujis(const char *start, const char *end)
+{
+ if (*(zend_uchar*)start < 0x80) {
+ return 0; /* invalid ujis character */
+ }
+ if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) {
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int mysqlnd_mbcharlen_ujis(unsigned int ujis)
+{
+ return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1);
+}
+/* }}} */
+
+
+
+/* {{{ utf16 functions */
+#define UTF16_HIGH_HEAD(x) ((((zend_uchar) (x)) & 0xFC) == 0xD8)
+#define UTF16_LOW_HEAD(x) ((((zend_uchar) (x)) & 0xFC) == 0xDC)
+
+static unsigned int check_mb_utf16(const char *start, const char *end)
+{
+ if (start + 2 > end) {
+ return 0;
+ }
+
+ if (UTF16_HIGH_HEAD(*start)) {
+ return (start + 4 <= end) && UTF16_LOW_HEAD(start[2]) ? 4 : 0;
+ }
+
+ if (UTF16_LOW_HEAD(*start)) {
+ return 0;
+ }
+ return 2;
+}
+
+
+static uint mysqlnd_mbcharlen_utf16(unsigned int utf16)
+{
+ return UTF16_HIGH_HEAD(utf16) ? 4 : 2;
+}
+/* }}} */
+
+
+/* {{{ utf32 functions */
+static uint
+check_mb_utf32(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 4;
+}
+
+
+static uint
+mysqlnd_mbcharlen_utf32(unsigned int utf32 __attribute((unused)))
+{
+ return 4;
+}
+/* }}} */
+
+/*
+ The server compiles sometimes the full utf-8 (the mb4) as utf8m4, and the old as utf8,
+ for BC reasons. Sometimes, utf8mb4 is just utf8 but the old charsets are utf8mb3.
+ Change easily now, with a macro, could be made compilastion dependable.
+*/
+
+#define UTF8_MB4 "utf8mb4"
+#define UTF8_MB3 "utf8"
+
+/* {{{ mysqlnd_charsets */
+const MYSQLND_CHARSET mysqlnd_charsets[] =
+{
+ { 1, "big5","big5_chinese_ci", 1, 2, "", mysqlnd_mbcharlen_big5, check_mb_big5},
+ { 3, "dec8", "dec8_swedisch_ci", 1, 1, "", NULL, NULL},
+ { 4, "cp850", "cp850_general_ci", 1, 1, "", NULL, NULL},
+ { 6, "hp8", "hp8_english_ci", 1, 1, "", NULL, NULL},
+ { 7, "koi8r", "koi8r_general_ci", 1, 1, "", NULL, NULL},
+ { 8, "latin1", "latin1_swedish_ci", 1, 1, "", NULL, NULL},
+ { 5, "latin1", "latin1_german_ci", 1, 1, "", NULL, NULL}, /* should be after 0x9 because swedish_ci is the default collation */
+ { 9, "latin2", "latin2_general_ci", 1, 1, "", NULL, NULL},
+ { 2, "latin2", "latin2_czech_cs", 1, 1, "", NULL, NULL}, /* should be after 0x9 because general_ci is the default collation */
+ { 10, "swe7", "swe7_swedish_ci", 1, 1, "", NULL, NULL},
+ { 11, "ascii", "ascii_general_ci", 1, 1, "", NULL, NULL},
+ { 12, "ujis", "ujis_japanese_ci", 1, 3, "", mysqlnd_mbcharlen_ujis, check_mb_ujis},
+ { 13, "sjis", "sjis_japanese_ci", 1, 2, "", mysqlnd_mbcharlen_sjis, check_mb_sjis},
+ { 16, "hebrew", "hebrew_general_ci", 1, 1, "", NULL, NULL},
+ { 17, "filename", "filename", 1, 5, "", NULL, NULL},
+ { 18, "tis620", "tis620_thai_ci", 1, 1, "", NULL, NULL},
+ { 19, "euckr", "euckr_korean_ci", 1, 2, "", mysqlnd_mbcharlen_euckr, check_mb_euckr},
+ { 21, "latin2", "latin2_hungarian_ci", 1, 1, "", NULL, NULL},
+ { 27, "latin2", "latin2_croatian_ci", 1, 1, "", NULL, NULL},
+ { 22, "koi8u", "koi8u_general_ci", 1, 1, "", NULL, NULL},
+ { 24, "gb2312", "gb2312_chinese_ci", 1, 2, "", mysqlnd_mbcharlen_gb2312, check_mb_gb2312},
+ { 25, "greek", "greek_general_ci", 1, 1, "", NULL, NULL},
+ { 26, "cp1250", "cp1250_general_ci", 1, 1, "", NULL, NULL},
+ { 28, "gbk", "gbk_chinese_ci", 1, 2, "", mysqlnd_mbcharlen_gbk, check_mb_gbk},
+ { 30, "latin5", "latin5_turkish_ci", 1, 1, "", NULL, NULL},
+ { 31, "latin1", "latin1_german2_ci", 1, 1, "", NULL, NULL},
+ { 15, "latin1", "latin1_danish_ci", 1, 1, "", NULL, NULL},
+ { 32, "armscii8", "armscii8_general_ci", 1, 1, "", NULL, NULL},
+ { 33, UTF8_MB3, UTF8_MB3"_general_ci", 1, 3, "UTF-8 Unicode", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 35, "ucs2", "ucs2_general_ci", 2, 2, "UCS-2 Unicode", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 36, "cp866", "cp866_general_ci", 1, 1, "", NULL, NULL},
+ { 37, "keybcs2", "keybcs2_general_ci", 1, 1, "", NULL, NULL},
+ { 38, "macce", "macce_general_ci", 1, 1, "", NULL, NULL},
+ { 39, "macroman", "macroman_general_ci", 1, 1, "", NULL, NULL},
+ { 40, "cp852", "cp852_general_ci", 1, 1, "", NULL, NULL},
+ { 41, "latin7", "latin7_general_ci", 1, 1, "", NULL, NULL},
+ { 20, "latin7", "latin7_estonian_cs", 1, 1, "", NULL, NULL},
+ { 57, "cp1256", "cp1256_general_ci", 1, 1, "", NULL, NULL},
+ { 59, "cp1257", "cp1257_general_ci", 1, 1, "", NULL, NULL},
+ { 63, "binary", "binary", 1, 1, "", NULL, NULL},
+ { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, "", mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 29, "cp1257", "cp1257_lithunian_ci", 1, 1, "", NULL, NULL},
+ { 31, "latin1", "latin1_german2_ci", 1, 1, "", NULL, NULL},
+ { 34, "cp1250", "cp1250_czech_cs", 1, 1, "", NULL, NULL},
+ { 42, "latin7", "latin7_general_cs", 1, 1, "", NULL, NULL},
+ { 43, "macce", "macce_bin", 1, 1, "", NULL, NULL},
+ { 44, "cp1250", "cp1250_croatian_ci", 1, 1, "", NULL, NULL},
+ { 45, UTF8_MB4, UTF8_MB4"_general_ci", 1, 4, "UTF-8 Unicode", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 46, UTF8_MB4, UTF8_MB4"_bin", 1, 4, "UTF-8 Unicode", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 47, "latin1", "latin1_bin", 1, 1, "", NULL, NULL},
+ { 48, "latin1", "latin1_general_ci", 1, 1, "", NULL, NULL},
+ { 49, "latin1", "latin1_general_cs", 1, 1, "", NULL, NULL},
+ { 51, "cp1251", "cp1251_general_ci", 1, 1, "", NULL, NULL},
+ { 14, "cp1251", "cp1251_bulgarian_ci", 1, 1, "", NULL, NULL},
+ { 23, "cp1251", "cp1251_ukrainian_ci", 1, 1, "", NULL, NULL},
+ { 50, "cp1251", "cp1251_bin", 1, 1, "", NULL, NULL},
+ { 52, "cp1251", "cp1251_general_cs", 1, 1, "", NULL, NULL},
+ { 53, "macroman", "macroman_bin", 1, 1, "", NULL, NULL},
+ { 54, "utf16", "utf16_general_ci", 2, 4, "UTF-16 Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16},
+ { 55, "utf16", "utf16_bin", 2, 4, "UTF-16 Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16},
+ { 56, "utf16le", "utf16le_general_ci", 2, 4, "UTF-16LE Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16},
+ { 58, "cp1257", "cp1257_bin", 1, 1, "", NULL, NULL},
+#ifdef USED_TO_BE_SO_BEFORE_MYSQL_5_5
+ { 60, "armascii8", "armascii8_bin", 1, 1, "", NULL, NULL},
+#endif
+/*55*/{ 60, "utf32", "utf32_general_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*55*/{ 61, "utf32", "utf32_bin", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+ { 62, "utf16le", "utf16le_bin", 2, 4, "UTF-16LE Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16},
+ { 65, "ascii", "ascii_bin", 1, 1, "", NULL, NULL},
+ { 66, "cp1250", "cp1250_bin", 1, 1, "", NULL, NULL},
+ { 67, "cp1256", "cp1256_bin", 1, 1, "", NULL, NULL},
+ { 68, "cp866", "cp866_bin", 1, 1, "", NULL, NULL},
+ { 69, "dec8", "dec8_bin", 1, 1, "", NULL, NULL},
+ { 70, "greek", "greek_bin", 1, 1, "", NULL, NULL},
+ { 71, "hebew", "hebrew_bin", 1, 1, "", NULL, NULL},
+ { 72, "hp8", "hp8_bin", 1, 1, "", NULL, NULL},
+ { 73, "keybcs2", "keybcs2_bin", 1, 1, "", NULL, NULL},
+ { 74, "koi8r", "koi8r_bin", 1, 1, "", NULL, NULL},
+ { 75, "koi8u", "koi8u_bin", 1, 1, "", NULL, NULL},
+ { 77, "latin2", "latin2_bin", 1, 1, "", NULL, NULL},
+ { 78, "latin5", "latin5_bin", 1, 1, "", NULL, NULL},
+ { 79, "latin7", "latin7_bin", 1, 1, "", NULL, NULL},
+ { 80, "cp850", "cp850_bin", 1, 1, "", NULL, NULL},
+ { 81, "cp852", "cp852_bin", 1, 1, "", NULL, NULL},
+ { 82, "swe7", "swe7_bin", 1, 1, "", NULL, NULL},
+ { 83, UTF8_MB3, UTF8_MB3"_bin", 1, 3, "UTF-8 Unicode", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 84, "big5", "big5_bin", 1, 2, "", mysqlnd_mbcharlen_big5, check_mb_big5},
+ { 85, "euckr", "euckr_bin", 1, 2, "", mysqlnd_mbcharlen_euckr, check_mb_euckr},
+ { 86, "gb2312", "gb2312_bin", 1, 2, "", mysqlnd_mbcharlen_gb2312, check_mb_gb2312},
+ { 87, "gbk", "gbk_bin", 1, 2, "", mysqlnd_mbcharlen_gbk, check_mb_gbk},
+ { 88, "sjis", "sjis_bin", 1, 2, "", mysqlnd_mbcharlen_sjis, check_mb_sjis},
+ { 89, "tis620", "tis620_bin", 1, 1, "", NULL, NULL},
+ { 90, "ucs2", "ucs2_bin", 2, 2, "UCS-2 Unicode", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 91, "ujis", "ujis_bin", 1, 3, "", mysqlnd_mbcharlen_ujis, check_mb_ujis},
+ { 92, "geostd8", "geostd8_general_ci", 1, 1, "", NULL, NULL},
+ { 93, "geostd8", "geostd8_bin", 1, 1, "", NULL, NULL},
+ { 94, "latin1", "latin1_spanish_ci", 1, 1, "", NULL, NULL},
+ { 95, "cp932", "cp932_japanese_ci", 1, 2, "", mysqlnd_mbcharlen_cp932, check_mb_cp932},
+ { 96, "cp932", "cp932_bin", 1, 2, "", mysqlnd_mbcharlen_cp932, check_mb_cp932},
+ { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, "", mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 98, "eucjpms", "eucjpms_bin", 1, 3, "", mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 99, "cp1250", "cp1250_polish_ci", 1, 1, "", NULL, NULL},
+ { 128, "ucs2", "ucs2_unicode_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 129, "ucs2", "ucs2_icelandic_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 130, "ucs2", "ucs2_latvian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 131, "ucs2", "ucs2_romanian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 132, "ucs2", "ucs2_slovenian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 133, "ucs2", "ucs2_polish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 134, "ucs2", "ucs2_estonian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 135, "ucs2", "ucs2_spanish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 136, "ucs2", "ucs2_swedish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 137, "ucs2", "ucs2_turkish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 138, "ucs2", "ucs2_czech_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 139, "ucs2", "ucs2_danish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 140, "ucs2", "ucs2_lithunian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 141, "ucs2", "ucs2_slovak_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 142, "ucs2", "ucs2_spanish2_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 143, "ucs2", "ucs2_roman_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 144, "ucs2", "ucs2_persian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 145, "ucs2", "ucs2_esperanto_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 146, "ucs2", "ucs2_hungarian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 147, "ucs2", "ucs2_sinhala_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 148, "ucs2", "ucs2_german2_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 149, "ucs2", "ucs2_croatian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 150, "ucs2", "ucs2_unicode_520_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 151, "ucs2", "ucs2_vietnamese_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+
+/*56*/{160, "utf32", "utf32_unicode_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{161, "utf32", "utf32_icelandic_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{162, "utf32", "utf32_latvian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{163, "utf32", "utf32_romanian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{164, "utf32", "utf32_slovenian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{165, "utf32", "utf32_polish_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{166, "utf32", "utf32_estonian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{167, "utf32", "utf32_spanish_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{168, "utf32", "utf32_swedish_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{169, "utf32", "utf32_turkish_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{170, "utf32", "utf32_czech_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{171, "utf32", "utf32_danish_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{172, "utf32", "utf32_lithuanian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{173, "utf32", "utf32_slovak_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{174, "utf32", "utf32_spanish2_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{175, "utf32", "utf32_roman_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{176, "utf32", "utf32_persian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{177, "utf32", "utf32_esperanto_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{178, "utf32", "utf32_hungarian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{179, "utf32", "utf32_sinhala_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{180, "utf32", "utf32_german2_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{181, "utf32", "utf32_croatian_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{182, "utf32", "utf32_unicode_520_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+/*56*/{183, "utf32", "utf32_vietnamese_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32},
+
+ { 192, UTF8_MB3, UTF8_MB3"_general_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 193, UTF8_MB3, UTF8_MB3"_icelandic_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 194, UTF8_MB3, UTF8_MB3"_latvian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 195, UTF8_MB3, UTF8_MB3"_romanian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 196, UTF8_MB3, UTF8_MB3"_slovenian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 197, UTF8_MB3, UTF8_MB3"_polish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 198, UTF8_MB3, UTF8_MB3"_estonian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 199, UTF8_MB3, UTF8_MB3"_spanish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 200, UTF8_MB3, UTF8_MB3"_swedish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 201, UTF8_MB3, UTF8_MB3"_turkish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 202, UTF8_MB3, UTF8_MB3"_czech_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 203, UTF8_MB3, UTF8_MB3"_danish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 204, UTF8_MB3, UTF8_MB3"_lithunian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 205, UTF8_MB3, UTF8_MB3"_slovak_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 206, UTF8_MB3, UTF8_MB3"_spanish2_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 207, UTF8_MB3, UTF8_MB3"_roman_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 208, UTF8_MB3, UTF8_MB3"_persian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 209, UTF8_MB3, UTF8_MB3"_esperanto_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 210, UTF8_MB3, UTF8_MB3"_hungarian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 211, UTF8_MB3, UTF8_MB3"_sinhala_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 211, UTF8_MB3, UTF8_MB3"_german2_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 213, UTF8_MB3, UTF8_MB3"_croatian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 214, UTF8_MB3, UTF8_MB3"_unicode_520_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 215, UTF8_MB3, UTF8_MB3"_vietnamese_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+
+ { 224, UTF8_MB4, UTF8_MB4"_unicode_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 225, UTF8_MB4, UTF8_MB4"_icelandic_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 226, UTF8_MB4, UTF8_MB4"_latvian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 227, UTF8_MB4, UTF8_MB4"_romanian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 228, UTF8_MB4, UTF8_MB4"_slovenian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 229, UTF8_MB4, UTF8_MB4"_polish_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 230, UTF8_MB4, UTF8_MB4"_estonian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 231, UTF8_MB4, UTF8_MB4"_spanish_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 232, UTF8_MB4, UTF8_MB4"_swedish_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 233, UTF8_MB4, UTF8_MB4"_turkish_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 234, UTF8_MB4, UTF8_MB4"_czech_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 235, UTF8_MB4, UTF8_MB4"_danish_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 236, UTF8_MB4, UTF8_MB4"_lithuanian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 237, UTF8_MB4, UTF8_MB4"_slovak_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 238, UTF8_MB4, UTF8_MB4"_spanish2_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 239, UTF8_MB4, UTF8_MB4"_roman_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 240, UTF8_MB4, UTF8_MB4"_persian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 241, UTF8_MB4, UTF8_MB4"_esperanto_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 242, UTF8_MB4, UTF8_MB4"_hungarian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 243, UTF8_MB4, UTF8_MB4"_sinhala_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 244, UTF8_MB4, UTF8_MB4"_german2_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 245, UTF8_MB4, UTF8_MB4"_croatian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 246, UTF8_MB4, UTF8_MB4"_unicode_520_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 247, UTF8_MB4, UTF8_MB4"_vietnamese_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+
+ { 254, UTF8_MB3, UTF8_MB3"_general_cs", 1, 3, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 0, NULL, NULL, 0, 0, NULL, NULL, NULL}
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_find_charset_nr */
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(unsigned int charsetnr)
+{
+ const MYSQLND_CHARSET * c = mysqlnd_charsets;
+
+ do {
+ if (c->nr == charsetnr) {
+ return c;
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ return NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_find_charset_name */
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const name)
+{
+ if (name) {
+ const MYSQLND_CHARSET * c = mysqlnd_charsets;
+ do {
+ if (!strcasecmp(c->name, name)) {
+ return c;
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ }
+ return NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_cset_escape_quotes */
+PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const cset, char *newstr,
+ const char * escapestr, size_t escapestr_len TSRMLS_DC)
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ zend_bool escape_overflow = FALSE;
+
+ DBG_ENTER("mysqlnd_cset_escape_quotes");
+
+ for (;escapestr < end; escapestr++) {
+ unsigned int len = 0;
+ /* check unicode characters */
+
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (*escapestr == '\'') {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = '\'';
+ *newstr++ = '\'';
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ DBG_RETURN((ulong)~0);
+ }
+ DBG_RETURN((ulong)(newstr - newstr_s));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_cset_escape_slashes */
+PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr,
+ const char * escapestr, size_t escapestr_len TSRMLS_DC)
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ zend_bool escape_overflow = FALSE;
+
+ DBG_ENTER("mysqlnd_cset_escape_slashes");
+ DBG_INF_FMT("charset=%s", cset->name);
+
+ for (;escapestr < end; escapestr++) {
+ char esc = '\0';
+ unsigned int len = 0;
+
+ /* check unicode characters */
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1) {
+ esc = *escapestr;
+ } else {
+ switch (*escapestr) {
+ case 0:
+ esc = '0';
+ break;
+ case '\n':
+ esc = 'n';
+ break;
+ case '\r':
+ esc = 'r';
+ break;
+ case '\\':
+ case '\'':
+ case '"':
+ esc = *escapestr;
+ break;
+ case '\032':
+ esc = 'Z';
+ break;
+ }
+ }
+ if (esc) {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy escaped character */
+ *newstr++ = '\\';
+ *newstr++ = esc;
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy non escaped character */
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ DBG_RETURN((ulong)~0);
+ }
+ DBG_RETURN((ulong)(newstr - newstr_s));
+}
+/* }}} */
+
+
+static struct st_mysqlnd_plugin_charsets mysqlnd_plugin_charsets_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "charsets",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* no statistics , will be filled later if there are some */
+ NULL, /* no statistics */
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ },
+ {/* methods */
+ mysqlnd_find_charset_nr,
+ mysqlnd_find_charset_name,
+ mysqlnd_cset_escape_quotes,
+ mysqlnd_cset_escape_slashes
+ }
+};
+
+
+/* {{{ mysqlnd_charsets_plugin_register */
+void
+mysqlnd_charsets_plugin_register(TSRMLS_D)
+{
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_charsets_plugin TSRMLS_CC);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_charset.h b/ext/mysqlnd/mysqlnd_charset.h
new file mode 100644
index 0000000..09d0eb4
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_charset.h
@@ -0,0 +1,53 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_CHARSET_H
+#define MYSQLND_CHARSET_H
+
+PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char *newstr,
+ const char *escapestr, size_t escapestr_len TSRMLS_DC);
+
+PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr,
+ const char *escapestr, size_t escapestr_len TSRMLS_DC);
+
+struct st_mysqlnd_plugin_charsets
+{
+ const struct st_mysqlnd_plugin_header plugin_header;
+ struct
+ {
+ const MYSQLND_CHARSET * (*const find_charset_by_nr)(unsigned int charsetnr);
+ const MYSQLND_CHARSET * (*const find_charset_by_name)(const char * const name);
+ unsigned long (*const escape_quotes)(const MYSQLND_CHARSET * const cset, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC);
+ unsigned long (*const escape_slashes)(const MYSQLND_CHARSET * const cset, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC);
+ } methods;
+};
+
+void mysqlnd_charsets_plugin_register(TSRMLS_D);
+
+#endif /* MYSQLND_CHARSET_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c
new file mode 100644
index 0000000..fb8a360
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_debug.c
@@ -0,0 +1,811 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+
+static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
+
+#ifdef ZTS
+#define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C
+#else
+#define MYSQLND_ZTS(self)
+#endif
+
+
+/* {{{ mysqlnd_debug::open */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
+{
+ MYSQLND_ZTS(self);
+
+ if (!self->file_name) {
+ return FAIL;
+ }
+
+ self->stream = php_stream_open_wrapper(self->file_name,
+ reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
+ REPORT_ERRORS, NULL);
+ return self->stream? PASS:FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_debug::log */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
+ unsigned int line, const char * const file,
+ unsigned int level, const char * type, const char * message)
+{
+ char pipe_buffer[512];
+ enum_func_status ret;
+ int i;
+ char * message_line;
+ unsigned int message_line_len;
+ unsigned int flags = self->flags;
+ char pid_buffer[10], time_buffer[30], file_buffer[200],
+ line_buffer[6], level_buffer[7];
+ MYSQLND_ZTS(self);
+
+ if (!self->stream && FAIL == self->m->open(self, FALSE)) {
+ return FAIL;
+ }
+
+ if (level == -1) {
+ level = zend_stack_count(&self->call_stack);
+ }
+ i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
+ pipe_buffer[i*2] = '\0';
+ for (;i > 0;i--) {
+ pipe_buffer[i*2 - 1] = ' ';
+ pipe_buffer[i*2 - 2] = '|';
+ }
+
+
+ if (flags & MYSQLND_DEBUG_DUMP_PID) {
+ snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
+ pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_TIME) {
+ /* The following from FF's DBUG library, which is in the public domain */
+#if defined(PHP_WIN32)
+ /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
+ in system ticks, 10 ms intervals. See my_getsystime.c for high res */
+ SYSTEMTIME loc_t;
+ GetLocalTime(&loc_t);
+ snprintf(time_buffer, sizeof(time_buffer) - 1,
+ /* "%04d-%02d-%02d " */
+ "%02d:%02d:%02d.%06d ",
+ /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
+ loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
+ time_buffer[sizeof(time_buffer) - 1 ] = '\0';
+#else
+ struct timeval tv;
+ struct tm *tm_p;
+ if (gettimeofday(&tv, NULL) != -1) {
+ if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
+ snprintf(time_buffer, sizeof(time_buffer) - 1,
+ /* "%04d-%02d-%02d " */
+ "%02d:%02d:%02d.%06d ",
+ /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
+ tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
+ (int) (tv.tv_usec));
+ time_buffer[sizeof(time_buffer) - 1 ] = '\0';
+ }
+ }
+#endif
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_FILE) {
+ snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
+ file_buffer[sizeof(file_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_LINE) {
+ snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
+ line_buffer[sizeof(line_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
+ snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
+ level_buffer[sizeof(level_buffer) - 1 ] = '\0';
+ }
+
+ message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
+ flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
+ pipe_buffer, type? type:"", message);
+
+ ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
+ mnd_sprintf_free(message_line);
+ if (flags & MYSQLND_DEBUG_FLUSH) {
+ self->m->close(self);
+ self->m->open(self, TRUE);
+ }
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_debug::log_va */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
+ unsigned int line, const char * const file,
+ unsigned int level, const char * type,
+ const char *format, ...)
+{
+ char pipe_buffer[512];
+ int i;
+ enum_func_status ret;
+ char * message_line, *buffer;
+ unsigned int message_line_len;
+ va_list args;
+ unsigned int flags = self->flags;
+ char pid_buffer[10], time_buffer[30], file_buffer[200],
+ line_buffer[6], level_buffer[7];
+ MYSQLND_ZTS(self);
+
+ if (!self->stream && FAIL == self->m->open(self, FALSE)) {
+ return FAIL;
+ }
+
+ if (level == -1) {
+ level = zend_stack_count(&self->call_stack);
+ }
+ i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
+ pipe_buffer[i*2] = '\0';
+ for (;i > 0;i--) {
+ pipe_buffer[i*2 - 1] = ' ';
+ pipe_buffer[i*2 - 2] = '|';
+ }
+
+
+ if (flags & MYSQLND_DEBUG_DUMP_PID) {
+ snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
+ pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_TIME) {
+ /* The following from FF's DBUG library, which is in the public domain */
+#if defined(PHP_WIN32)
+ /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
+ in system ticks, 10 ms intervals. See my_getsystime.c for high res */
+ SYSTEMTIME loc_t;
+ GetLocalTime(&loc_t);
+ snprintf(time_buffer, sizeof(time_buffer) - 1,
+ /* "%04d-%02d-%02d " */
+ "%02d:%02d:%02d.%06d ",
+ /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
+ loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
+ time_buffer[sizeof(time_buffer) - 1 ] = '\0';
+#else
+ struct timeval tv;
+ struct tm *tm_p;
+ if (gettimeofday(&tv, NULL) != -1) {
+ if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
+ snprintf(time_buffer, sizeof(time_buffer) - 1,
+ /* "%04d-%02d-%02d " */
+ "%02d:%02d:%02d.%06d ",
+ /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
+ tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
+ (int) (tv.tv_usec));
+ time_buffer[sizeof(time_buffer) - 1 ] = '\0';
+ }
+ }
+#endif
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_FILE) {
+ snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
+ file_buffer[sizeof(file_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_LINE) {
+ snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
+ line_buffer[sizeof(line_buffer) - 1 ] = '\0';
+ }
+ if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
+ snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
+ level_buffer[sizeof(level_buffer) - 1 ] = '\0';
+ }
+
+ va_start(args, format);
+ mnd_vsprintf(&buffer, 0, format, args);
+ va_end(args);
+
+ message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
+ flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
+ flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
+ pipe_buffer, type? type:"", buffer);
+ mnd_sprintf_free(buffer);
+ ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
+ mnd_sprintf_free(message_line);
+
+ if (flags & MYSQLND_DEBUG_FLUSH) {
+ self->m->close(self);
+ self->m->open(self, TRUE);
+ }
+ return ret;
+}
+/* }}} */
+
+
+/* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */
+/* {{{ mysqlnd_debug::func_enter */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
+ unsigned int line, const char * const file,
+ const char * const func_name, unsigned int func_name_len)
+{
+ if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
+ return FALSE;
+ }
+ if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
+ return FALSE;
+ }
+
+ if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && self->skip_functions) {
+ const char ** p = self->skip_functions;
+ while (*p) {
+ if (*p == func_name) {
+ zend_stack_push(&self->call_stack, "", sizeof(""));
+#ifndef MYSQLND_PROFILING_DISABLED
+ if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
+ uint64_t some_time = 0;
+ zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
+ }
+#endif
+ return FALSE;
+ }
+ p++;
+ }
+ }
+
+ zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
+#ifndef MYSQLND_PROFILING_DISABLED
+ if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
+ uint64_t some_time = 0;
+ zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
+ }
+#endif
+
+ if (zend_hash_num_elements(&self->not_filtered_functions) &&
+ 0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
+ {
+ return FALSE;
+ }
+
+ self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
+ return TRUE;
+}
+/* }}} */
+
+#ifndef MYSQLND_PROFILING_DISABLED
+struct st_mysqlnd_dbg_function_profile {
+ uint64_t calls;
+ uint64_t min_own;
+ uint64_t max_own;
+ uint64_t avg_own;
+ uint64_t own_underporm_calls;
+ uint64_t min_in_calls;
+ uint64_t max_in_calls;
+ uint64_t avg_in_calls;
+ uint64_t in_calls_underporm_calls;
+ uint64_t min_total;
+ uint64_t max_total;
+ uint64_t avg_total;
+ uint64_t total_underporm_calls;
+};
+#define PROFILE_UNDERPERFORM_THRESHOLD 10
+#endif
+
+/* {{{ mysqlnd_debug::func_leave */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time)
+{
+ char *func_name;
+ uint64_t * parent_non_own_time_ptr = NULL, * mine_non_own_time_ptr = NULL;
+ uint64_t mine_non_own_time = 0;
+ zend_bool profile_calls = self->flags & MYSQLND_DEBUG_PROFILE_CALLS? TRUE:FALSE;
+
+ if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
+ return PASS;
+ }
+ if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
+ return PASS;
+ }
+
+ zend_stack_top(&self->call_stack, (void **)&func_name);
+
+#ifndef MYSQLND_PROFILING_DISABLED
+ if (profile_calls) {
+ zend_stack_top(&self->call_time_stack, (void **)&mine_non_own_time_ptr);
+ mine_non_own_time = *mine_non_own_time_ptr;
+ zend_stack_del_top(&self->call_time_stack); /* callee - removing ourselves */
+ }
+#endif
+
+ if (func_name[0] == '\0') {
+ ; /* don't log that function */
+ } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
+ 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
+ {
+#ifndef MYSQLND_PROFILING_DISABLED
+ if (FALSE == profile_calls) {
+#endif
+ self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
+
+#ifndef MYSQLND_PROFILING_DISABLED
+ } else {
+ struct st_mysqlnd_dbg_function_profile f_profile_stack = {0};
+ struct st_mysqlnd_dbg_function_profile * f_profile = NULL;
+ uint64_t own_time = call_time - mine_non_own_time;
+ uint func_name_len = strlen(func_name);
+
+ self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s (total=%u own=%u in_calls=%u)",
+ func_name, (unsigned int) call_time, (unsigned int) own_time, (unsigned int) mine_non_own_time
+ );
+
+ if (SUCCESS == zend_hash_find(&self->function_profiles, func_name, func_name_len + 1, (void **) &f_profile)) {
+ /* found */
+ if (f_profile) {
+ if (mine_non_own_time < f_profile->min_in_calls) {
+ f_profile->min_in_calls = mine_non_own_time;
+ } else if (mine_non_own_time > f_profile->max_in_calls) {
+ f_profile->max_in_calls = mine_non_own_time;
+ }
+ f_profile->avg_in_calls = (f_profile->avg_in_calls * f_profile->calls + mine_non_own_time) / (f_profile->calls + 1);
+
+ if (own_time < f_profile->min_own) {
+ f_profile->min_own = own_time;
+ } else if (own_time > f_profile->max_own) {
+ f_profile->max_own = own_time;
+ }
+ f_profile->avg_own = (f_profile->avg_own * f_profile->calls + own_time) / (f_profile->calls + 1);
+
+ if (call_time < f_profile->min_total) {
+ f_profile->min_total = call_time;
+ } else if (call_time > f_profile->max_total) {
+ f_profile->max_total = call_time;
+ }
+ f_profile->avg_total = (f_profile->avg_total * f_profile->calls + call_time) / (f_profile->calls + 1);
+
+ ++f_profile->calls;
+ if (f_profile->calls > PROFILE_UNDERPERFORM_THRESHOLD) {
+ if (f_profile->avg_in_calls < mine_non_own_time) {
+ f_profile->in_calls_underporm_calls++;
+ }
+ if (f_profile->avg_own < own_time) {
+ f_profile->own_underporm_calls++;
+ }
+ if (f_profile->avg_total < call_time) {
+ f_profile->total_underporm_calls++;
+ }
+ }
+ }
+ } else {
+ /* add */
+ f_profile = &f_profile_stack;
+ f_profile->min_in_calls = f_profile->max_in_calls = f_profile->avg_in_calls = mine_non_own_time;
+ f_profile->min_total = f_profile->max_total = f_profile->avg_total = call_time;
+ f_profile->min_own = f_profile->max_own = f_profile->avg_own = own_time;
+ f_profile->calls = 1;
+ zend_hash_add(&self->function_profiles, func_name, func_name_len+1, f_profile, sizeof(struct st_mysqlnd_dbg_function_profile), NULL);
+ }
+ if ((uint) zend_stack_count(&self->call_time_stack)) {
+ uint64_t parent_non_own_time = 0;
+
+ zend_stack_top(&self->call_time_stack, (void **)&parent_non_own_time_ptr);
+ parent_non_own_time = *parent_non_own_time_ptr;
+ parent_non_own_time += call_time;
+ zend_stack_del_top(&self->call_time_stack); /* the caller */
+ zend_stack_push(&self->call_time_stack, &parent_non_own_time, sizeof(parent_non_own_time)); /* add back the caller */
+ }
+ }
+#endif
+ }
+
+ return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_debug::close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
+{
+ MYSQLND_ZTS(self);
+ if (self->stream) {
+#ifndef MYSQLND_PROFILING_DISABLED
+ if (!(self->flags & MYSQLND_DEBUG_FLUSH) && (self->flags & MYSQLND_DEBUG_PROFILE_CALLS)) {
+ struct st_mysqlnd_dbg_function_profile * f_profile;
+ HashPosition pos_values;
+
+ self->m->log_va(self, __LINE__, __FILE__, 0, "info : ",
+ "number of functions: %d", zend_hash_num_elements(&self->function_profiles));
+ zend_hash_internal_pointer_reset_ex(&self->function_profiles, &pos_values);
+ while (zend_hash_get_current_data_ex(&self->function_profiles, (void **) &f_profile, &pos_values) == SUCCESS) {
+ char *string_key = NULL;
+ uint string_key_len;
+ ulong num_key;
+
+ zend_hash_get_current_key_ex(&self->function_profiles, &string_key, &string_key_len, &num_key, 0, &pos_values);
+
+ self->m->log_va(self, __LINE__, __FILE__, -1, "info : ",
+ "%-40s\tcalls=%5llu own_slow=%5llu in_calls_slow=%5llu total_slow=%5llu"
+ " min_own=%5llu max_own=%7llu avg_own=%7llu "
+ " min_in_calls=%5llu max_in_calls=%7llu avg_in_calls=%7llu"
+ " min_total=%5llu max_total=%7llu avg_total=%7llu"
+ ,string_key
+ ,(uint64_t) f_profile->calls
+ ,(uint64_t) f_profile->own_underporm_calls
+ ,(uint64_t) f_profile->in_calls_underporm_calls
+ ,(uint64_t) f_profile->total_underporm_calls
+
+ ,(uint64_t) f_profile->min_own
+ ,(uint64_t) f_profile->max_own
+ ,(uint64_t) f_profile->avg_own
+ ,(uint64_t) f_profile->min_in_calls
+ ,(uint64_t) f_profile->max_in_calls
+ ,(uint64_t) f_profile->avg_in_calls
+ ,(uint64_t) f_profile->min_total
+ ,(uint64_t) f_profile->max_total
+ ,(uint64_t) f_profile->avg_total
+ );
+ zend_hash_move_forward_ex(&self->function_profiles, &pos_values);
+ }
+ }
+#endif
+
+ php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
+ self->stream = NULL;
+ }
+ /* no DBG_RETURN please */
+ return PASS;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::free */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
+{
+ if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
+ efree(self->file_name);
+ self->file_name = NULL;
+ }
+ zend_stack_destroy(&self->call_stack);
+ zend_stack_destroy(&self->call_time_stack);
+ zend_hash_destroy(&self->not_filtered_functions);
+ zend_hash_destroy(&self->function_profiles);
+ free(self);
+ return PASS;
+}
+/* }}} */
+
+enum mysqlnd_debug_parser_state
+{
+ PARSER_WAIT_MODIFIER,
+ PARSER_WAIT_COLON,
+ PARSER_WAIT_VALUE
+};
+
+
+/* {{{ mysqlnd_res_meta::set_mode */
+static void
+MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
+{
+ unsigned int mode_len, i;
+ enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
+
+ mode_len = mode? strlen(mode) : 0;
+
+ self->flags = 0;
+ self->nest_level_limit = 0;
+ if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
+ efree(self->file_name);
+ self->file_name = NULL;
+ }
+ if (zend_hash_num_elements(&self->not_filtered_functions)) {
+ zend_hash_destroy(&self->not_filtered_functions);
+ zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
+ }
+
+ for (i = 0; i < mode_len; i++) {
+ switch (mode[i]) {
+ case 'O':
+ case 'A':
+ self->flags |= MYSQLND_DEBUG_FLUSH;
+ case 'a':
+ case 'o':
+ if (mode[i] == 'a' || mode[i] == 'A') {
+ self->flags |= MYSQLND_DEBUG_APPEND;
+ }
+ if (i + 1 < mode_len && mode[i+1] == ',') {
+ unsigned int j = i + 2;
+#ifdef PHP_WIN32
+ if (i+4 < mode_len && mode[i+3] == ':' && (mode[i+4] == '\\' || mode[i+5] == '/')) {
+ j = i + 5;
+ }
+#endif
+ while (j < mode_len) {
+ if (mode[j] == ':') {
+ break;
+ }
+ j++;
+ }
+ if (j > i + 2) {
+ self->file_name = estrndup(mode + i + 2, j - i - 2);
+ }
+ i = j;
+ } else {
+ if (!self->file_name)
+ self->file_name = (char *) mysqlnd_debug_default_trace_file;
+ }
+ state = PARSER_WAIT_COLON;
+ break;
+ case ':':
+#if 0
+ if (state != PARSER_WAIT_COLON) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i);
+ }
+#endif
+ state = PARSER_WAIT_MODIFIER;
+ break;
+ case 'f': /* limit output to these functions */
+ if (i + 1 < mode_len && mode[i+1] == ',') {
+ unsigned int j = i + 2;
+ i++;
+ while (j < mode_len) {
+ if (mode[j] == ':') {
+ /* function names with :: */
+ if ((j + 1 < mode_len) && mode[j+1] == ':') {
+ j += 2;
+ continue;
+ }
+ }
+ if (mode[j] == ',' || mode[j] == ':') {
+ if (j > i + 2) {
+ char func_name[1024];
+ unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
+ memcpy(func_name, mode + i + 1, func_name_len);
+ func_name[func_name_len] = '\0';
+
+ zend_hash_add_empty_element(&self->not_filtered_functions,
+ func_name, func_name_len + 1);
+ i = j;
+ }
+ if (mode[j] == ':') {
+ break;
+ }
+ }
+ j++;
+ }
+ i = j;
+ } else {
+#if 0
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Expected list of functions for '%c' found none", mode[i]);
+#endif
+ }
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'D':
+ case 'd':
+ case 'g':
+ case 'p':
+ /* unsupported */
+ if ((i + 1) < mode_len && mode[i+1] == ',') {
+ i+= 2;
+ while (i < mode_len) {
+ if (mode[i] == ':') {
+ break;
+ }
+ i++;
+ }
+ }
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'F':
+ self->flags |= MYSQLND_DEBUG_DUMP_FILE;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'i':
+ self->flags |= MYSQLND_DEBUG_DUMP_PID;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'L':
+ self->flags |= MYSQLND_DEBUG_DUMP_LINE;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'n':
+ self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 't':
+ if (mode[i+1] == ',') {
+ unsigned int j = i + 2;
+ while (j < mode_len) {
+ if (mode[j] == ':') {
+ break;
+ }
+ j++;
+ }
+ if (j > i + 2) {
+ char *value_str = estrndup(mode + i + 2, j - i - 2);
+ self->nest_level_limit = atoi(value_str);
+ efree(value_str);
+ }
+ i = j;
+ } else {
+ self->nest_level_limit = 200; /* default value for FF DBUG */
+ }
+ self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'T':
+ self->flags |= MYSQLND_DEBUG_DUMP_TIME;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'N':
+ case 'P':
+ case 'r':
+ case 'S':
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'm': /* mysqlnd extension - trace memory functions */
+ self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
+ state = PARSER_WAIT_COLON;
+ break;
+ case 'x': /* mysqlnd extension - profile calls */
+ self->flags |= MYSQLND_DEBUG_PROFILE_CALLS;
+ state = PARSER_WAIT_COLON;
+ break;
+ default:
+ if (state == PARSER_WAIT_MODIFIER) {
+#if 0
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]);
+#endif
+ if (i+1 < mode_len && mode[i+1] == ',') {
+ i+= 2;
+ while (i < mode_len) {
+ if (mode[i] == ':') {
+ break;
+ }
+ i++;
+ }
+ }
+ state = PARSER_WAIT_COLON;
+ } else if (state == PARSER_WAIT_COLON) {
+#if 0
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);
+#endif
+ }
+ break;
+ }
+ }
+}
+/* }}} */
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
+ MYSQLND_METHOD(mysqlnd_debug, open),
+ MYSQLND_METHOD(mysqlnd_debug, set_mode),
+ MYSQLND_METHOD(mysqlnd_debug, log),
+ MYSQLND_METHOD(mysqlnd_debug, log_va),
+ MYSQLND_METHOD(mysqlnd_debug, func_enter),
+ MYSQLND_METHOD(mysqlnd_debug, func_leave),
+ MYSQLND_METHOD(mysqlnd_debug, close),
+ MYSQLND_METHOD(mysqlnd_debug, free),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_debug_init */
+PHPAPI MYSQLND_DEBUG *
+mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC)
+{
+ MYSQLND_DEBUG *ret = calloc(1, sizeof(MYSQLND_DEBUG));
+#ifdef ZTS
+ ret->TSRMLS_C = TSRMLS_C;
+#endif
+ ret->nest_level_limit = 0;
+ ret->pid = getpid();
+ zend_stack_init(&ret->call_stack);
+ zend_stack_init(&ret->call_time_stack);
+ zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
+ zend_hash_init(&ret->function_profiles, 0, NULL, NULL, 0);
+
+ ret->m = & mysqlnd_mysqlnd_debug_methods;
+ ret->skip_functions = skip_functions;
+
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_debug */
+PHPAPI void _mysqlnd_debug(const char * mode TSRMLS_DC)
+{
+#if PHP_DEBUG
+ MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
+ if (!dbg) {
+ MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
+ if (!dbg) {
+ return;
+ }
+ }
+
+ dbg->m->close(dbg);
+ dbg->m->set_mode(dbg, mode);
+ while (zend_stack_count(&dbg->call_stack)) {
+ zend_stack_del_top(&dbg->call_stack);
+ }
+ while (zend_stack_count(&dbg->call_time_stack)) {
+ zend_stack_del_top(&dbg->call_time_stack);
+ }
+#endif
+}
+/* }}} */
+
+
+static struct st_mysqlnd_plugin_trace_log mysqlnd_plugin_trace_log_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "debug_trace",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* no statistics , will be filled later if there are some */
+ NULL, /* no statistics */
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ },
+ {/* methods */
+ mysqlnd_debug_init,
+ mysqlnd_get_backtrace
+ }
+};
+
+
+/* {{{ mysqlnd_debug_trace_plugin_register */
+void
+mysqlnd_debug_trace_plugin_register(TSRMLS_D)
+{
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_trace_log_plugin TSRMLS_CC);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h
new file mode 100644
index 0000000..f98c163
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_debug.h
@@ -0,0 +1,193 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_DEBUG_H
+#define MYSQLND_DEBUG_H
+
+#include "mysqlnd_alloc.h"
+#include "zend_stack.h"
+
+struct st_mysqlnd_debug_methods
+{
+ enum_func_status (*open)(MYSQLND_DEBUG * self, zend_bool reopen);
+ void (*set_mode)(MYSQLND_DEBUG * self, const char * const mode);
+ enum_func_status (*log)(MYSQLND_DEBUG * self, unsigned int line, const char * const file,
+ unsigned int level, const char * type, const char *message);
+ enum_func_status (*log_va)(MYSQLND_DEBUG * self, unsigned int line, const char * const file,
+ unsigned int level, const char * type, const char *format, ...);
+ zend_bool (*func_enter)(MYSQLND_DEBUG * self, unsigned int line, const char * const file,
+ const char * const func_name, unsigned int func_name_len);
+ enum_func_status (*func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time);
+ enum_func_status (*close)(MYSQLND_DEBUG * self);
+ enum_func_status (*free_handle)(MYSQLND_DEBUG * self);
+};
+
+
+struct st_mysqlnd_debug
+{
+ php_stream *stream;
+#ifdef ZTS
+ TSRMLS_D;
+#endif
+ unsigned int flags;
+ unsigned int nest_level_limit;
+ int pid;
+ char * file_name;
+ zend_stack call_stack;
+ zend_stack call_time_stack;
+ HashTable not_filtered_functions;
+ HashTable function_profiles;
+ struct st_mysqlnd_debug_methods *m;
+ const char ** skip_functions;
+};
+
+struct st_mysqlnd_plugin_trace_log
+{
+ struct st_mysqlnd_plugin_header plugin_header;
+ struct
+ {
+ MYSQLND_DEBUG * (*trace_instance_init)(const char * skip_functions[] TSRMLS_DC);
+ char * (*get_backtrace)(uint max_levels, size_t * length TSRMLS_DC);
+ } methods;
+};
+
+void mysqlnd_debug_trace_plugin_register(TSRMLS_D);
+
+PHPAPI MYSQLND_DEBUG * mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC);
+
+PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC);
+
+#if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 1400))
+#ifdef PHP_WIN32
+#include "win32/time.h"
+#elif defined(NETWARE)
+#include <sys/timeval.h>
+#include <sys/time.h>
+#else
+#include <sys/time.h>
+#endif
+
+#ifndef MYSQLND_PROFILING_DISABLED
+#define DBG_PROFILE_TIMEVAL_TO_DOUBLE(tp) ((tp.tv_sec * 1000000LL)+ tp.tv_usec)
+#define DBG_PROFILE_START_TIME() gettimeofday(&__dbg_prof_tp, NULL); __dbg_prof_start = DBG_PROFILE_TIMEVAL_TO_DOUBLE(__dbg_prof_tp);
+#define DBG_PROFILE_END_TIME(duration) gettimeofday(&__dbg_prof_tp, NULL); (duration) = (DBG_PROFILE_TIMEVAL_TO_DOUBLE(__dbg_prof_tp) - __dbg_prof_start);
+#else
+#define DBG_PROFILE_TIMEVAL_TO_DOUBLE(tp)
+#define DBG_PROFILE_START_TIME()
+#define DBG_PROFILE_END_TIME(duration)
+#endif
+
+#define DBG_INF_EX(dbg_obj, msg) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log((dbg_obj), __LINE__, __FILE__, -1, "info : ", (msg)); } while (0)
+#define DBG_ERR_EX(dbg_obj, msg) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log((dbg_obj), __LINE__, __FILE__, -1, "error: ", (msg)); } while (0)
+#define DBG_INF_FMT_EX(dbg_obj, ...) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log_va((dbg_obj), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0)
+#define DBG_ERR_FMT_EX(dbg_obj, ...) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log_va((dbg_obj), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0)
+
+#define DBG_BLOCK_ENTER_EX(dbg_obj, block_name) \
+ { \
+ DBG_ENTER_EX(dbg_obj, (block_name));
+
+#define DBG_BLOCK_LEAVE_EX(dbg_obj) \
+ DBG_LEAVE_EX((dbg_obj), ;) \
+ } \
+
+
+#define DBG_ENTER_EX(dbg_obj, func_name) \
+ struct timeval __dbg_prof_tp = {0}; \
+ uint64_t __dbg_prof_start = 0; /* initialization is needed */ \
+ zend_bool dbg_skip_trace = TRUE; \
+ if ((dbg_obj)) { \
+ dbg_skip_trace = !(dbg_obj)->m->func_enter((dbg_obj), __LINE__, __FILE__, func_name, strlen(func_name)); \
+ } \
+ do { \
+ if ((dbg_obj) && (dbg_obj)->flags & MYSQLND_DEBUG_PROFILE_CALLS) { \
+ DBG_PROFILE_START_TIME(); \
+ } \
+ } while (0);
+
+#define DBG_LEAVE_EX(dbg_obj, leave) \
+ do {\
+ if ((dbg_obj)) { \
+ uint64_t this_call_duration = 0; \
+ if ((dbg_obj)->flags & MYSQLND_DEBUG_PROFILE_CALLS) { \
+ DBG_PROFILE_END_TIME(this_call_duration); \
+ } \
+ (dbg_obj)->m->func_leave((dbg_obj), __LINE__, __FILE__, this_call_duration); \
+ } \
+ leave \
+ } while (0);
+
+#define DBG_RETURN_EX(dbg_obj, value) DBG_LEAVE_EX(dbg_obj, return (value);)
+
+#define DBG_VOID_RETURN_EX(dbg_obj) DBG_LEAVE_EX(dbg_obj, return;)
+
+
+
+#else
+static inline void DBG_INF_EX(MYSQLND_DEBUG * dbg_obj, const char * const msg) {}
+static inline void DBG_ERR_EX(MYSQLND_DEBUG * dbg_obj, const char * const msg) {}
+static inline void DBG_INF_FMT_EX(MYSQLND_DEBUG * dbg_obj, ...) {}
+static inline void DBG_ERR_FMT_EX(MYSQLND_DEBUG * dbg_obj, ...) {}
+static inline void DBG_ENTER_EX(MYSQLND_DEBUG * dbg_obj, const char * const func_name) {}
+#define DBG_BLOCK_ENTER(bname) {
+#define DBG_RETURN_EX(dbg_obj, value) return (value)
+#define DBG_VOID_RETURN_EX(dbg_obj) return
+#define DBG_BLOCK_LEAVE_EX(dbg_obj) }
+
+#endif
+
+#if MYSQLND_DBG_ENABLED == 1
+
+#define DBG_INF(msg) DBG_INF_EX(MYSQLND_G(dbg), (msg))
+#define DBG_ERR(msg) DBG_ERR_EX(MYSQLND_G(dbg), (msg))
+#define DBG_INF_FMT(...) DBG_INF_FMT_EX(MYSQLND_G(dbg), __VA_ARGS__)
+#define DBG_ERR_FMT(...) DBG_ERR_FMT_EX(MYSQLND_G(dbg), __VA_ARGS__)
+
+#define DBG_ENTER(func_name) DBG_ENTER_EX(MYSQLND_G(dbg), (func_name))
+#define DBG_BLOCK_ENTER(bname) DBG_BLOCK_ENTER_EX(MYSQLND_G(dbg), (bname))
+#define DBG_RETURN(value) DBG_RETURN_EX(MYSQLND_G(dbg), (value))
+#define DBG_VOID_RETURN DBG_VOID_RETURN_EX(MYSQLND_G(dbg))
+#define DBG_BLOCK_LEAVE DBG_BLOCK_LEAVE_EX(MYSQLND_G(dbg))
+
+#elif MYSQLND_DBG_ENABLED == 0
+
+static inline void DBG_INF(const char * const msg) {}
+static inline void DBG_ERR(const char * const msg) {}
+static inline void DBG_INF_FMT(const char * const format, ...) {}
+static inline void DBG_ERR_FMT(const char * const format, ...) {}
+static inline void DBG_ENTER(const char * const func_name) {}
+#define DBG_BLOCK_ENTER(bname) {
+#define DBG_RETURN(value) return (value)
+#define DBG_VOID_RETURN return
+#define DBG_BLOCK_LEAVE }
+
+#endif
+
+#endif /* MYSQLND_DEBUG_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_driver.c b/ext/mysqlnd/mysqlnd_driver.c
new file mode 100644
index 0000000..e209a71
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_driver.c
@@ -0,0 +1,306 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 317989 2011-10-10 20:49:28Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_reverse_api.h"
+#include "mysqlnd_ext_plugin.h"
+
+static zend_bool mysqlnd_library_initted = FALSE;
+
+static struct st_mysqlnd_plugin_core mysqlnd_plugin_core =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "mysqlnd",
+ MYSQLND_VERSION_ID,
+ MYSQLND_VERSION,
+ "PHP License 3.01",
+ "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
+ {
+ NULL, /* will be filled later */
+ mysqlnd_stats_values_names,
+ },
+ {
+ NULL /* plugin shutdown */
+ }
+ }
+};
+
+
+/* {{{ mysqlnd_library_end */
+PHPAPI void mysqlnd_library_end(TSRMLS_D)
+{
+ if (mysqlnd_library_initted == TRUE) {
+ mysqlnd_plugin_subsystem_end(TSRMLS_C);
+ mysqlnd_stats_end(mysqlnd_global_stats);
+ mysqlnd_global_stats = NULL;
+ mysqlnd_library_initted = FALSE;
+ mysqlnd_reverse_api_end(TSRMLS_C);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_library_init */
+PHPAPI void mysqlnd_library_init(TSRMLS_D)
+{
+ if (mysqlnd_library_initted == FALSE) {
+ mysqlnd_library_initted = TRUE;
+ mysqlnd_conn_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn));
+ mysqlnd_conn_data_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn_data));
+ _mysqlnd_init_ps_subsystem();
+ /* Should be calloc, as mnd_calloc will reference LOCK_access*/
+ mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST);
+ mysqlnd_plugin_subsystem_init(TSRMLS_C);
+ {
+ mysqlnd_plugin_core.plugin_header.plugin_stats.values = mysqlnd_global_stats;
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_core TSRMLS_CC);
+ }
+ mysqlnd_example_plugin_register(TSRMLS_C);
+ mysqlnd_debug_trace_plugin_register(TSRMLS_C);
+ mysqlnd_register_builtin_authentication_plugins(TSRMLS_C);
+
+ mysqlnd_reverse_api_init(TSRMLS_C);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_list_pdtor */
+static void
+mysqlnd_error_list_pdtor(void * pDest)
+{
+ MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
+#ifdef ZTS
+ TSRMLS_FETCH();
+#endif
+ DBG_ENTER("mysqlnd_error_list_pdtor");
+ if (element->error) {
+ mnd_pefree(element->error, TRUE);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_connection */
+static MYSQLND *
+MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
+ size_t alloc_size_ret_data = sizeof(MYSQLND_CONN_DATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND * new_object;
+ MYSQLND_CONN_DATA * data;
+
+ DBG_ENTER("mysqlnd_driver::get_connection");
+ DBG_INF_FMT("persistent=%u", persistent);
+ new_object = mnd_pecalloc(1, alloc_size_ret, persistent);
+ if (!new_object) {
+ DBG_RETURN(NULL);
+ }
+ new_object->data = mnd_pecalloc(1, alloc_size_ret_data, persistent);
+ if (!new_object->data) {
+ mnd_pefree(new_object, persistent);
+ DBG_RETURN(NULL);
+ }
+ new_object->persistent = persistent;
+ new_object->m = mysqlnd_conn_get_methods();
+ data = new_object->data;
+
+ data->error_info = &(data->error_info_impl);
+ data->options = &(data->options_impl);
+ data->upsert_status = &(data->upsert_status_impl);
+
+ data->persistent = persistent;
+ data->m = mysqlnd_conn_data_get_methods();
+ CONN_SET_STATE(data, CONN_ALLOCED);
+ data->m->get_reference(data TSRMLS_CC);
+
+ if (PASS != data->m->init(data TSRMLS_CC)) {
+ new_object->m->dtor(new_object TSRMLS_CC);
+ DBG_RETURN(NULL);
+ }
+
+ data->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
+ if (!data->error_info->error_list) {
+ new_object->m->dtor(new_object TSRMLS_CC);
+ DBG_RETURN(NULL);
+ } else {
+ zend_llist_init(data->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t)mysqlnd_error_list_pdtor, persistent);
+ }
+
+ DBG_RETURN(new_object);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::clone_connection_object */
+static MYSQLND *
+MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object)(MYSQLND * to_be_cloned TSRMLS_DC)
+{
+ size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND * new_object;
+
+ DBG_ENTER("mysqlnd_driver::clone_connection_object");
+ DBG_INF_FMT("persistent=%u", to_be_cloned->persistent);
+ if (!to_be_cloned || !to_be_cloned->data) {
+ DBG_RETURN(NULL);
+ }
+ new_object = mnd_pecalloc(1, alloc_size_ret, to_be_cloned->persistent);
+ if (!new_object) {
+ DBG_RETURN(NULL);
+ }
+ new_object->persistent = to_be_cloned->persistent;
+ new_object->m = to_be_cloned->m;
+
+ new_object->data = to_be_cloned->data->m->get_reference(to_be_cloned->data TSRMLS_CC);
+ if (!new_object->data) {
+ new_object->m->dtor(new_object TSRMLS_CC);
+ new_object = NULL;
+ }
+ DBG_RETURN(new_object);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_prepared_statement */
+static MYSQLND_STMT *
+MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
+ MYSQLND_STMT_DATA * stmt = NULL;
+
+ DBG_ENTER("mysqlnd_object_factory::get_prepared_statement");
+ do {
+ if (!ret) {
+ break;
+ }
+ ret->m = mysqlnd_stmt_get_methods();
+ ret->persistent = conn->persistent;
+
+ stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
+ DBG_INF_FMT("stmt=%p", stmt);
+ if (!stmt) {
+ break;
+ }
+ stmt->persistent = conn->persistent;
+ stmt->error_info = &(stmt->error_info_impl);
+ stmt->upsert_status = &(stmt->upsert_status_impl);
+ stmt->state = MYSQLND_STMT_INITTED;
+ stmt->execute_cmd_buffer.length = 4096;
+ stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent);
+ if (!stmt->execute_cmd_buffer.buffer) {
+ break;
+ }
+
+ stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
+ /*
+ Mark that we reference the connection, thus it won't be
+ be destructed till there is open statements. The last statement
+ or normal query result will close it then.
+ */
+ stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
+ stmt->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent);
+ if (!stmt->error_info->error_list) {
+ break;
+ }
+
+ zend_llist_init(stmt->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, conn->persistent);
+
+ DBG_RETURN(ret);
+ } while (0);
+
+ SET_OOM_ERROR(*conn->error_info);
+ if (ret) {
+ ret->m->dtor(ret, TRUE TSRMLS_CC);
+ ret = NULL;
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_io_channel */
+PHPAPI MYSQLND_NET *
+MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_NET) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_NET * net = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_object_factory::get_io_channel");
+ DBG_INF_FMT("persistent=%u", persistent);
+ if (net) {
+ net->persistent = persistent;
+ net->m = *mysqlnd_net_get_methods();
+
+ if (PASS != net->m.init(net, stats, error_info TSRMLS_CC)) {
+ net->m.dtor(net, stats, error_info TSRMLS_CC);
+ net = NULL;
+ }
+ }
+ DBG_RETURN(net);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_protocol_decoder */
+PHPAPI MYSQLND_PROTOCOL *
+MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)(zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_object_factory::get_protocol_decoder");
+ DBG_INF_FMT("persistent=%u", persistent);
+ if (ret) {
+ ret->persistent = persistent;
+ ret->m = mysqlnd_mysqlnd_protocol_methods;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
+ MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
+ MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)
+MYSQLND_CLASS_METHODS_END;
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
new file mode 100644
index 0000000..92f6cb3
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -0,0 +1,605 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#ifndef MYSQLND_ENUM_N_DEF_H
+#define MYSQLND_ENUM_N_DEF_H
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+#define MYSQLND_MIN_COMPRESS_LEN 0
+
+#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1)
+
+#define MYSQLND_ASSEMBLED_PACKET_MAX_SIZE 3UL*1024UL*1024UL*1024UL
+
+#define MYSQLND_ERRMSG_SIZE 512
+#define MYSQLND_SQLSTATE_LENGTH 5
+#define MYSQLND_SQLSTATE_NULL "00000"
+
+#define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte . MySQL supports now only 16 char, but let it be forward compatible */
+#define MYSQLND_MAX_ALLOWED_DB_LEN 1024 /* 256 char * 4byte. MySQL supports now only 64 char in the tables, but on the FS could be different. Forward compatible. */
+
+#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE 4096
+#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR "4096"
+
+#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
+#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
+#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
+#define SERVER_QUERY_NO_GOOD_INDEX_USED 16
+#define SERVER_QUERY_NO_INDEX_USED 32
+/*
+ The server was able to fulfill the clients request and opened a
+ read-only non-scrollable cursor for a query. This flag comes
+ in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
+*/
+#define SERVER_STATUS_CURSOR_EXISTS 64
+/*
+ This flag is sent when a read-only cursor is exhausted, in reply to
+ COM_STMT_FETCH command.
+*/
+#define SERVER_STATUS_LAST_ROW_SENT 128
+#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
+#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512
+#define SERVER_QUERY_WAS_SLOW 2048
+#define SERVER_PS_OUT_PARAMS 4096
+
+#define MYSQLND_NO_DATA 100
+#define MYSQLND_DATA_TRUNCATED 101
+
+#define SHA1_MAX_LENGTH 20
+#define SCRAMBLE_LENGTH 20
+#define SCRAMBLE_LENGTH_323 8
+
+#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
+#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
+#define CLIENT_LONG_FLAG 4 /* Get all column flags */
+#define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */
+#define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */
+#define CLIENT_COMPRESS 32 /* Can use compression protocol */
+#define CLIENT_ODBC 64 /* Odbc client */
+#define CLIENT_LOCAL_FILES 128 /* Can use LOAD DATA LOCAL */
+#define CLIENT_IGNORE_SPACE 256 /* Ignore spaces before '(' */
+#define CLIENT_PROTOCOL_41 512 /* New 4.1 protocol */
+#define CLIENT_INTERACTIVE 1024 /* This is an interactive client */
+#define CLIENT_SSL 2048 /* Switch to SSL after handshake */
+#define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */
+#define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */
+#define CLIENT_RESERVED 16384 /* Old flag for 4.1 protocol */
+#define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */
+#define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */
+#define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */
+#define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
+#define CLIENT_PLUGIN_AUTH (1UL << 19) /* Client supports plugin authentication */
+#define CLIENT_CONNECT_ATTRS (1UL << 20) /* Client supports connection attributes */
+#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA (1UL << 21) /* Enable authentication response packet to be larger than 255 bytes. */
+#define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) /* Don't close the connection for a connection with expired password. */
+#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
+
+#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
+ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
+ CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS | CLIENT_LOCAL_FILES | CLIENT_PLUGIN_AUTH)
+
+#define MYSQLND_NET_FLAG_USE_COMPRESSION 1
+
+typedef enum mysqlnd_extension
+{
+ MYSQLND_MYSQL = 0,
+ MYSQLND_MYSQLI
+} enum_mysqlnd_extension;
+
+enum
+{
+ MYSQLND_FETCH_ASSOC = 1,
+ MYSQLND_FETCH_NUM = 2,
+ MYSQLND_FETCH_BOTH = 1|2
+};
+
+/* Follow libmysql convention */
+typedef enum func_status
+{
+ PASS = 0,
+ FAIL = 1
+} enum_func_status;
+
+typedef enum mysqlnd_query_type
+{
+ QUERY_UPSERT,
+ QUERY_SELECT,
+ QUERY_LOAD_LOCAL
+} enum_mysqlnd_query_type;
+
+typedef enum mysqlnd_res_type
+{
+ MYSQLND_RES_NORMAL = 1,
+ MYSQLND_RES_PS_BUF,
+ MYSQLND_RES_PS_UNBUF
+} enum_mysqlnd_res_type;
+
+typedef enum mysqlnd_option
+{
+ MYSQL_OPT_CONNECT_TIMEOUT,
+ MYSQL_OPT_COMPRESS,
+ MYSQL_OPT_NAMED_PIPE,
+ MYSQL_INIT_COMMAND,
+ MYSQL_READ_DEFAULT_FILE,
+ MYSQL_READ_DEFAULT_GROUP,
+ MYSQL_SET_CHARSET_DIR,
+ MYSQL_SET_CHARSET_NAME,
+ MYSQL_OPT_LOCAL_INFILE,
+ MYSQL_OPT_PROTOCOL,
+ MYSQL_SHARED_MEMORY_BASE_NAME,
+ MYSQL_OPT_READ_TIMEOUT,
+ MYSQL_OPT_WRITE_TIMEOUT,
+ MYSQL_OPT_USE_RESULT,
+ MYSQL_OPT_USE_REMOTE_CONNECTION,
+ MYSQL_OPT_USE_EMBEDDED_CONNECTION,
+ MYSQL_OPT_GUESS_CONNECTION,
+ MYSQL_SET_CLIENT_IP,
+ MYSQL_SECURE_AUTH,
+ MYSQL_REPORT_DATA_TRUNCATION,
+ MYSQL_OPT_RECONNECT,
+ MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ MYSQL_PLUGIN_DIR,
+ MYSQL_DEFAULT_AUTH,
+ MYSQL_SERVER_PUBLIC_KEY,
+ MYSQL_ENABLE_CLEARTEXT_PLUGIN,
+ MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
+#if MYSQLND_UNICODE
+ MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200,
+#endif
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ MYSQLND_OPT_INT_AND_FLOAT_NATIVE = 201,
+#endif
+ MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202,
+ MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203,
+ MYSQLND_OPT_SSL_KEY = 204,
+ MYSQLND_OPT_SSL_CERT = 205,
+ MYSQLND_OPT_SSL_CA = 206,
+ MYSQLND_OPT_SSL_CAPATH = 207,
+ MYSQLND_OPT_SSL_CIPHER = 208,
+ MYSQLND_OPT_SSL_PASSPHRASE = 209,
+ MYSQLND_OPT_MAX_ALLOWED_PACKET = 210,
+ MYSQLND_OPT_AUTH_PROTOCOL = 211
+} enum_mysqlnd_option;
+
+typedef enum mysqlnd_protocol_type
+{
+ MYSQL_PROTOCOL_DEFAULT = 0,
+ MYSQL_PROTOCOL_TCP, /* all, supported */
+ MYSQL_PROTOCOL_SOCKET, /* unix, supported */
+ MYSQL_PROTOCOL_PIPE, /* win32, not-supported */
+ MYSQL_PROTOCOL_MEMORY, /* win32, not-supported */
+ MYSQL_PROTOCOL_LAST
+} enum_mysqlnd_protocol_type;
+
+typedef enum mysqlnd_field_types
+{
+ MYSQL_TYPE_DECIMAL,
+ MYSQL_TYPE_TINY,
+ MYSQL_TYPE_SHORT,
+ MYSQL_TYPE_LONG,
+ MYSQL_TYPE_FLOAT,
+ MYSQL_TYPE_DOUBLE,
+ MYSQL_TYPE_NULL,
+ MYSQL_TYPE_TIMESTAMP,
+ MYSQL_TYPE_LONGLONG,
+ MYSQL_TYPE_INT24,
+ MYSQL_TYPE_DATE,
+ MYSQL_TYPE_TIME,
+ MYSQL_TYPE_DATETIME,
+ MYSQL_TYPE_YEAR,
+ MYSQL_TYPE_NEWDATE,
+ MYSQL_TYPE_VARCHAR,
+ MYSQL_TYPE_BIT,
+ MYSQL_TYPE_NEWDECIMAL=246,
+ MYSQL_TYPE_ENUM=247,
+ MYSQL_TYPE_SET=248,
+ MYSQL_TYPE_TINY_BLOB=249,
+ MYSQL_TYPE_MEDIUM_BLOB=250,
+ MYSQL_TYPE_LONG_BLOB=251,
+ MYSQL_TYPE_BLOB=252,
+ MYSQL_TYPE_VAR_STRING=253,
+ MYSQL_TYPE_STRING=254,
+ MYSQL_TYPE_GEOMETRY=255
+} enum_mysqlnd_field_types;
+
+/* Please update this if there is a new type after MYSQL_TYPE_GEOMETRY */
+#define MYSQL_TYPE_LAST MYSQL_TYPE_GEOMETRY
+
+
+typedef enum mysqlnd_server_option
+{
+ MYSQL_OPTION_MULTI_STATEMENTS_ON,
+ MYSQL_OPTION_MULTI_STATEMENTS_OFF
+} enum_mysqlnd_server_option;
+
+
+#define FIELD_TYPE_DECIMAL MYSQL_TYPE_DECIMAL
+#define FIELD_TYPE_NEWDECIMAL MYSQL_TYPE_NEWDECIMAL
+#define FIELD_TYPE_TINY MYSQL_TYPE_TINY
+#define FIELD_TYPE_SHORT MYSQL_TYPE_SHORT
+#define FIELD_TYPE_LONG MYSQL_TYPE_LONG
+#define FIELD_TYPE_FLOAT MYSQL_TYPE_FLOAT
+#define FIELD_TYPE_DOUBLE MYSQL_TYPE_DOUBLE
+#define FIELD_TYPE_NULL MYSQL_TYPE_NULL
+#define FIELD_TYPE_TIMESTAMP MYSQL_TYPE_TIMESTAMP
+#define FIELD_TYPE_LONGLONG MYSQL_TYPE_LONGLONG
+#define FIELD_TYPE_INT24 MYSQL_TYPE_INT24
+#define FIELD_TYPE_DATE MYSQL_TYPE_DATE
+#define FIELD_TYPE_TIME MYSQL_TYPE_TIME
+#define FIELD_TYPE_DATETIME MYSQL_TYPE_DATETIME
+#define FIELD_TYPE_YEAR MYSQL_TYPE_YEAR
+#define FIELD_TYPE_NEWDATE MYSQL_TYPE_NEWDATE
+#define FIELD_TYPE_ENUM MYSQL_TYPE_ENUM
+#define FIELD_TYPE_SET MYSQL_TYPE_SET
+#define FIELD_TYPE_TINY_BLOB MYSQL_TYPE_TINY_BLOB
+#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB
+#define FIELD_TYPE_LONG_BLOB MYSQL_TYPE_LONG_BLOB
+#define FIELD_TYPE_BLOB MYSQL_TYPE_BLOB
+#define FIELD_TYPE_VAR_STRING MYSQL_TYPE_VAR_STRING
+#define FIELD_TYPE_STRING MYSQL_TYPE_STRING
+#define FIELD_TYPE_CHAR MYSQL_TYPE_TINY
+#define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM
+#define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY
+#define FIELD_TYPE_BIT MYSQL_TYPE_BIT
+
+#define NOT_NULL_FLAG 1
+#define PRI_KEY_FLAG 2
+#define UNIQUE_KEY_FLAG 4
+#define MULTIPLE_KEY_FLAG 8
+#define BLOB_FLAG 16
+#define UNSIGNED_FLAG 32
+#define ZEROFILL_FLAG 64
+#define BINARY_FLAG 128
+#define ENUM_FLAG 256
+#define AUTO_INCREMENT_FLAG 512
+#define TIMESTAMP_FLAG 1024
+#define SET_FLAG 2048
+#define NO_DEFAULT_VALUE_FLAG 4096
+#define ON_UPDATE_NOW_FLAG 8192
+#define PART_KEY_FLAG 16384
+#define GROUP_FLAG 32768
+#define NUM_FLAG 32768
+
+#define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG)
+#define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG)
+#define IS_BLOB(n) ((n) & BLOB_FLAG)
+#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL)
+
+
+/* see mysqlnd_charset.c for more information */
+#define MYSQLND_BINARY_CHARSET_NR 63
+
+
+/*
+ /-----> CONN_CLOSE <---------------\
+ | ^ \
+ | | \
+ CONN_READY -> CONN_QUERY_SENT -> CONN_FETCHING_DATA
+ ^ |
+ \-------------------------------------/
+*/
+typedef enum mysqlnd_connection_state
+{
+ CONN_ALLOCED = 0,
+ CONN_READY,
+ CONN_QUERY_SENT,
+ CONN_SENDING_LOAD_DATA,
+ CONN_FETCHING_DATA,
+ CONN_NEXT_RESULT_PENDING,
+ CONN_QUIT_SENT /* object is "destroyed" at this stage */
+} enum_mysqlnd_connection_state;
+
+
+typedef enum mysqlnd_stmt_state
+{
+ MYSQLND_STMT_INITTED = 0,
+ MYSQLND_STMT_PREPARED,
+ MYSQLND_STMT_EXECUTED,
+ MYSQLND_STMT_WAITING_USE_OR_STORE,
+ MYSQLND_STMT_USE_OR_STORE_CALLED,
+ MYSQLND_STMT_USER_FETCHING /* fetch_row_buff or fetch_row_unbuf */
+} enum_mysqlnd_stmt_state;
+
+
+typedef enum param_bind_flags
+{
+ MYSQLND_PARAM_BIND_BLOB_USED = 1
+} enum_param_bind_flags;
+
+
+/* PS */
+enum mysqlnd_stmt_attr
+{
+ STMT_ATTR_UPDATE_MAX_LENGTH,
+ STMT_ATTR_CURSOR_TYPE,
+ STMT_ATTR_PREFETCH_ROWS
+};
+
+enum myslqnd_cursor_type
+{
+ CURSOR_TYPE_NO_CURSOR= 0,
+ CURSOR_TYPE_READ_ONLY= 1,
+ CURSOR_TYPE_FOR_UPDATE= 2,
+ CURSOR_TYPE_SCROLLABLE= 4
+};
+
+typedef enum mysqlnd_connection_close_type
+{
+ MYSQLND_CLOSE_EXPLICIT = 0,
+ MYSQLND_CLOSE_IMPLICIT,
+ MYSQLND_CLOSE_DISCONNECTED,
+ MYSQLND_CLOSE_LAST /* for checking, should always be last */
+} enum_connection_close_type;
+
+
+typedef enum mysqlnd_collected_stats
+{
+ STAT_BYTES_SENT,
+ STAT_BYTES_RECEIVED,
+ STAT_PACKETS_SENT,
+ STAT_PACKETS_RECEIVED,
+ STAT_PROTOCOL_OVERHEAD_IN,
+ STAT_PROTOCOL_OVERHEAD_OUT,
+ STAT_BYTES_RECEIVED_OK,
+ STAT_BYTES_RECEIVED_EOF,
+ STAT_BYTES_RECEIVED_RSET_HEADER,
+ STAT_BYTES_RECEIVED_RSET_FIELD_META,
+ STAT_BYTES_RECEIVED_RSET_ROW,
+ STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
+ STAT_BYTES_RECEIVED_CHANGE_USER,
+ STAT_PACKETS_SENT_CMD,
+ STAT_PACKETS_RECEIVED_OK,
+ STAT_PACKETS_RECEIVED_EOF,
+ STAT_PACKETS_RECEIVED_RSET_HEADER,
+ STAT_PACKETS_RECEIVED_RSET_FIELD_META,
+ STAT_PACKETS_RECEIVED_RSET_ROW,
+ STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
+ STAT_PACKETS_RECEIVED_CHANGE_USER,
+ STAT_RSET_QUERY,
+ STAT_NON_RSET_QUERY,
+ STAT_NO_INDEX_USED,
+ STAT_BAD_INDEX_USED,
+ STAT_QUERY_WAS_SLOW,
+ STAT_BUFFERED_SETS,
+ STAT_UNBUFFERED_SETS,
+ STAT_PS_BUFFERED_SETS,
+ STAT_PS_UNBUFFERED_SETS,
+ STAT_FLUSHED_NORMAL_SETS,
+ STAT_FLUSHED_PS_SETS,
+ STAT_PS_PREPARED_NEVER_EXECUTED,
+ STAT_PS_PREPARED_ONCE_USED,
+ STAT_ROWS_FETCHED_FROM_SERVER_NORMAL,
+ STAT_ROWS_FETCHED_FROM_SERVER_PS,
+ STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
+ STAT_ROWS_BUFFERED_FROM_CLIENT_PS,
+ STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF,
+ STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF,
+ STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF,
+ STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF,
+ STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR,
+ STAT_ROWS_AFFECTED_NORMAL,
+ STAT_ROWS_AFFECTED_PS,
+ STAT_ROWS_SKIPPED_NORMAL,
+ STAT_ROWS_SKIPPED_PS,
+ STAT_COPY_ON_WRITE_SAVED,
+ STAT_COPY_ON_WRITE_PERFORMED,
+ STAT_CMD_BUFFER_TOO_SMALL,
+ STAT_CONNECT_SUCCESS,
+ STAT_CONNECT_FAILURE,
+ STAT_CONNECT_REUSED,
+ STAT_RECONNECT,
+ STAT_PCONNECT_SUCCESS,
+ STAT_OPENED_CONNECTIONS,
+ STAT_OPENED_PERSISTENT_CONNECTIONS,
+ STAT_CLOSE_EXPLICIT,
+ STAT_CLOSE_IMPLICIT,
+ STAT_CLOSE_DISCONNECT,
+ STAT_CLOSE_IN_MIDDLE,
+ STAT_FREE_RESULT_EXPLICIT,
+ STAT_FREE_RESULT_IMPLICIT,
+ STAT_STMT_CLOSE_EXPLICIT,
+ STAT_STMT_CLOSE_IMPLICIT,
+ STAT_MEM_EMALLOC_COUNT,
+ STAT_MEM_EMALLOC_AMOUNT,
+ STAT_MEM_ECALLOC_COUNT,
+ STAT_MEM_ECALLOC_AMOUNT,
+ STAT_MEM_EREALLOC_COUNT,
+ STAT_MEM_EREALLOC_AMOUNT,
+ STAT_MEM_EFREE_COUNT,
+ STAT_MEM_EFREE_AMOUNT,
+ STAT_MEM_MALLOC_COUNT,
+ STAT_MEM_MALLOC_AMOUNT,
+ STAT_MEM_CALLOC_COUNT,
+ STAT_MEM_CALLOC_AMOUNT,
+ STAT_MEM_REALLOC_COUNT,
+ STAT_MEM_REALLOC_AMOUNT,
+ STAT_MEM_FREE_COUNT,
+ STAT_MEM_FREE_AMOUNT,
+ STAT_MEM_ESTRNDUP_COUNT,
+ STAT_MEM_STRNDUP_COUNT,
+ STAT_MEM_ESTRDUP_COUNT,
+ STAT_MEM_STRDUP_COUNT,
+ STAT_TEXT_TYPE_FETCHED_NULL,
+ STAT_TEXT_TYPE_FETCHED_BIT,
+ STAT_TEXT_TYPE_FETCHED_INT8,
+ STAT_TEXT_TYPE_FETCHED_INT16,
+ STAT_TEXT_TYPE_FETCHED_INT24,
+ STAT_TEXT_TYPE_FETCHED_INT32,
+ STAT_TEXT_TYPE_FETCHED_INT64,
+ STAT_TEXT_TYPE_FETCHED_DECIMAL,
+ STAT_TEXT_TYPE_FETCHED_FLOAT,
+ STAT_TEXT_TYPE_FETCHED_DOUBLE,
+ STAT_TEXT_TYPE_FETCHED_DATE,
+ STAT_TEXT_TYPE_FETCHED_YEAR,
+ STAT_TEXT_TYPE_FETCHED_TIME,
+ STAT_TEXT_TYPE_FETCHED_DATETIME,
+ STAT_TEXT_TYPE_FETCHED_TIMESTAMP,
+ STAT_TEXT_TYPE_FETCHED_STRING,
+ STAT_TEXT_TYPE_FETCHED_BLOB,
+ STAT_TEXT_TYPE_FETCHED_ENUM,
+ STAT_TEXT_TYPE_FETCHED_SET,
+ STAT_TEXT_TYPE_FETCHED_GEOMETRY,
+ STAT_TEXT_TYPE_FETCHED_OTHER,
+ STAT_BINARY_TYPE_FETCHED_NULL,
+ STAT_BINARY_TYPE_FETCHED_BIT,
+ STAT_BINARY_TYPE_FETCHED_INT8,
+ STAT_BINARY_TYPE_FETCHED_INT16,
+ STAT_BINARY_TYPE_FETCHED_INT24,
+ STAT_BINARY_TYPE_FETCHED_INT32,
+ STAT_BINARY_TYPE_FETCHED_INT64,
+ STAT_BINARY_TYPE_FETCHED_DECIMAL,
+ STAT_BINARY_TYPE_FETCHED_FLOAT,
+ STAT_BINARY_TYPE_FETCHED_DOUBLE,
+ STAT_BINARY_TYPE_FETCHED_DATE,
+ STAT_BINARY_TYPE_FETCHED_YEAR,
+ STAT_BINARY_TYPE_FETCHED_TIME,
+ STAT_BINARY_TYPE_FETCHED_DATETIME,
+ STAT_BINARY_TYPE_FETCHED_TIMESTAMP,
+ STAT_BINARY_TYPE_FETCHED_STRING,
+ STAT_BINARY_TYPE_FETCHED_BLOB,
+ STAT_BINARY_TYPE_FETCHED_ENUM,
+ STAT_BINARY_TYPE_FETCHED_SET,
+ STAT_BINARY_TYPE_FETCHED_GEOMETRY,
+ STAT_BINARY_TYPE_FETCHED_OTHER,
+ STAT_INIT_COMMAND_EXECUTED_COUNT,
+ STAT_INIT_COMMAND_FAILED_COUNT,
+ STAT_COM_QUIT,
+ STAT_COM_INIT_DB,
+ STAT_COM_QUERY,
+ STAT_COM_FIELD_LIST,
+ STAT_COM_CREATE_DB,
+ STAT_COM_DROP_DB,
+ STAT_COM_REFRESH,
+ STAT_COM_SHUTDOWN,
+ STAT_COM_STATISTICS,
+ STAT_COM_PROCESS_INFO,
+ STAT_COM_CONNECT,
+ STAT_COM_PROCESS_KILL,
+ STAT_COM_DEBUG,
+ STAT_COM_PING,
+ STAT_COM_TIME,
+ STAT_COM_DELAYED_INSERT,
+ STAT_COM_CHANGE_USER,
+ STAT_COM_BINLOG_DUMP,
+ STAT_COM_TABLE_DUMP,
+ STAT_COM_CONNECT_OUT,
+ STAT_COM_REGISTER_SLAVE,
+ STAT_COM_STMT_PREPARE,
+ STAT_COM_STMT_EXECUTE,
+ STAT_COM_STMT_SEND_LONG_DATA,
+ STAT_COM_STMT_CLOSE,
+ STAT_COM_STMT_RESET,
+ STAT_COM_SET_OPTION,
+ STAT_COM_STMT_FETCH,
+ STAT_COM_DAEMON,
+ STAT_BYTES_RECEIVED_PURE_DATA_TEXT,
+ STAT_BYTES_RECEIVED_PURE_DATA_PS,
+ STAT_LAST /* Should be always the last */
+} enum_mysqlnd_collected_stats;
+
+
+/* Enums */
+enum mysqlnd_packet_type
+{
+ PROT_GREET_PACKET= 0,
+ PROT_AUTH_PACKET,
+ PROT_AUTH_RESP_PACKET,
+ PROT_CHANGE_AUTH_RESP_PACKET,
+ PROT_OK_PACKET,
+ PROT_EOF_PACKET,
+ PROT_CMD_PACKET,
+ PROT_RSET_HEADER_PACKET,
+ PROT_RSET_FLD_PACKET,
+ PROT_ROW_PACKET,
+ PROT_STATS_PACKET,
+ PROT_PREPARE_RESP_PACKET,
+ PROT_CHG_USER_RESP_PACKET,
+ PROT_LAST /* should always be last */
+};
+
+
+enum php_mysqlnd_server_command
+{
+ COM_SLEEP = 0,
+ COM_QUIT,
+ COM_INIT_DB,
+ COM_QUERY,
+ COM_FIELD_LIST,
+ COM_CREATE_DB,
+ COM_DROP_DB,
+ COM_REFRESH,
+ COM_SHUTDOWN,
+ COM_STATISTICS,
+ COM_PROCESS_INFO,
+ COM_CONNECT,
+ COM_PROCESS_KILL,
+ COM_DEBUG,
+ COM_PING,
+ COM_TIME = 15,
+ COM_DELAYED_INSERT,
+ COM_CHANGE_USER,
+ COM_BINLOG_DUMP,
+ COM_TABLE_DUMP,
+ COM_CONNECT_OUT = 20,
+ COM_REGISTER_SLAVE,
+ COM_STMT_PREPARE = 22,
+ COM_STMT_EXECUTE = 23,
+ COM_STMT_SEND_LONG_DATA = 24,
+ COM_STMT_CLOSE = 25,
+ COM_STMT_RESET = 26,
+ COM_SET_OPTION = 27,
+ COM_STMT_FETCH = 28,
+ COM_DAEMON,
+ COM_END
+};
+
+
+#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1
+
+#define MYSQLND_REFRESH_GRANT 1 /* Refresh grant tables */
+#define MYSQLND_REFRESH_LOG 2 /* Start on new log file */
+#define MYSQLND_REFRESH_TABLES 4 /* close all tables */
+#define MYSQLND_REFRESH_HOSTS 8 /* Flush host cache */
+#define MYSQLND_REFRESH_STATUS 16 /* Flush status variables */
+#define MYSQLND_REFRESH_THREADS 32 /* Flush thread cache */
+#define MYSQLND_REFRESH_SLAVE 64 /* Reset master info and restart slave */
+#define MYSQLND_REFRESH_MASTER 128 /* Remove all bin logs in the index */
+#define MYSQLND_REFRESH_BACKUP_LOG 0x200000L
+
+#endif /* MYSQLND_ENUM_N_DEF_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c
new file mode 100644
index 0000000..21be3fd
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.c
@@ -0,0 +1,226 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 318221 2011-10-19 15:04:12Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_debug.h"
+
+static struct st_mysqlnd_conn_methods * mysqlnd_conn_methods;
+static struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_methods;
+static struct st_mysqlnd_stmt_methods * mysqlnd_stmt_methods;
+
+/* {{{ _mysqlnd_plugin_get_plugin_connection_data */
+PHPAPI void **
+_mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!conn || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)conn + sizeof(MYSQLND) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_connection_data_data */
+PHPAPI void **
+_mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!conn || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)conn + sizeof(MYSQLND_CONN_DATA) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_result_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!result || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
+PHPAPI void **
+_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_stmt_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_stmt_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!stmt || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)stmt + sizeof(MYSQLND_STMT) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_net_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_net_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!net || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)net + sizeof(MYSQLND_NET) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_conn_get_methods */
+PHPAPI struct st_mysqlnd_conn_methods *
+mysqlnd_conn_get_methods()
+{
+ return mysqlnd_conn_methods;
+}
+/* }}} */
+
+/* {{{ mysqlnd_conn_set_methods */
+PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
+{
+ mysqlnd_conn_methods = methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn_get_methods */
+PHPAPI struct st_mysqlnd_conn_data_methods *
+mysqlnd_conn_data_get_methods()
+{
+ return mysqlnd_conn_data_methods;
+}
+/* }}} */
+
+/* {{{ mysqlnd_conn_set_methods */
+PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods)
+{
+ mysqlnd_conn_data_methods = methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_get_methods */
+PHPAPI struct st_mysqlnd_res_methods *
+mysqlnd_result_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_set_methods */
+PHPAPI void
+mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res) = *methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_get_methods */
+PHPAPI struct st_mysqlnd_stmt_methods *
+mysqlnd_stmt_get_methods()
+{
+ return mysqlnd_stmt_methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_set_methods */
+PHPAPI void
+mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods)
+{
+ mysqlnd_stmt_methods = methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol_get_methods */
+PHPAPI struct st_mysqlnd_protocol_methods *
+mysqlnd_protocol_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol_set_methods */
+PHPAPI void
+mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol) = *methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net_get_methods */
+PHPAPI struct st_mysqlnd_net_methods *
+mysqlnd_net_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net_set_methods */
+PHPAPI void
+mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net) = *methods;
+}
+/* }}} */
+
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h
new file mode 100644
index 0000000..ef9d085
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.h
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+/* $Id: mysqlnd.h 318221 2011-10-19 15:04:12Z andrey $ */
+
+#ifndef MYSQLND_EXT_PLUGIN_H
+#define MYSQLND_EXT_PLUGIN_H
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_connection_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data((c), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data_data((c), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_result_data(r, p_id) _mysqlnd_plugin_get_plugin_result_data((r), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_protocol_data(p, p_id) _mysqlnd_plugin_get_plugin_protocol_data((p), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_net_data(n, p_id) _mysqlnd_plugin_get_plugin_net_data((n), (p_id) TSRMLS_CC)
+
+
+PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods();
+PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods * methods);
+
+PHPAPI struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_get_methods();
+PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods);
+
+PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods();
+PHPAPI void mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods);
+
+PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods();
+PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods * methods);
+
+PHPAPI struct st_mysqlnd_protocol_methods * mysqlnd_protocol_get_methods();
+PHPAPI void mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods);
+
+PHPAPI struct st_mysqlnd_net_methods * mysqlnd_net_get_methods();
+PHPAPI void mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods);
+
+
+#endif /* MYSQLND_EXT_PLUGIN_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h
new file mode 100644
index 0000000..c967fb3
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h
@@ -0,0 +1,135 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+
+*/
+
+#ifndef MYSQLND_LIBMYSQL_COMPAT_H
+#define MYSQLND_LIBMYSQL_COMPAT_H
+
+/* Global types and definitions*/
+#define MYSQL_NO_DATA MYSQLND_NO_DATA
+#define MYSQL_DATA_TRUNCATED MYSQLND_DATA_TRUNCATED
+#define MYSQL_STMT MYSQLND_STMT
+#define MYSQL_FIELD MYSQLND_FIELD
+#define MYSQL_RES MYSQLND_RES
+#define MYSQL_ROW MYSQLND_ROW_C
+#define MYSQL MYSQLND
+#define my_bool zend_bool
+#define my_ulonglong uint64_t
+
+#define MYSQL_VERSION_ID MYSQLND_VERSION_ID
+#define MYSQL_SERVER_VERSION MYSQLND_VERSION
+#define MYSQL_ERRMSG_SIZE MYSQLND_ERRMSG_SIZE
+#define SQLSTATE_LENGTH MYSQLND_SQLSTATE_LENGTH
+
+/* functions */
+#define mysql_affected_rows(r) mysqlnd_affected_rows((r))
+#define mysql_autocommit(r,m) mysqlnd_autocommit((r),(m))
+#define mysql_change_user(r,a,b,c) mysqlnd_change_user((r), (a), (b), (c), FALSE)
+#define mysql_character_set_name(c) mysqlnd_character_set_name((c))
+#define mysql_close(r) mysqlnd_close((r), MYSQLND_CLOSE_EXPLICIT)
+#define mysql_commit(r) mysqlnd_commit((r))
+#define mysql_data_seek(r,o) mysqlnd_data_seek((r),(o))
+#define mysql_debug(x) mysqlnd_debug((x))
+#define mysql_dump_debug_info(r) mysqlnd_dump_debug_info((r))
+#define mysql_errno(r) mysqlnd_errno((r))
+#define mysql_error(r) mysqlnd_error((r))
+#define mysql_escape_string(a,b,c) mysqlnd_escape_string((a), (b), (c))
+#define mysql_fetch_field(r) mysqlnd_fetch_field((r))
+#define mysql_fetch_field_direct(r,o) mysqlnd_fetch_field_direct((r), (o))
+#define mysql_fetch_fields(r) mysqlnd_fetch_fields((r))
+#define mysql_fetch_lengths(r) mysqlnd_fetch_lengths((r))
+#define mysql_fetch_row(r) mysqlnd_fetch_row_c((r))
+#define mysql_field_count(r) mysqlnd_field_count((r))
+#define mysql_field_seek(r,o) mysqlnd_field_seek((r), (o))
+#define mysql_field_tell(r) mysqlnd_field_tell((r))
+#define mysql_init(a) mysqlnd_init((a))
+#define mysql_insert_id(r) mysqlnd_insert_id((r))
+#define mysql_kill(r,n) mysqlnd_kill((r), (n))
+#define mysql_list_dbs(c, wild) mysqlnd_list_dbs((c), (wild))
+#define mysql_list_fields(c, tab, wild) mysqlnd_list_fields((c), (tab), (wild))
+#define mysql_list_processes(c) mysqlnd_list_processes((c))
+#define mysql_list_tables(c, wild) mysqlnd_list_tables((c), (wild))
+#define mysql_more_results(r) mysqlnd_more_results((r))
+#define mysql_next_result(r) mysqlnd_next_result((r))
+#define mysql_num_fields(r) mysqlnd_num_fields((r))
+#define mysql_num_rows(r) mysqlnd_num_rows((r))
+#define mysql_ping(r) mysqlnd_ping((r))
+#define mysql_real_escape_string(r,a,b,c) mysqlnd_real_escape_string((r), (a), (b), (c))
+#define mysql_real_query(r,a,b) mysqlnd_query((r), (a), (b))
+#define mysql_refresh(conn, options) mysqlnd_refresh((conn), (options))
+#define mysql_rollback(r) mysqlnd_rollback((r))
+#define mysql_select_db(r,a) mysqlnd_select_db((r), (a) ,strlen((a)))
+#define mysql_set_server_option(r,o) mysqlnd_set_server_option((r), (o))
+#define mysql_set_character_set(r,a) mysqlnd_set_character_set((r), (a))
+#define mysql_sqlstate(r) mysqlnd_sqlstate((r))
+#define mysql_ssl_set(c,key,cert,ca,capath,cipher) mysqlnd_ssl_set((c), (key), (cert), (ca), (capath), (cipher))
+#define mysql_stmt_affected_rows(s) mysqlnd_stmt_affected_rows((s))
+#define mysql_stmt_field_count(s) mysqlnd_stmt_field_count((s))
+#define mysql_stmt_param_count(s) mysqlnd_stmt_param_count((s))
+#define mysql_stmt_num_rows(s) mysqlnd_stmt_num_rows((s))
+#define mysql_stmt_insert_id(s) mysqlnd_stmt_insert_id((s))
+#define mysql_stmt_close(s) mysqlnd_stmt_close((s))
+#define mysql_stmt_bind_param(s,b) mysqlnd_stmt_bind_param((s), (b))
+#define mysql_stmt_bind_result(s,b) mysqlnd_stmt_bind_result((s), (b))
+#define mysql_stmt_errno(s) mysqlnd_stmt_errno((s))
+#define mysql_stmt_error(s) mysqlnd_stmt_error((s))
+#define mysql_stmt_sqlstate(s) mysqlnd_stmt_sqlstate((s))
+#define mysql_stmt_prepare(s,q,l) mysqlnd_stmt_prepare((s), (q), (l))
+#define mysql_stmt_execute(s) mysqlnd_stmt_execute((s))
+#define mysql_stmt_reset(s) mysqlnd_stmt_reset((s))
+#define mysql_stmt_store_result(s) mysqlnd_stmt_store_result((s))
+#define mysql_stmt_free_result(s) mysqlnd_stmt_free_result((s))
+#define mysql_stmt_data_seek(s,r) mysqlnd_stmt_data_seek((s), (r))
+#define mysql_stmt_send_long_data(s,p,d,l) mysqlnd_stmt_send_long_data((s), (p), (d), (l))
+#define mysql_stmt_attr_get(s,a,v) mysqlnd_stmt_attr_get((s), (a), (v))
+#define mysql_stmt_attr_set(s,a,v) mysqlnd_stmt_attr_set((s), (a), (v))
+#define mysql_stmt_param_metadata(s) mysqlnd_stmt_param_metadata((s))
+#define mysql_stmt_result_metadata(s) mysqlnd_stmt_result_metadata((s))
+#define mysql_stmt_next_result(s) mysqlnd_stmt_next_result((s))
+#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_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))
+#define mysql_use_result(r) mysqlnd_use_result((r))
+#define mysql_async_store_result(r) mysqlnd_async_store_result((r))
+#define mysql_thread_id(r) mysqlnd_thread_id((r))
+#define mysql_get_client_info() mysqlnd_get_client_info()
+#define mysql_get_client_version() mysqlnd_get_client_version()
+#define mysql_get_host_info(r) mysqlnd_get_host_info((r))
+#define mysql_get_proto_info(r) mysqlnd_get_proto_info((r))
+#define mysql_get_server_info(r) mysqlnd_get_server_info((r))
+#define mysql_get_server_version(r) mysqlnd_get_server_version((r))
+#define mysql_warning_count(r) mysqlnd_warning_count((r))
+#define mysql_eof(r) (((r)->unbuf && (r)->unbuf->eof_reached) || (r)->stored_data)
+
+#define REFRESH_GRANT MYSQLND_REFRESH_GRANT
+#define REFRESH_LOG MYSQLND_REFRESH_LOG
+#define REFRESH_TABLES MYSQLND_REFRESH_TABLES
+#define REFRESH_HOSTS MYSQLND_REFRESH_HOSTS
+#define REFRESH_STATUS MYSQLND_REFRESH_STATUS
+#define REFRESH_THREADS MYSQLND_REFRESH_THREADS
+#define REFRESH_SLAVE MYSQLND_REFRESH_SLAVE
+#define REFRESH_MASTER MYSQLND_REFRESH_MASTER
+#define REFRESH_BACKUP_LOG MYSQLND_REFRESH_BACKUP_LOG
+
+#endif /* MYSQLND_LIBMYSQL_COMPAT_H */
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
new file mode 100644
index 0000000..40de45b
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -0,0 +1,249 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "php_globals.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+
+/* {{{ mysqlnd_local_infile_init */
+static
+int mysqlnd_local_infile_init(void ** ptr, char * filename, void ** userdata TSRMLS_DC)
+{
+ MYSQLND_INFILE_INFO *info;
+ php_stream_context *context = NULL;
+
+ DBG_ENTER("mysqlnd_local_infile_init");
+
+ info = ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO)));
+ if (!info) {
+ DBG_RETURN(1);
+ }
+
+ *ptr = info;
+
+ /* check open_basedir */
+ if (PG(open_basedir)) {
+ if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) {
+ strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file");
+ info->error_no = CR_UNKNOWN_ERROR;
+ DBG_RETURN(1);
+ }
+ }
+
+ info->filename = filename;
+ info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context);
+
+ if (info->fd == NULL) {
+ snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename);
+ info->error_no = MYSQLND_EE_FILENOTFOUND;
+ DBG_RETURN(1);
+ }
+
+ DBG_RETURN(0);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_local_infile_read */
+static
+int mysqlnd_local_infile_read(void * ptr, zend_uchar * buf, unsigned int buf_len TSRMLS_DC)
+{
+ MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
+ int count;
+
+ DBG_ENTER("mysqlnd_local_infile_read");
+
+ count = (int)php_stream_read(info->fd, (char *) buf, buf_len);
+
+ if (count < 0) {
+ strcpy(info->error_msg, "Error reading file");
+ info->error_no = CR_UNKNOWN_ERROR;
+ }
+
+ DBG_RETURN(count);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_local_infile_error */
+static
+int mysqlnd_local_infile_error(void * ptr, char *error_buf, unsigned int error_buf_len TSRMLS_DC)
+{
+ MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
+
+ DBG_ENTER("mysqlnd_local_infile_error");
+
+ if (info) {
+ strlcpy(error_buf, info->error_msg, error_buf_len);
+ DBG_INF_FMT("have info, %d", info->error_no);
+ DBG_RETURN(info->error_no);
+ }
+
+ strlcpy(error_buf, "Unknown error", error_buf_len);
+ DBG_INF_FMT("no info, %d", CR_UNKNOWN_ERROR);
+ DBG_RETURN(CR_UNKNOWN_ERROR);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_local_infile_end */
+static
+void mysqlnd_local_infile_end(void * ptr TSRMLS_DC)
+{
+ MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
+
+ if (info) {
+ /* php_stream_close segfaults on NULL */
+ if (info->fd) {
+ php_stream_close(info->fd);
+ info->fd = NULL;
+ }
+ mnd_efree(info);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_local_infile_default */
+PHPAPI void
+mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn)
+{
+ conn->infile.local_infile_init = mysqlnd_local_infile_init;
+ conn->infile.local_infile_read = mysqlnd_local_infile_read;
+ conn->infile.local_infile_error = mysqlnd_local_infile_error;
+ conn->infile.local_infile_end = mysqlnd_local_infile_end;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_set_local_infile_handler */
+PHPAPI void
+mysqlnd_set_local_infile_handler(MYSQLND_CONN_DATA * const conn, const char * const funcname)
+{
+ if (!conn->infile.callback) {
+ MAKE_STD_ZVAL(conn->infile.callback);
+ } else {
+ zval_dtor(conn->infile.callback);
+ }
+ ZVAL_STRING(conn->infile.callback, (char*) funcname, 1);
+}
+/* }}} */
+
+
+static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file";
+
+
+/* {{{ mysqlnd_handle_local_infile */
+enum_func_status
+mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zend_bool * is_warning TSRMLS_DC)
+{
+ zend_uchar *buf = NULL;
+ zend_uchar empty_packet[MYSQLND_HEADER_SIZE];
+ enum_func_status result = FAIL;
+ unsigned int buflen = 4096;
+ void *info = NULL;
+ int bufsize;
+ size_t ret;
+ MYSQLND_INFILE infile;
+ MYSQLND_NET * net = conn->net;
+
+ DBG_ENTER("mysqlnd_handle_local_infile");
+
+ if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden");
+ /* write empty packet to server */
+ ret = net->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info TSRMLS_CC);
+ *is_warning = TRUE;
+ goto infile_error;
+ }
+
+ infile = conn->infile;
+ /* allocate buffer for reading data */
+ buf = (zend_uchar *) mnd_ecalloc(1, buflen);
+
+ *is_warning = FALSE;
+
+ /* init handler: allocate read buffer and open file */
+ if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) {
+ char tmp_buf[sizeof(conn->error_info->error)];
+ int tmp_error_no;
+ *is_warning = TRUE;
+ /* error occurred */
+ tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC);
+ SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ /* write empty packet to server */
+ ret = net->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info TSRMLS_CC);
+ goto infile_error;
+ }
+
+ /* read data */
+ while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) {
+ if ((ret = net->m.send_ex(net, buf, bufsize, conn->stats, conn->error_info TSRMLS_CC)) == 0) {
+ DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ goto infile_error;
+ }
+ }
+
+ /* send empty packet for eof */
+ if ((ret = net->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info TSRMLS_CC)) == 0) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ goto infile_error;
+ }
+
+ /* error during read occurred */
+ if (bufsize < 0) {
+ char tmp_buf[sizeof(conn->error_info->error)];
+ int tmp_error_no;
+ *is_warning = TRUE;
+ DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf) TSRMLS_CC);
+ SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ goto infile_error;
+ }
+
+ result = PASS;
+
+infile_error:
+ /* get response from server and update upsert values */
+ if (FAIL == conn->m->simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY, FALSE TSRMLS_CC)) {
+ result = FAIL;
+ }
+
+ (*conn->infile.local_infile_end)(info TSRMLS_CC);
+ if (buf) {
+ mnd_efree(buf);
+ }
+ DBG_INF_FMT("%s", result == PASS? "PASS":"FAIL");
+ DBG_RETURN(result);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c
new file mode 100644
index 0000000..21241b3
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_net.c
@@ -0,0 +1,1069 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd_ps.c 316906 2011-09-17 10:24:18Z pajoye $ */
+#include "php.h"
+#include "php_globals.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
+#include "php_network.h"
+#include "zend_ini.h"
+#ifdef MYSQLND_COMPRESSION_ENABLED
+#include <zlib.h>
+#endif
+
+#ifndef PHP_WIN32
+#include <netinet/tcp.h>
+#else
+#include <winsock.h>
+#endif
+
+
+/* {{{ mysqlnd_set_sock_no_delay */
+static int
+mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)
+{
+
+ int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
+ int ret = SUCCESS;
+ int flag = 1;
+ int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+
+ DBG_ENTER("mysqlnd_set_sock_no_delay");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::network_read_ex */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ enum_func_status return_value = PASS;
+ size_t to_read = count, ret;
+ size_t old_chunk_size = net->stream->chunk_size;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_net::network_read_ex");
+ DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
+
+ net->stream->chunk_size = MIN(to_read, net->options.net_read_buffer_size);
+ while (to_read) {
+ if (!(ret = php_stream_read(net->stream, (char *) p, to_read))) {
+ DBG_ERR_FMT("Error while reading header from socket");
+ return_value = FAIL;
+ break;
+ }
+ p += ret;
+ to_read -= ret;
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
+ net->stream->chunk_size = old_chunk_size;
+ DBG_RETURN(return_value);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::network_write_ex */
+static size_t
+MYSQLND_METHOD(mysqlnd_net, network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ size_t ret;
+ DBG_ENTER("mysqlnd_net::network_write_ex");
+ ret = php_stream_write(net->stream, (char *)buffer, count);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/* {{{ mysqlnd_net::open_pipe */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
+ const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+#if PHP_API_VERSION < 20100412
+ unsigned int streams_options = ENFORCE_SAFE_MODE;
+#else
+ unsigned int streams_options = 0;
+#endif
+ DBG_ENTER("mysqlnd_net::open_pipe");
+ if (persistent) {
+ streams_options |= STREAM_OPEN_PERSISTENT;
+ }
+ streams_options |= IGNORE_URL;
+ net->stream = php_stream_open_wrapper((char*) scheme + sizeof("pipe://") - 1, "r+", streams_options, NULL);
+ if (!net->stream) {
+ SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
+ DBG_RETURN(FAIL);
+ }
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered yntil the script ends. So, we need to take care of that.
+ */
+ net->stream->in_free = 1;
+ zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id);
+ net->stream->in_free = 0;
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::open_tcp_or_unix */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
+ const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+#if PHP_API_VERSION < 20100412
+ unsigned int streams_options = ENFORCE_SAFE_MODE;
+#else
+ unsigned int streams_options = 0;
+#endif
+ unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
+ char * hashed_details = NULL;
+ int hashed_details_len = 0;
+ char * errstr = NULL;
+ int errcode = 0;
+ struct timeval tv;
+
+ DBG_ENTER("mysqlnd_net::open_tcp_or_unix");
+
+ if (persistent) {
+ hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", net);
+ DBG_INF_FMT("hashed_details=%s", hashed_details);
+ }
+
+ if (net->options.timeout_connect) {
+ tv.tv_sec = net->options.timeout_connect;
+ tv.tv_usec = 0;
+ }
+
+ DBG_INF_FMT("calling php_stream_xport_create");
+ net->stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags,
+ hashed_details, (net->options.timeout_connect) ? &tv : NULL,
+ NULL /*ctx*/, &errstr, &errcode);
+ if (errstr || !net->stream) {
+ DBG_ERR("Error");
+ if (hashed_details) {
+ mnd_sprintf_free(hashed_details);
+ }
+ errcode = CR_CONNECTION_ERROR;
+ SET_CLIENT_ERROR(*error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
+ if (errstr) {
+ /* no mnd_ since we don't allocate it */
+ efree(errstr);
+ }
+ DBG_RETURN(FAIL);
+ }
+ if (hashed_details) {
+ /*
+ If persistent, the streams register it in EG(persistent_list).
+ This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
+ whatever they have to.
+ */
+ zend_rsrc_list_entry *le;
+
+ if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, (void*) &le) == SUCCESS) {
+ /*
+ in_free will let streams code skip destructing - big HACK,
+ but STREAMS suck big time regarding persistent streams.
+ Just not compatible for extensions that need persistency.
+ */
+ net->stream->in_free = 1;
+ zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1);
+ net->stream->in_free = 0;
+ }
+#if ZEND_DEBUG
+ /* Shut-up the streams, they don't know what they are doing */
+ net->stream->__exposed = 1;
+#endif
+ mnd_sprintf_free(hashed_details);
+ }
+
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered yntil the script ends. So, we need to take care of that.
+ */
+ net->stream->in_free = 1;
+ zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id);
+ net->stream->in_free = 0;
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::connect_ex */
+static void
+MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt)(MYSQLND_NET * const net,
+ const char * const scheme, const size_t scheme_len,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net::post_connect_set_opt");
+ if (net->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
+ tv.tv_sec = net->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ if (!memcmp(scheme, "tcp://", sizeof("tcp://") - 1)) {
+ /* TCP -> Set TCP_NODELAY */
+ mysqlnd_set_sock_no_delay(net->stream TSRMLS_CC);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::connect_ex */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
+ const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ func_mysqlnd_net__open_stream open_stream = NULL;
+ DBG_ENTER("mysqlnd_net::connect_ex");
+
+ net->packet_no = net->compressed_envelope_packet_no = 0;
+
+ net->m.close_stream(net, conn_stats, error_info TSRMLS_CC);
+
+ open_stream = (scheme_len > (sizeof("pipe://") - 1) && !memcmp(scheme, "pipe://", sizeof("pipe://") - 1))? net->m.open_pipe:
+ net->m.open_tcp_or_unix;
+
+ if (PASS == (ret = open_stream(net, scheme, scheme_len, persistent, conn_stats, error_info TSRMLS_CC))) {
+ net->m.post_connect_set_opt(net, scheme, scheme_len, conn_stats, error_info TSRMLS_CC);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
+#define COPY_HEADER(T,A) do { \
+ *(((char *)(T))) = *(((char *)(A)));\
+ *(((char *)(T))+1) = *(((char *)(A))+1);\
+ *(((char *)(T))+2) = *(((char *)(A))+2);\
+ *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
+#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer))
+#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
+
+
+/* {{{ mysqlnd_net::send_ex */
+/*
+ IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
+ This is done for performance reasons in the caller of this function.
+ Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
+ Neither are quick, thus the clients of this function are obligated to do
+ what they are asked for.
+
+ `count` is actually the length of the payload data. Thus :
+ count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
+*/
+static size_t
+MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
+ zend_uchar * safe_storage = safe_buf;
+ size_t bytes_sent, packets_sent = 1;
+ size_t left = count;
+ zend_uchar * p = (zend_uchar *) buffer;
+ zend_uchar * compress_buf = NULL;
+ size_t to_be_sent;
+
+ DBG_ENTER("mysqlnd_net::send_ex");
+ DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->compressed);
+
+ if (net->compressed == TRUE) {
+ size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
+ compress_buf = mnd_emalloc(comp_buf_size);
+ }
+
+ do {
+ to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (net->compressed == TRUE) {
+ /* here we need to compress the data and then write it, first comes the compressed header */
+ size_t tmp_complen = to_be_sent;
+ size_t payload_size;
+ zend_uchar * uncompressed_payload = p; /* should include the header */
+
+ STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
+ int3store(uncompressed_payload, to_be_sent);
+ int1store(uncompressed_payload + 3, net->packet_no);
+ if (PASS == net->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
+ uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC))
+ {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = tmp_complen;
+ } else {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
+ memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
+ }
+ RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
+
+ int3store(compress_buf, payload_size);
+ int1store(compress_buf + 3, net->packet_no);
+ DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
+ bytes_sent = net->m.network_write_ex(net, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE,
+ conn_stats, error_info TSRMLS_CC);
+ net->compressed_envelope_packet_no++;
+ #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
+ if (res == Z_OK) {
+ size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
+ zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
+ int error = net->m.decode(decompressed_data, decompressed_size,
+ compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
+ if (error == Z_OK) {
+ int i;
+ DBG_INF("success decompressing");
+ for (i = 0 ; i < decompressed_size; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ }
+ } else {
+ DBG_INF("error decompressing");
+ }
+ mnd_free(decompressed_data);
+ }
+ #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
+ } else
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ {
+ DBG_INF("no compression");
+ STORE_HEADER_SIZE(safe_storage, p);
+ int3store(p, to_be_sent);
+ int1store(p + 3, net->packet_no);
+ bytes_sent = net->m.network_write_ex(net, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC);
+ RESTORE_HEADER_SIZE(p, safe_storage);
+ net->compressed_envelope_packet_no++;
+ }
+ net->packet_no++;
+
+ p += to_be_sent;
+ left -= to_be_sent;
+ packets_sent++;
+ /*
+ if left is 0 then there is nothing more to send, but if the last packet was exactly
+ with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
+ empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
+ indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
+ packet will be sent and this loop will end.
+ */
+ } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
+
+ DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
+ STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PACKETS_SENT, packets_sent);
+
+ if (compress_buf) {
+ mnd_efree(compress_buf);
+ }
+
+ /* Even for zero size payload we have to send a packet */
+ if (!bytes_sent) {
+ DBG_ERR_FMT("Can't %u send bytes", count);
+ SET_CLIENT_ERROR(*error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ }
+ DBG_RETURN(bytes_sent);
+}
+/* }}} */
+
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+/* {{{ php_mysqlnd_read_buffer_is_empty */
+static zend_bool
+php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
+{
+ return buffer->len? FALSE:TRUE;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_read_buffer_read */
+static void
+php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
+{
+ if (buffer->len >= count) {
+ memcpy(dest, buffer->data + buffer->offset, count);
+ buffer->offset += count;
+ buffer->len -= count;
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_read_buffer_bytes_left */
+static size_t
+php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
+{
+ return buffer->len;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_read_buffer_free */
+static void
+php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
+{
+ DBG_ENTER("php_mysqlnd_read_buffer_free");
+ if (*buffer) {
+ mnd_efree((*buffer)->data);
+ mnd_efree(*buffer);
+ *buffer = NULL;
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_create_read_buffer */
+static MYSQLND_READ_BUFFER *
+mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
+{
+ MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
+ DBG_ENTER("mysqlnd_create_read_buffer");
+ ret->is_empty = php_mysqlnd_read_buffer_is_empty;
+ ret->read = php_mysqlnd_read_buffer_read;
+ ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
+ ret->free_buffer = php_mysqlnd_read_buffer_free;
+ ret->data = mnd_emalloc(count);
+ ret->size = ret->len = count;
+ ret->offset = 0;
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer)
+ (MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
+{
+ size_t decompressed_size;
+ enum_func_status ret = PASS;
+ zend_uchar * compressed_data = NULL;
+ zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
+ DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffe");
+
+ /* Read the compressed header */
+ if (FAIL == net->m.network_read_ex(net, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) {
+ DBG_RETURN(FAIL);
+ }
+ decompressed_size = uint3korr(comp_header);
+
+ /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
+ /* we need to decompress the data */
+
+ if (decompressed_size) {
+ compressed_data = mnd_emalloc(net_payload_size);
+ if (FAIL == net->m.network_read_ex(net, compressed_data, net_payload_size, conn_stats, error_info TSRMLS_CC)) {
+ ret = FAIL;
+ goto end;
+ }
+ net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
+ ret = net->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size TSRMLS_CC);
+ if (ret == FAIL) {
+ goto end;
+ }
+ } else {
+ DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
+ net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
+ if (FAIL == net->m.network_read_ex(net, net->uncompressed_data->data, net_payload_size, conn_stats, error_info TSRMLS_CC)) {
+ ret = FAIL;
+ goto end;
+ }
+ }
+end:
+ if (compressed_data) {
+ mnd_efree(compressed_data);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+
+
+/* {{{ mysqlnd_net::decode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
+ const zend_uchar * const compressed_data, const size_t compressed_data_len TSRMLS_DC)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = uncompressed_data_len;
+ DBG_ENTER("mysqlnd_net::decode");
+ error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
+
+ DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
+ if (error != Z_OK) {
+ DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ }
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_net::decode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::encode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
+ const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len TSRMLS_DC)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = *compress_buffer_len;
+ DBG_ENTER("mysqlnd_net::encode");
+ error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
+
+ if (error != Z_OK) {
+ DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ } else {
+ *compress_buffer_len = tmp_complen;
+ DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
+ }
+
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_net::encode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::receive_ex */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ size_t to_read = count;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_net::receive_ex");
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (net->compressed) {
+ if (net->uncompressed_data) {
+ size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
+ DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer);
+ if (to_read_from_buffer) {
+ net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
+ p += to_read_from_buffer;
+ to_read -= to_read_from_buffer;
+ }
+ DBG_INF_FMT("left %u to read", to_read);
+ if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
+ /* Everything was consumed. This should never happen here, but for security */
+ net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
+ }
+ }
+ if (to_read) {
+ zend_uchar net_header[MYSQLND_HEADER_SIZE];
+ size_t net_payload_size;
+ zend_uchar packet_no;
+
+ if (FAIL == net->m.network_read_ex(net, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) {
+ DBG_RETURN(FAIL);
+ }
+ net_payload_size = uint3korr(net_header);
+ packet_no = uint1korr(net_header + 3);
+ if (net->compressed_envelope_packet_no != packet_no) {
+ DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ net->compressed_envelope_packet_no, packet_no, net_payload_size);
+
+ php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ net->compressed_envelope_packet_no, packet_no, net_payload_size);
+ DBG_RETURN(FAIL);
+ }
+ net->compressed_envelope_packet_no++;
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (unsigned long) net_payload_size);
+#endif
+ /* Now let's read from the wire, decompress it and fill the read buffer */
+ net->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, net_payload_size, conn_stats, error_info TSRMLS_CC);
+
+ /*
+ Now a bit of recursion - read from the read buffer,
+ if the data which we have just read from the wire
+ is not enough, then the recursive call will try to
+ satisfy it until it is satisfied.
+ */
+ DBG_RETURN(net->m.receive_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC));
+ }
+ DBG_RETURN(PASS);
+ }
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ DBG_RETURN(net->m.network_read_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mysqlnd_option option, const char * const value TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net::set_client_option");
+ DBG_INF_FMT("option=%u", option);
+ switch (option) {
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
+ if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
+ DBG_RETURN(FAIL);
+ }
+ net->cmd_buffer.length = *(unsigned int*) value;
+ DBG_INF_FMT("new_length=%u", net->cmd_buffer.length);
+ if (!net->cmd_buffer.buffer) {
+ net->cmd_buffer.buffer = mnd_pemalloc(net->cmd_buffer.length, net->persistent);
+ } else {
+ net->cmd_buffer.buffer = mnd_perealloc(net->cmd_buffer.buffer, net->cmd_buffer.length, net->persistent);
+ }
+ break;
+ case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
+ net->options.net_read_buffer_size = *(unsigned int*) value;
+ DBG_INF_FMT("new_length=%u", net->options.net_read_buffer_size);
+ break;
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
+ net->options.timeout_connect = *(unsigned int*) value;
+ break;
+ case MYSQLND_OPT_SSL_KEY:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_key) {
+ mnd_pefree(net->options.ssl_key, pers);
+ }
+ net->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CERT:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_cert) {
+ mnd_pefree(net->options.ssl_cert, pers);
+ }
+ net->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CA:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_ca) {
+ mnd_pefree(net->options.ssl_ca, pers);
+ }
+ net->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CAPATH:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_capath) {
+ mnd_pefree(net->options.ssl_capath, pers);
+ }
+ net->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CIPHER:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_cipher) {
+ mnd_pefree(net->options.ssl_cipher, pers);
+ }
+ net->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_PASSPHRASE:
+ {
+ zend_bool pers = net->persistent;
+ if (net->options.ssl_passphrase) {
+ mnd_pefree(net->options.ssl_passphrase, pers);
+ }
+ net->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE;
+ break;
+ case MYSQL_OPT_READ_TIMEOUT:
+ net->options.timeout_read = *(unsigned int*) value;
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ net->options.timeout_write = *(unsigned int*) value;
+ break;
+#endif
+ case MYSQL_OPT_COMPRESS:
+ net->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION;
+ break;
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+/* {{{ mysqlnd_net::consume_uneaten_data */
+size_t
+MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC)
+{
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ /*
+ Switch to non-blocking mode and try to consume something from
+ the line, if possible, then continue. This saves us from looking for
+ the actuall place where out-of-order packets have been sent.
+ If someone is completely sure that everything is fine, he can switch it
+ off.
+ */
+ char tmp_buf[256];
+ size_t skipped_bytes = 0;
+ int opt = PHP_STREAM_OPTION_BLOCKING;
+ int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC);
+
+ DBG_ENTER("mysqlnd_net::consume_uneaten_data");
+
+ if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
+ /* Do a read of 1 byte */
+ int bytes_consumed;
+
+ do {
+ skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf)));
+ } while (bytes_consumed == sizeof(tmp_buf));
+
+ if (was_blocked) {
+ net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
+ "consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ }
+ }
+ net->last_command = cmd;
+
+ DBG_RETURN(skipped_bytes);
+#else
+ return 0;
+#endif
+}
+/* }}} */
+
+/*
+ in libmyusql, if cert and !key then key=cert
+*/
+/* {{{ mysqlnd_net::enable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
+{
+#ifdef MYSQLND_SSL_SUPPORTED
+ php_stream_context *context = php_stream_context_alloc(TSRMLS_C);
+ DBG_ENTER("mysqlnd_net::enable_ssl");
+ if (!context) {
+ DBG_RETURN(FAIL);
+ }
+
+ if (net->options.ssl_key) {
+ zval key_zval;
+ ZVAL_STRING(&key_zval, net->options.ssl_key, 0);
+ php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
+ }
+ if (net->options.ssl_verify_peer) {
+ zval verify_peer_zval;
+ ZVAL_TRUE(&verify_peer_zval);
+ php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
+ }
+ if (net->options.ssl_cert) {
+ zval cert_zval;
+ ZVAL_STRING(&cert_zval, net->options.ssl_cert, 0);
+ php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
+ if (!net->options.ssl_key) {
+ php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
+ }
+ }
+ if (net->options.ssl_ca) {
+ zval cafile_zval;
+ ZVAL_STRING(&cafile_zval, net->options.ssl_ca, 0);
+ php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
+ }
+ if (net->options.ssl_capath) {
+ zval capath_zval;
+ ZVAL_STRING(&capath_zval, net->options.ssl_capath, 0);
+ php_stream_context_set_option(context, "ssl", "cafile", &capath_zval);
+ }
+ if (net->options.ssl_passphrase) {
+ zval passphrase_zval;
+ ZVAL_STRING(&passphrase_zval, net->options.ssl_passphrase, 0);
+ php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
+ }
+ if (net->options.ssl_cipher) {
+ zval cipher_zval;
+ ZVAL_STRING(&cipher_zval, net->options.ssl_cipher, 0);
+ php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
+ }
+ php_stream_context_set(net->stream, context);
+ if (php_stream_xport_crypto_setup(net->stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0 ||
+ php_stream_xport_crypto_enable(net->stream, 1 TSRMLS_CC) < 0)
+ {
+ DBG_ERR("Cannot connect to MySQL by using SSL");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot connect to MySQL by using SSL");
+ DBG_RETURN(FAIL);
+ }
+ /*
+ get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
+ then the context would not survive cleaning of EG(regular_list), where it is registered, as a
+ resource. What happens is that after this destruction any use of the network will mean usage
+ of the context, which means usage of already freed memory, bad. Actually we don't need this
+ context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
+ */
+ php_stream_context_set(net->stream, NULL);
+
+ if (net->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
+ tv.tv_sec = net->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ DBG_RETURN(PASS);
+#else
+ DBG_ENTER("mysqlnd_net::enable_ssl");
+ DBG_RETURN(PASS);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::disable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net::disable_ssl");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC)
+{
+ zend_bool pers = net->persistent;
+ DBG_ENTER("mysqlnd_net::free_contents");
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (net->uncompressed_data) {
+ net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
+ }
+#endif
+ if (net->options.ssl_key) {
+ mnd_pefree(net->options.ssl_key, pers);
+ net->options.ssl_key = NULL;
+ }
+ if (net->options.ssl_cert) {
+ mnd_pefree(net->options.ssl_cert, pers);
+ net->options.ssl_cert = NULL;
+ }
+ if (net->options.ssl_ca) {
+ mnd_pefree(net->options.ssl_ca, pers);
+ net->options.ssl_ca = NULL;
+ }
+ if (net->options.ssl_capath) {
+ mnd_pefree(net->options.ssl_capath, pers);
+ net->options.ssl_capath = NULL;
+ }
+ if (net->options.ssl_cipher) {
+ mnd_pefree(net->options.ssl_cipher, pers);
+ net->options.ssl_cipher = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::close_stream */
+static void
+MYSQLND_METHOD(mysqlnd_net, close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net::close_stream");
+ if (net && net->stream) {
+ zend_bool pers = net->persistent;
+ DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract);
+ if (pers) {
+ if (EG(active)) {
+ php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
+ } else {
+ /*
+ otherwise we will crash because the EG(persistent_list) has been freed already,
+ before the modules are shut down
+ */
+ php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
+ }
+ } else {
+ php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE);
+ }
+ net->stream = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ unsigned int buf_size;
+ DBG_ENTER("mysqlnd_net::init");
+
+ buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
+ net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC);
+
+ buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
+ net->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC);
+
+ buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
+ net->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size TSRMLS_CC);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net::dtor */
+static void
+MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net::dtor");
+ if (net) {
+ zend_bool pers = net->persistent;
+
+ net->m.free_contents(net TSRMLS_CC);
+ net->m.close_stream(net, stats, error_info TSRMLS_CC);
+ if (net->cmd_buffer.buffer) {
+ DBG_INF("Freeing cmd buffer");
+ mnd_pefree(net->cmd_buffer.buffer, pers);
+ net->cmd_buffer.buffer = NULL;
+ }
+ mnd_pefree(net, pers);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_net)
+ MYSQLND_METHOD(mysqlnd_net, init),
+ MYSQLND_METHOD(mysqlnd_net, dtor),
+ MYSQLND_METHOD(mysqlnd_net, connect_ex),
+ MYSQLND_METHOD(mysqlnd_net, close_stream),
+ MYSQLND_METHOD(mysqlnd_net, open_pipe),
+ MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix),
+ NULL, /* unused 1 */
+ NULL, /* unused 2 */
+ MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt),
+ MYSQLND_METHOD(mysqlnd_net, set_client_option),
+ MYSQLND_METHOD(mysqlnd_net, decode),
+ MYSQLND_METHOD(mysqlnd_net, encode),
+ MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data),
+ MYSQLND_METHOD(mysqlnd_net, free_contents),
+ MYSQLND_METHOD(mysqlnd_net, enable_ssl),
+ MYSQLND_METHOD(mysqlnd_net, disable_ssl),
+ MYSQLND_METHOD(mysqlnd_net, network_read_ex),
+ MYSQLND_METHOD(mysqlnd_net, network_write_ex),
+ MYSQLND_METHOD(mysqlnd_net, send_ex),
+ MYSQLND_METHOD(mysqlnd_net, receive_ex),
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer)
+#else
+ NULL
+#endif
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_net_init */
+PHPAPI MYSQLND_NET *
+mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
+{
+ MYSQLND_NET * net;
+ DBG_ENTER("mysqlnd_net_init");
+ net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_io_channel(persistent, stats, error_info TSRMLS_CC);
+ DBG_RETURN(net);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_net_free */
+PHPAPI void
+mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_net_free");
+ if (net) {
+ net->m.dtor(net, stats, error_info TSRMLS_CC);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_net.h b/ext/mysqlnd/mysqlnd_net.h
new file mode 100644
index 0000000..568a0e1
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_net.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd_wireprotocol.h 291983 2009-12-11 11:58:57Z andrey $ */
+
+#ifndef MYSQLND_NET_H
+#define MYSQLND_NET_H
+
+PHPAPI MYSQLND_NET * mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC);
+PHPAPI void mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC);
+
+#endif /* MYSQLND_NET_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_plugin.c b/ext/mysqlnd/mysqlnd_plugin.c
new file mode 100644
index 0000000..f32fe4a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_plugin.c
@@ -0,0 +1,209 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 306407 2010-12-16 12:56:19Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+
+/*--------------------------------------------------------------------*/
+
+static enum_func_status mysqlnd_example_plugin_end(void * p TSRMLS_DC);
+
+static MYSQLND_STATS * mysqlnd_plugin_example_stats = NULL;
+
+enum mysqlnd_plugin_example_collected_stats
+{
+ EXAMPLE_STAT1,
+ EXAMPLE_STAT2,
+ EXAMPLE_STAT_LAST
+};
+
+static const MYSQLND_STRING mysqlnd_plugin_example_stats_values_names[EXAMPLE_STAT_LAST] =
+{
+ { MYSQLND_STR_W_LEN("stat1") },
+ { MYSQLND_STR_W_LEN("stat2") }
+};
+
+static struct st_mysqlnd_typeii_plugin_example mysqlnd_example_plugin =
+{
+ {
+ MYSQLND_PLUGIN_API_VERSION,
+ "example",
+ 10001L,
+ "1.00.01",
+ "PHP License",
+ "Andrey Hristov <andrey@php.net>",
+ {
+ NULL, /* will be filled later */
+ mysqlnd_plugin_example_stats_values_names,
+ },
+ {
+ mysqlnd_example_plugin_end
+ }
+ },
+ NULL, /* methods */
+};
+
+
+/* {{{ mysqlnd_example_plugin_end */
+static
+enum_func_status mysqlnd_example_plugin_end(void * p TSRMLS_DC)
+{
+ struct st_mysqlnd_typeii_plugin_example * plugin = (struct st_mysqlnd_typeii_plugin_example *) p;
+ DBG_ENTER("mysqlnd_example_plugin_end");
+ mysqlnd_stats_end(plugin->plugin_header.plugin_stats.values);
+ plugin->plugin_header.plugin_stats.values = NULL;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_example_plugin_register */
+void
+mysqlnd_example_plugin_register(TSRMLS_D)
+{
+ mysqlnd_stats_init(&mysqlnd_plugin_example_stats, EXAMPLE_STAT_LAST);
+ mysqlnd_example_plugin.plugin_header.plugin_stats.values = mysqlnd_plugin_example_stats;
+ mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_example_plugin TSRMLS_CC);
+}
+/* }}} */
+
+/*--------------------------------------------------------------------*/
+
+static HashTable mysqlnd_registered_plugins;
+
+static unsigned int mysqlnd_plugins_counter = 0;
+
+
+/* {{{ mysqlnd_plugin_subsystem_init */
+void
+mysqlnd_plugin_subsystem_init(TSRMLS_D)
+{
+ zend_hash_init(&mysqlnd_registered_plugins, 4 /* initial hash size */, NULL /* hash_func */, NULL /* dtor */, TRUE /* pers */);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_end_apply_func */
+int
+mysqlnd_plugin_end_apply_func(void *pDest TSRMLS_DC)
+{
+ struct st_mysqlnd_plugin_header * plugin_header = *(struct st_mysqlnd_plugin_header **) pDest;
+ if (plugin_header->m.plugin_shutdown) {
+ plugin_header->m.plugin_shutdown(plugin_header TSRMLS_CC);
+ }
+ return ZEND_HASH_APPLY_REMOVE;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_subsystem_end */
+void
+mysqlnd_plugin_subsystem_end(TSRMLS_D)
+{
+ zend_hash_apply(&mysqlnd_registered_plugins, mysqlnd_plugin_end_apply_func TSRMLS_CC);
+ zend_hash_destroy(&mysqlnd_registered_plugins);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_register */
+PHPAPI unsigned int mysqlnd_plugin_register()
+{
+ TSRMLS_FETCH();
+ return mysqlnd_plugin_register_ex(NULL TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_register_ex */
+PHPAPI unsigned int mysqlnd_plugin_register_ex(struct st_mysqlnd_plugin_header * plugin TSRMLS_DC)
+{
+ if (plugin) {
+ if (plugin->plugin_api_version == MYSQLND_PLUGIN_API_VERSION) {
+ zend_hash_update(&mysqlnd_registered_plugins, plugin->plugin_name, strlen(plugin->plugin_name) + 1, &plugin, sizeof(void *), NULL);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Plugin API version mismatch while loading plugin %s. Expected %d, got %d",
+ plugin->plugin_name, MYSQLND_PLUGIN_API_VERSION, plugin->plugin_api_version);
+ return 0xCAFE;
+ }
+ }
+ return mysqlnd_plugins_counter++;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_find */
+PHPAPI void * _mysqlnd_plugin_find(const char * const name TSRMLS_DC)
+{
+ void * plugin;
+ if (SUCCESS == zend_hash_find(&mysqlnd_registered_plugins, name, strlen(name) + 1, (void **) &plugin)) {
+ return (void *)*(char **) plugin;
+ }
+ return NULL;
+
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_apply_with_argument */
+PHPAPI void _mysqlnd_plugin_apply_with_argument(apply_func_arg_t apply_func, void * argument TSRMLS_DC)
+{
+ /* Note: We want to be thread-safe (read-only), so we can use neither
+ * zend_hash_apply_with_argument nor zend_hash_internal_pointer_reset and
+ * friends
+ */
+ Bucket *p;
+
+ p = mysqlnd_registered_plugins.pListHead;
+ while (p != NULL) {
+ int result = apply_func(p->pData, argument TSRMLS_CC);
+
+ if (result & ZEND_HASH_APPLY_REMOVE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd_plugin_apply_with_argument must not remove table entries");
+ }
+ p = p->pListNext;
+ if (result & ZEND_HASH_APPLY_STOP) {
+ break;
+ }
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_plugin_count */
+PHPAPI unsigned int mysqlnd_plugin_count()
+{
+ return mysqlnd_plugins_counter;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_portability.h b/ext/mysqlnd/mysqlnd_portability.h
new file mode 100644
index 0000000..b947915
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_portability.h
@@ -0,0 +1,506 @@
+/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
+This file is public domain and comes with NO WARRANTY of any kind */
+
+/*
+ Parts of the original, which are not applicable to mysqlnd have been removed.
+
+ With small modifications, mostly casting but adding few more macros by
+ Andrey Hristov <andrey@mysql.com> . The additions are in the public domain and
+ were added to improve the header file, to get it more consistent.
+*/
+
+#ifndef MYSQLND_PORTABILITY_H
+#define MYSQLND_PORTABILITY_H
+
+
+
+/* Comes from global.h as OFFSET, renamed to STRUCT_OFFSET */
+#define STRUCT_OFFSET(t, f) ((size_t)(char *)&((t *)0)->f)
+
+#ifndef __attribute
+#if !defined(__GNUC__)
+#define __attribute(A)
+#endif
+#endif
+
+#ifdef __CYGWIN__
+/* We use a Unix API, so pretend it's not Windows */
+#undef WIN
+#undef WIN32
+#undef _WIN
+#undef _WIN32
+#undef _WIN64
+#undef __WIN__
+#undef __WIN32__
+#endif /* __CYGWIN__ */
+
+#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
+# include "ext/mysqlnd/config-win.h"
+#else
+# include <ext/mysqlnd/php_mysqlnd_config.h>
+#endif /* _WIN32... */
+
+#if __STDC_VERSION__ < 199901L && !defined(atoll)
+ /* "inline" is a keyword */
+ #define atoll atol
+#endif
+
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#if SIZEOF_LONG_LONG > 4 && !defined(_LONG_LONG)
+#define _LONG_LONG 1 /* For AIX string library */
+#endif
+
+
+/* Go around some bugs in different OS and compilers */
+#if defined(_HPUX_SOURCE) && defined(HAVE_SYS_STREAM_H)
+#include <sys/stream.h> /* HPUX 10.20 defines ulong here. UGLY !!! */
+#define HAVE_ULONG
+#endif
+
+
+#if SIZEOF_LONG_LONG > 4
+#define HAVE_LONG_LONG 1
+#endif
+
+
+/* Typdefs for easyier portability */
+#ifndef HAVE_INT8_T
+#ifndef HAVE_INT8
+typedef signed char int8_t; /* Signed integer >= 8 bits */
+#else
+typedef int8 int8_t; /* Signed integer >= 8 bits */
+#endif
+#endif
+
+#ifndef HAVE_UINT8_T
+#ifndef HAVE_UINT8
+typedef unsigned char uint8_t; /* Unsigned integer >= 8 bits */
+#else
+typedef uint8 uint8_t; /* Signed integer >= 8 bits */
+#endif
+#endif
+
+#ifndef HAVE_INT16_T
+#ifndef HAVE_INT16
+typedef signed short int16_t; /* Signed integer >= 16 bits */
+#else
+typedef int16 int16_t; /* Signed integer >= 16 bits */
+#endif
+#endif
+
+#ifndef HAVE_UINT16_T
+#ifndef HAVE_UINT16
+typedef unsigned short uint16_t; /* Signed integer >= 16 bits */
+#else
+typedef uint16 uint16_t; /* Signed integer >= 16 bits */
+#endif
+#endif
+
+
+#ifndef HAVE_INT32_T
+#ifdef HAVE_INT32
+typedef int32 int32_t;
+#elif SIZEOF_INT == 4
+typedef signed int int32_t;
+#elif SIZEOF_LONG == 4
+typedef signed long int32_t;
+#else
+error "Neither int nor long is of 4 bytes width"
+#endif
+#endif /* HAVE_INT32_T */
+
+#ifndef HAVE_UINT32_T
+#ifdef HAVE_UINT32
+typedef uint32 uint32_t;
+#elif SIZEOF_INT == 4
+typedef unsigned int uint32_t;
+#elif SIZEOF_LONG == 4
+typedef unsigned long uint32_t;
+#else
+#error "Neither int nor long is of 4 bytes width"
+#endif
+#endif /* HAVE_UINT32_T */
+
+#ifndef HAVE_INT64_T
+#ifdef HAVE_INT64
+typedef int64 int64_t;
+#elif SIZEOF_INT == 8
+typedef signed int int64_t;
+#elif SIZEOF_LONG == 8
+typedef signed long int64_t;
+#elif SIZEOF_LONG_LONG == 8
+#ifdef PHP_WIN32
+typedef __int64 int64_t;
+#else
+typedef signed long long int64_t;
+#endif
+#else
+#error "Neither int nor long nor long long is of 8 bytes width"
+#endif
+#endif /* HAVE_INT64_T */
+
+#ifndef HAVE_UINT64_T
+#ifdef HAVE_UINT64
+typedef uint64 uint64_t;
+#elif SIZEOF_INT == 8
+typedef unsigned int uint64_t;
+#elif SIZEOF_LONG == 8
+typedef unsigned long uint64_t;
+#elif SIZEOF_LONG_LONG == 8
+#ifdef PHP_WIN32
+typedef unsigned __int64 uint64_t;
+#else
+typedef unsigned long long uint64_t;
+#endif
+#else
+#error "Neither int nor long nor long long is of 8 bytes width"
+#endif
+#endif /* HAVE_INT64_T */
+
+
+#ifdef PHP_WIN32
+#define MYSQLND_LLU_SPEC "%I64u"
+#define MYSQLND_LL_SPEC "%I64d"
+#define MYSQLND_SZ_T_SPEC "%Id"
+#ifndef L64
+#define L64(x) x##i64
+#endif
+#else
+
+#if __i386__
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
+#if __ia64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __powerpc64__ || __ppc64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if (__powerpc__ || __ppc__ ) && !(__powerpc64__ || __ppc64__)
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
+#if __x86_64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __s390x__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __s390__ && !__s390x__
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
+#ifdef _AIX
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
+#ifndef MYSQLND_LL_SPEC
+ #if SIZEOF_LONG == 8
+ #define MYSQLND_LL_SPEC "%li"
+ #elif SIZEOF_LONG == 4
+ #define MYSQLND_LL_SPEC "%lli"
+ #endif
+#endif
+
+#ifndef MYSQLND_LLU_SPEC
+ #if SIZEOF_LONG == 8
+ #define MYSQLND_LLU_SPEC "%lu"
+ #elif SIZEOF_LONG == 4
+ #define MYSQLND_LLU_SPEC "%llu"
+ #endif
+#endif /* MYSQLND_LLU_SPEC*/
+
+
+#define MYSQLND_SZ_T_SPEC "%zd"
+#ifndef L64
+#define L64(x) x##LL
+#endif
+#endif
+
+
+#define int1store(T,A) do { *((int8_t*) (T)) = (A); } while(0)
+#define uint1korr(A) (*(((uint8_t*)(A))))
+
+/* Bit values are sent in reverted order of bytes, compared to normal !!! */
+#define bit_uint2korr(A) ((uint16_t) (((uint16_t) (((unsigned char*) (A))[1])) +\
+ ((uint16_t) (((unsigned char*) (A))[0]) << 8)))
+#define bit_uint3korr(A) ((uint32_t) (((uint32_t) (((unsigned char*) (A))[2])) +\
+ (((uint32_t) (((unsigned char*) (A))[1])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[0])) << 16)))
+#define bit_uint4korr(A) ((uint32_t) (((uint32_t) (((unsigned char*) (A))[3])) +\
+ (((uint32_t) (((unsigned char*) (A))[2])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[1])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[0])) << 24)))
+#define bit_uint5korr(A) ((uint64_t)(((uint32_t) (((unsigned char*) (A))[4])) +\
+ (((uint32_t) (((unsigned char*) (A))[3])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[2])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[1])) << 24)) +\
+ (((uint64_t) (((unsigned char*) (A))[0])) << 32))
+#define bit_uint6korr(A) ((uint64_t)(((uint32_t) (((unsigned char*) (A))[5])) +\
+ (((uint32_t) (((unsigned char*) (A))[4])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[3])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[2])) << 24)) +\
+ (((uint64_t) (((uint32_t) (((unsigned char*) (A))[1])) +\
+ (((uint32_t) (((unsigned char*) (A))[0]) << 8)))) <<\
+ 32))
+#define bit_uint7korr(A) ((uint64_t)(((uint32_t) (((unsigned char*) (A))[6])) +\
+ (((uint32_t) (((unsigned char*) (A))[5])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[4])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[3])) << 24)) +\
+ (((uint64_t) (((uint32_t) (((unsigned char*) (A))[2])) +\
+ (((uint32_t) (((unsigned char*) (A))[1])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[0])) << 16))) <<\
+ 32))
+#define bit_uint8korr(A) ((uint64_t)(((uint32_t) (((unsigned char*) (A))[7])) +\
+ (((uint32_t) (((unsigned char*) (A))[6])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[5])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[4])) << 24)) +\
+ (((uint64_t) (((uint32_t) (((unsigned char*) (A))[3])) +\
+ (((uint32_t) (((unsigned char*) (A))[2])) << 8) +\
+ (((uint32_t) (((unsigned char*) (A))[1])) << 16) +\
+ (((uint32_t) (((unsigned char*) (A))[0])) << 24))) <<\
+ 32))
+
+
+/*
+** Define-funktions for reading and storing in machine independent format
+** (low byte first)
+*/
+
+/* Optimized store functions for Intel x86, non-valid for WIN64. __i386__ is GCC */
+#if defined(__i386__) && !defined(_WIN64)
+#define sint2korr(A) (*((int16_t *) (A)))
+#define sint3korr(A) ((int32_t) ((((zend_uchar) (A)[2]) & 128) ? \
+ (((uint32_t) 255L << 24) | \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])) : \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])))
+#define sint4korr(A) (*((long *) (A)))
+
+#define uint2korr(A) (*((uint16_t *) (A)))
+#define uint3korr(A) (uint32_t) (((uint32_t) ((zend_uchar) (A)[0])) +\
+ (((uint32_t) ((zend_uchar) (A)[1])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[2])) << 16))
+#define uint4korr(A) (*((unsigned long *) (A)))
+
+
+
+#define uint8korr(A) (*((uint64_t *) (A)))
+#define sint8korr(A) (*((int64_t *) (A)))
+#define int2store(T,A) *((uint16_t*) (T))= (uint16_t) (A)
+#define int3store(T,A) { \
+ *(T)= (zend_uchar) ((A));\
+ *(T+1)=(zend_uchar) (((uint32_t) (A) >> 8));\
+ *(T+2)=(zend_uchar) (((A) >> 16)); }
+#define int4store(T,A) *((long *) (T))= (long) (A)
+#define int5store(T,A) { \
+ *((zend_uchar *)(T))= (zend_uchar)((A));\
+ *(((zend_uchar *)(T))+1)=(zend_uchar) (((A) >> 8));\
+ *(((zend_uchar *)(T))+2)=(zend_uchar) (((A) >> 16));\
+ *(((zend_uchar *)(T))+3)=(zend_uchar) (((A) >> 24)); \
+ *(((zend_uchar *)(T))+4)=(zend_uchar) (((A) >> 32)); }
+
+/* From Andrey Hristov, based on int5store() */
+#define int6store(T,A) { \
+ *(((zend_uchar *)(T)))= (zend_uchar)((A));\
+ *(((zend_uchar *)(T))+1))=(zend_uchar) (((A) >> 8));\
+ *(((zend_uchar *)(T))+2))=(zend_uchar) (((A) >> 16));\
+ *(((zend_uchar *)(T))+3))=(zend_uchar) (((A) >> 24)); \
+ *(((zend_uchar *)(T))+4))=(zend_uchar) (((A) >> 32)); \
+ *(((zend_uchar *)(T))+5))=(zend_uchar) (((A) >> 40)); }
+
+#define int8store(T,A) *((uint64_t *) (T))= (uint64_t) (A)
+
+typedef union {
+ double v;
+ long m[2];
+} float8get_union;
+#define float8get(V,M) { ((float8get_union *)&(V))->m[0] = *((long*) (M)); \
+ ((float8get_union *)&(V))->m[1] = *(((long*) (M))+1); }
+#define float8store(T,V) { *((long *) (T)) = ((float8get_union *)&(V))->m[0]; \
+ *(((long *) (T))+1) = ((float8get_union *)&(V))->m[1]; }
+#define float4get(V,M) { *((float *) &(V)) = *((float*) (M)); }
+/* From Andrey Hristov based on float8get */
+#define floatget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(float))
+#endif /* __i386__ */
+
+
+/* If we haven't defined sint2korr, which is because the platform is not x86 or it's WIN64 */
+#ifndef sint2korr
+#define sint2korr(A) (int16_t) (((int16_t) ((zend_uchar) (A)[0])) +\
+ ((int16_t) ((int16_t) (A)[1]) << 8))
+#define sint3korr(A) ((int32_t) ((((zend_uchar) (A)[2]) & 128) ? \
+ (((uint32_t) 255L << 24) | \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])) : \
+ (((uint32_t) (zend_uchar) (A)[2]) << 16) |\
+ (((uint32_t) (zend_uchar) (A)[1]) << 8) | \
+ ((uint32_t) (zend_uchar) (A)[0])))
+#define sint4korr(A) (int32_t) (((int32_t) ((zend_uchar) (A)[0])) +\
+ (((int32_t) ((zend_uchar) (A)[1]) << 8)) +\
+ (((int32_t) ((zend_uchar) (A)[2]) << 16)) +\
+ (((int32_t) ((int16_t) (A)[3]) << 24)))
+
+#define sint8korr(A) (int64_t) uint8korr(A)
+#define uint2korr(A) (uint16_t) (((uint16_t) ((zend_uchar) (A)[0])) +\
+ ((uint16_t) ((zend_uchar) (A)[1]) << 8))
+#define uint3korr(A) (uint32_t) (((uint32_t) ((zend_uchar) (A)[0])) +\
+ (((uint32_t) ((zend_uchar) (A)[1])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[2])) << 16))
+#define uint4korr(A) (uint32_t) (((uint32_t) ((zend_uchar) (A)[0])) +\
+ (((uint32_t) ((zend_uchar) (A)[1])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[2])) << 16) +\
+ (((uint32_t) ((zend_uchar) (A)[3])) << 24))
+
+#define uint8korr(A) ((uint64_t)(((uint32_t) ((zend_uchar) (A)[0])) +\
+ (((uint32_t) ((zend_uchar) (A)[1])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[2])) << 16) +\
+ (((uint32_t) ((zend_uchar) (A)[3])) << 24)) +\
+ (((uint64_t) (((uint32_t) ((zend_uchar) (A)[4])) +\
+ (((uint32_t) ((zend_uchar) (A)[5])) << 8) +\
+ (((uint32_t) ((zend_uchar) (A)[6])) << 16) +\
+ (((uint32_t) ((zend_uchar) (A)[7])) << 24))) << 32))
+
+
+#define int2store(T,A) do { uint32_t def_temp= (uint32_t) (A) ;\
+ *((zend_uchar*) (T)) = (zend_uchar)(def_temp); \
+ *((zend_uchar*) (T+1)) = (zend_uchar)((def_temp >> 8)); } while (0)
+#define int3store(T,A) do { /*lint -save -e734 */\
+ *(((char *)(T))) = (char) ((A));\
+ *(((char *)(T))+1) = (char) (((A) >> 8));\
+ *(((char *)(T))+2) = (char) (((A) >> 16)); \
+ /*lint -restore */} while (0)
+#define int4store(T,A) do { \
+ *(((char *)(T))) = (char) ((A));\
+ *(((char *)(T))+1) = (char) (((A) >> 8));\
+ *(((char *)(T))+2) = (char) (((A) >> 16));\
+ *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0)
+#define int5store(T,A) do { \
+ *(((char *)(T))) = (char)((A));\
+ *(((char *)(T))+1) = (char)(((A) >> 8));\
+ *(((char *)(T))+2) = (char)(((A) >> 16));\
+ *(((char *)(T))+3) = (char)(((A) >> 24)); \
+ *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0)
+/* Based on int5store() from Andrey Hristov */
+#define int6store(T,A) do { \
+ *(((char *)(T))) = (char)((A));\
+ *(((char *)(T))+1) = (char)(((A) >> 8));\
+ *(((char *)(T))+2) = (char)(((A) >> 16));\
+ *(((char *)(T))+3) = (char)(((A) >> 24)); \
+ *(((char *)(T))+4) = (char)(((A) >> 32)); \
+ *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0)
+#define int8store(T,A) { uint32_t def_temp= (uint32_t) (A), def_temp2= (uint32_t) ((A) >> 32); \
+ int4store((T),def_temp); \
+ int4store((T+4),def_temp2); \
+ }
+#ifdef WORDS_BIGENDIAN
+#define float4get(V,M) do { float def_temp;\
+ ((char*) &def_temp)[0] = (M)[3];\
+ ((char*) &def_temp)[1] = (M)[2];\
+ ((char*) &def_temp)[2] = (M)[1];\
+ ((char*) &def_temp)[3] = (M)[0];\
+ (V)=def_temp; } while (0)
+#define float8store(T,V) do { \
+ *(((char *)(T))) = (char) ((char *) &(V))[7];\
+ *(((char *)(T))+1) = (char) ((char *) &(V))[6];\
+ *(((char *)(T))+2) = (char) ((char *) &(V))[5];\
+ *(((char *)(T))+3) = (char) ((char *) &(V))[4];\
+ *(((char *)(T))+4) = (char) ((char *) &(V))[3];\
+ *(((char *)(T))+5) = (char) ((char *) &(V))[2];\
+ *(((char *)(T))+6) = (char) ((char *) &(V))[1];\
+ *(((char *)(T))+7) = (char) ((char *) &(V))[0]; } while (0)
+
+#define float8get(V,M) do { double def_temp;\
+ ((char*) &def_temp)[0] = (M)[7];\
+ ((char*) &def_temp)[1] = (M)[6];\
+ ((char*) &def_temp)[2] = (M)[5];\
+ ((char*) &def_temp)[3] = (M)[4];\
+ ((char*) &def_temp)[4] = (M)[3];\
+ ((char*) &def_temp)[5] = (M)[2];\
+ ((char*) &def_temp)[6] = (M)[1];\
+ ((char*) &def_temp)[7] = (M)[0];\
+ (V) = def_temp; \
+ } while (0)
+#else
+#define float4get(V,M) memcpy((char*) &(V),(char*) (M),sizeof(float))
+
+#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
+#define float8store(T,V) do { \
+ *(((char *)(T)))= ((char *) &(V))[4];\
+ *(((char *)(T))+1)=(char) ((char *) &(V))[5];\
+ *(((char *)(T))+2)=(char) ((char *) &(V))[6];\
+ *(((char *)(T))+3)=(char) ((char *) &(V))[7];\
+ *(((char *)(T))+4)=(char) ((char *) &(V))[0];\
+ *(((char *)(T))+5)=(char) ((char *) &(V))[1];\
+ *(((char *)(T))+6)=(char) ((char *) &(V))[2];\
+ *(((char *)(T))+7)=(char) ((char *) &(V))[3];} while (0)
+#define float8get(V,M) do { double def_temp;\
+ ((char*) &def_temp)[0]=(M)[4];\
+ ((char*) &def_temp)[1]=(M)[5];\
+ ((char*) &def_temp)[2]=(M)[6];\
+ ((char*) &def_temp)[3]=(M)[7];\
+ ((char*) &def_temp)[4]=(M)[0];\
+ ((char*) &def_temp)[5]=(M)[1];\
+ ((char*) &def_temp)[6]=(M)[2];\
+ ((char*) &def_temp)[7]=(M)[3];\
+ (V) = def_temp; } while (0)
+#endif /* __FLOAT_WORD_ORDER */
+
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* sint2korr */
+/* To here if the platform is not x86 or it's WIN64 */
+
+
+/* Define-funktions for reading and storing in machine format from/to
+ short/long to/from some place in memory V should be a (not
+ register) variable, M is a pointer to byte */
+
+#ifndef float8get
+
+#ifdef WORDS_BIGENDIAN
+#define float8get(V,M) memcpy((char*) &(V),(char*) (M), sizeof(double))
+#define float8store(T,V) memcpy((char*) (T),(char*) &(V), sizeof(double))
+#else
+#define float8get(V,M) memcpy((char*) &(V),(char*) (M),sizeof(double))
+#define float8store(T,V) memcpy((char*) (T),(char*) &(V),sizeof(double))
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* float8get */
+
+#endif /* MYSQLND_PORTABILITY_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
new file mode 100644
index 0000000..190ed69
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -0,0 +1,273 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_PRIV_H
+#define MYSQLND_PRIV_H
+
+#ifndef Z_ADDREF_P
+/* PHP 5.2, old GC */
+#define Z_ADDREF_P(pz) (++(pz)->refcount)
+#define Z_DELREF_P(pz) (--(pz)->refcount)
+#define Z_REFCOUNT_P(pz) ((pz)->refcount)
+#define Z_SET_REFCOUNT_P(pz, rc) ((pz)->refcount = rc)
+#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz))
+#define Z_DELREF_PP(ppz) Z_DELREF_P(*(ppz))
+#endif
+
+#if PHP_MAJOR_VERSION >= 6
+#define MYSQLND_UNICODE 1
+#else
+#define MYSQLND_UNICODE 0
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#ifndef pestrndup
+#define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length)))
+#endif
+
+#if MYSQLND_UNICODE
+#define mysqlnd_array_init(arg, field_count) \
+{ \
+ ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\
+ zend_u_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0, 0);\
+ Z_TYPE_P(arg) = IS_ARRAY;\
+}
+#else
+#define mysqlnd_array_init(arg, field_count) \
+{ \
+ ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));\
+ zend_hash_init(Z_ARRVAL_P(arg), (field_count), NULL, ZVAL_PTR_DTOR, 0); \
+ Z_TYPE_P(arg) = IS_ARRAY;\
+}
+#endif
+
+#define MYSQLND_STR_W_LEN(str) str, (sizeof(str) - 1)
+
+#define MYSQLND_DEBUG_DUMP_TIME 1
+#define MYSQLND_DEBUG_DUMP_TRACE 2
+#define MYSQLND_DEBUG_DUMP_PID 4
+#define MYSQLND_DEBUG_DUMP_LINE 8
+#define MYSQLND_DEBUG_DUMP_FILE 16
+#define MYSQLND_DEBUG_DUMP_LEVEL 32
+#define MYSQLND_DEBUG_APPEND 64
+#define MYSQLND_DEBUG_FLUSH 128
+#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
+#define MYSQLND_DEBUG_PROFILE_CALLS 512
+
+
+/* Client Error codes */
+#define CR_UNKNOWN_ERROR 2000
+#define CR_CONNECTION_ERROR 2002
+#define CR_SERVER_GONE_ERROR 2006
+#define CR_OUT_OF_MEMORY 2008
+#define CR_SERVER_LOST 2013
+#define CR_COMMANDS_OUT_OF_SYNC 2014
+#define CR_CANT_FIND_CHARSET 2019
+#define CR_MALFORMED_PACKET 2027
+#define CR_NOT_IMPLEMENTED 2054
+#define CR_NO_PREPARE_STMT 2030
+#define CR_PARAMS_NOT_BOUND 2031
+#define CR_INVALID_PARAMETER_NO 2034
+#define CR_INVALID_BUFFER_USE 2035
+
+#define MYSQLND_EE_FILENOTFOUND 7890
+
+#define UNKNOWN_SQLSTATE "HY000"
+
+#define MAX_CHARSET_LEN 32
+
+
+#define SET_ERROR_AFF_ROWS(s) (s)->upsert_status->affected_rows = (uint64_t) ~0
+
+/* Error handling */
+#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ } \
+ if ((message)) { \
+ (buf) = mnd_pestrndup((message), (len), (persistent)); \
+ } else { \
+ (buf) = NULL; \
+ } \
+ (buf_len) = (len); \
+ }
+
+#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ (buf) = NULL; \
+ } \
+ (buf_len) = 0; \
+ }
+
+
+#define SET_EMPTY_ERROR(error_info) \
+ { \
+ (error_info).error_no = 0; \
+ (error_info).error[0] = '\0'; \
+ strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \
+ if ((error_info).error_list) { \
+ zend_llist_clean((error_info).error_list); \
+ } \
+ }
+
+
+#define SET_CLIENT_ERROR(error_info, a, b, c) \
+{ \
+ if (0 == (a)) { \
+ SET_EMPTY_ERROR((error_info)); \
+ } else { \
+ (error_info).error_no = (a); \
+ strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \
+ strlcpy((error_info).error, (c), sizeof((error_info).error)); \
+ if ((error_info).error_list) {\
+ MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0}; \
+ \
+ error_for_the_list.error_no = (a); \
+ strlcpy(error_for_the_list.sqlstate, (b), sizeof(error_for_the_list.sqlstate)); \
+ error_for_the_list.error = mnd_pestrdup((c), TRUE); \
+ if (error_for_the_list.error) { \
+ DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error); \
+ zend_llist_add_element((error_info).error_list, &error_for_the_list); \
+ } \
+ } \
+ } \
+}
+
+
+#define COPY_CLIENT_ERROR(error_info_to, error_info_from) \
+ { \
+ SET_CLIENT_ERROR((error_info_to), (error_info_from).error_no, (error_info_from).sqlstate, (error_info_from).error); \
+ }
+
+
+#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
+
+
+#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(*(stmt)->error_info, a, b, c)
+
+#define CONN_GET_STATE(c) (c)->m->get_state((c) TSRMLS_CC)
+#define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s) TSRMLS_CC)
+
+/* PS stuff */
+typedef void (*ps_field_fetch_func)(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool everything_as_unicode TSRMLS_DC);
+struct st_mysqlnd_perm_bind {
+ ps_field_fetch_func func;
+ /* should be signed int */
+ int pack_len;
+ unsigned int php_type;
+ zend_bool is_possibly_blob;
+ zend_bool can_ret_as_str_in_uni;
+};
+
+extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
+
+PHPAPI extern const char * const mysqlnd_old_passwd;
+PHPAPI extern const char * const mysqlnd_out_of_sync;
+PHPAPI extern const char * const mysqlnd_server_gone;
+PHPAPI extern const char * const mysqlnd_out_of_memory;
+
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_object_factory);
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn);
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn_data);
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_res);
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol);
+extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_net);
+
+enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char *filename, zend_bool *is_warning TSRMLS_DC);
+
+
+
+void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
+void _mysqlnd_init_ps_fetch_subsystem();
+
+void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row, zend_bool as_unicode,
+ unsigned int byte_count TSRMLS_DC);
+
+void mysqlnd_plugin_subsystem_init(TSRMLS_D);
+void mysqlnd_plugin_subsystem_end(TSRMLS_D);
+
+void mysqlnd_register_builtin_authentication_plugins(TSRMLS_D);
+
+void mysqlnd_example_plugin_register(TSRMLS_D);
+
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_authentication_plugin;
+
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_OPTIONS * const options,
+ unsigned long mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ TSRMLS_DC);
+
+enum_func_status
+mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
+ const char * const user,
+ const size_t user_len,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const zend_bool silent,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ zend_uchar * auth_plugin_data,
+ size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ TSRMLS_DC);
+
+#endif /* MYSQLND_PRIV_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
new file mode 100644
index 0000000..99bd021
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -0,0 +1,2374 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_result_meta.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_block_alloc.h"
+#include "mysqlnd_ext_plugin.h"
+
+#define MYSQLND_SILENT
+
+
+const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
+const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
+
+/* Exported by mysqlnd_ps_codec.c */
+enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
+
+enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param,
+ unsigned int flags,
+ zend_bool *fetched_anything TSRMLS_DC);
+
+enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
+ unsigned int flags,
+ zend_bool *fetched_anything TSRMLS_DC);
+
+static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
+static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
+
+
+/* {{{ mysqlnd_stmt::store_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret;
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_RES * result;
+
+ DBG_ENTER("mysqlnd_stmt::store_result");
+ if (!stmt || !stmt->conn || !stmt->result) {
+ DBG_RETURN(NULL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ conn = stmt->conn;
+
+ /* be compliant with libmysql - NULL will turn */
+ if (!stmt->field_count) {
+ DBG_RETURN(NULL);
+ }
+
+ if (stmt->cursor_exists) {
+ /* Silently convert buffered to unbuffered, for now */
+ DBG_RETURN(s->m->use_result(s TSRMLS_CC));
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
+ stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
+ {
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_RETURN(NULL);
+ }
+
+ stmt->default_rset_handler = s->m->store_result;
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*conn->error_info);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
+
+ result = stmt->result;
+ result->type = MYSQLND_RES_PS_BUF;
+ result->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
+ result->m.fetch_lengths = NULL;/* makes no sense */
+ result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+
+ result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
+
+ ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE TSRMLS_CC);
+
+ if (PASS == ret) {
+ /* libmysql API docs say it should be so for SELECT statements */
+ stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count;
+
+ stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
+ } else {
+ COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
+ mnd_efree(stmt->result);
+ stmt->result = NULL;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::get_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_stmt::get_result");
+ if (!stmt || !stmt->conn || !stmt->result) {
+ DBG_RETURN(NULL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ conn = stmt->conn;
+
+ /* be compliant with libmysql - NULL will turn */
+ if (!stmt->field_count) {
+ DBG_RETURN(NULL);
+ }
+
+ if (stmt->cursor_exists) {
+ /* Silently convert buffered to unbuffered, for now */
+ DBG_RETURN(s->m->use_result(s TSRMLS_CC));
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_RETURN(NULL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*conn->error_info);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
+
+ do {
+ result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC);
+ if (!result) {
+ SET_OOM_ERROR(*conn->error_info);
+ break;
+ }
+
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
+ if (!result->meta) {
+ SET_OOM_ERROR(*conn->error_info);
+ break;
+ }
+
+ if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
+ stmt->upsert_status->affected_rows = result->stored_data->row_count;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ result->type = MYSQLND_RES_PS_BUF;
+ } else {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ stmt->state = MYSQLND_STMT_PREPARED;
+ break;
+ }
+ DBG_RETURN(result);
+ } while (0);
+
+ if (result) {
+ result->m.free_result(result, TRUE TSRMLS_CC);
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::more_results */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::more_results");
+ /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
+ DBG_RETURN((stmt && stmt->conn && (stmt->conn->m->get_server_status(stmt->conn TSRMLS_CC) & SERVER_MORE_RESULTS_EXISTS))?
+ TRUE:
+ FALSE);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::next_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_CONN_DATA * conn;
+
+ DBG_ENTER("mysqlnd_stmt::next_result");
+ if (!stmt || !stmt->conn || !stmt->result) {
+ DBG_RETURN(FAIL);
+ }
+ conn = stmt->conn;
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS)) {
+ DBG_RETURN(FAIL);
+ }
+
+ DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ /* Free space for next result */
+ s->m->free_stmt_content(s TSRMLS_CC);
+ {
+ enum_func_status ret = s->m->parse_execute_response(s TSRMLS_CC);
+ DBG_RETURN(ret);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_skip_metadata */
+static enum_func_status
+mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ /* Follows parameter metadata, we have just to skip it, as libmysql does */
+ unsigned int i = 0;
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_RES_FIELD * field_packet;
+
+ DBG_ENTER("mysqlnd_stmt_skip_metadata");
+ if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
+ if (!field_packet) {
+ SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(*stmt->conn->error_info);
+ } else {
+ ret = PASS;
+ field_packet->skip_parsing = TRUE;
+ for (;i < stmt->param_count; i++) {
+ if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
+ ret = FAIL;
+ break;
+ }
+ }
+ PACKET_FREE(field_packet);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_read_prepare_response */
+static enum_func_status
+mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_stmt_read_prepare_response");
+ if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
+ if (!prepare_resp) {
+ SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(*stmt->conn->error_info);
+ goto done;
+ }
+
+ if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
+ goto done;
+ }
+
+ if (0xFF == prepare_resp->error_code) {
+ COPY_CLIENT_ERROR(*stmt->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(*stmt->conn->error_info, prepare_resp->error_info);
+ goto done;
+ }
+ ret = PASS;
+ stmt->stmt_id = prepare_resp->stmt_id;
+ stmt->warning_count = stmt->conn->upsert_status->warning_count = prepare_resp->warning_count;
+ stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
+ stmt->param_count = prepare_resp->param_count;
+done:
+ PACKET_FREE(prepare_resp);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_prepare_read_eof */
+static enum_func_status
+mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_PACKET_EOF * fields_eof;
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
+ if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
+ if (!fields_eof) {
+ SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(*stmt->conn->error_info);
+ } else {
+ if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
+ if (stmt->result) {
+ stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
+ mnd_efree(stmt->result);
+ memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
+ stmt->state = MYSQLND_STMT_INITTED;
+ }
+ } else {
+ stmt->upsert_status->server_status = fields_eof->server_status;
+ stmt->upsert_status->warning_count = fields_eof->warning_count;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+ PACKET_FREE(fields_eof);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::prepare */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT * s_to_prepare = s;
+ MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
+
+ DBG_ENTER("mysqlnd_stmt::prepare");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+ DBG_INF_FMT("query=%s", query);
+
+ SET_ERROR_AFF_ROWS(stmt);
+ SET_ERROR_AFF_ROWS(stmt->conn);
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->state > MYSQLND_STMT_INITTED) {
+ /* See if we have to clean the wire */
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ /* Do implicit use_result and then flush the result */
+ stmt->default_rset_handler = s->m->use_result;
+ stmt->default_rset_handler(s TSRMLS_CC);
+ }
+ /* No 'else' here please :) */
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) {
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+ /*
+ Create a new test statement, which we will prepare, but if anything
+ fails, we will scrap it.
+ */
+ s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC);
+ if (!s_to_prepare) {
+ goto fail;
+ }
+ stmt_to_prepare = s_to_prepare->data;
+ }
+
+ if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, (const zend_uchar *) query, query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) ||
+ FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC))
+ {
+ goto fail;
+ }
+
+ if (stmt_to_prepare->param_count) {
+ if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) ||
+ FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
+ {
+ goto fail;
+ }
+ }
+
+ /*
+ Read metadata only if there is actual result set.
+ Beware that SHOW statements bypass the PS framework and thus they send
+ no metadata at prepare.
+ */
+ if (stmt_to_prepare->field_count) {
+ MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC);
+ if (!result) {
+ SET_OOM_ERROR(*stmt->conn->error_info);
+ goto fail;
+ }
+ /* Allocate the result now as it is needed for the reading of metadata */
+ stmt_to_prepare->result = result;
+
+ result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
+
+ result->type = MYSQLND_RES_PS_BUF;
+
+ if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) ||
+ FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
+ {
+ goto fail;
+ }
+ }
+
+ if (stmt_to_prepare != stmt) {
+ /* swap */
+ size_t real_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
+ char * tmp_swap = mnd_malloc(real_size);
+ memcpy(tmp_swap, s, real_size);
+ memcpy(s, s_to_prepare, real_size);
+ memcpy(s_to_prepare, tmp_swap, real_size);
+ mnd_free(tmp_swap);
+ {
+ MYSQLND_STMT_DATA * tmp_swap_data = stmt_to_prepare;
+ stmt_to_prepare = stmt;
+ stmt = tmp_swap_data;
+ }
+ s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+
+fail:
+ if (stmt_to_prepare != stmt && s_to_prepare) {
+ s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
+ }
+ stmt->state = MYSQLND_STMT_INITTED;
+
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_execute_parse_response */
+static enum_func_status
+mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret;
+ MYSQLND_CONN_DATA * conn;
+
+ DBG_ENTER("mysqlnd_stmt_execute_parse_response");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ conn = stmt->conn;
+ CONN_SET_STATE(conn, CONN_QUERY_SENT);
+
+ ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC);
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ stmt->upsert_status->affected_rows = conn->upsert_status->affected_rows;
+ if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
+ /* close the statement here, the connection has been closed */
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ stmt->send_types_to_server = 1;
+ } else {
+ /*
+ stmt->send_types_to_server has already been set to 0 in
+ mysqlnd_stmt_execute_generate_request / mysqlnd_stmt_execute_store_params
+ In case there is a situation in which binding was done for integer and the
+ value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
+ to resend the types. Next execution will also need to resend the type.
+ */
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+ *stmt->upsert_status = *conn->upsert_status; /* copy status */
+ stmt->state = MYSQLND_STMT_EXECUTED;
+ if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+ }
+
+ stmt->result->type = MYSQLND_RES_PS_BUF;
+ if (!stmt->result->conn) {
+ /*
+ For SHOW we don't create (bypasses PS in server)
+ a result set at prepare and thus a connection was missing
+ */
+ stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
+ }
+
+ /* Update stmt->field_count as SHOW sets it to 0 at prepare */
+ stmt->field_count = stmt->result->field_count = conn->field_count;
+ stmt->result->lengths = NULL;
+ if (stmt->field_count) {
+ stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
+ /*
+ We need to set this because the user might not call
+ use_result() or store_result() and we should be able to scrap the
+ data on the line, if he just decides to close the statement.
+ */
+ DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status,
+ stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ if (stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ DBG_INF("cursor exists");
+ stmt->cursor_exists = TRUE;
+ CONN_SET_STATE(conn, CONN_READY);
+ /* Only cursor read */
+ stmt->default_rset_handler = s->m->use_result;
+ DBG_INF("use_result");
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
+ DBG_INF("asked for cursor but got none");
+ /*
+ We have asked for CURSOR but got no cursor, because the condition
+ above is not fulfilled. Then...
+
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+ /* preferred is buffered read */
+ stmt->default_rset_handler = s->m->store_result;
+ DBG_INF("store_result");
+ } else {
+ DBG_INF("no cursor");
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = s->m->use_result;
+ DBG_INF("use_result");
+ }
+ }
+ }
+#ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
+ if (stmt->upsert_status->server_status & SERVER_PS_OUT_PARAMS) {
+ s->m->free_stmt_content(s TSRMLS_CC);
+ DBG_INF("PS OUT Variable RSet, skipping");
+ /* OUT params result set. Skip for now to retain compatibility */
+ ret = mysqlnd_stmt_execute_parse_response(s TSRMLS_CC);
+ }
+#endif
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::execute */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret;
+ MYSQLND_CONN_DATA * conn;
+ zend_uchar *request = NULL;
+ size_t request_len;
+ zend_bool free_request;
+
+ DBG_ENTER("mysqlnd_stmt::execute");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ conn = stmt->conn;
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ SET_ERROR_AFF_ROWS(stmt);
+ SET_ERROR_AFF_ROWS(stmt->conn);
+
+ if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
+ /*
+ We don need to copy the data from the buffers which we will clean.
+ Because it has already been copied. See
+ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ if (stmt->result_bind &&
+ stmt->result_zvals_separated_once == TRUE &&
+ stmt->state >= MYSQLND_STMT_USER_FETCHING)
+ {
+ /*
+ We need to copy the data from the buffers which we will clean.
+ The bound variables point to them only if the user has started
+ to fetch data (MYSQLND_STMT_USER_FETCHING).
+ We need to check 'result_zvals_separated_once' or we will leak
+ in the following scenario
+ prepare("select 1 from dual");
+ execute();
+ fetch(); <-- no binding, but that's not a problem
+ bind_result();
+ execute(); <-- here we will leak because we separate without need
+ */
+ unsigned int i;
+ for (i = 0; i < stmt->field_count; i++) {
+ if (stmt->result_bind[i].bound == TRUE) {
+ zval_copy_ctor(stmt->result_bind[i].zv);
+ }
+ }
+ }
+#endif
+
+ s->m->flush(s TSRMLS_CC);
+
+ /*
+ Executed, but the user hasn't started to fetch
+ This will clean also the metadata, but after the EXECUTE call we will
+ have it again.
+ */
+ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
+
+ stmt->state = MYSQLND_STMT_PREPARED;
+ } else if (stmt->state < MYSQLND_STMT_PREPARED) {
+ /* Only initted - error */
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+ mysqlnd_out_of_sync);
+ SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+
+ if (stmt->param_count) {
+ unsigned int i, not_bound = 0;
+ if (!stmt->param_bind) {
+ SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
+ "No data supplied for parameters in prepared statement");
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ for (i = 0; i < stmt->param_count; i++) {
+ if (stmt->param_bind[i].zv == NULL) {
+ not_bound++;
+ }
+ }
+ if (not_bound) {
+ char * msg;
+ mnd_sprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
+ not_bound, not_bound>1 ?"s":"");
+ SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
+ if (msg) {
+ mnd_sprintf_free(msg);
+ }
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ }
+ ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC);
+ if (ret == PASS) {
+ /* support for buffer types should be added here ! */
+ ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, request, request_len,
+ PROT_LAST /* we will handle the response packet*/,
+ FALSE, FALSE TSRMLS_CC);
+ } else {
+ SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
+ }
+
+ if (free_request) {
+ mnd_efree(request);
+ }
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ stmt->execute_count++;
+
+ ret = s->m->parse_execute_response(s TSRMLS_CC);
+
+ DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status->affected_rows) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status->affected_rows);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_fetch_row_buffered */
+enum_func_status
+mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+{
+ MYSQLND_STMT * s = (MYSQLND_STMT *) param;
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+ unsigned int field_count = result->meta->field_count;
+
+ DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
+ *fetched_anything = FALSE;
+ DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
+
+ /* If we haven't read everything */
+ if (set->data_cursor &&
+ (set->data_cursor - set->data) < (set->row_count * field_count))
+ {
+ /* The user could have skipped binding - don't crash*/
+ if (stmt->result_bind) {
+ unsigned int i;
+ MYSQLND_RES_METADATA * meta = result->meta;
+ zval **current_row = set->data_cursor;
+
+ if (NULL == current_row[0]) {
+ uint64_t row_num = (set->data_cursor - set->data) / field_count;
+ enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
+ current_row,
+ meta->field_count,
+ meta->fields,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (PASS != rc) {
+ DBG_RETURN(FAIL);
+ }
+ set->initialized_rows++;
+ if (stmt->update_max_length) {
+ for (i = 0; i < result->field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < result->field_count; i++) {
+ /* Clean what we copied last time */
+#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ if (stmt->result_bind[i].zv) {
+ zval_dtor(stmt->result_bind[i].zv);
+ }
+#endif
+ /* copy the type */
+ if (stmt->result_bind[i].bound == TRUE) {
+ DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
+ if (Z_TYPE_P(current_row[i]) != IS_NULL) {
+ /*
+ Copy the value.
+ Pre-condition is that the zvals in the result_bind buffer
+ have been ZVAL_NULL()-ed or to another simple type
+ (int, double, bool but not string). Because of the reference
+ counting the user can't delete the strings the variables point to.
+ */
+
+ Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
+ stmt->result_bind[i].zv->value = current_row[i]->value;
+#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[i].zv);
+#endif
+ } else {
+ ZVAL_NULL(stmt->result_bind[i].zv);
+ }
+ }
+ }
+ }
+ set->data_cursor += field_count;
+ *fetched_anything = TRUE;
+ /* buffered result sets don't have a connection */
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
+ DBG_INF("row fetched");
+ } else {
+ set->data_cursor = NULL;
+ DBG_INF("no more data");
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
+static enum_func_status
+mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND_STMT * s = (MYSQLND_STMT *) param;
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_PACKET_ROW * row_packet;
+
+ DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
+
+ *fetched_anything = FALSE;
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ DBG_INF("EOF already reached");
+ DBG_RETURN(PASS);
+ }
+ if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR("command out of sync");
+ DBG_RETURN(FAIL);
+ }
+ if (!(row_packet = result->row_packet)) {
+ DBG_RETURN(FAIL);
+ }
+
+ /* Let the row packet fill our buffer and skip additional malloc + memcpy */
+ row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE;
+
+ /*
+ If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
+ result->m.unbuffered_free_last_data() before it. The function returns always true.
+ */
+ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ unsigned int i, field_count = result->field_count;
+
+ if (!row_packet->skip_extraction) {
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+
+ result->unbuf->last_row_data = row_packet->fields;
+ result->unbuf->last_row_buffer = row_packet->row_buffer;
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
+
+ if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
+ result->unbuf->last_row_data,
+ row_packet->field_count,
+ row_packet->fields_metadata,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC))
+ {
+ DBG_RETURN(FAIL);
+ }
+
+ for (i = 0; i < field_count; i++) {
+ if (stmt->result_bind[i].bound == TRUE) {
+ zval *data = result->unbuf->last_row_data[i];
+ /*
+ stmt->result_bind[i].zv has been already destructed
+ in result->m.unbuffered_free_last_data()
+ */
+#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_dtor(stmt->result_bind[i].zv);
+#endif
+ if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
+ if (
+ (Z_TYPE_P(data) == IS_STRING
+#if MYSQLND_UNICODE
+ || Z_TYPE_P(data) == IS_UNICODE
+#endif
+ )
+ && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
+ {
+ result->meta->fields[i].max_length = Z_STRLEN_P(data);
+ }
+ stmt->result_bind[i].zv->value = data->value;
+ /* copied data, thus also the ownership. Thus null data */
+ ZVAL_NULL(data);
+ }
+ }
+ }
+ MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
+ } else {
+ DBG_INF("skipping extraction");
+ /*
+ Data has been allocated and usually result->m.unbuffered_free_last_data()
+ frees it but we can't call this function as it will cause problems with
+ the bound variables. Thus we need to do part of what it does or Zend will
+ report leaks.
+ */
+ row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
+ row_packet->row_buffer = NULL;
+ }
+
+ result->unbuf->row_count++;
+ *fetched_anything = TRUE;
+ } else if (ret == FAIL) {
+ if (row_packet->error_info.error_no) {
+ COPY_CLIENT_ERROR(*stmt->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(*stmt->error_info, row_packet->error_info);
+ }
+ CONN_SET_STATE(result->conn, CONN_READY);
+ result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
+ } else if (row_packet->eof) {
+ DBG_INF("EOF");
+ /* Mark the connection as usable again */
+ result->unbuf->eof_reached = TRUE;
+ result->conn->upsert_status->warning_count = row_packet->warning_count;
+ result->conn->upsert_status->server_status = row_packet->server_status;
+ /*
+ result->row_packet will be cleaned when
+ destroying the result object
+ */
+ if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(result->conn, CONN_READY);
+ }
+ }
+
+ DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::use_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_RES * result;
+ MYSQLND_CONN_DATA * conn;
+
+ DBG_ENTER("mysqlnd_stmt::use_result");
+ if (!stmt || !stmt->conn || !stmt->result) {
+ DBG_RETURN(NULL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ conn = stmt->conn;
+
+ if (!stmt->field_count ||
+ (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
+ (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
+ (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
+ {
+ SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR("command out of sync");
+ DBG_RETURN(NULL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+
+ MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
+ result = stmt->result;
+
+ result->m.use_result(stmt->result, TRUE TSRMLS_CC);
+ result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
+ mysqlnd_stmt_fetch_row_unbuffered;
+ stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
+
+ DBG_INF_FMT("%p", result);
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+#define STMT_ID_LENGTH 4
+
+/* {{{ mysqlnd_fetch_row_cursor */
+enum_func_status
+mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND_STMT * s = (MYSQLND_STMT *) param;
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
+ MYSQLND_PACKET_ROW * row_packet;
+
+ DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
+
+ if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
+ DBG_ERR("no statement");
+ DBG_RETURN(FAIL);
+ }
+
+ DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
+
+ if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
+ /* Only initted - error */
+ SET_CLIENT_ERROR(*stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
+ mysqlnd_out_of_sync);
+ DBG_ERR("command out of sync");
+ DBG_RETURN(FAIL);
+ }
+ if (!(row_packet = result->row_packet)) {
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ int4store(buf, stmt->stmt_id);
+ int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
+
+ if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
+ PROT_LAST /* we will handle the response packet*/,
+ FALSE, TRUE TSRMLS_CC)) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *stmt->conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
+
+ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ unsigned int i, field_count = result->field_count;
+
+ if (!row_packet->skip_extraction) {
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+
+ result->unbuf->last_row_data = row_packet->fields;
+ result->unbuf->last_row_buffer = row_packet->row_buffer;
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
+
+ if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
+ result->unbuf->last_row_data,
+ row_packet->field_count,
+ row_packet->fields_metadata,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC))
+ {
+ DBG_RETURN(FAIL);
+ }
+
+ /* If no result bind, do nothing. We consumed the data */
+ for (i = 0; i < field_count; i++) {
+ if (stmt->result_bind[i].bound == TRUE) {
+ zval *data = result->unbuf->last_row_data[i];
+ /*
+ stmt->result_bind[i].zv has been already destructed
+ in result->m.unbuffered_free_last_data()
+ */
+#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_dtor(stmt->result_bind[i].zv);
+#endif
+ DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
+ Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
+ if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
+ if ((Z_TYPE_P(data) == IS_STRING
+#if MYSQLND_UNICODE
+ || Z_TYPE_P(data) == IS_UNICODE
+#endif
+ )
+ && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
+ {
+ result->meta->fields[i].max_length = Z_STRLEN_P(data);
+ }
+ stmt->result_bind[i].zv->value = data->value;
+ /* copied data, thus also the ownership. Thus null data */
+ ZVAL_NULL(data);
+ }
+ }
+ }
+ } else {
+ DBG_INF("skipping extraction");
+ /*
+ Data has been allocated and usually result->m.unbuffered_free_last_data()
+ frees it but we can't call this function as it will cause problems with
+ the bound variables. Thus we need to do part of what it does or Zend will
+ report leaks.
+ */
+ row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
+ row_packet->row_buffer = NULL;
+ }
+ /* We asked for one row, the next one should be EOF, eat it */
+ ret = PACKET_READ(row_packet, result->conn);
+ if (row_packet->row_buffer) {
+ row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
+ row_packet->row_buffer = NULL;
+ }
+ MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
+
+ result->unbuf->row_count++;
+ *fetched_anything = TRUE;
+ } else {
+ *fetched_anything = FALSE;
+
+ stmt->upsert_status->warning_count =
+ stmt->conn->upsert_status->warning_count =
+ row_packet->warning_count;
+
+ stmt->upsert_status->server_status =
+ stmt->conn->upsert_status->server_status =
+ row_packet->server_status;
+
+ result->unbuf->eof_reached = row_packet->eof;
+ }
+ stmt->upsert_status->warning_count =
+ stmt->conn->upsert_status->warning_count =
+ row_packet->warning_count;
+ stmt->upsert_status->server_status =
+ stmt->conn->upsert_status->server_status =
+ row_packet->server_status;
+
+ DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
+ ret == PASS? "PASS":"FAIL", *fetched_anything,
+ row_packet->server_status, row_packet->warning_count,
+ result->unbuf->eof_reached);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::fetch */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret;
+ DBG_ENTER("mysqlnd_stmt::fetch");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (!stmt->result ||
+ stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+
+ DBG_ERR("command out of sync");
+ DBG_RETURN(FAIL);
+ } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ /* Execute only once. We have to free the previous contents of user's bound vars */
+
+ stmt->default_rset_handler(s TSRMLS_CC);
+ }
+ stmt->state = MYSQLND_STMT_USER_FETCHING;
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once);
+ /*
+ The user might have not bound any variables for result.
+ Do the binding once she does it.
+ */
+ if (stmt->result_bind && !stmt->result_zvals_separated_once) {
+ unsigned int i;
+ /*
+ mysqlnd_stmt_store_result() has been called free the bind
+ variables to prevent leaking of their previous content.
+ */
+ for (i = 0; i < stmt->result->field_count; i++) {
+ if (stmt->result_bind[i].bound == TRUE) {
+ zval_dtor(stmt->result_bind[i].zv);
+ ZVAL_NULL(stmt->result_bind[i].zv);
+ }
+ }
+ stmt->result_zvals_separated_once = TRUE;
+ }
+
+ ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::reset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret = PASS;
+ zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+
+ DBG_ENTER("mysqlnd_stmt::reset");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->stmt_id) {
+ MYSQLND_CONN_DATA * conn = stmt->conn;
+ if (stmt->param_bind) {
+ unsigned int i;
+ DBG_INF("resetting long data");
+ /* Reset Long Data */
+ for (i = 0; i < stmt->param_count; i++) {
+ if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
+ stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
+ }
+ }
+ }
+
+ s->m->flush(s TSRMLS_CC);
+
+ /*
+ Don't free now, let the result be usable. When the stmt will again be
+ executed then the result set will be cleaned, the bound variables will
+ be separated before that.
+ */
+
+ int4store(cmd_buf, stmt->stmt_id);
+ if (CONN_GET_STATE(conn) == CONN_READY &&
+ FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
+ sizeof(cmd_buf), PROT_OK_PACKET,
+ FALSE, TRUE TSRMLS_CC))) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ }
+ *stmt->upsert_status = *conn->upsert_status;
+ }
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::flush */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret = PASS;
+
+ DBG_ENTER("mysqlnd_stmt::flush");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (stmt->stmt_id) {
+ /*
+ If the user decided to close the statement right after execute()
+ We have to call the appropriate use_result() or store_result() and
+ clean.
+ */
+ do {
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("fetching result set header");
+ stmt->default_rset_handler(s TSRMLS_CC);
+ stmt->state = MYSQLND_STMT_USER_FETCHING;
+ }
+
+ if (stmt->result) {
+ DBG_INF("skipping result");
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+ } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
+ }
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::send_long_data */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
+ const char * const data, unsigned long length TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn;
+ zend_uchar * cmd_buf;
+ enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
+
+ DBG_ENTER("mysqlnd_stmt::send_long_data");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
+
+ conn = stmt->conn;
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+ if (!stmt->param_bind) {
+ SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR("command out of sync");
+ DBG_RETURN(FAIL);
+ }
+ if (param_no >= stmt->param_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ DBG_ERR("invalid param_no");
+ DBG_RETURN(FAIL);
+ }
+ if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
+ SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
+ DBG_ERR("param_no is not of a blob type");
+ DBG_RETURN(FAIL);
+ }
+
+ /*
+ XXX: Unfortunately we have to allocate additional buffer to be able the
+ additional data, which is like a header inside the payload.
+ This should be optimised, but it will be a pervasive change, so
+ conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
+ terminated by NULL, to send. If the strings are not big, we can collapse them
+ on the buffer every connection has, but otherwise we will just send them
+ one by one to the wire.
+ */
+
+ if (CONN_GET_STATE(conn) == CONN_READY) {
+ size_t packet_len;
+ cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
+ if (cmd_buf) {
+ stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
+
+ int4store(cmd_buf, stmt->stmt_id);
+ int2store(cmd_buf + STMT_ID_LENGTH, param_no);
+ memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
+
+ /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
+ ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
+ mnd_efree(cmd_buf);
+ if (FAIL == ret) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ }
+ } else {
+ ret = FAIL;
+ SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(*conn->error_info);
+ }
+ /*
+ Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
+ sent response packets. According to documentation the only way to get an error
+ is to have out-of-memory on the server-side. However, that's not true, as if
+ max_allowed_packet_size is smaller than the chunk being sent to the server, the
+ latter will complain with an error message. However, normally we don't expect
+ an error message, thus we continue. When sending the next command, which expects
+ response we will read the unexpected data and error message will look weird.
+ Therefore we do non-blocking read to clean the line, if there is a need.
+ Nevertheless, there is a built-in protection when sending a command packet, that
+ checks if the line is clear - useful for debug purposes and to be switched off
+ in release builds.
+
+ Maybe we can make it automatic by checking what's the value of
+ max_allowed_packet_size on the server and resending the data.
+ */
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+#if HAVE_USLEEP && !defined(PHP_WIN32)
+ usleep(120000);
+#endif
+ if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
+ "while sending long data. Probably max_allowed_packet_size "
+ "is smaller than the data. You have to increase it or send "
+ "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
+ SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
+ "Server responded to COM_STMT_SEND_LONG_DATA.");
+ ret = FAIL;
+ }
+#endif
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::bind_parameters */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::bind_param");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ if (param_bind) {
+ s->m->free_parameter_bind(s, param_bind TSRMLS_CC);
+ }
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->param_count) {
+ unsigned int i = 0;
+
+ if (!param_bind) {
+ SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
+ DBG_ERR("Re-binding (still) not supported");
+ DBG_RETURN(FAIL);
+ } else if (stmt->param_bind) {
+ DBG_INF("Binding");
+ /*
+ There is already result bound.
+ Forbid for now re-binding!!
+ */
+ for (i = 0; i < stmt->param_count; i++) {
+ /*
+ We may have the last reference, then call zval_ptr_dtor() or we may leak memory.
+ Switching from bind_one_parameter to bind_parameters may result in zv being NULL
+ */
+ if (stmt->param_bind[i].zv) {
+ zval_ptr_dtor(&stmt->param_bind[i].zv);
+ }
+ }
+ if (stmt->param_bind != param_bind) {
+ s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
+ }
+ }
+
+ stmt->param_bind = param_bind;
+ for (i = 0; i < stmt->param_count; i++) {
+ /* The client will use stmt_send_long_data */
+ DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
+ /* Prevent from freeing */
+ /* Don't update is_ref, or we will leak during conversion */
+ Z_ADDREF_P(stmt->param_bind[i].zv);
+ stmt->param_bind[i].flags = 0;
+ if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
+ stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
+ }
+ }
+ stmt->send_types_to_server = 1;
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::bind_one_parameter */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
+ zval * const zv, zend_uchar type TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ if (param_no >= stmt->param_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ DBG_ERR("invalid param_no");
+ DBG_RETURN(FAIL);
+ }
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->param_count) {
+ if (!stmt->param_bind) {
+ stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND));
+ if (!stmt->param_bind) {
+ DBG_RETURN(FAIL);
+ }
+ }
+
+ /* Prevent from freeing */
+ /* Don't update is_ref, or we will leak during conversion */
+ Z_ADDREF_P(zv);
+ DBG_INF("Binding");
+ /* Release what we had, if we had */
+ if (stmt->param_bind[param_no].zv) {
+ zval_ptr_dtor(&stmt->param_bind[param_no].zv);
+ }
+ if (type == MYSQL_TYPE_LONG_BLOB) {
+ /* The client will use stmt_send_long_data */
+ stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
+ }
+ stmt->param_bind[param_no].zv = zv;
+ stmt->param_bind[param_no].type = type;
+
+ stmt->send_types_to_server = 1;
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::refresh_bind_param */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->param_count) {
+ stmt->send_types_to_server = 1;
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::bind_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
+ MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::bind_result");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ if (result_bind) {
+ s->m->free_result_bind(s, result_bind TSRMLS_CC);
+ }
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->field_count) {
+ unsigned int i = 0;
+
+ if (!result_bind) {
+ DBG_ERR("no result bind passed");
+ DBG_RETURN(FAIL);
+ }
+
+ mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
+ stmt->result_zvals_separated_once = FALSE;
+ stmt->result_bind = result_bind;
+ for (i = 0; i < stmt->field_count; i++) {
+ /* Prevent from freeing */
+ Z_ADDREF_P(stmt->result_bind[i].zv);
+ DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv));
+ /*
+ Don't update is_ref !!! it's not our job
+ Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
+ will fail.
+ */
+ stmt->result_bind[i].bound = TRUE;
+ }
+ } else if (result_bind) {
+ s->m->free_result_bind(s, result_bind TSRMLS_CC);
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::bind_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::bind_result");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
+
+ if (stmt->state < MYSQLND_STMT_PREPARED) {
+ SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ if (param_no >= stmt->field_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ DBG_ERR("invalid param_no");
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ if (stmt->field_count) {
+ mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC);
+ /* Guaranteed is that stmt->result_bind is NULL */
+ if (!stmt->result_bind) {
+ stmt->result_bind = mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
+ } else {
+ stmt->result_bind = mnd_perealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND), stmt->persistent);
+ }
+ if (!stmt->result_bind) {
+ DBG_RETURN(FAIL);
+ }
+ ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
+ /*
+ Don't update is_ref !!! it's not our job
+ Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
+ will fail.
+ */
+ stmt->result_bind[param_no].bound = TRUE;
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::insert_id */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->upsert_status->last_insert_id : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::affected_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->upsert_status->affected_rows : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::num_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::warning_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->upsert_status->warning_count : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::server_status */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->upsert_status->server_status : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::field_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->field_count : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::param_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->param_count : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::errno */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->error_info->error_no : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::error */
+static const char *
+MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt? stmt->error_info->error : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::sqlstate */
+static const char *
+MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::data_seek */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::param_metadata */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ if (!stmt || !stmt->param_count) {
+ return NULL;
+ }
+ return NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::result_metadata */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_stmt::result_metadata");
+ if (!stmt) {
+ DBG_RETURN(NULL);
+ }
+ DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
+
+ if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
+ DBG_INF("NULL");
+ DBG_RETURN(NULL);
+ }
+
+ if (stmt->update_max_length && stmt->result->stored_data) {
+ /* stored result, we have to update the max_length before we clone the meta data :( */
+ stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC);
+ }
+ /*
+ TODO: This implementation is kind of a hack,
+ find a better way to do it. In different functions I have put
+ fuses to check for result->m.fetch_row() being NULL. This should
+ be handled in a better way.
+
+ In the meantime we don't need a zval cache reference for this fake
+ result set, so we don't get one.
+ */
+ do {
+ result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC);
+ if (!result) {
+ break;
+ }
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ if (!result->unbuf) {
+ break;
+ }
+ result->unbuf->eof_reached = TRUE;
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
+ if (!result->meta) {
+ break;
+ }
+
+ DBG_INF_FMT("result=%p", result);
+ DBG_RETURN(result);
+ } while (0);
+
+ SET_OOM_ERROR(*stmt->conn->error_info);
+ if (result) {
+ result->m.free_result(result, TRUE TSRMLS_CC);
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::attr_set */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
+ enum mysqlnd_stmt_attr attr_type,
+ const void * const value TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::attr_set");
+ if (!stmt) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
+
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:{
+ zend_uchar bval = *(zend_uchar *) value;
+ /*
+ XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
+ and mysqlnd won't be used out of the scope of PHP -> use ulong.
+ */
+ stmt->update_max_length = bval? TRUE:FALSE;
+ break;
+ }
+ case STMT_ATTR_CURSOR_TYPE: {
+ unsigned int ival = *(unsigned int *) value;
+ if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) {
+ SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ stmt->flags = ival;
+ break;
+ }
+ case STMT_ATTR_PREFETCH_ROWS: {
+ unsigned int ival = *(unsigned int *) value;
+ if (ival == 0) {
+ ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
+ } else if (ival > 1) {
+ SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ stmt->prefetch_rows = ival;
+ break;
+ }
+ default:
+ SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::attr_get */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
+ enum mysqlnd_stmt_attr attr_type,
+ void * const value TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::attr_set");
+ if (!stmt) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
+
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ *(zend_bool *) value= stmt->update_max_length;
+ break;
+ case STMT_ATTR_CURSOR_TYPE:
+ *(unsigned long *) value= stmt->flags;
+ break;
+ case STMT_ATTR_PREFETCH_ROWS:
+ *(unsigned long *) value= stmt->prefetch_rows;
+ break;
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("value=%lu", value);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* free_result() doesn't actually free stmt->result but only the buffers */
+/* {{{ mysqlnd_stmt::free_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::free_result");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (!stmt->result) {
+ DBG_INF("no result");
+ DBG_RETURN(PASS);
+ }
+
+ /*
+ If right after execute() we have to call the appropriate
+ use_result() or store_result() and clean.
+ */
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("fetching result set header");
+ /* Do implicit use_result and then flush the result */
+ stmt->default_rset_handler = s->m->use_result;
+ stmt->default_rset_handler(s TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("skipping result");
+ /* Flush if anything is left and unbuffered set */
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ /*
+ Separate the bound variables, which point to the result set, then
+ destroy the set.
+ */
+ mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
+
+ /* Now we can destroy the result set */
+ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_PREPARED) {
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+
+ /* Line is free! */
+ CONN_SET_STATE(stmt->conn, CONN_READY);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_separate_result_bind */
+static void
+mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ unsigned int i;
+
+ DBG_ENTER("mysqlnd_stmt_separate_result_bind");
+ if (!stmt) {
+ DBG_VOID_RETURN;
+ }
+ DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count);
+
+ if (!stmt->result_bind) {
+ DBG_VOID_RETURN;
+ }
+
+ /*
+ Because only the bound variables can point to our internal buffers, then
+ separate or free only them. Free is possible because the user could have
+ lost reference.
+ */
+ for (i = 0; i < stmt->field_count; i++) {
+ /* Let's try with no cache */
+ if (stmt->result_bind[i].bound == TRUE) {
+ DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv));
+ /*
+ We have to separate the actual zval value of the bound
+ variable from our allocated zvals or we will face double-free
+ */
+ if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) {
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[i].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[i].zv);
+ } else {
+ /*
+ If it is a string, what is pointed will be freed
+ later in free_result(). We need to remove the variable to
+ which the user has lost reference.
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ ZVAL_NULL(stmt->result_bind[i].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[i].zv);
+ }
+ }
+ }
+ s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC);
+ stmt->result_bind = NULL;
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_separate_one_result_bind */
+static void
+mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
+ if (!stmt) {
+ DBG_VOID_RETURN;
+ }
+ DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
+
+ if (!stmt->result_bind) {
+ DBG_VOID_RETURN;
+ }
+
+ /*
+ Because only the bound variables can point to our internal buffers, then
+ separate or free only them. Free is possible because the user could have
+ lost reference.
+ */
+ /* Let's try with no cache */
+ if (stmt->result_bind[param_no].bound == TRUE) {
+ DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
+ /*
+ We have to separate the actual zval value of the bound
+ variable from our allocated zvals or we will face double-free
+ */
+ if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ } else {
+ /*
+ If it is a string, what is pointed will be freed
+ later in free_result(). We need to remove the variable to
+ which the user has lost reference.
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ ZVAL_NULL(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ }
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::free_stmt_content */
+static void
+MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::free_stmt_content");
+ if (!stmt) {
+ DBG_VOID_RETURN;
+ }
+ DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count);
+
+ /* Destroy the input bind */
+ if (stmt->param_bind) {
+ unsigned int i;
+ /*
+ Because only the bound variables can point to our internal buffers, then
+ separate or free only them. Free is possible because the user could have
+ lost reference.
+ */
+ for (i = 0; i < stmt->param_count; i++) {
+ /*
+ If bind_one_parameter was used, but not everything was
+ bound and nothing was fetched, then some `zv` could be NULL
+ */
+ if (stmt->param_bind[i].zv) {
+ zval_ptr_dtor(&stmt->param_bind[i].zv);
+ }
+ }
+ s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
+ stmt->param_bind = NULL;
+ }
+
+ /*
+ First separate the bound variables, which point to the result set, then
+ destroy the set.
+ */
+ mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
+ /* Not every statement has a result set attached */
+ if (stmt->result) {
+ stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
+ stmt->result = NULL;
+ }
+ if (stmt->error_info->error_list) {
+ zend_llist_clean(stmt->error_info->error_list);
+ mnd_pefree(stmt->error_info->error_list, s->persistent);
+ stmt->error_info->error_list = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::net_close */
+static enum_func_status
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_CONN_DATA * conn;
+ zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ enum_mysqlnd_collected_stats statistic = STAT_LAST;
+
+ DBG_ENTER("mysqlnd_stmt::net_close");
+ if (!stmt || !stmt->conn) {
+ DBG_RETURN(FAIL);
+ }
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ conn = stmt->conn;
+
+ SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(*stmt->conn->error_info);
+
+ /*
+ If the user decided to close the statement right after execute()
+ We have to call the appropriate use_result() or store_result() and
+ clean.
+ */
+ do {
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("fetching result set header");
+ stmt->default_rset_handler(s TSRMLS_CC);
+ stmt->state = MYSQLND_STMT_USER_FETCHING;
+ }
+
+ /* unbuffered set not fetched to the end ? Clean the line */
+ if (stmt->result) {
+ DBG_INF("skipping result");
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+ } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
+ /*
+ After this point we are allowed to free the result set,
+ as we have cleaned the line
+ */
+ if (stmt->stmt_id) {
+ MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
+ STAT_FREE_RESULT_EXPLICIT);
+
+ int4store(cmd_buf, stmt->stmt_id);
+ if (CONN_GET_STATE(conn) == CONN_READY &&
+ FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
+ PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
+ FALSE, TRUE TSRMLS_CC)) {
+ COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+ }
+ switch (stmt->execute_count) {
+ case 0:
+ statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
+ break;
+ case 1:
+ statistic = STAT_PS_PREPARED_ONCE_USED;
+ break;
+ default:
+ break;
+ }
+ if (statistic != STAT_LAST) {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
+ }
+
+ if (stmt->execute_cmd_buffer.buffer) {
+ mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
+ stmt->execute_cmd_buffer.buffer = NULL;
+ }
+
+ s->m->free_stmt_content(s TSRMLS_CC);
+
+ if (stmt->conn) {
+ stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
+ stmt->conn = NULL;
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+/* {{{ mysqlnd_stmt::dtor */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
+ enum_func_status ret = FAIL;
+ zend_bool persistent = (s != NULL) ? s->persistent : 0;
+
+ DBG_ENTER("mysqlnd_stmt::dtor");
+ if (stmt) {
+ DBG_INF_FMT("stmt=%p", stmt);
+
+ MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
+ STAT_STMT_CLOSE_EXPLICIT);
+
+ ret = s->m->net_close(s, implicit TSRMLS_CC);
+ mnd_pefree(stmt, persistent);
+ }
+ mnd_pefree(s, persistent);
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::alloc_param_bind */
+static MYSQLND_PARAM_BIND *
+MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
+ if (!stmt) {
+ DBG_RETURN(NULL);
+ }
+ DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::alloc_result_bind */
+static MYSQLND_RESULT_BIND *
+MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
+ if (!stmt) {
+ DBG_RETURN(NULL);
+ }
+ DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
+}
+/* }}} */
+
+
+/* {{{ param_bind::free_parameter_bind */
+PHPAPI void
+MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ if (stmt) {
+ mnd_pefree(param_bind, stmt->persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::free_result_bind */
+PHPAPI void
+MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ if (stmt) {
+ mnd_pefree(result_bind, stmt->persistent);
+ }
+}
+/* }}} */
+
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
+ MYSQLND_METHOD(mysqlnd_stmt, prepare),
+ MYSQLND_METHOD(mysqlnd_stmt, execute),
+ MYSQLND_METHOD(mysqlnd_stmt, use_result),
+ MYSQLND_METHOD(mysqlnd_stmt, store_result),
+ MYSQLND_METHOD(mysqlnd_stmt, get_result),
+ MYSQLND_METHOD(mysqlnd_stmt, more_results),
+ MYSQLND_METHOD(mysqlnd_stmt, next_result),
+ MYSQLND_METHOD(mysqlnd_stmt, free_result),
+ MYSQLND_METHOD(mysqlnd_stmt, data_seek),
+ MYSQLND_METHOD(mysqlnd_stmt, reset),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
+ MYSQLND_METHOD(mysqlnd_stmt, dtor),
+
+ MYSQLND_METHOD(mysqlnd_stmt, fetch),
+
+ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
+ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
+ MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
+ MYSQLND_METHOD(mysqlnd_stmt, bind_result),
+ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
+ MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
+ MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
+ MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
+
+ MYSQLND_METHOD(mysqlnd_stmt, insert_id),
+ MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
+ MYSQLND_METHOD(mysqlnd_stmt, num_rows),
+
+ MYSQLND_METHOD(mysqlnd_stmt, param_count),
+ MYSQLND_METHOD(mysqlnd_stmt, field_count),
+ MYSQLND_METHOD(mysqlnd_stmt, warning_count),
+
+ MYSQLND_METHOD(mysqlnd_stmt, errno),
+ MYSQLND_METHOD(mysqlnd_stmt, error),
+ MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
+
+ MYSQLND_METHOD(mysqlnd_stmt, attr_get),
+ MYSQLND_METHOD(mysqlnd_stmt, attr_set),
+
+
+ MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
+ MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
+ MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
+ MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
+ MYSQLND_METHOD(mysqlnd_stmt, server_status),
+ mysqlnd_stmt_execute_generate_request,
+ mysqlnd_stmt_execute_parse_response,
+ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content),
+ MYSQLND_METHOD(mysqlnd_stmt, flush)
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ _mysqlnd_stmt_init */
+MYSQLND_STMT *
+_mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
+{
+ MYSQLND_STMT * ret;
+ DBG_ENTER("_mysqlnd_stmt_init");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_prepared_statement(conn TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_init_ps_subsystem */
+void _mysqlnd_init_ps_subsystem()
+{
+ mysqlnd_stmt_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt));
+ _mysqlnd_init_ps_fetch_subsystem();
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
new file mode 100644
index 0000000..5ead1b0
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -0,0 +1,952 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+
+#define MYSQLND_SILENT
+
+
+enum mysqlnd_timestamp_type
+{
+ MYSQLND_TIMESTAMP_NONE= -2,
+ MYSQLND_TIMESTAMP_ERROR= -1,
+ MYSQLND_TIMESTAMP_DATE= 0,
+ MYSQLND_TIMESTAMP_DATETIME= 1,
+ MYSQLND_TIMESTAMP_TIME= 2
+};
+
+
+struct st_mysqlnd_time
+{
+ unsigned int year, month, day, hour, minute, second;
+ unsigned long second_part;
+ zend_bool neg;
+ enum mysqlnd_timestamp_type time_type;
+};
+
+
+struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
+
+#define MYSQLND_PS_SKIP_RESULT_W_LEN -1
+#define MYSQLND_PS_SKIP_RESULT_STR -2
+
+/* {{{ ps_fetch_from_1_to_8_bytes */
+void ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row, zend_bool as_unicode,
+ unsigned int byte_count TSRMLS_DC)
+{
+ char tmp[22];
+ size_t tmp_len = 0;
+ zend_bool is_bit = field->type == MYSQL_TYPE_BIT;
+ DBG_ENTER("ps_fetch_from_1_to_8_bytes");
+ DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count);
+ if (field->flags & UNSIGNED_FLAG) {
+ uint64_t uval = 0;
+
+ switch (byte_count) {
+ case 8:uval = is_bit? (uint64_t) bit_uint8korr(*row):(uint64_t) uint8korr(*row);break;
+ case 7:uval = bit_uint7korr(*row);break;
+ case 6:uval = bit_uint6korr(*row);break;
+ case 5:uval = bit_uint5korr(*row);break;
+ case 4:uval = is_bit? (uint64_t) bit_uint4korr(*row):(uint64_t) uint4korr(*row);break;
+ case 3:uval = is_bit? (uint64_t) bit_uint3korr(*row):(uint64_t) uint3korr(*row);break;
+ case 2:uval = is_bit? (uint64_t) bit_uint2korr(*row):(uint64_t) uint2korr(*row);break;
+ case 1:uval = (uint64_t) uint1korr(*row);break;
+ }
+
+#if SIZEOF_LONG==4
+ if (uval > INT_MAX) {
+ DBG_INF("stringify");
+ tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
+ } else
+#endif /* #if SIZEOF_LONG==4 */
+ {
+ if (byte_count < 8 || uval <= L64(9223372036854775807)) {
+ ZVAL_LONG(zv, (long) uval); /* the cast is safe, we are in the range */
+ } else {
+ DBG_INF("stringify");
+ tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
+ }
+ }
+ } else {
+ /* SIGNED */
+ int64_t lval = 0;
+ switch (byte_count) {
+ case 8:lval = (int64_t) sint8korr(*row);break;
+ /*
+ 7, 6 and 5 are not possible.
+ BIT is only unsigned, thus only uint5|6|7 macroses exist
+ */
+ case 4:lval = (int64_t) sint4korr(*row);break;
+ case 3:lval = (int64_t) sint3korr(*row);break;
+ case 2:lval = (int64_t) sint2korr(*row);break;
+ case 1:lval = (int64_t) *(int8_t*)*row;break;
+ }
+
+#if SIZEOF_LONG==4
+ if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) {
+ DBG_INF("stringify");
+ tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval);
+ } else
+#endif /* SIZEOF */
+ {
+ ZVAL_LONG(zv, (long) lval); /* the cast is safe, we are in the range */
+ }
+ }
+
+ if (tmp_len) {
+#if MYSQLND_UNICODE
+ if (as_unicode) {
+ DBG_INF("stringify");
+ ZVAL_UTF8_STRINGL(zv, tmp, tmp_len, ZSTR_DUPLICATE);
+ } else
+#endif
+ {
+ DBG_INF("stringify");
+ ZVAL_STRINGL(zv, tmp, tmp_len, 1);
+ }
+ }
+ (*row)+= byte_count;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_null */
+static
+void ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ ZVAL_NULL(zv);
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int8 */
+static
+void ps_fetch_int8(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 1 TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int16 */
+static
+void ps_fetch_int16(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 2 TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int32 */
+static
+void ps_fetch_int32(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 4 TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int64 */
+static
+void ps_fetch_int64(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, 8 TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_float */
+static
+void ps_fetch_float(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ float value;
+ DBG_ENTER("ps_fetch_float");
+ float4get(value, *row);
+ ZVAL_DOUBLE(zv, value);
+ (*row)+= 4;
+ DBG_INF_FMT("value=%f", value);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_double */
+static
+void ps_fetch_double(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ double value;
+ DBG_ENTER("ps_fetch_double");
+ float8get(value, *row);
+ ZVAL_DOUBLE(zv, value);
+ (*row)+= 8;
+ DBG_INF_FMT("value=%f", value);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_time */
+static
+void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ struct st_mysqlnd_time t;
+ unsigned int length; /* First byte encodes the length*/
+ char * value;
+ DBG_ENTER("ps_fetch_time");
+
+ if ((length = php_mysqlnd_net_field_length(row))) {
+ zend_uchar *to= *row;
+
+ t.time_type = MYSQLND_TIMESTAMP_TIME;
+ t.neg = (zend_bool) to[0];
+
+ t.day = (unsigned long) sint4korr(to+1);
+ t.hour = (unsigned int) to[5];
+ t.minute = (unsigned int) to[6];
+ t.second = (unsigned int) to[7];
+ t.second_part = (length > 8) ? (unsigned long) sint4korr(to+8) : 0;
+ t.year = t.month= 0;
+ if (t.day) {
+ /* Convert days to hours at once */
+ t.hour += t.day*24;
+ t.day = 0;
+ }
+
+ (*row) += length;
+ } else {
+ memset(&t, 0, sizeof(t));
+ t.time_type = MYSQLND_TIMESTAMP_TIME;
+ }
+
+ length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
+
+ DBG_INF_FMT("%s", value);
+#if MYSQLND_UNICODE
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, value, length, 1);
+ mnd_sprintf_free(value);
+#if MYSQLND_UNICODE
+ } else {
+ ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE);
+ }
+#endif
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_date */
+static
+void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ struct st_mysqlnd_time t = {0};
+ unsigned int length; /* First byte encodes the length*/
+ char * value;
+ DBG_ENTER("ps_fetch_date");
+
+ if ((length = php_mysqlnd_net_field_length(row))) {
+ zend_uchar *to= *row;
+
+ t.time_type= MYSQLND_TIMESTAMP_DATE;
+ t.neg= 0;
+
+ t.second_part = t.hour = t.minute = t.second = 0;
+
+ t.year = (unsigned int) sint2korr(to);
+ t.month = (unsigned int) to[2];
+ t.day = (unsigned int) to[3];
+
+ (*row)+= length;
+ } else {
+ memset(&t, 0, sizeof(t));
+ t.time_type = MYSQLND_TIMESTAMP_DATE;
+ }
+
+ length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
+
+ DBG_INF_FMT("%s", value);
+#if MYSQLND_UNICODE
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, value, length, 1);
+ mnd_sprintf_free(value);
+#if MYSQLND_UNICODE
+ } else {
+ ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE);
+ }
+#endif
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_datetime */
+static
+void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ struct st_mysqlnd_time t;
+ unsigned int length; /* First byte encodes the length*/
+ char * value;
+ DBG_ENTER("ps_fetch_datetime");
+
+ if ((length = php_mysqlnd_net_field_length(row))) {
+ zend_uchar *to= *row;
+
+ t.time_type = MYSQLND_TIMESTAMP_DATETIME;
+ t.neg = 0;
+
+ t.year = (unsigned int) sint2korr(to);
+ t.month = (unsigned int) to[2];
+ t.day = (unsigned int) to[3];
+
+ if (length > 4) {
+ t.hour = (unsigned int) to[4];
+ t.minute = (unsigned int) to[5];
+ t.second = (unsigned int) to[6];
+ } else {
+ t.hour = t.minute = t.second= 0;
+ }
+ t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0;
+
+ (*row)+= length;
+ } else {
+ memset(&t, 0, sizeof(t));
+ t.time_type = MYSQLND_TIMESTAMP_DATETIME;
+ }
+
+ length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
+
+ DBG_INF_FMT("%s", value);
+#if MYSQLND_UNICODE
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, value, length, 1);
+ mnd_sprintf_free(value);
+#if MYSQLND_UNICODE
+ } else {
+ ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);
+ }
+#endif
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_string */
+static
+void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ /*
+ For now just copy, before we make it possible
+ to write \0 to the row buffer
+ */
+ unsigned long length = php_mysqlnd_net_field_length(row);
+ DBG_ENTER("ps_fetch_string");
+ DBG_INF_FMT("len = %lu", length);
+#if MYSQLND_UNICODE
+ if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) {
+ DBG_INF("Binary charset");
+ ZVAL_STRINGL(zv, (char *)*row, length, 1);
+ } else {
+ DBG_INF_FMT("copying from the row buffer");
+ ZVAL_UTF8_STRINGL(zv, (char*)*row, length, ZSTR_DUPLICATE);
+ }
+#else
+ DBG_INF("copying from the row buffer");
+ ZVAL_STRINGL(zv, (char *)*row, length, 1);
+#endif
+
+ (*row) += length;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_bit */
+static
+void ps_fetch_bit(zval *zv, const MYSQLND_FIELD * const field,
+ unsigned int pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ unsigned long length= php_mysqlnd_net_field_length(row);
+ ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, length TSRMLS_CC);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_init_ps_fetch_subsystem */
+void _mysqlnd_init_ps_fetch_subsystem()
+{
+ memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions));
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func = ps_fetch_null;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type = IS_DOUBLE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_time;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_date;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bit;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = 8;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type = IS_LONG;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type = IS_STRING;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type = IS_STRING;
+
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
+ mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_copy_it */
+static enum_func_status
+mysqlnd_stmt_copy_it(zval *** copies, zval *original, unsigned int param_count, unsigned int current TSRMLS_DC)
+{
+ if (!*copies) {
+ *copies = mnd_ecalloc(param_count, sizeof(zval *));
+ }
+ if (*copies) {
+ MAKE_STD_ZVAL((*copies)[current]);
+ *(*copies)[current] = *original;
+ Z_SET_REFCOUNT_P((*copies)[current], 1);
+ zval_copy_ctor((*copies)[current]);
+ return PASS;
+ }
+ return FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_execute_store_params */
+static enum_func_status
+mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s->data;
+ unsigned int i = 0;
+ zend_uchar * provided_buffer = *buf;
+ size_t left = (*buf_len - (*p - *buf));
+ size_t data_size = 0;
+ zval **copies = NULL;/* if there are different types */
+ enum_func_status ret = FAIL;
+ int resend_types_next_time = 0;
+ size_t null_byte_offset;
+
+ DBG_ENTER("mysqlnd_stmt_execute_store_params");
+
+ {
+ unsigned int null_count = (stmt->param_count + 7) / 8;
+ /* give it some reserved space - 20 bytes */
+ if (left < (null_count + 20)) {
+ unsigned int offset = *p - *buf;
+ zend_uchar *tmp_buf;
+ *buf_len = offset + null_count + 20;
+ tmp_buf = mnd_emalloc(*buf_len);
+ if (!tmp_buf) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ memcpy(tmp_buf, *buf, offset);
+ if (*buf != provided_buffer) {
+ mnd_efree(*buf);
+ }
+ *buf = tmp_buf;
+
+ /* Update our pos pointer */
+ *p = *buf + offset;
+ }
+ /* put `null` bytes */
+ null_byte_offset = *p - *buf;
+ memset(*p, 0, null_count);
+ *p += null_count;
+ }
+
+/* 1. Store type information */
+ /*
+ check if need to send the types even if stmt->send_types_to_server is 0. This is because
+ if we send "i" (42) then the type will be int and the server will expect int. However, if next
+ time we try to send > LONG_MAX, the conversion to string will send a string and the server
+ won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
+ occur, and force resend for the next execution.
+ */
+ for (i = 0; i < stmt->param_count; i++) {
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL &&
+ (stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG))
+ {
+ /* always copy the var, because we do many conversions */
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
+ PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
+ {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ /*
+ if it doesn't fit in a long send it as a string.
+ Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ */
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ convert_to_double_ex(&tmp_data);
+ if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
+ stmt->send_types_to_server = resend_types_next_time = 1;
+ }
+ }
+ }
+ }
+
+ int1store(*p, stmt->send_types_to_server);
+ (*p)++;
+
+ if (stmt->send_types_to_server) {
+ /* 2 bytes per type, and leave 20 bytes for future use */
+ if (left < ((stmt->param_count * 2) + 20)) {
+ unsigned int offset = *p - *buf;
+ zend_uchar *tmp_buf;
+ *buf_len = offset + stmt->param_count * 2 + 20;
+ tmp_buf = mnd_emalloc(*buf_len);
+ if (!tmp_buf) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ memcpy(tmp_buf, *buf, offset);
+ if (*buf != provided_buffer) {
+ mnd_efree(*buf);
+ }
+ *buf = tmp_buf;
+
+ /* Update our pos pointer */
+ *p = *buf + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++) {
+ short current_type = stmt->param_bind[i].type;
+ /* our types are not unsigned */
+#if SIZEOF_LONG==8
+ if (current_type == MYSQL_TYPE_LONG) {
+ current_type = MYSQL_TYPE_LONGLONG;
+ }
+#endif
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ /*
+ if it doesn't fit in a long send it as a string.
+ Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ */
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+
+ convert_to_double_ex(&tmp_data);
+ if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
+ convert_to_string_ex(&tmp_data);
+ current_type = MYSQL_TYPE_VAR_STRING;
+ /*
+ don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
+ we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
+ if the type is however not long, then we will do a goto in the next switch.
+ We want to preserve the original bind type given by the user. Thus, we do these hacks.
+ */
+ } else {
+ convert_to_long_ex(&tmp_data);
+ }
+ }
+ }
+ int2store(*p, current_type);
+ *p+= 2;
+ }
+ }
+ stmt->send_types_to_server = resend_types_next_time;
+
+/* 2. Store data */
+ /* 2.1 Calculate how much space we need */
+ for (i = 0; i < stmt->param_count; i++) {
+ unsigned int j;
+ zval *the_var = stmt->param_bind[i].zv;
+
+ if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
+ continue;
+ }
+ for (j = i + 1; j < stmt->param_count; j++) {
+ if (stmt->param_bind[j].zv == the_var) {
+ /* Double binding of the same zval, make a copy */
+ if (!copies || !copies[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ }
+ break;
+ }
+ }
+
+ switch (stmt->param_bind[i].type) {
+ case MYSQL_TYPE_DOUBLE:
+ data_size += 8;
+ if (Z_TYPE_P(the_var) != IS_DOUBLE) {
+ if (!copies || !copies[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ }
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ if (Z_TYPE_P(tmp_data) == IS_STRING) {
+ goto use_string;
+ }
+ convert_to_long_ex(&tmp_data);
+ }
+ data_size += 8;
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ if (Z_TYPE_P(tmp_data) == IS_STRING) {
+ goto use_string;
+ }
+ convert_to_long_ex(&tmp_data);
+ }
+ data_size += 4;
+ break;
+ case MYSQL_TYPE_LONG_BLOB:
+ if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
+ /*
+ User hasn't sent anything, we will send empty string.
+ Empty string has length of 0, encoded in 1 byte. No real
+ data will follows after it.
+ */
+ data_size++;
+ }
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+use_string:
+ data_size += 8; /* max 8 bytes for size */
+#if MYSQLND_UNICODE
+ if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
+#else
+ if (Z_TYPE_P(the_var) != IS_STRING)
+#endif
+ {
+ if (!copies || !copies[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ }
+ the_var = copies[i];
+#if MYSQLND_UNICODE
+ if (Z_TYPE_P(the_var) == IS_UNICODE) {
+ zval_unicode_to_string_ex(the_var, UG(utf8_conv) TSRMLS_CC);
+ }
+#endif
+ }
+ convert_to_string_ex(&the_var);
+ data_size += Z_STRLEN_P(the_var);
+ break;
+ }
+ }
+
+ /* 2.2 Enlarge the buffer, if needed */
+ left = (*buf_len - (*p - *buf));
+ if (left < data_size) {
+ unsigned int offset = *p - *buf;
+ zend_uchar *tmp_buf;
+ *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
+ tmp_buf = mnd_emalloc(*buf_len);
+ if (!tmp_buf) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ memcpy(tmp_buf, *buf, offset);
+ /*
+ When too many columns the buffer provided to the function might not be sufficient.
+ In this case new buffer has been allocated above. When we allocate a buffer and then
+ allocate a bigger one here, we should free the first one.
+ */
+ if (*buf != provided_buffer) {
+ mnd_efree(*buf);
+ }
+ *buf = tmp_buf;
+ /* Update our pos pointer */
+ *p = *buf + offset;
+ }
+
+ /* 2.3 Store the actual data */
+ for (i = 0; i < stmt->param_count; i++) {
+ zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ /* Handle long data */
+ if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
+ (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
+ } else {
+ switch (stmt->param_bind[i].type) {
+ case MYSQL_TYPE_DOUBLE:
+ convert_to_double_ex(&data);
+ float8store(*p, Z_DVAL_P(data));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ if (Z_TYPE_P(data) == IS_STRING) {
+ goto send_string;
+ }
+ /* data has alreade been converted to long */
+ int8store(*p, Z_LVAL_P(data));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONG:
+ if (Z_TYPE_P(data) == IS_STRING) {
+ goto send_string;
+ }
+ /* data has alreade been converted to long */
+ int4store(*p, Z_LVAL_P(data));
+ (*p) += 4;
+ break;
+ case MYSQL_TYPE_LONG_BLOB:
+ if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
+ stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
+ } else {
+ /* send_long_data() not called, send empty string */
+ *p = php_mysqlnd_net_store_length(*p, 0);
+ }
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+send_string:
+ {
+ unsigned int len = Z_STRLEN_P(data);
+ /* to is after p. The latter hasn't been moved */
+ *p = php_mysqlnd_net_store_length(*p, len);
+ memcpy(*p, Z_STRVAL_P(data), len);
+ (*p) += len;
+ }
+ break;
+ default:
+ /* Won't happen, but set to NULL */
+ (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
+ break;
+ }
+ }
+ }
+ ret = PASS;
+end:
+ if (copies) {
+ for (i = 0; i < stmt->param_count; i++) {
+ if (copies[i]) {
+ zval_ptr_dtor(&copies[i]);
+ }
+ }
+ mnd_efree(copies);
+ }
+
+ DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_execute_generate_request */
+enum_func_status
+mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s->data;
+ zend_uchar *p = stmt->execute_cmd_buffer.buffer,
+ *cmd_buffer = stmt->execute_cmd_buffer.buffer;
+ size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_stmt_execute_generate_request");
+
+ int4store(p, stmt->stmt_id);
+ p += 4;
+
+ /* flags is 4 bytes, we store just 1 */
+ int1store(p, (zend_uchar) stmt->flags);
+ p++;
+
+ /* Make it all zero */
+ int4store(p, 0);
+
+ int1store(p, 1); /* and send 1 for iteration count */
+ p+= 4;
+
+ ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length TSRMLS_CC);
+
+ *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
+ *request_len = (p - cmd_buffer);
+ *request = cmd_buffer;
+ DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
new file mode 100644
index 0000000..2e28906
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -0,0 +1,1702 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_block_alloc.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_result_meta.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
+
+#define MYSQLND_SILENT
+
+
+/* {{{ mysqlnd_res::initialize_result_set_rest */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC)
+{
+ unsigned int i;
+ zval **data_cursor = result->stored_data? result->stored_data->data:NULL;
+ zval **data_begin = result->stored_data? result->stored_data->data:NULL;
+ unsigned int field_count = result->meta? result->meta->field_count : 0;
+ uint64_t row_count = result->stored_data? result->stored_data->row_count:0;
+ enum_func_status ret = PASS;
+ DBG_ENTER("mysqlnd_res::initialize_result_set_rest");
+
+ if (!data_cursor || row_count == result->stored_data->initialized_rows) {
+ DBG_RETURN(ret);
+ }
+ while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
+ if (NULL == data_cursor[0]) {
+ enum_func_status rc = result->m.row_decoder(
+ result->stored_data->row_buffers[(data_cursor - data_begin) / field_count],
+ data_cursor,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (rc != PASS) {
+ ret = FAIL;
+ break;
+ }
+ result->stored_data->initialized_rows++;
+ for (i = 0; i < result->field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(data_cursor[i]);
+ if (result->meta->fields[i].max_length < len) {
+ result->meta->fields[i].max_length = len;
+ }
+ }
+ }
+ }
+ data_cursor += field_count;
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_rset_zval_ptr_dtor */
+static void
+mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_rset_zval_ptr_dtor");
+ if (!zv || !*zv) {
+ *copy_ctor_called = FALSE;
+ DBG_ERR_FMT("zv was NULL");
+ DBG_VOID_RETURN;
+ }
+ /*
+ This zval is not from the cache block.
+ Thus the refcount is -1 than of a zval from the cache,
+ because the zvals from the cache are owned by it.
+ */
+ if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) {
+ *copy_ctor_called = FALSE;
+ ; /* do nothing, zval_ptr_dtor will do the job*/
+ } else if (Z_REFCOUNT_PP(zv) > 1) {
+ /*
+ Not a prepared statement, then we have to
+ call copy_ctor and then zval_ptr_dtor()
+
+ In Unicode mode the destruction of the zvals should not call
+ zval_copy_ctor() because then we will leak.
+ I suppose we can use UG(unicode) in mysqlnd.c when freeing a result set
+ to check if we need to call copy_ctor().
+
+ If the type is IS_UNICODE, which can happen with PHP6, then we don't
+ need to copy_ctor, as the data doesn't point to our internal buffers.
+ If it's string (in PHP5 always) and in PHP6 if data is binary, then
+ it still points to internal buffers and has to be copied.
+ */
+ if (Z_TYPE_PP(zv) == IS_STRING) {
+ zval_copy_ctor(*zv);
+ }
+ *copy_ctor_called = TRUE;
+ } else {
+ /*
+ noone but us point to this, so we can safely ZVAL_NULL the zval,
+ so Zend does not try to free what the zval points to - which is
+ in result set buffers
+ */
+ *copy_ctor_called = FALSE;
+ if (Z_TYPE_PP(zv) == IS_STRING) {
+ ZVAL_NULL(*zv);
+ }
+ }
+ zval_ptr_dtor(zv);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::unbuffered_free_last_data */
+static void
+MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRMLS_DC)
+{
+ MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
+
+ DBG_ENTER("mysqlnd_res::unbuffered_free_last_data");
+
+ if (!unbuf) {
+ DBG_VOID_RETURN;
+ }
+
+ if (unbuf->last_row_data) {
+ unsigned int i, ctor_called_count = 0;
+ zend_bool copy_ctor_called;
+ MYSQLND_STATS *global_stats = result->conn? result->conn->stats:NULL;
+
+ for (i = 0; i < result->field_count; i++) {
+ mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, &copy_ctor_called TSRMLS_CC);
+ if (copy_ctor_called) {
+ ++ctor_called_count;
+ }
+ }
+ DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count);
+ /* By using value3 macros we hold a mutex only once, there is no value2 */
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats,
+ STAT_COPY_ON_WRITE_PERFORMED,
+ ctor_called_count,
+ STAT_COPY_ON_WRITE_SAVED,
+ result->field_count - ctor_called_count);
+ /* Free last row's zvals */
+ mnd_efree(unbuf->last_row_data);
+ unbuf->last_row_data = NULL;
+ }
+ if (unbuf->last_row_buffer) {
+ DBG_INF("Freeing last row buffer");
+ /* Nothing points to this buffer now, free it */
+ unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer TSRMLS_CC);
+ unbuf->last_row_buffer = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_buffered_data */
+static void
+MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC)
+{
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+ unsigned int field_count = result->field_count;
+ int64_t row;
+
+ DBG_ENTER("mysqlnd_res::free_buffered_data");
+ DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
+
+ if (set->data) {
+ unsigned int copy_on_write_performed = 0;
+ unsigned int copy_on_write_saved = 0;
+
+ for (row = set->row_count - 1; row >= 0; row--) {
+ zval **current_row = set->data + row * field_count;
+ MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
+ int64_t col;
+
+ if (current_row != NULL) {
+ for (col = field_count - 1; col >= 0; --col) {
+ if (current_row[col]) {
+ zend_bool copy_ctor_called;
+ mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, &copy_ctor_called TSRMLS_CC);
+ if (copy_ctor_called) {
+ ++copy_on_write_performed;
+ } else {
+ ++copy_on_write_saved;
+ }
+ }
+ }
+ }
+ current_buffer->free_chunk(current_buffer TSRMLS_CC);
+ }
+
+ MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed,
+ STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved);
+ mnd_efree(set->data);
+ set->data = NULL;
+ }
+
+ if (set->row_buffers) {
+ mnd_efree(set->row_buffers);
+ set->row_buffers = NULL;
+ }
+ set->data_cursor = NULL;
+ set->row_count = 0;
+
+ mnd_efree(set);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_result_buffers */
+static void
+MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::free_result_buffers");
+ DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
+
+ if (result->unbuf) {
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ mnd_efree(result->unbuf);
+ result->unbuf = NULL;
+ } else if (result->stored_data) {
+ result->m.free_buffered_data(result TSRMLS_CC);
+ result->stored_data = NULL;
+ }
+
+ if (result->lengths) {
+ mnd_efree(result->lengths);
+ result->lengths = NULL;
+ }
+
+ if (result->row_packet) {
+ PACKET_FREE(result->row_packet);
+ result->row_packet = NULL;
+ }
+
+ if (result->result_set_memory_pool) {
+ mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
+ result->result_set_memory_pool = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result_contents */
+static
+void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_internal_free_result_contents");
+
+ result->m.free_result_buffers(result TSRMLS_CC);
+
+ if (result->meta) {
+ result->meta->m->free_metadata(result->meta TSRMLS_CC);
+ result->meta = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_result */
+static
+void mysqlnd_internal_free_result(MYSQLND_RES * result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_internal_free_result");
+ result->m.free_result_contents(result TSRMLS_CC);
+
+ if (result->conn) {
+ result->conn->m->free_reference(result->conn TSRMLS_CC);
+ result->conn = NULL;
+ }
+
+ mnd_pefree(result, result->persistent);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::read_result_metadata */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::read_result_metadata");
+
+ /*
+ Make it safe to call it repeatedly for PS -
+ better free and allocate a new because the number of field might change
+ (select *) with altered table. Also for statements which skip the PS
+ infrastructure!
+ */
+ if (result->meta) {
+ result->meta->m->free_metadata(result->meta TSRMLS_CC);
+ result->meta = NULL;
+ }
+
+ result->meta = result->m.result_meta_init(result->field_count, result->persistent TSRMLS_CC);
+ if (!result->meta) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ /* 1. Read all fields metadata */
+
+ /* It's safe to reread without freeing */
+ if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) {
+ result->m.free_result_contents(result TSRMLS_CC);
+ DBG_RETURN(FAIL);
+ }
+ /* COM_FIELD_LIST is broken and has premature EOF, thus we need to hack here and in mysqlnd_res_meta.c */
+ result->field_count = result->meta->field_count;
+
+ /*
+ 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
+ should consume.
+ 3. If there is a result set, it follows. The last packet will have 'eof' set
+ If PS, then no result set follows.
+ */
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_query_read_result_set_header */
+enum_func_status
+mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
+ enum_func_status ret;
+ MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
+ MYSQLND_PACKET_EOF * fields_eof = NULL;
+
+ DBG_ENTER("mysqlnd_query_read_result_set_header");
+ DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);
+
+ ret = FAIL;
+ do {
+ rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!rset_header) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ break;
+ }
+
+ SET_ERROR_AFF_ROWS(conn);
+
+ if (FAIL == (ret = PACKET_READ(rset_header, conn))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header");
+ break;
+ }
+
+ if (rset_header->error_info.error_no) {
+ /*
+ Cover a protocol design error: error packet does not
+ contain the server status. Therefore, the client has no way
+ to find out whether there are more result sets of
+ a multiple-result-set statement pending. Luckily, in 5.0 an
+ error always aborts execution of a statement, wherever it is
+ a multi-statement or a stored procedure, so it should be
+ safe to unconditionally turn off the flag here.
+ */
+ conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ /*
+ This will copy the error code and the messages, as they
+ are buffers in the struct
+ */
+ COPY_CLIENT_ERROR(*conn->error_info, rset_header->error_info);
+ ret = FAIL;
+ DBG_ERR_FMT("error=%s", rset_header->error_info.error);
+ /* Return back from CONN_QUERY_SENT */
+ CONN_SET_STATE(conn, CONN_READY);
+ break;
+ }
+ conn->error_info->error_no = 0;
+
+ switch (rset_header->field_count) {
+ case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */
+ zend_bool is_warning;
+ DBG_INF("LOAD DATA");
+ conn->last_query_type = QUERY_LOAD_LOCAL;
+ conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
+ CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
+ ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning TSRMLS_CC);
+ CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
+ break;
+ }
+ case 0: /* UPSERT */
+ DBG_INF("UPSERT");
+ conn->last_query_type = QUERY_UPSERT;
+ conn->field_count = rset_header->field_count;
+ conn->upsert_status->warning_count = rset_header->warning_count;
+ conn->upsert_status->server_status = rset_header->server_status;
+ conn->upsert_status->affected_rows = rset_header->affected_rows;
+ conn->upsert_status->last_insert_id = rset_header->last_insert_id;
+ SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
+ rset_header->info_or_local_file, rset_header->info_or_local_file_len,
+ conn->persistent);
+ /* Result set can follow UPSERT statement, check server_status */
+ if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(conn, CONN_READY);
+ }
+ ret = PASS;
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
+ break;
+ default: do { /* Result set */
+ MYSQLND_RES * result;
+ enum_mysqlnd_collected_stats statistic = STAT_LAST;
+
+ DBG_INF("Result set pending");
+ SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);
+
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
+ memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ /* restore after zeroing */
+ SET_ERROR_AFF_ROWS(conn);
+
+ conn->last_query_type = QUERY_SELECT;
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ /* PS has already allocated it */
+ conn->field_count = rset_header->field_count;
+ if (!stmt) {
+ result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent TSRMLS_CC);
+ } else {
+ if (!stmt->result) {
+ DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
+ /*
+ This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
+ prepared statements can't send result set metadata for these queries
+ on prepare stage. Read it now.
+ */
+ result = stmt->result = conn->m->result_init(rset_header->field_count, stmt->persistent TSRMLS_CC);
+ } else {
+ /*
+ Update result set metadata if it for some reason changed between
+ prepare and execute, i.e.:
+ - in case of 'SELECT ?' we don't know column type unless data was
+ supplied to mysql_stmt_execute, so updated column type is sent
+ now.
+ - if data dictionary changed between prepare and execute, for
+ example a table used in the query was altered.
+ Note, that now (4.1.3) we always send metadata in reply to
+ COM_STMT_EXECUTE (even if it is not necessary), so either this or
+ previous branch always works.
+ */
+ }
+ result = stmt->result;
+ }
+ if (!result) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ break;
+ }
+
+ if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) {
+ /* For PS, we leave them in Prepared state */
+ if (!stmt && conn->current_result) {
+ mnd_efree(conn->current_result);
+ conn->current_result = NULL;
+ }
+ DBG_ERR("Error ocurred while reading metadata");
+ break;
+ }
+
+ /* Check for SERVER_STATUS_MORE_RESULTS if needed */
+ fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!fields_eof) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ break;
+ }
+ if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
+ DBG_ERR("Error ocurred while reading the EOF packet");
+ result->m.free_result_contents(result TSRMLS_CC);
+ mnd_efree(result);
+ if (!stmt) {
+ conn->current_result = NULL;
+ } else {
+ stmt->result = NULL;
+ memset(stmt, 0, sizeof(MYSQLND_STMT));
+ stmt->state = MYSQLND_STMT_INITTED;
+ }
+ } else {
+ unsigned int to_log = MYSQLND_G(log_mask);
+ to_log &= fields_eof->server_status;
+ DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
+ conn->upsert_status->warning_count = fields_eof->warning_count;
+ /*
+ If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
+ The first packet after sending the query/com_execute has the bit set only
+ in this cases. Not sure why it's a needed but it marks that the whole stream
+ will include many result sets. What actually matters are the bits set at the end
+ of every result set (the EOF packet).
+ */
+ conn->upsert_status->server_status = fields_eof->server_status;
+ if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
+ statistic = STAT_BAD_INDEX_USED;
+ } else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
+ statistic = STAT_NO_INDEX_USED;
+ } else if (fields_eof->server_status & SERVER_QUERY_WAS_SLOW) {
+ statistic = STAT_QUERY_WAS_SLOW;
+ }
+ if (to_log) {
+#if A0
+ char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
+ php_log_err(backtrace TSRMLS_CC);
+ efree(backtrace);
+#endif
+ }
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
+ }
+ } while (0);
+ PACKET_FREE(fields_eof);
+ break; /* switch break */
+ }
+ } while (0);
+ PACKET_FREE(rset_header);
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_lengths_buffered */
+/*
+ Do lazy initialization for buffered results. As PHP strings have
+ length inside, this function makes not much sense in the context
+ of PHP, to be called as separate function. But let's have it for
+ completeness.
+*/
+static unsigned long *
+mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC)
+{
+ unsigned int i;
+ zval **previous_row;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+
+ /*
+ If:
+ - unbuffered result
+ - first row has not been read
+ - last_row has been read
+ */
+ if (set->data_cursor == NULL ||
+ set->data_cursor == set->data ||
+ ((set->data_cursor - set->data) > (set->row_count * result->meta->field_count) ))
+ {
+ return NULL;/* No rows or no more rows */
+ }
+
+ previous_row = set->data_cursor - result->meta->field_count;
+ for (i = 0; i < result->meta->field_count; i++) {
+ result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]);
+ }
+
+ return result->lengths;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_lengths_unbuffered */
+static unsigned long *
+mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC)
+{
+ /* simulate output of libmysql */
+ return (!result->unbuf || result->unbuf->last_row_data || result->unbuf->eof_reached)? result->lengths:NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_lengths */
+PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC)
+{
+ return result->m.fetch_lengths? result->m.fetch_lengths(result TSRMLS_CC) : NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_unbuffered_c */
+static MYSQLND_ROW_C
+mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND_ROW_C retrow = NULL;
+ unsigned int i,
+ field_count = result->field_count;
+ MYSQLND_PACKET_ROW *row_packet = result->row_packet;
+ unsigned long *lengths = result->lengths;
+
+ DBG_ENTER("mysqlnd_fetch_row_unbuffered_c");
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ DBG_RETURN(retrow);
+ }
+ if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_RETURN(retrow);
+ }
+ if (!row_packet) {
+ /* Not fully initialized object that is being cleaned up */
+ DBG_RETURN(retrow);
+ }
+ /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
+ row_packet->skip_extraction = FALSE;
+
+ /*
+ If we skip rows (row == NULL) we have to
+ result->m.unbuffered_free_last_data() before it. The function returns always true.
+ */
+ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ result->unbuf->row_count++;
+
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+
+ result->unbuf->last_row_data = row_packet->fields;
+ result->unbuf->last_row_buffer = row_packet->row_buffer;
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
+
+ MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+
+ if (!row_packet->skip_extraction) {
+ MYSQLND_FIELD *field = result->meta->fields;
+ struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
+
+ enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
+ result->unbuf->last_row_data,
+ row_packet->field_count,
+ row_packet->fields_metadata,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (PASS != rc) {
+ DBG_RETURN(retrow);
+ }
+
+ retrow = mnd_malloc(result->field_count * sizeof(char *));
+ if (retrow) {
+ for (i = 0; i < field_count; i++, field++, hash_key++) {
+ zval *data = result->unbuf->last_row_data[i];
+ unsigned int len;
+
+ if (Z_TYPE_P(data) != IS_NULL) {
+ convert_to_string(data);
+ retrow[i] = Z_STRVAL_P(data);
+ len = Z_STRLEN_P(data);
+ } else {
+ retrow[i] = NULL;
+ len = 0;
+ }
+
+ if (lengths) {
+ lengths[i] = len;
+ }
+
+ if (field->max_length < len) {
+ field->max_length = len;
+ }
+ }
+ } else {
+ SET_OOM_ERROR(*result->conn->error_info);
+ }
+ }
+ } else if (ret == FAIL) {
+ if (row_packet->error_info.error_no) {
+ COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
+ }
+ CONN_SET_STATE(result->conn, CONN_READY);
+ result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
+ } else if (row_packet->eof) {
+ /* Mark the connection as usable again */
+ DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
+ result->unbuf->eof_reached = TRUE;
+ result->conn->upsert_status->warning_count = row_packet->warning_count;
+ result->conn->upsert_status->server_status = row_packet->server_status;
+ /*
+ result->row_packet will be cleaned when
+ destroying the result object
+ */
+ if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(result->conn, CONN_READY);
+ }
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ }
+
+ DBG_RETURN(retrow);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_unbuffered */
+static enum_func_status
+mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+{
+ enum_func_status ret;
+ zval *row = (zval *) param;
+ MYSQLND_PACKET_ROW *row_packet = result->row_packet;
+
+ DBG_ENTER("mysqlnd_fetch_row_unbuffered");
+
+ *fetched_anything = FALSE;
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ DBG_RETURN(PASS);
+ }
+ if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_RETURN(FAIL);
+ }
+ if (!row_packet) {
+ /* Not fully initialized object that is being cleaned up */
+ DBG_RETURN(FAIL);
+ }
+ /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
+ row_packet->skip_extraction = row? FALSE:TRUE;
+
+ /*
+ If we skip rows (row == NULL) we have to
+ result->m.unbuffered_free_last_data() before it. The function returns always true.
+ */
+ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+
+ result->unbuf->last_row_data = row_packet->fields;
+ result->unbuf->last_row_buffer = row_packet->row_buffer;
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
+
+ MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+
+ if (!row_packet->skip_extraction) {
+ HashTable *row_ht = Z_ARRVAL_P(row);
+ MYSQLND_FIELD *field = result->meta->fields;
+ struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
+ unsigned int i, field_count = result->field_count;
+ unsigned long *lengths = result->lengths;
+
+ enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
+ result->unbuf->last_row_data,
+ field_count,
+ row_packet->fields_metadata,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (PASS != rc) {
+ DBG_RETURN(FAIL);
+ }
+ for (i = 0; i < field_count; i++, field++, hash_key++) {
+ zval *data = result->unbuf->last_row_data[i];
+ unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+
+ if (lengths) {
+ lengths[i] = len;
+ }
+
+ if (flags & MYSQLND_FETCH_NUM) {
+ Z_ADDREF_P(data);
+ zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL);
+ }
+ if (flags & MYSQLND_FETCH_ASSOC) {
+ /* zend_hash_quick_update needs length + trailing zero */
+ /* QQ: Error handling ? */
+ /*
+ zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
+ the index is a numeric and convert it to it. This however means constant
+ hashing of the column name, which is not needed as it can be precomputed.
+ */
+ Z_ADDREF_P(data);
+ if (hash_key->is_numeric == FALSE) {
+#if MYSQLND_UNICODE
+ zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
+ hash_key->ustr,
+ hash_key->ulen + 1,
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+#else
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ field->name,
+ field->name_length + 1,
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+#endif
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ }
+ if (field->max_length < len) {
+ field->max_length = len;
+ }
+ }
+ }
+ *fetched_anything = TRUE;
+ result->unbuf->row_count++;
+ } else if (ret == FAIL) {
+ if (row_packet->error_info.error_no) {
+ COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
+ }
+ CONN_SET_STATE(result->conn, CONN_READY);
+ result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
+ } else if (row_packet->eof) {
+ /* Mark the connection as usable again */
+ DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
+ result->unbuf->eof_reached = TRUE;
+ result->conn->upsert_status->warning_count = row_packet->warning_count;
+ result->conn->upsert_status->server_status = row_packet->server_status;
+ /*
+ result->row_packet will be cleaned when
+ destroying the result object
+ */
+ if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(result->conn, CONN_READY);
+ }
+ result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ }
+
+ DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::use_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::use_result");
+
+ SET_EMPTY_ERROR(*result->conn->error_info);
+
+ if (ps == FALSE) {
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered;
+ result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
+ result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+ if (!result->lengths) {
+ goto oom;
+ }
+ } else {
+ result->type = MYSQLND_RES_PS_UNBUF;
+ result->m.fetch_row = NULL;
+ /* result->m.fetch_row() will be set in mysqlnd_ps.c */
+ result->m.fetch_lengths = NULL; /* makes no sense */
+ result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+ result->lengths = NULL;
+ }
+
+ result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ if (!result->result_set_memory_pool || !result->unbuf) {
+ goto oom;
+ }
+
+ /*
+ Will be freed in the mysqlnd_internal_free_result_contents() called
+ by the resource destructor. mysqlnd_fetch_row_unbuffered() expects
+ this to be not NULL.
+ */
+ /* FALSE = non-persistent */
+ result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
+ if (!result->row_packet) {
+ goto oom;
+ }
+ result->row_packet->result_set_memory_pool = result->result_set_memory_pool;
+ result->row_packet->field_count = result->field_count;
+ result->row_packet->binary_protocol = ps;
+ result->row_packet->fields_metadata = result->meta->fields;
+ result->row_packet->bit_fields_count = result->meta->bit_fields_count;
+ result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
+
+ DBG_RETURN(result);
+oom:
+ SET_OOM_ERROR(*result->conn->error_info);
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_buffered_c */
+static MYSQLND_ROW_C
+mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC)
+{
+ MYSQLND_ROW_C ret = NULL;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+
+ DBG_ENTER("mysqlnd_fetch_row_buffered_c");
+
+ /* If we haven't read everything */
+ if (set->data_cursor &&
+ (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
+ {
+ zval **current_row = set->data_cursor;
+ MYSQLND_FIELD *field = result->meta->fields;
+ struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
+ unsigned int i;
+
+ if (NULL == current_row[0]) {
+ uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
+ enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
+ current_row,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (rc != PASS) {
+ DBG_RETURN(ret);
+ }
+ set->initialized_rows++;
+ for (i = 0; i < result->field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (field->max_length < len) {
+ field->max_length = len;
+ }
+ }
+ }
+ }
+
+ set->data_cursor += result->meta->field_count;
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+
+ ret = mnd_malloc(result->field_count * sizeof(char *));
+ if (ret) {
+ for (i = 0; i < result->field_count; i++, field++, hash_key++) {
+ zval *data = current_row[i];
+
+ if (Z_TYPE_P(data) != IS_NULL) {
+ convert_to_string(data);
+ ret[i] = Z_STRVAL_P(data);
+ } else {
+ ret[i] = NULL;
+ }
+ }
+ }
+ /* there is no conn handle in this function thus we can't set OOM in error_info */
+ } else {
+ set->data_cursor = NULL;
+ DBG_INF("EOF reached");
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_row_buffered */
+static enum_func_status
+mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+{
+ unsigned int i;
+ zval *row = (zval *) param;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_fetch_row_buffered");
+
+ /* If we haven't read everything */
+ if (set->data_cursor &&
+ (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
+ {
+ zval **current_row = set->data_cursor;
+ MYSQLND_FIELD *field = result->meta->fields;
+ struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
+
+ if (NULL == current_row[0]) {
+ uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
+ enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
+ current_row,
+ result->meta->field_count,
+ result->meta->fields,
+ result->conn->options->numeric_and_datetime_as_unicode,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (rc != PASS) {
+ DBG_RETURN(FAIL);
+ }
+ set->initialized_rows++;
+ for (i = 0; i < result->field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (field->max_length < len) {
+ field->max_length = len;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < result->field_count; i++, field++, hash_key++) {
+ zval *data = current_row[i];
+
+ if (flags & MYSQLND_FETCH_NUM) {
+ Z_ADDREF_P(data);
+ zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
+ }
+ if (flags & MYSQLND_FETCH_ASSOC) {
+ /* zend_hash_quick_update needs length + trailing zero */
+ /* QQ: Error handling ? */
+ /*
+ zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
+ the index is a numeric and convert it to it. This however means constant
+ hashing of the column name, which is not needed as it can be precomputed.
+ */
+ Z_ADDREF_P(data);
+ if (hash_key->is_numeric == FALSE) {
+#if MYSQLND_UNICODE
+ zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
+ hash_key->ustr,
+ hash_key->ulen + 1,
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+#else
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ field->name,
+ field->name_length + 1,
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+#endif
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ hash_key->key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ }
+ }
+ set->data_cursor += result->meta->field_count;
+ *fetched_anything = TRUE;
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ ret = PASS;
+ } else {
+ set->data_cursor = NULL;
+ *fetched_anything = FALSE;
+ ret = PASS;
+ DBG_INF("EOF reached");
+ }
+ DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+#define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY 2
+
+/* {{{ mysqlnd_res::store_result_fetch_data */
+enum_func_status
+MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result,
+ MYSQLND_RES_METADATA *meta,
+ zend_bool binary_protocol TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND_PACKET_ROW *row_packet = NULL;
+ unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
+ MYSQLND_RES_BUFFERED *set;
+
+ DBG_ENTER("mysqlnd_res::store_result_fetch_data");
+
+ result->stored_data = set = mnd_ecalloc(1, sizeof(MYSQLND_RES_BUFFERED));
+ if (!set) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ if (free_rows) {
+ set->row_buffers = mnd_emalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
+ if (!set->row_buffers) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ }
+ set->references = 1;
+
+ /* non-persistent */
+ row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!row_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ row_packet->result_set_memory_pool = result->result_set_memory_pool;
+ row_packet->field_count = meta->field_count;
+ row_packet->binary_protocol = binary_protocol;
+ row_packet->fields_metadata = meta->fields;
+ row_packet->bit_fields_count = meta->bit_fields_count;
+ row_packet->bit_fields_total_len = meta->bit_fields_total_len;
+
+ row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
+
+ while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
+ if (!free_rows) {
+ uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
+ MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
+ total_allocated_rows += set->row_count;
+
+ /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
+ if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ new_row_buffers = mnd_erealloc(set->row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
+ if (!new_row_buffers) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ set->row_buffers = new_row_buffers;
+ }
+ free_rows--;
+ set->row_buffers[set->row_count] = row_packet->row_buffer;
+
+ set->row_count++;
+
+ /* So row_packet's destructor function won't efree() it */
+ row_packet->fields = NULL;
+ row_packet->row_buffer = NULL;
+
+ /*
+ No need to FREE_ALLOCA as we can reuse the
+ 'lengths' and 'fields' arrays. For lengths its absolutely safe.
+ 'fields' is reused because the ownership of the strings has been
+ transfered above.
+ */
+ }
+ /* Overflow ? */
+ if (set->row_count) {
+ /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
+ if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
+ set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *)));
+ if (!set->data) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *)));
+ }
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
+ binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
+ STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
+ set->row_count);
+
+ /* Finally clean */
+ if (row_packet->eof) {
+ conn->upsert_status->warning_count = row_packet->warning_count;
+ conn->upsert_status->server_status = row_packet->server_status;
+ }
+ /* save some memory */
+ if (free_rows) {
+ /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
+ if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ goto end;
+ }
+ set->row_buffers = mnd_erealloc(set->row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
+ }
+
+ if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
+ CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ } else {
+ CONN_SET_STATE(conn, CONN_READY);
+ }
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
+ } else {
+ /* Position at the first row */
+ set->data_cursor = set->data;
+
+ /* libmysql's documentation says it should be so for SELECT statements */
+ conn->upsert_status->affected_rows = set->row_count;
+ }
+ DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
+ ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
+end:
+ PACKET_FREE(row_packet);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::store_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
+ MYSQLND_CONN_DATA * const conn,
+ zend_bool ps_protocol TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_res::store_result");
+
+ /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
+ result->conn = conn->m->get_reference(conn TSRMLS_CC);
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_buffered;
+ result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
+ result->m.row_decoder = ps_protocol? php_mysqlnd_rowp_read_binary_protocol:
+ php_mysqlnd_rowp_read_text_protocol;
+
+ result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
+ result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+
+ if (!result->result_set_memory_pool || !result->lengths) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(NULL);
+ }
+
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+
+ ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC);
+ if (FAIL == ret) {
+ if (result->stored_data) {
+ COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ } else {
+ SET_OOM_ERROR(*conn->error_info);
+ }
+ DBG_RETURN(NULL);
+ }
+ /* libmysql's documentation says it should be so for SELECT statements */
+ conn->upsert_status->affected_rows = result->stored_data->row_count;
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::skip_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC)
+{
+ zend_bool fetched_anything;
+
+ DBG_ENTER("mysqlnd_res::skip_result");
+ /*
+ Unbuffered sets
+ A PS could be prepared - there is metadata and thus a stmt->result but the
+ fetch_row function isn't actually set (NULL), thus we have to skip these.
+ */
+ if (!result->stored_data && result->unbuf &&
+ !result->unbuf->eof_reached && result->m.fetch_row)
+ {
+ DBG_INF("skipping result");
+ /* We have to fetch all data to clean the line */
+ MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
+ result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
+ STAT_FLUSHED_PS_SETS);
+
+ while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && fetched_anything == TRUE) {
+ /* do nothing */;
+ }
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::free_result");
+
+ result->m.skip_result(result TSRMLS_CC);
+ MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
+ implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
+ STAT_FREE_RESULT_EXPLICIT);
+
+ result->m.free_result_internal(result TSRMLS_CC);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::data_seek */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::data_seek");
+ DBG_INF_FMT("row=%lu", row);
+
+ if (!result->stored_data) {
+ return FAIL;
+ }
+
+ /* libmysql just moves to the end, it does traversing of a linked list */
+ if (row >= result->stored_data->row_count) {
+ result->stored_data->data_cursor = NULL;
+ } else {
+ result->stored_data->data_cursor = result->stored_data->data + row * result->meta->field_count;
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::num_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result TSRMLS_DC)
+{
+ /* Be compatible with libmysql. We count row_count, but will return 0 */
+ return result->stored_data? result->stored_data->row_count:(result->unbuf && result->unbuf->eof_reached? result->unbuf->row_count:0);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::num_fields */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const result TSRMLS_DC)
+{
+ return result->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::fetch_field");
+ do {
+ if (result->meta) {
+ /*
+ We optimize the result set, so we don't convert all the data from raw buffer format to
+ zval arrays during store. In the case someone doesn't read all the lines this will
+ save time. However, when a metadata call is done, we need to calculate max_length.
+ We don't have control whether max_length will be used, unfortunately. Otherwise we
+ could have been able to skip that step.
+ Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
+ then we can have max_length as dynamic property, which will be calculated during runtime and
+ not during mysqli_fetch_field() time.
+ */
+ if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
+ /* we have to initialize the rest to get the updated max length */
+ if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ break;
+ }
+ }
+ DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC));
+ }
+ } while (0);
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field_direct */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::fetch_field_direct");
+ do {
+ if (result->meta) {
+ /*
+ We optimize the result set, so we don't convert all the data from raw buffer format to
+ zval arrays during store. In the case someone doesn't read all the lines this will
+ save time. However, when a metadata call is done, we need to calculate max_length.
+ We don't have control whether max_length will be used, unfortunately. Otherwise we
+ could have been able to skip that step.
+ Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
+ then we can have max_length as dynamic property, which will be calculated during runtime and
+ not during mysqli_fetch_field_direct() time.
+ */
+ if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
+ /* we have to initialized the rest to get the updated max length */
+ if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ break;
+ }
+ }
+ DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC));
+ }
+ } while (0);
+
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::fetch_fields");
+ do {
+ if (result->meta) {
+ if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ /* we have to initialize the rest to get the updated max length */
+ if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ break;
+ }
+ }
+ DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC));
+ }
+ } while (0);
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_res::field_seek */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
+{
+ MYSQLND_FIELD_OFFSET return_value = 0;
+ if (result->meta) {
+ return_value = result->meta->current_field;
+ result->meta->current_field = field_offset;
+ }
+ return return_value;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::field_tell */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_DC)
+{
+ return result->meta? result->meta->m->field_tell(result->meta TSRMLS_CC) : 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_into */
+static void
+MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags,
+ zval *return_value,
+ enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ zend_bool fetched_anything;
+
+ DBG_ENTER("mysqlnd_res::fetch_into");
+
+ if (!result->m.fetch_row) {
+ RETVAL_NULL();
+ DBG_VOID_RETURN;
+ }
+ /*
+ Hint Zend how many elements we will have in the hash. Thus it won't
+ extend and rehash the hash constantly.
+ */
+ mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2);
+ if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row");
+ RETVAL_FALSE;
+ } else if (fetched_anything == FALSE) {
+ zval_dtor(return_value);
+ switch (extension) {
+ case MYSQLND_MYSQLI:
+ RETVAL_NULL();
+ break;
+ case MYSQLND_MYSQL:
+ RETVAL_FALSE;
+ break;
+ default:exit(0);
+ }
+ }
+ /*
+ return_value is IS_NULL for no more data and an array for data. Thus it's ok
+ to return here.
+ */
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_row_c */
+static MYSQLND_ROW_C
+MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC)
+{
+ MYSQLND_ROW_C ret = NULL;
+ DBG_ENTER("mysqlnd_res::fetch_row_c");
+
+ if (result->m.fetch_row) {
+ if (result->m.fetch_row == result->m.fetch_row_normal_buffered) {
+ DBG_RETURN(mysqlnd_fetch_row_buffered_c(result TSRMLS_CC));
+ } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) {
+ DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC));
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
+ }
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_all */
+static void
+MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ zval *row;
+ ulong i = 0;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
+
+ DBG_ENTER("mysqlnd_res::fetch_all");
+
+ if ((!result->unbuf && !set)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "fetch_all can be used only with buffered sets");
+ if (result->conn) {
+ SET_CLIENT_ERROR(*result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
+ }
+ RETVAL_NULL();
+ DBG_VOID_RETURN;
+ }
+
+ /* 4 is a magic value. The cast is safe, if larger then the array will be later extended - no big deal :) */
+ mysqlnd_array_init(return_value, set? (unsigned int) set->row_count : 4);
+
+ do {
+ MAKE_STD_ZVAL(row);
+ mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
+ if (Z_TYPE_P(row) != IS_ARRAY) {
+ zval_ptr_dtor(&row);
+ break;
+ }
+ add_index_zval(return_value, i++, row);
+ } while (1);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field_data */
+static void
+MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int offset, zval *return_value TSRMLS_DC)
+{
+ zval row;
+ zval **entry;
+ unsigned int i = 0;
+
+ DBG_ENTER("mysqlnd_res::fetch_field_data");
+ DBG_INF_FMT("offset=%u", offset);
+
+ if (!result->m.fetch_row) {
+ RETVAL_NULL();
+ DBG_VOID_RETURN;
+ }
+ /*
+ Hint Zend how many elements we will have in the hash. Thus it won't
+ extend and rehash the hash constantly.
+ */
+ INIT_PZVAL(&row);
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL);
+ if (Z_TYPE(row) != IS_ARRAY) {
+ zval_dtor(&row);
+ RETVAL_NULL();
+ DBG_VOID_RETURN;
+ }
+ zend_hash_internal_pointer_reset(Z_ARRVAL(row));
+ while (i++ < offset) {
+ zend_hash_move_forward(Z_ARRVAL(row));
+ zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
+ }
+
+ zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
+
+ *return_value = **entry;
+ zval_copy_ctor(return_value);
+ Z_SET_REFCOUNT_P(return_value, 1);
+ zval_dtor(&row);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_res)
+ NULL, /* fetch_row */
+ mysqlnd_fetch_row_buffered,
+ mysqlnd_fetch_row_unbuffered,
+ MYSQLND_METHOD(mysqlnd_res, use_result),
+ MYSQLND_METHOD(mysqlnd_res, store_result),
+ MYSQLND_METHOD(mysqlnd_res, fetch_into),
+ MYSQLND_METHOD(mysqlnd_res, fetch_row_c),
+ MYSQLND_METHOD(mysqlnd_res, fetch_all),
+ MYSQLND_METHOD(mysqlnd_res, fetch_field_data),
+ MYSQLND_METHOD(mysqlnd_res, num_rows),
+ MYSQLND_METHOD(mysqlnd_res, num_fields),
+ MYSQLND_METHOD(mysqlnd_res, skip_result),
+ MYSQLND_METHOD(mysqlnd_res, data_seek),
+ MYSQLND_METHOD(mysqlnd_res, field_seek),
+ MYSQLND_METHOD(mysqlnd_res, field_tell),
+ MYSQLND_METHOD(mysqlnd_res, fetch_field),
+ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct),
+ MYSQLND_METHOD(mysqlnd_res, fetch_fields),
+ MYSQLND_METHOD(mysqlnd_res, read_result_metadata),
+ NULL, /* fetch_lengths */
+ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data),
+ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest),
+ MYSQLND_METHOD(mysqlnd_res, free_result_buffers),
+ MYSQLND_METHOD(mysqlnd_res, free_result),
+
+ mysqlnd_internal_free_result, /* free_result_internal */
+ mysqlnd_internal_free_result_contents, /* free_result_contents */
+ MYSQLND_METHOD(mysqlnd_res, free_buffered_data),
+ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data),
+
+ NULL /* row_decoder */,
+ mysqlnd_result_meta_init
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_result_init */
+PHPAPI MYSQLND_RES *
+mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_result_init");
+
+ if (!ret) {
+ DBG_RETURN(NULL);
+ }
+
+ ret->persistent = persistent;
+ ret->field_count = field_count;
+ ret->m = *mysqlnd_result_get_methods();
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h
new file mode 100644
index 0000000..f821b91
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -0,0 +1,39 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_RESULT_H
+#define MYSQLND_RESULT_H
+
+PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC);
+
+enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt TSRMLS_DC);
+
+#endif /* MYSQLND_RESULT_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c
new file mode 100644
index 0000000..1d22e6c
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -0,0 +1,532 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_debug.h"
+#include "ext/standard/basic_functions.h"
+
+
+/* {{{ php_mysqlnd_free_field_metadata */
+static void
+php_mysqlnd_free_field_metadata(MYSQLND_FIELD *meta, zend_bool persistent TSRMLS_DC)
+{
+ if (meta) {
+ if (meta->root) {
+ mnd_pefree(meta->root, persistent);
+ meta->root = NULL;
+ }
+ if (meta->def) {
+ mnd_pefree(meta->def, persistent);
+ meta->def = NULL;
+ }
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_handle_numeric */
+/*
+ The following code is stolen from ZE - HANDLE_NUMERIC() macro from zend_hash.c
+ and modified for the needs of mysqlnd.
+*/
+static zend_bool
+mysqlnd_is_key_numeric(const char * key, size_t length, long *idx)
+{
+ register const char * tmp = key;
+
+ if (*tmp=='-') {
+ tmp++;
+ }
+ if ((*tmp>='0' && *tmp<='9')) {
+ do { /* possibly a numeric index */
+ const char *end=key+length-1;
+
+ if (*tmp++=='0' && length>2) { /* don't accept numbers with leading zeros */
+ break;
+ }
+ while (tmp<end) {
+ if (!(*tmp>='0' && *tmp<='9')) {
+ break;
+ }
+ tmp++;
+ }
+ if (tmp==end && *tmp=='\0') { /* a numeric index */
+ if (*key=='-') {
+ *idx = strtol(key, NULL, 10);
+ if (*idx!=LONG_MIN) {
+ return TRUE;
+ }
+ } else {
+ *idx = strtol(key, NULL, 10);
+ if (*idx!=LONG_MAX) {
+ return TRUE;
+ }
+ }
+ }
+ } while (0);
+ }
+ return FALSE;
+}
+/* }}} */
+
+
+#if MYSQLND_UNICODE
+/* {{{ mysqlnd_unicode_is_key_numeric */
+static zend_bool
+mysqlnd_unicode_is_key_numeric(UChar *key, size_t length, long *idx)
+{
+ register UChar * tmp=key;
+
+ if (*tmp==0x2D /*'-'*/) {
+ tmp++;
+ }
+ if ((*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) { /* possibly a numeric index */
+ do {
+ UChar *end=key+length-1;
+
+ if (*tmp++==0x30 && length>2) { /* don't accept numbers with leading zeros */
+ break;
+ }
+ while (tmp<end) {
+ if (!(*tmp>=0x30 /*'0'*/ && *tmp<=0x39 /*'9'*/)) {
+ break;
+ }
+ tmp++;
+ }
+ if (tmp==end && *tmp==0) { /* a numeric index */
+ if (*key==0x2D /*'-'*/) {
+ *idx = zend_u_strtol(key, NULL, 10);
+ if (*idx!=LONG_MIN) {
+ return TRUE;
+ }
+ } else {
+ *idx = zend_u_strtol(key, NULL, 10);
+ if (*idx!=LONG_MAX) {
+ return TRUE;
+ }
+ }
+ }
+ } while (0);
+ }
+ return FALSE;
+}
+/* }}} */
+#endif
+
+
+/* {{{ mysqlnd_res_meta::read_metadata */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ unsigned int i = 0;
+ MYSQLND_PACKET_RES_FIELD * field_packet;
+#if MYSQLND_UNICODE
+ UChar *ustr;
+ int ulen;
+#endif
+
+ DBG_ENTER("mysqlnd_res_meta::read_metadata");
+
+ field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE TSRMLS_CC);
+ if (!field_packet) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+ field_packet->persistent_alloc = meta->persistent;
+ for (;i < meta->field_count; i++) {
+ long idx;
+
+ if (meta->fields[i].root) {
+ /* We re-read metadata for PS */
+ mnd_pefree(meta->fields[i].root, meta->persistent);
+ meta->fields[i].root = NULL;
+ }
+
+ field_packet->metadata = &(meta->fields[i]);
+ if (FAIL == PACKET_READ(field_packet, conn)) {
+ PACKET_FREE(field_packet);
+ DBG_RETURN(FAIL);
+ }
+ if (field_packet->error_info.error_no) {
+ COPY_CLIENT_ERROR(*conn->error_info, field_packet->error_info);
+ /* Return back from CONN_QUERY_SENT */
+ PACKET_FREE(field_packet);
+ DBG_RETURN(FAIL);
+ }
+
+ if (field_packet->stupid_list_fields_eof == TRUE) {
+ meta->field_count = i;
+ break;
+ }
+
+ if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
+ DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers",
+ meta->fields[i].type);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Unknown type %u sent by the server. "
+ "Please send a report to the developers",
+ meta->fields[i].type);
+ PACKET_FREE(field_packet);
+ DBG_RETURN(FAIL);
+ }
+ if (meta->fields[i].type == MYSQL_TYPE_BIT) {
+ size_t field_len;
+ DBG_INF("BIT");
+ ++meta->bit_fields_count;
+ /* .length is in bits */
+ field_len = meta->fields[i].length / 8;
+ /*
+ If there is rest, add one byte :
+ 8 bits = 1 byte but 9 bits = 2 bytes
+ */
+ if (meta->fields[i].length % 8) {
+ ++field_len;
+ }
+ switch (field_len) {
+ case 8:
+ case 7:
+ case 6:
+ case 5:
+ meta->bit_fields_total_len += 20;/* 21 digis, no sign*/
+ break;
+ case 4:
+ meta->bit_fields_total_len += 10;/* 2 000 000 000*/
+ break;
+ case 3:
+ meta->bit_fields_total_len += 8;/* 12 000 000*/
+ break;
+ case 2:
+ meta->bit_fields_total_len += 5;/* 32 500 */
+ break;
+ case 1:
+ meta->bit_fields_total_len += 3;/* 120 */
+ break;
+ }
+ }
+
+#if MYSQLND_UNICODE
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen,
+ meta->fields[i].name,
+ meta->fields[i].name_length TSRMLS_CC);
+ if ((meta->zend_hash_keys[i].is_numeric =
+ mysqlnd_unicode_is_key_numeric(ustr, ulen + 1, &idx)))
+ {
+ meta->zend_hash_keys[i].key = idx;
+ mnd_efree(ustr);
+ } else {
+ meta->zend_hash_keys[i].ustr.u = ustr;
+ meta->zend_hash_keys[i].ulen = ulen;
+ meta->zend_hash_keys[i].key = zend_u_get_hash_value(IS_UNICODE, ZSTR(ustr), ulen + 1);
+ }
+#else
+ /* For BC we have to check whether the key is numeric and use it like this */
+ if ((meta->zend_hash_keys[i].is_numeric =
+ mysqlnd_is_key_numeric(field_packet->metadata->name,
+ field_packet->metadata->name_length + 1,
+ &idx)))
+ {
+ meta->zend_hash_keys[i].key = idx;
+ } else {
+ meta->zend_hash_keys[i].key =
+ zend_get_hash_value(field_packet->metadata->name,
+ field_packet->metadata->name_length + 1);
+ }
+#endif
+ }
+ PACKET_FREE(field_packet);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::free */
+static void
+MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta TSRMLS_DC)
+{
+ int i;
+ MYSQLND_FIELD *fields;
+ DBG_ENTER("mysqlnd_res_meta::free");
+ DBG_INF_FMT("persistent=%u", meta->persistent);
+
+ if ((fields = meta->fields)) {
+ DBG_INF("Freeing fields metadata");
+ i = meta->field_count;
+ while (i--) {
+ php_mysqlnd_free_field_metadata(fields++, meta->persistent TSRMLS_CC);
+ }
+ mnd_pefree(meta->fields, meta->persistent);
+ meta->fields = NULL;
+ }
+
+ if (meta->zend_hash_keys) {
+ DBG_INF("Freeing zend_hash_keys");
+#if MYSQLND_UNICODE
+ if (UG(unicode)) {
+ for (i = 0; i < meta->field_count; i++) {
+ if (meta->zend_hash_keys[i].ustr.v) {
+ mnd_pefree(meta->zend_hash_keys[i].ustr.v, meta->persistent);
+ }
+ }
+ }
+#endif
+ mnd_pefree(meta->zend_hash_keys, meta->persistent);
+ meta->zend_hash_keys = NULL;
+ }
+ DBG_INF("Freeing metadata structure");
+ mnd_pefree(meta, meta->persistent);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::clone_metadata */
+static MYSQLND_RES_METADATA *
+MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC)
+{
+ unsigned int i;
+ /* +1 is to have empty marker at the end */
+ MYSQLND_RES_METADATA * new_meta = NULL;
+ MYSQLND_FIELD * new_fields;
+ MYSQLND_FIELD * orig_fields = meta->fields;
+ size_t len = meta->field_count * sizeof(struct mysqlnd_field_hash_key);
+
+ DBG_ENTER("mysqlnd_res_meta::clone_metadata");
+ DBG_INF_FMT("persistent=%u", persistent);
+
+ new_meta = mnd_pecalloc(1, sizeof(MYSQLND_RES_METADATA), persistent);
+ if (!new_meta) {
+ goto oom;
+ }
+ new_meta->persistent = persistent;
+ new_meta->m = meta->m;
+
+ new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
+ if (!new_fields) {
+ goto oom;
+ }
+
+ new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
+ if (!new_meta->zend_hash_keys) {
+ goto oom;
+ }
+ memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
+
+ /*
+ This will copy also the strings and the root, which we will have
+ to adjust in the loop
+ */
+ memcpy(new_fields, orig_fields, (meta->field_count) * sizeof(MYSQLND_FIELD));
+ for (i = 0; i < meta->field_count; i++) {
+ /* First copy the root, then field by field adjust the pointers */
+ new_fields[i].root = mnd_pemalloc(orig_fields[i].root_len, persistent);
+ if (!new_fields[i].root) {
+ goto oom;
+ }
+ memcpy(new_fields[i].root, orig_fields[i].root, new_fields[i].root_len);
+
+ if (orig_fields[i].name && orig_fields[i].name != mysqlnd_empty_string) {
+ new_fields[i].name = new_fields[i].root +
+ (orig_fields[i].name - orig_fields[i].root);
+ }
+ if (orig_fields[i].org_name && orig_fields[i].org_name != mysqlnd_empty_string) {
+ new_fields[i].org_name = new_fields[i].root +
+ (orig_fields[i].org_name - orig_fields[i].root);
+ }
+ if (orig_fields[i].table && orig_fields[i].table != mysqlnd_empty_string) {
+ new_fields[i].table = new_fields[i].root +
+ (orig_fields[i].table - orig_fields[i].root);
+ }
+ if (orig_fields[i].org_table && orig_fields[i].org_table != mysqlnd_empty_string) {
+ new_fields[i].org_table = new_fields[i].root +
+ (orig_fields[i].org_table - orig_fields[i].root);
+ }
+ if (orig_fields[i].db && orig_fields[i].db != mysqlnd_empty_string) {
+ new_fields[i].db = new_fields[i].root + (orig_fields[i].db - orig_fields[i].root);
+ }
+ if (orig_fields[i].catalog && orig_fields[i].catalog != mysqlnd_empty_string) {
+ new_fields[i].catalog = new_fields[i].root + (orig_fields[i].catalog - orig_fields[i].root);
+ }
+ /* def is not on the root, if allocated at all */
+ if (orig_fields[i].def) {
+ new_fields[i].def = mnd_pemalloc(orig_fields[i].def_length + 1, persistent);
+ if (!new_fields[i].def) {
+ goto oom;
+ }
+ /* copy the trailing \0 too */
+ memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
+ }
+#if MYSQLND_UNICODE
+ if (new_meta->zend_hash_keys[i].ustr.u) {
+ new_meta->zend_hash_keys[i].ustr.u =
+ eustrndup(new_meta->zend_hash_keys[i].ustr.u, new_meta->zend_hash_keys[i].ulen);
+ if (!new_meta->zend_hash_keys[i].ustr.u) {
+ goto oom;
+ }
+ }
+#endif
+ }
+ new_meta->current_field = 0;
+ new_meta->field_count = meta->field_count;
+
+ new_meta->fields = new_fields;
+
+ DBG_RETURN(new_meta);
+oom:
+ if (new_meta) {
+ new_meta->m->free_metadata(new_meta TSRMLS_CC);
+ new_meta = NULL;
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+/* {{{ mysqlnd_res_meta::fetch_field */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res_meta::fetch_field");
+ if (meta->current_field >= meta->field_count) {
+ DBG_INF("no more fields");
+ DBG_RETURN(NULL);
+ }
+ DBG_INF_FMT("name=%s max_length=%u",
+ meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
+ meta->fields[meta->current_field].max_length);
+ DBG_RETURN(&meta->fields[meta->current_field++]);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::fetch_field_direct */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
+ DBG_INF_FMT("fieldnr=%u", fieldnr);
+ DBG_INF_FMT("name=%s max_length=%u",
+ meta->fields[meta->current_field].name? meta->fields[meta->current_field].name:"",
+ meta->fields[meta->current_field].max_length);
+ DBG_RETURN(&meta->fields[fieldnr]);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::fetch_fields */
+static const MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res_meta::fetch_fields");
+ DBG_RETURN(meta->fields);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::field_tell */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC)
+{
+ return meta->current_field;
+}
+/* }}} */
+
+
+static
+MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
+ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field),
+ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
+ MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
+ MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
+ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
+ MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
+ MYSQLND_METHOD(mysqlnd_res_meta, free),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_result_meta_init */
+PHPAPI MYSQLND_RES_METADATA *
+mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_RES_METADATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_RES_METADATA *ret = mnd_pecalloc(1, alloc_size, persistent);
+ DBG_ENTER("mysqlnd_result_meta_init");
+ DBG_INF_FMT("persistent=%u", persistent);
+
+ do {
+ if (!ret) {
+ break;
+ }
+ ret->m = & mysqlnd_mysqlnd_res_meta_methods;
+
+ ret->persistent = persistent;
+ ret->field_count = field_count;
+ /* +1 is to have empty marker at the end */
+ ret->fields = mnd_pecalloc(field_count + 1, sizeof(MYSQLND_FIELD), ret->persistent);
+ ret->zend_hash_keys = mnd_pecalloc(field_count, sizeof(struct mysqlnd_field_hash_key), ret->persistent);
+ if (!ret->fields || !ret->zend_hash_keys) {
+ break;
+ }
+ DBG_INF_FMT("meta=%p", ret);
+ DBG_RETURN(ret);
+ } while (0);
+ if (ret) {
+ ret->m->free_metadata(ret TSRMLS_CC);
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta_get_methods */
+PHPAPI struct st_mysqlnd_res_meta_methods *
+mysqlnd_result_metadata_get_methods()
+{
+ return &mysqlnd_mysqlnd_res_meta_methods;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_result_metadata_data */
+PHPAPI void **
+_mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_result_metadata_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!meta || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)meta + sizeof(MYSQLND_RES_METADATA) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h
new file mode 100644
index 0000000..8fa9989
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result_meta.h
@@ -0,0 +1,39 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_RESULT_META_H
+#define MYSQLND_RESULT_META_H
+
+PHPAPI MYSQLND_RES_METADATA * mysqlnd_result_meta_init(unsigned int field_count, zend_bool persistent TSRMLS_DC);
+PHPAPI struct st_mysqlnd_res_meta_methods * mysqlnd_result_metadata_get_methods();
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_metadata_data(const MYSQLND_RES_METADATA * meta, unsigned int plugin_id TSRMLS_DC);
+
+#endif /* MYSQLND_RESULT_META_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.c b/ext/mysqlnd/mysqlnd_reverse_api.c
new file mode 100644
index 0000000..daa43ec
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_reverse_api.c
@@ -0,0 +1,100 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: mysqlnd.c 317989 2011-10-10 20:49:28Z andrey $ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_reverse_api.h"
+
+
+static HashTable mysqlnd_api_ext_ht;
+
+
+/* {{{ mysqlnd_reverse_api_init */
+PHPAPI void
+mysqlnd_reverse_api_init(TSRMLS_D)
+{
+ zend_hash_init(&mysqlnd_api_ext_ht, 3, NULL, NULL, 1);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_reverse_api_end */
+PHPAPI void
+mysqlnd_reverse_api_end(TSRMLS_D)
+{
+ zend_hash_destroy(&mysqlnd_api_ext_ht);
+}
+/* }}} */
+
+
+/* {{{ myslqnd_get_api_extensions */
+PHPAPI HashTable *
+mysqlnd_reverse_api_get_api_list(TSRMLS_D)
+{
+ return &mysqlnd_api_ext_ht;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_reverse_api_register_api */
+PHPAPI void
+mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC)
+{
+ zend_hash_add(&mysqlnd_api_ext_ht, apiext->module->name, strlen(apiext->module->name) + 1, &apiext,
+ sizeof(MYSQLND_REVERSE_API), NULL);
+}
+/* }}} */
+
+
+/* {{{ zval_to_mysqlnd */
+PHPAPI MYSQLND *
+zval_to_mysqlnd(zval * zv TSRMLS_DC)
+{
+ MYSQLND * retval;
+ MYSQLND_REVERSE_API ** elem;
+
+ for (zend_hash_internal_pointer_reset(&mysqlnd_api_ext_ht);
+ zend_hash_get_current_data(&mysqlnd_api_ext_ht, (void **)&elem) == SUCCESS;
+ zend_hash_move_forward(&mysqlnd_api_ext_ht))
+ {
+ if ((*elem)->conversion_cb) {
+ retval = (*elem)->conversion_cb(zv TSRMLS_CC);
+ if (retval) {
+ return retval;
+ }
+ }
+ }
+
+ return NULL;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.h b/ext/mysqlnd/mysqlnd_reverse_api.h
new file mode 100644
index 0000000..dd23555
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_reverse_api.h
@@ -0,0 +1,50 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+/* $Id: mysqlnd.h 318051 2011-10-12 16:18:02Z andrey $ */
+
+#ifndef MYSQLND_REVERSE_API_H
+#define MYSQLND_REVERSE_API_H
+typedef struct st_mysqlnd_reverse_api
+{
+ zend_module_entry * module;
+ MYSQLND *(*conversion_cb)(zval * zv TSRMLS_DC);
+} MYSQLND_REVERSE_API;
+
+
+PHPAPI void mysqlnd_reverse_api_init(TSRMLS_D);
+PHPAPI void mysqlnd_reverse_api_end(TSRMLS_D);
+
+PHPAPI HashTable * mysqlnd_reverse_api_get_api_list(TSRMLS_D);
+
+PHPAPI void mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC);
+
+PHPAPI MYSQLND * zval_to_mysqlnd(zval * zv TSRMLS_DC);
+
+#endif /* MYSQLND_REVERSE_API_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c
new file mode 100644
index 0000000..271cb29
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_statistics.c
@@ -0,0 +1,317 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+
+
+/* {{{ mysqlnd_stats_values_names
+ */
+
+const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
+{
+ { MYSQLND_STR_W_LEN("bytes_sent") },
+ { MYSQLND_STR_W_LEN("bytes_received") },
+ { MYSQLND_STR_W_LEN("packets_sent") },
+ { MYSQLND_STR_W_LEN("packets_received") },
+ { MYSQLND_STR_W_LEN("protocol_overhead_in") },
+ { MYSQLND_STR_W_LEN("protocol_overhead_out") },
+ { MYSQLND_STR_W_LEN("bytes_received_ok_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_eof_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_rset_header_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_rset_field_meta_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_rset_row_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_prepare_response_packet") },
+ { MYSQLND_STR_W_LEN("bytes_received_change_user_packet") },
+ { MYSQLND_STR_W_LEN("packets_sent_command") },
+ { MYSQLND_STR_W_LEN("packets_received_ok") },
+ { MYSQLND_STR_W_LEN("packets_received_eof") },
+ { MYSQLND_STR_W_LEN("packets_received_rset_header") },
+ { MYSQLND_STR_W_LEN("packets_received_rset_field_meta") },
+ { MYSQLND_STR_W_LEN("packets_received_rset_row") },
+ { MYSQLND_STR_W_LEN("packets_received_prepare_response") },
+ { MYSQLND_STR_W_LEN("packets_received_change_user") },
+ { MYSQLND_STR_W_LEN("result_set_queries") },
+ { MYSQLND_STR_W_LEN("non_result_set_queries") },
+ { MYSQLND_STR_W_LEN("no_index_used") },
+ { MYSQLND_STR_W_LEN("bad_index_used") },
+ { MYSQLND_STR_W_LEN("slow_queries") },
+ { MYSQLND_STR_W_LEN("buffered_sets") },
+ { MYSQLND_STR_W_LEN("unbuffered_sets") },
+ { MYSQLND_STR_W_LEN("ps_buffered_sets") },
+ { MYSQLND_STR_W_LEN("ps_unbuffered_sets") },
+ { MYSQLND_STR_W_LEN("flushed_normal_sets") },
+ { MYSQLND_STR_W_LEN("flushed_ps_sets") },
+ { MYSQLND_STR_W_LEN("ps_prepared_never_executed") },
+ { MYSQLND_STR_W_LEN("ps_prepared_once_executed") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_server_normal") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_server_ps") },
+ { MYSQLND_STR_W_LEN("rows_buffered_from_client_normal") },
+ { MYSQLND_STR_W_LEN("rows_buffered_from_client_ps") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_client_normal_buffered") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_client_normal_unbuffered") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_buffered") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_unbuffered") },
+ { MYSQLND_STR_W_LEN("rows_fetched_from_client_ps_cursor") },
+ { MYSQLND_STR_W_LEN("rows_affected_normal") },
+ { MYSQLND_STR_W_LEN("rows_affected_ps") },
+ { MYSQLND_STR_W_LEN("rows_skipped_normal") },
+ { MYSQLND_STR_W_LEN("rows_skipped_ps") },
+ { MYSQLND_STR_W_LEN("copy_on_write_saved") },
+ { MYSQLND_STR_W_LEN("copy_on_write_performed") },
+ { MYSQLND_STR_W_LEN("command_buffer_too_small") },
+ { MYSQLND_STR_W_LEN("connect_success") },
+ { MYSQLND_STR_W_LEN("connect_failure") },
+ { MYSQLND_STR_W_LEN("connection_reused") },
+ { MYSQLND_STR_W_LEN("reconnect") },
+ { MYSQLND_STR_W_LEN("pconnect_success") },
+ { MYSQLND_STR_W_LEN("active_connections") },
+ { MYSQLND_STR_W_LEN("active_persistent_connections") },
+ { MYSQLND_STR_W_LEN("explicit_close") },
+ { MYSQLND_STR_W_LEN("implicit_close") },
+ { MYSQLND_STR_W_LEN("disconnect_close") },
+ { MYSQLND_STR_W_LEN("in_middle_of_command_close") },
+ { MYSQLND_STR_W_LEN("explicit_free_result") },
+ { MYSQLND_STR_W_LEN("implicit_free_result") },
+ { MYSQLND_STR_W_LEN("explicit_stmt_close") },
+ { MYSQLND_STR_W_LEN("implicit_stmt_close") },
+ { MYSQLND_STR_W_LEN("mem_emalloc_count") },
+ { MYSQLND_STR_W_LEN("mem_emalloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_ecalloc_count") },
+ { MYSQLND_STR_W_LEN("mem_ecalloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_erealloc_count") },
+ { MYSQLND_STR_W_LEN("mem_erealloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_efree_count") },
+ { MYSQLND_STR_W_LEN("mem_efree_amount") },
+ { MYSQLND_STR_W_LEN("mem_malloc_count") },
+ { MYSQLND_STR_W_LEN("mem_malloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_calloc_count") },
+ { MYSQLND_STR_W_LEN("mem_calloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_realloc_count") },
+ { MYSQLND_STR_W_LEN("mem_realloc_amount") },
+ { MYSQLND_STR_W_LEN("mem_free_count") },
+ { MYSQLND_STR_W_LEN("mem_free_amount") },
+ { MYSQLND_STR_W_LEN("mem_estrndup_count") },
+ { MYSQLND_STR_W_LEN("mem_strndup_count") },
+ { MYSQLND_STR_W_LEN("mem_estndup_count") },
+ { MYSQLND_STR_W_LEN("mem_strdup_count") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_null") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_bit") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_tinyint") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_short") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_int24") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_int") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_bigint") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_decimal") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_float") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_double") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_date") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_year") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_time") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_datetime") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_timestamp") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_string") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_blob") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_enum") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_set") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_geometry") },
+ { MYSQLND_STR_W_LEN("proto_text_fetched_other") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_null") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_bit") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_tinyint") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_short") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_int24") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_int") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_bigint") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_decimal") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_float") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_double") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_date") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_year") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_time") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_datetime") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_timestamp") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_string") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_blob") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_enum") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_set") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_geometry") },
+ { MYSQLND_STR_W_LEN("proto_binary_fetched_other") },
+ { MYSQLND_STR_W_LEN("init_command_executed_count") },
+ { MYSQLND_STR_W_LEN("init_command_failed_count") },
+ { MYSQLND_STR_W_LEN("com_quit") },
+ { MYSQLND_STR_W_LEN("com_init_db") },
+ { MYSQLND_STR_W_LEN("com_query") },
+ { MYSQLND_STR_W_LEN("com_field_list") },
+ { MYSQLND_STR_W_LEN("com_create_db") },
+ { MYSQLND_STR_W_LEN("com_drop_db") },
+ { MYSQLND_STR_W_LEN("com_refresh") },
+ { MYSQLND_STR_W_LEN("com_shutdown") },
+ { MYSQLND_STR_W_LEN("com_statistics") },
+ { MYSQLND_STR_W_LEN("com_process_info") },
+ { MYSQLND_STR_W_LEN("com_connect") },
+ { MYSQLND_STR_W_LEN("com_process_kill") },
+ { MYSQLND_STR_W_LEN("com_debug") },
+ { MYSQLND_STR_W_LEN("com_ping") },
+ { MYSQLND_STR_W_LEN("com_time") },
+ { MYSQLND_STR_W_LEN("com_delayed_insert") },
+ { MYSQLND_STR_W_LEN("com_change_user") },
+ { MYSQLND_STR_W_LEN("com_binlog_dump") },
+ { MYSQLND_STR_W_LEN("com_table_dump") },
+ { MYSQLND_STR_W_LEN("com_connect_out") },
+ { MYSQLND_STR_W_LEN("com_register_slave") },
+ { MYSQLND_STR_W_LEN("com_stmt_prepare") },
+ { MYSQLND_STR_W_LEN("com_stmt_execute") },
+ { MYSQLND_STR_W_LEN("com_stmt_send_long_data") },
+ { MYSQLND_STR_W_LEN("com_stmt_close") },
+ { MYSQLND_STR_W_LEN("com_stmt_reset") },
+ { MYSQLND_STR_W_LEN("com_stmt_set_option") },
+ { MYSQLND_STR_W_LEN("com_stmt_fetch") },
+ { MYSQLND_STR_W_LEN("com_deamon") },
+ { MYSQLND_STR_W_LEN("bytes_received_real_data_normal") },
+ { MYSQLND_STR_W_LEN("bytes_received_real_data_ps") }
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_fill_stats_hash */
+PHPAPI void
+mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ unsigned int i;
+
+ mysqlnd_array_init(return_value, stats->count);
+ for (i = 0; i < stats->count; i++) {
+#if MYSQLND_UNICODE
+ UChar *ustr, *tstr;
+ int ulen, tlen;
+#endif
+ char tmp[25];
+
+ sprintf((char *)&tmp, MYSQLND_LLU_SPEC, stats->values[i]);
+#if MYSQLND_UNICODE
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, names[i].s, names[i].l + 1 TSRMLS_CC);
+ zend_string_to_unicode(UG(utf8_conv), &tstr, &tlen, tmp, strlen(tmp) + 1 TSRMLS_CC);
+ add_u_assoc_unicode_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen, tstr, 1);
+ efree(ustr);
+ efree(tstr);
+#else
+ add_assoc_string_ex(return_value, names[i].s, names[i].l + 1, tmp, 1);
+#endif
+ }
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_get_client_stats */
+PHPAPI void
+_mysqlnd_get_client_stats(zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats;
+ DBG_ENTER("_mysqlnd_get_client_stats");
+ if (!stats_ptr) {
+ memset(&stats, 0, sizeof(stats));
+ stats_ptr = &stats;
+ }
+ mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stats_init */
+PHPAPI void
+mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count)
+{
+ *stats = calloc(1, sizeof(MYSQLND_STATS));
+ if (*stats == NULL) {
+ return;
+ }
+ (*stats)->values = calloc(statistic_count, sizeof(uint64_t));
+ (*stats)->triggers = calloc(statistic_count, sizeof(mysqlnd_stat_trigger));
+ (*stats)->in_trigger = FALSE;
+ (*stats)->count = statistic_count;
+#ifdef ZTS
+ (*stats)->LOCK_access = tsrm_mutex_alloc();
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stats_end */
+PHPAPI void
+mysqlnd_stats_end(MYSQLND_STATS * stats)
+{
+#ifdef ZTS
+ tsrm_mutex_free(stats->LOCK_access);
+#endif
+ free(stats->triggers);
+ free(stats->values);
+ /* mnd_free will reference LOCK_access and crash...*/
+ free(stats);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stats_set_trigger */
+PHPAPI mysqlnd_stat_trigger
+mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats statistic, mysqlnd_stat_trigger trigger TSRMLS_DC)
+{
+ mysqlnd_stat_trigger ret = NULL;
+ DBG_ENTER("mysqlnd_stats_set_trigger");
+ if (stats) {
+ MYSQLND_STATS_LOCK(stats);
+ ret = stats->triggers[statistic];
+ stats->triggers[statistic] = trigger;
+ MYSQLND_STATS_UNLOCK(stats);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stats_set_handler */
+PHPAPI mysqlnd_stat_trigger
+mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats TSRMLS_DC)
+{
+ mysqlnd_stat_trigger ret = NULL;
+ DBG_ENTER("mysqlnd_stats_reset_trigger");
+ if (stats) {
+ MYSQLND_STATS_LOCK(stats);
+ memset(stats->triggers, 0, stats->count * sizeof(mysqlnd_stat_trigger));
+ MYSQLND_STATS_UNLOCK(stats);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h
new file mode 100644
index 0000000..d1fd03b
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_statistics.h
@@ -0,0 +1,177 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_STATISTICS_H
+#define MYSQLND_STATISTICS_H
+
+
+PHPAPI extern MYSQLND_STATS * mysqlnd_global_stats;
+
+extern const MYSQLND_STRING mysqlnd_stats_values_names[];
+
+#ifdef ZTS
+#define MYSQLND_STATS_LOCK(stats) tsrm_mutex_lock((stats)->LOCK_access)
+#define MYSQLND_STATS_UNLOCK(stats) tsrm_mutex_unlock((stats)->LOCK_access)
+#else
+#define MYSQLND_STATS_LOCK(stats)
+#define MYSQLND_STATS_UNLOCK(stats)
+#endif
+
+#ifndef MYSQLND_CORE_STATISTICS_TRIGGERS_DISABLED
+#define MYSQLND_STAT_CALL_TRIGGER(s_array, statistic, val) \
+ if ((s_array)->triggers[(statistic)] && (s_array)->in_trigger == FALSE) { \
+ (s_array)->in_trigger = TRUE; \
+ MYSQLND_STATS_UNLOCK((s_array)); \
+ \
+ (s_array)->triggers[(statistic)]((s_array), (statistic), (val) TSRMLS_CC); \
+ \
+ MYSQLND_STATS_LOCK((s_array)); \
+ (s_array)->in_trigger = FALSE; \
+ }
+#else
+#define MYSQLND_STAT_CALL_TRIGGER(s_array, statistic, val)
+#endif /* MYSQLND_CORE_STATISTICS_TRIGGERS_DISABLED */
+
+#define MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(stats, statistic, value) \
+ { \
+ MYSQLND_STATS_LOCK(stats); \
+ (stats)->values[(statistic)] += (value); \
+ MYSQLND_STAT_CALL_TRIGGER((stats), (statistic), (value)); \
+ MYSQLND_STATS_UNLOCK(_p_s); \
+ }
+
+#define MYSQLND_DEC_STATISTIC(enabler, stats, statistic) \
+ { \
+ enum_mysqlnd_collected_stats _s = (statistic);\
+ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \
+ if ((enabler) && _p_s && _s != _p_s->count) { \
+ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, -1); \
+ }\
+ }
+
+#define MYSQLND_INC_STATISTIC(enabler, stats, statistic) \
+ { \
+ enum_mysqlnd_collected_stats _s = (statistic);\
+ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \
+ if ((enabler) && _p_s && _s != _p_s->count) { \
+ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, 1); \
+ }\
+ }
+
+#define MYSQLND_INC_STATISTIC_W_VALUE(enabler, stats, statistic, value) \
+ { \
+ enum_mysqlnd_collected_stats _s = (statistic);\
+ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \
+ if ((enabler) && _p_s && _s != _p_s->count) { \
+ uint64_t v = (uint64_t) (value); \
+ MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s, v); \
+ }\
+ }
+
+#define MYSQLND_INC_STATISTIC_W_VALUE2(enabler, stats, statistic1, value1, statistic2, value2) \
+ { \
+ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \
+ if ((enabler) && _p_s) { \
+ uint64_t v1 = (uint64_t) (value1); \
+ uint64_t v2 = (uint64_t) (value2); \
+ enum_mysqlnd_collected_stats _s1 = (statistic1);\
+ enum_mysqlnd_collected_stats _s2 = (statistic2);\
+ if (_s1 != _p_s->count) MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s1, v1); \
+ if (_s2 != _p_s->count) MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s2, v2); \
+ }\
+ }
+
+#define MYSQLND_INC_STATISTIC_W_VALUE3(enabler, stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ { \
+ MYSQLND_STATS * _p_s = (MYSQLND_STATS *) (stats); \
+ if ((enabler) && _p_s) { \
+ uint64_t v1 = (uint64_t) (value1); \
+ uint64_t v2 = (uint64_t) (value2); \
+ uint64_t v3 = (uint64_t) (value3); \
+ enum_mysqlnd_collected_stats _s1 = (statistic1);\
+ enum_mysqlnd_collected_stats _s2 = (statistic2);\
+ enum_mysqlnd_collected_stats _s3 = (statistic3);\
+ if (_s1 != _p_s->count) MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s1, v1); \
+ if (_s2 != _p_s->count) MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s2, v2); \
+ if (_s3 != _p_s->count) MYSQLND_UPDATE_VALUE_AND_CALL_TRIGGER(_p_s, _s3, v3); \
+ }\
+ }
+
+
+
+#ifndef MYSQLND_CORE_STATISTICS_DISABLED
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \
+ MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2))
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3));
+
+#else
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
+#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic)
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3)
+
+#endif /* MYSQLND_CORE_STATISTICS_DISABLED */
+
+PHPAPI void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+
+PHPAPI void mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count);
+PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats);
+
+PHPAPI mysqlnd_stat_trigger mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats stat, mysqlnd_stat_trigger trigger TSRMLS_DC);
+PHPAPI mysqlnd_stat_trigger mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats TSRMLS_DC);
+
+#endif /* MYSQLND_STATISTICS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
new file mode 100644
index 0000000..5c07a3c
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -0,0 +1,1085 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_STRUCTS_H
+#define MYSQLND_STRUCTS_H
+
+#define MYSQLND_TYPEDEFED_METHODS
+
+#define MYSQLND_CLASS_METHOD_TABLE_NAME(class) mysqlnd_##class##_methods
+#define MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) struct st_##class##_methods MYSQLND_CLASS_METHOD_TABLE_NAME(class)
+
+#define MYSQLND_CLASS_METHODS_START(class) MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) = {
+#define MYSQLND_CLASS_METHODS_END }
+
+typedef struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL;
+typedef struct st_mysqlnd_memory_pool_chunk MYSQLND_MEMORY_POOL_CHUNK;
+typedef struct st_mysqlnd_memory_pool_chunk_llist MYSQLND_MEMORY_POOL_CHUNK_LLIST;
+
+
+#define MYSQLND_MEMORY_POOL_CHUNK_LIST_SIZE 100
+
+struct st_mysqlnd_memory_pool
+{
+ zend_uchar *arena;
+ unsigned int refcount;
+ unsigned int arena_size;
+ unsigned int free_size;
+
+ MYSQLND_MEMORY_POOL_CHUNK* (*get_chunk)(MYSQLND_MEMORY_POOL * pool, unsigned int size TSRMLS_DC);
+};
+
+struct st_mysqlnd_memory_pool_chunk
+{
+ size_t app;
+ MYSQLND_MEMORY_POOL *pool;
+ zend_uchar *ptr;
+ unsigned int size;
+ enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size TSRMLS_DC);
+ void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk TSRMLS_DC);
+ zend_bool from_pool;
+};
+
+
+typedef struct st_mysqlnd_cmd_buffer
+{
+ zend_uchar *buffer;
+ size_t length;
+} MYSQLND_CMD_BUFFER;
+
+
+typedef struct st_mysqlnd_field
+{
+ const char *name; /* Name of column */
+ const char *org_name; /* Original column name, if an alias */
+ const char *table; /* Table of column if column was a field */
+ const char *org_table; /* Org table name, if table was an alias */
+ const char *db; /* Database for table */
+ const char *catalog; /* Catalog for table */
+ char *def; /* Default value (set by mysql_list_fields) */
+ unsigned long length; /* Width of column (create length) */
+ unsigned long max_length; /* Max width for selected set */
+ unsigned int name_length;
+ unsigned int org_name_length;
+ unsigned int table_length;
+ unsigned int org_table_length;
+ unsigned int db_length;
+ unsigned int catalog_length;
+ unsigned int def_length;
+ unsigned int flags; /* Diverse flags */
+ unsigned int decimals; /* Number of decimals in field */
+ unsigned int charsetnr; /* Character set */
+ enum mysqlnd_field_types type; /* Type of field. See mysql_com.h for types */
+ char *root;
+ size_t root_len;
+} MYSQLND_FIELD;
+
+
+typedef struct st_mysqlnd_upsert_result
+{
+ unsigned int warning_count;
+ unsigned int server_status;
+ uint64_t affected_rows;
+ uint64_t last_insert_id;
+} MYSQLND_UPSERT_STATUS;
+
+
+typedef struct st_mysqlnd_error_info
+{
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+ zend_llist * error_list;
+} MYSQLND_ERROR_INFO;
+
+
+typedef struct st_mysqlnd_error_list_element
+{
+ char * error;
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+} MYSQLND_ERROR_LIST_ELEMENT;
+
+
+typedef struct st_mysqlnd_infile_info
+{
+ php_stream *fd;
+ int error_no;
+ char error_msg[MYSQLND_ERRMSG_SIZE + 1];
+ const char *filename;
+} MYSQLND_INFILE_INFO;
+
+
+/* character set information */
+typedef struct st_mysqlnd_charset
+{
+ unsigned int nr;
+ const char *name;
+ const char *collation;
+ unsigned int char_minlen;
+ unsigned int char_maxlen;
+ const char *comment;
+ unsigned int (*mb_charlen)(unsigned int c);
+ unsigned int (*mb_valid)(const char *start, const char *end);
+} MYSQLND_CHARSET;
+
+
+/* local infile handler */
+typedef struct st_mysqlnd_infile
+{
+ int (*local_infile_init)(void **ptr, char *filename, void **userdata TSRMLS_DC);
+ int (*local_infile_read)(void *ptr, zend_uchar * buf, unsigned int buf_len TSRMLS_DC);
+ int (*local_infile_error)(void *ptr, char *error_msg, unsigned int error_msg_len TSRMLS_DC);
+ void (*local_infile_end)(void *ptr TSRMLS_DC);
+ zval *callback;
+ void *userdata;
+} MYSQLND_INFILE;
+
+typedef struct st_mysqlnd_options
+{
+ ulong flags;
+
+ /* init commands - we need to send them to server directly after connect */
+ unsigned int num_commands;
+ char **init_commands;
+
+ /* configuration file information */
+ char *cfg_file;
+ char *cfg_section;
+
+ char *auth_protocol;
+ /*
+ We need to keep these because otherwise st_mysqlnd_conn will be changed.
+ 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;
+ char * unused3;
+ char * unused4;
+ char * unused5;
+
+ enum_mysqlnd_protocol_type protocol;
+
+ char *charset_name;
+ /* maximum allowed packet size for communication */
+ ulong max_allowed_packet;
+
+ zend_bool numeric_and_datetime_as_unicode;
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ zend_bool int_and_float_native;
+#endif
+} MYSQLND_OPTIONS;
+
+typedef struct st_mysqlnd_net_options
+{
+ /* timeouts */
+ unsigned int timeout_connect;
+ unsigned int timeout_read;
+ unsigned int timeout_write;
+
+ unsigned int net_read_buffer_size;
+
+ /* SSL information */
+ char *ssl_key;
+ char *ssl_cert;
+ char *ssl_ca;
+ char *ssl_capath;
+ char *ssl_cipher;
+ char *ssl_passphrase;
+ zend_bool ssl_verify_peer;
+ uint64_t flags;
+} MYSQLND_NET_OPTIONS;
+
+
+typedef struct st_mysqlnd_connection MYSQLND;
+typedef struct st_mysqlnd_connection_data MYSQLND_CONN_DATA;
+typedef struct st_mysqlnd_net MYSQLND_NET;
+typedef struct st_mysqlnd_protocol MYSQLND_PROTOCOL;
+typedef struct st_mysqlnd_res MYSQLND_RES;
+typedef char** MYSQLND_ROW_C; /* return data as array of strings */
+typedef struct st_mysqlnd_stmt_data MYSQLND_STMT_DATA;
+typedef struct st_mysqlnd_stmt MYSQLND_STMT;
+typedef unsigned int MYSQLND_FIELD_OFFSET;
+
+typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND;
+
+typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND;
+
+typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA;
+typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED;
+typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED;
+
+typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
+
+
+typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC);
+typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result,
+ void *param,
+ unsigned int flags,
+ zend_bool *fetched_anything
+ TSRMLS_DC);
+
+typedef struct st_mysqlnd_stats MYSQLND_STATS;
+
+typedef void (*mysqlnd_stat_trigger)(MYSQLND_STATS * stats, enum_mysqlnd_collected_stats stat, int64_t change TSRMLS_DC);
+
+struct st_mysqlnd_stats
+{
+ uint64_t *values;
+ mysqlnd_stat_trigger *triggers;
+ size_t count;
+ zend_bool in_trigger;
+#ifdef ZTS
+ MUTEX_T LOCK_access;
+#endif
+};
+
+
+typedef struct st_mysqlnd_read_buffer {
+ zend_uchar * data;
+ size_t offset;
+ size_t size;
+ size_t len;
+ zend_bool (*is_empty)(struct st_mysqlnd_read_buffer *);
+ void (*read)(struct st_mysqlnd_read_buffer *, size_t count, zend_uchar * dest);
+ size_t (*bytes_left)(struct st_mysqlnd_read_buffer *);
+ void (*free_buffer)(struct st_mysqlnd_read_buffer ** TSRMLS_DC);
+} MYSQLND_READ_BUFFER;
+
+
+
+typedef enum_func_status (*func_mysqlnd_net__set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_option option, const char * const value TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, const zend_uchar * const compressed_data, const size_t compressed_data_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len TSRMLS_DC);
+typedef size_t (*func_mysqlnd_net__consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC);
+typedef void (*func_mysqlnd_net__free_contents)(MYSQLND_NET * net TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__enable_ssl)(MYSQLND_NET * const net TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__disable_ssl)(MYSQLND_NET * const net TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef size_t (*func_mysqlnd_net__network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buf, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef size_t (*func_mysqlnd_net__send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef void (*func_mysqlnd_net__dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef void (*func_mysqlnd_net__close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef void (*func_mysqlnd_net__post_connect_set_opt)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer)(MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC);
+
+struct st_mysqlnd_net_methods
+{
+ func_mysqlnd_net__init init;
+ func_mysqlnd_net__dtor dtor;
+ func_mysqlnd_net__connect_ex connect_ex;
+ func_mysqlnd_net__close_stream close_stream;
+ func_mysqlnd_net__open_stream open_pipe;
+ func_mysqlnd_net__open_stream open_tcp_or_unix;
+
+ void * unused1;
+ void * unused2;
+ func_mysqlnd_net__post_connect_set_opt post_connect_set_opt;
+
+ func_mysqlnd_net__set_client_option set_client_option;
+ func_mysqlnd_net__decode decode;
+ func_mysqlnd_net__encode encode;
+ func_mysqlnd_net__consume_uneaten_data consume_uneaten_data;
+ func_mysqlnd_net__free_contents free_contents;
+ func_mysqlnd_net__enable_ssl enable_ssl;
+ func_mysqlnd_net__disable_ssl disable_ssl;
+
+ func_mysqlnd_net__network_read_ex network_read_ex;
+ func_mysqlnd_net__network_write_ex network_write_ex;
+ func_mysqlnd_net__send_ex send_ex;
+ func_mysqlnd_net__receive_ex receive_ex;
+
+ func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer read_compressed_packet_from_stream_and_fill_read_buffer;
+
+ void * unused3;
+ void * unused4;
+ void * unused5;
+ void * unused6;
+ void * unused7;
+};
+
+
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_auth;
+struct st_mysqlnd_packet_ok;
+struct st_mysqlnd_packet_command;
+struct st_mysqlnd_packet_eof;
+struct st_mysqlnd_packet_rset_header;
+struct st_mysqlnd_packet_res_field;
+struct st_mysqlnd_packet_row;
+struct st_mysqlnd_packet_stats;
+struct st_mysqlnd_packet_prepare_response;
+struct st_mysqlnd_packet_chg_user_resp;
+struct st_mysqlnd_packet_auth_pam;
+
+typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol__get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol__get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_auth_response *(*func_mysqlnd_protocol__get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_change_auth_response * (*func_mysqlnd_protocol__get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol__get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol__get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol__get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol__get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol__get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol__get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol__get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol__get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol__get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC);
+
+struct st_mysqlnd_protocol_methods
+{
+ func_mysqlnd_protocol__get_greet_packet get_greet_packet;
+ func_mysqlnd_protocol__get_auth_packet get_auth_packet;
+ func_mysqlnd_protocol__get_auth_response_packet get_auth_response_packet;
+ func_mysqlnd_protocol__get_change_auth_response_packet get_change_auth_response_packet;
+ func_mysqlnd_protocol__get_ok_packet get_ok_packet;
+ func_mysqlnd_protocol__get_command_packet get_command_packet;
+ func_mysqlnd_protocol__get_eof_packet get_eof_packet;
+ func_mysqlnd_protocol__get_rset_header_packet get_rset_header_packet;
+ func_mysqlnd_protocol__get_result_field_packet get_result_field_packet;
+ func_mysqlnd_protocol__get_row_packet get_row_packet;
+ func_mysqlnd_protocol__get_stats_packet get_stats_packet;
+ func_mysqlnd_protocol__get_prepare_response_packet get_prepare_response_packet;
+ func_mysqlnd_protocol__get_change_user_response_packet get_change_user_response_packet;
+
+ void * unused1;
+ void * unused2;
+ void * unused3;
+ void * unused4;
+ void * unused5;
+};
+
+
+typedef MYSQLND * (*func_mysqlnd_object_factory__get_connection)(zend_bool persistent TSRMLS_DC);
+typedef MYSQLND * (*func_mysqlnd_object_factory__clone_connection_object)(MYSQLND * conn TSRMLS_DC);
+typedef MYSQLND_STMT * (*func_mysqlnd_object_factory__get_prepared_statement)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef MYSQLND_NET * (*func_mysqlnd_object_factory__get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC);
+typedef MYSQLND_PROTOCOL * (*func_mysqlnd_object_factory__get_protocol_decoder)(zend_bool persistent TSRMLS_DC);
+
+
+struct st_mysqlnd_object_factory_methods
+{
+ func_mysqlnd_object_factory__get_connection get_connection;
+ func_mysqlnd_object_factory__clone_connection_object clone_connection_object;
+ func_mysqlnd_object_factory__get_prepared_statement get_prepared_statement;
+ func_mysqlnd_object_factory__get_io_channel get_io_channel;
+ func_mysqlnd_object_factory__get_protocol_decoder get_protocol_decoder;
+};
+
+
+typedef enum_func_status (*func_mysqlnd_conn_data__init)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__connect)(MYSQLND_CONN_DATA * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags TSRMLS_DC);
+typedef ulong (*func_mysqlnd_conn_data__escape_string)(MYSQLND_CONN_DATA * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_charset)(MYSQLND_CONN_DATA * const conn, const char * const charset TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char *query, unsigned int query_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef zend_bool (*func_mysqlnd_conn_data__more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+typedef MYSQLND_STMT * (*func_mysqlnd_conn_data__stmt_init)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__shutdown_server)(MYSQLND_CONN_DATA * const conn, uint8_t level TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__refresh_server)(MYSQLND_CONN_DATA * const conn, uint8_t options TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__ping)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__kill_connection)(MYSQLND_CONN_DATA * conn, unsigned int pid TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__server_dump_debug_information)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__change_user)(MYSQLND_CONN_DATA * const conn, const char * user, const char * passwd, const char * db, zend_bool silent, size_t passwd_len TSRMLS_DC);
+
+typedef unsigned int (*func_mysqlnd_conn_data__get_error_no)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__get_error_str)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__get_sqlstate)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_conn_data__get_thread_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef void (*func_mysqlnd_conn_data__get_statistics)(const MYSQLND_CONN_DATA * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+
+typedef unsigned long (*func_mysqlnd_conn_data__get_server_version)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__get_server_information)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__get_server_statistics)(MYSQLND_CONN_DATA * conn, char **message, unsigned int * message_len TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__get_host_information)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_conn_data__get_protocol_information)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__get_last_message)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef const char * (*func_mysqlnd_conn_data__charset_name)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_fields)(MYSQLND_CONN_DATA * conn, const char * table, const char * achtung_wild TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char * achtung_wild, char *par1 TSRMLS_DC);
+
+typedef uint64_t (*func_mysqlnd_conn_data__get_last_insert_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_conn_data__get_affected_rows)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_conn_data__get_warning_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+typedef unsigned int (*func_mysqlnd_conn_data__get_field_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+typedef unsigned int (*func_mysqlnd_conn_data__get_server_status)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC);
+typedef void (*func_mysqlnd_conn_data__free_contents)(MYSQLND_CONN_DATA * conn TSRMLS_DC);/* private */
+typedef void (*func_mysqlnd_conn_data__free_options)(MYSQLND_CONN_DATA * conn TSRMLS_DC); /* private */
+typedef void (*func_mysqlnd_conn_data__dtor)(MYSQLND_CONN_DATA * conn TSRMLS_DC); /* private */
+
+typedef enum_func_status (*func_mysqlnd_conn_data__query_read_result_set_header)(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt TSRMLS_DC);
+
+typedef MYSQLND_CONN_DATA * (*func_mysqlnd_conn_data__get_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__free_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef enum mysqlnd_connection_state (*func_mysqlnd_conn_data__get_state)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef void (*func_mysqlnd_conn_data__set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command, zend_bool ignore_upsert_status TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__restart_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__end_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_close)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert, const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC);
+
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__result_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__tx_commit)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__tx_rollback)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status TSRMLS_DC);
+
+
+struct st_mysqlnd_conn_data_methods
+{
+ func_mysqlnd_conn_data__init init;
+ func_mysqlnd_conn_data__connect connect;
+ func_mysqlnd_conn_data__escape_string escape_string;
+ func_mysqlnd_conn_data__set_charset set_charset;
+ func_mysqlnd_conn_data__query query;
+ func_mysqlnd_conn_data__send_query send_query;
+ func_mysqlnd_conn_data__reap_query reap_query;
+ func_mysqlnd_conn_data__use_result use_result;
+ func_mysqlnd_conn_data__store_result store_result;
+ func_mysqlnd_conn_data__next_result next_result;
+ func_mysqlnd_conn_data__more_results more_results;
+
+ func_mysqlnd_conn_data__stmt_init stmt_init;
+
+ func_mysqlnd_conn_data__shutdown_server shutdown_server;
+ func_mysqlnd_conn_data__refresh_server refresh_server;
+
+ func_mysqlnd_conn_data__ping ping;
+ func_mysqlnd_conn_data__kill_connection kill_connection;
+ func_mysqlnd_conn_data__select_db select_db;
+ func_mysqlnd_conn_data__server_dump_debug_information server_dump_debug_information;
+ func_mysqlnd_conn_data__change_user change_user;
+
+ func_mysqlnd_conn_data__get_error_no get_error_no;
+ func_mysqlnd_conn_data__get_error_str get_error_str;
+ func_mysqlnd_conn_data__get_sqlstate get_sqlstate;
+ func_mysqlnd_conn_data__get_thread_id get_thread_id;
+ func_mysqlnd_conn_data__get_statistics get_statistics;
+
+ func_mysqlnd_conn_data__get_server_version get_server_version;
+ func_mysqlnd_conn_data__get_server_information get_server_information;
+ func_mysqlnd_conn_data__get_server_statistics get_server_statistics;
+ func_mysqlnd_conn_data__get_host_information get_host_information;
+ func_mysqlnd_conn_data__get_protocol_information get_protocol_information;
+ func_mysqlnd_conn_data__get_last_message get_last_message;
+ func_mysqlnd_conn_data__charset_name charset_name;
+ func_mysqlnd_conn_data__list_fields list_fields;
+ func_mysqlnd_conn_data__list_method list_method;
+
+ func_mysqlnd_conn_data__get_last_insert_id get_last_insert_id;
+ func_mysqlnd_conn_data__get_affected_rows get_affected_rows;
+ func_mysqlnd_conn_data__get_warning_count get_warning_count;
+
+ func_mysqlnd_conn_data__get_field_count get_field_count;
+
+ func_mysqlnd_conn_data__get_server_status get_server_status;
+
+ func_mysqlnd_conn_data__set_server_option set_server_option;
+ func_mysqlnd_conn_data__set_client_option set_client_option;
+ func_mysqlnd_conn_data__free_contents free_contents;
+ func_mysqlnd_conn_data__free_options free_options;
+ func_mysqlnd_conn_data__dtor dtor;
+
+ func_mysqlnd_conn_data__query_read_result_set_header query_read_result_set_header;
+
+ func_mysqlnd_conn_data__get_reference get_reference;
+ func_mysqlnd_conn_data__free_reference free_reference;
+ func_mysqlnd_conn_data__get_state get_state;
+ func_mysqlnd_conn_data__set_state set_state;
+
+ func_mysqlnd_conn_data__simple_command simple_command;
+ func_mysqlnd_conn_data__simple_command_handle_response simple_command_handle_response;
+
+ func_mysqlnd_conn_data__restart_psession restart_psession;
+ func_mysqlnd_conn_data__end_psession end_psession;
+ func_mysqlnd_conn_data__send_close send_close;
+
+ func_mysqlnd_conn_data__ssl_set ssl_set;
+
+ func_mysqlnd_conn_data__result_init result_init;
+ func_mysqlnd_conn_data__set_autocommit set_autocommit;
+ func_mysqlnd_conn_data__tx_commit tx_commit;
+ func_mysqlnd_conn_data__tx_rollback tx_rollback;
+
+ func_mysqlnd_conn_data__local_tx_start local_tx_start;
+ func_mysqlnd_conn_data__local_tx_end local_tx_end;
+};
+
+
+typedef enum_func_status (*func_mysqlnd_data__connect)(MYSQLND * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags TSRMLS_DC);
+typedef MYSQLND * (*func_mysqlnd_conn__clone_object)(MYSQLND * const conn TSRMLS_DC);
+typedef void (*func_mysqlnd_conn__dtor)(MYSQLND * conn TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC);
+
+struct st_mysqlnd_conn_methods
+{
+ func_mysqlnd_data__connect connect;
+ func_mysqlnd_conn__clone_object clone_object;
+ func_mysqlnd_conn__dtor dtor;
+ func_mysqlnd_conn__close close;
+};
+
+
+typedef mysqlnd_fetch_row_func fetch_row;
+typedef mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */
+typedef mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */
+
+typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, zend_bool ps TSRMLS_DC);
+typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC);
+typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result TSRMLS_DC);
+typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_res__num_fields)(const MYSQLND_RES * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__skip_result)(MYSQLND_RES * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * result, uint64_t row TSRMLS_DC);
+typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC);
+typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__field_tell)(const MYSQLND_RES * const result TSRMLS_DC);
+typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field)(MYSQLND_RES * const result TSRMLS_DC);
+typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result TSRMLS_DC);
+
+typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef unsigned long * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC);
+
+typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */
+typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC);
+typedef void (*func_mysqlnd_res__free_result_internal)(MYSQLND_RES *result TSRMLS_DC);
+typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result TSRMLS_DC);
+typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result TSRMLS_DC);
+typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC);
+
+ /* for decoding - binary or text protocol */
+typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, MYSQLND_FIELD *fields_metadata,
+ zend_bool as_unicode, zend_bool as_int_or_float,
+ MYSQLND_STATS * stats TSRMLS_DC);
+
+typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC);
+
+struct st_mysqlnd_res_methods
+{
+ mysqlnd_fetch_row_func fetch_row;
+ mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */
+ mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */
+
+ func_mysqlnd_res__use_result use_result;
+ func_mysqlnd_res__store_result store_result;
+ func_mysqlnd_res__fetch_into fetch_into;
+ func_mysqlnd_res__fetch_row_c fetch_row_c;
+ func_mysqlnd_res__fetch_all fetch_all;
+ func_mysqlnd_res__fetch_field_data fetch_field_data;
+ func_mysqlnd_res__num_rows num_rows;
+ func_mysqlnd_res__num_fields num_fields;
+ func_mysqlnd_res__skip_result skip_result;
+ func_mysqlnd_res__seek_data seek_data;
+ func_mysqlnd_res__seek_field seek_field;
+ func_mysqlnd_res__field_tell field_tell;
+ func_mysqlnd_res__fetch_field fetch_field;
+ func_mysqlnd_res__fetch_field_direct fetch_field_direct;
+ func_mysqlnd_res__fetch_fields fetch_fields;
+ func_mysqlnd_res__read_result_metadata read_result_metadata;
+ func_mysqlnd_res__fetch_lengths fetch_lengths;
+ func_mysqlnd_res__store_result_fetch_data store_result_fetch_data;
+ func_mysqlnd_res__initialize_result_set_rest initialize_result_set_rest;
+ func_mysqlnd_res__free_result_buffers free_result_buffers;
+ func_mysqlnd_res__free_result free_result;
+ func_mysqlnd_res__free_result_internal free_result_internal;
+ func_mysqlnd_res__free_result_contents free_result_contents;
+ func_mysqlnd_res__free_buffered_data free_buffered_data;
+ func_mysqlnd_res__unbuffered_free_last_data unbuffered_free_last_data;
+
+ /* for decoding - binary or text protocol */
+ func_mysqlnd_res__row_decoder row_decoder;
+
+ func_mysqlnd_res__result_meta_init result_meta_init;
+
+ void * unused1;
+ void * unused2;
+ void * unused3;
+ void * unused4;
+ void * unused5;
+};
+
+
+typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC);
+typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC);
+typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC);
+typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC);
+typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta TSRMLS_DC);
+
+struct st_mysqlnd_res_meta_methods
+{
+ func_mysqlnd_res_meta__fetch_field fetch_field;
+ func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct;
+ func_mysqlnd_res_meta__fetch_fields fetch_fields;
+ func_mysqlnd_res_meta__field_tell field_tell;
+ func_mysqlnd_res_meta__read_metadata read_metadata;
+ func_mysqlnd_res_meta__clone_metadata clone_metadata;
+ func_mysqlnd_res_meta__free_metadata free_metadata;
+};
+
+
+typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__execute)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef zend_bool (*func_mysqlnd_stmt__more_results)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__next_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__free_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__seek_data)(const MYSQLND_STMT * const stmt, uint64_t row TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__reset)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */
+typedef enum_func_status (*func_mysqlnd_stmt__dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */
+typedef enum_func_status (*func_mysqlnd_stmt__fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, zval * const zv, zend_uchar type TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__bind_one_result)(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num, const char * const data, unsigned long length TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_parameter_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_stmt__get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_stmt__get_last_insert_id)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_stmt__get_affected_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef uint64_t (*func_mysqlnd_stmt__get_num_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_stmt__get_param_count)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_stmt__get_field_count)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_stmt__get_warning_count)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_stmt__get_error_no)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef const char * (*func_mysqlnd_stmt__get_error_str)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef const char * (*func_mysqlnd_stmt__get_sqlstate)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__get_attribute)(const MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC);
+typedef MYSQLND_PARAM_BIND *(*func_mysqlnd_stmt__alloc_param_bind)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef MYSQLND_RESULT_BIND*(*func_mysqlnd_stmt__alloc_result_bind)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef void (*func_mysqlnd_stmt__free_parameter_bind)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * TSRMLS_DC);
+typedef void (*func_mysqlnd_stmt__free_result_bind)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_stmt__server_status)(const MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__generate_execute_request)(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__parse_execute_response)(MYSQLND_STMT * const s TSRMLS_DC);
+typedef void (*func_mysqlnd_stmt__free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_stmt__flush)(MYSQLND_STMT * const stmt TSRMLS_DC);
+
+struct st_mysqlnd_stmt_methods
+{
+ func_mysqlnd_stmt__prepare prepare;
+ func_mysqlnd_stmt__execute execute;
+ func_mysqlnd_stmt__use_result use_result;
+ func_mysqlnd_stmt__store_result store_result;
+ func_mysqlnd_stmt__get_result get_result;
+ func_mysqlnd_stmt__more_results more_results;
+ func_mysqlnd_stmt__next_result next_result;
+ func_mysqlnd_stmt__free_result free_result;
+ func_mysqlnd_stmt__seek_data seek_data;
+ func_mysqlnd_stmt__reset reset;
+ func_mysqlnd_stmt__net_close net_close;
+ func_mysqlnd_stmt__dtor dtor;
+ func_mysqlnd_stmt__fetch fetch;
+
+ func_mysqlnd_stmt__bind_parameters bind_parameters;
+ func_mysqlnd_stmt__bind_one_parameter bind_one_parameter;
+ func_mysqlnd_stmt__refresh_bind_param refresh_bind_param;
+ func_mysqlnd_stmt__bind_result bind_result;
+ func_mysqlnd_stmt__bind_one_result bind_one_result;
+ func_mysqlnd_stmt__send_long_data send_long_data;
+ func_mysqlnd_stmt__get_parameter_metadata get_parameter_metadata;
+ func_mysqlnd_stmt__get_result_metadata get_result_metadata;
+
+ func_mysqlnd_stmt__get_last_insert_id get_last_insert_id;
+ func_mysqlnd_stmt__get_affected_rows get_affected_rows;
+ func_mysqlnd_stmt__get_num_rows get_num_rows;
+
+ func_mysqlnd_stmt__get_param_count get_param_count;
+ func_mysqlnd_stmt__get_field_count get_field_count;
+ func_mysqlnd_stmt__get_warning_count get_warning_count;
+
+ func_mysqlnd_stmt__get_error_no get_error_no;
+ func_mysqlnd_stmt__get_error_str get_error_str;
+ func_mysqlnd_stmt__get_sqlstate get_sqlstate;
+
+ func_mysqlnd_stmt__get_attribute get_attribute;
+ func_mysqlnd_stmt__set_attribute set_attribute;
+
+ func_mysqlnd_stmt__alloc_param_bind alloc_parameter_bind;
+ func_mysqlnd_stmt__alloc_result_bind alloc_result_bind;
+
+ func_mysqlnd_stmt__free_parameter_bind free_parameter_bind;
+ func_mysqlnd_stmt__free_result_bind free_result_bind;
+
+ func_mysqlnd_stmt__server_status get_server_status;
+
+ func_mysqlnd_stmt__generate_execute_request generate_execute_request;
+ func_mysqlnd_stmt__parse_execute_response parse_execute_response;
+
+ func_mysqlnd_stmt__free_stmt_content free_stmt_content;
+
+ func_mysqlnd_stmt__flush flush;
+};
+
+
+struct st_mysqlnd_net
+{
+ php_stream *stream;
+ /* sequence for simple checking of correct packets */
+ zend_uchar packet_no;
+ zend_bool compressed;
+ zend_uchar compressed_envelope_packet_no;
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_READ_BUFFER * uncompressed_data;
+#else
+ void * unused_pad1;
+#endif
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ zend_uchar last_command;
+#else
+ zend_uchar unused_pad2;
+#endif
+ /* cmd buffer */
+ MYSQLND_CMD_BUFFER cmd_buffer;
+
+ MYSQLND_NET_OPTIONS options;
+
+ zend_bool persistent;
+
+ struct st_mysqlnd_net_methods m;
+};
+
+
+struct st_mysqlnd_protocol
+{
+ zend_bool persistent;
+ struct st_mysqlnd_protocol_methods m;
+};
+
+
+struct st_mysqlnd_connection_data
+{
+/* Operation related */
+ MYSQLND_NET * net;
+ MYSQLND_PROTOCOL * protocol;
+
+/* Information related */
+ char *host;
+ unsigned int host_len;
+ char *unix_socket;
+ unsigned int unix_socket_len;
+ char *user;
+ unsigned int user_len;
+ char *passwd;
+ unsigned int passwd_len;
+ char *scheme;
+ unsigned int scheme_len;
+ uint64_t thread_id;
+ char *server_version;
+ char *host_info;
+ zend_uchar *auth_plugin_data;
+ size_t auth_plugin_data_len;
+ const MYSQLND_CHARSET *charset;
+ const MYSQLND_CHARSET *greet_charset;
+ char *connect_or_select_db;
+ unsigned int connect_or_select_db_len;
+ MYSQLND_INFILE infile;
+ unsigned int protocol_version;
+ unsigned long max_packet_size;
+ unsigned int port;
+ unsigned long client_flag;
+ unsigned long server_capabilities;
+
+ /* For UPSERT queries */
+ MYSQLND_UPSERT_STATUS * upsert_status;
+ MYSQLND_UPSERT_STATUS upsert_status_impl;
+ char *last_message;
+ unsigned int last_message_len;
+
+ /* If error packet, we use these */
+ MYSQLND_ERROR_INFO * error_info;
+ MYSQLND_ERROR_INFO error_info_impl;
+
+ /*
+ To prevent queries during unbuffered fetches. Also to
+ mark the connection as destroyed for garbage collection.
+ */
+ enum mysqlnd_connection_state state;
+ enum_mysqlnd_query_type last_query_type;
+ /* Temporary storage between query and (use|store)_result() call */
+ MYSQLND_RES *current_result;
+
+ /*
+ How many result sets reference this connection.
+ It won't be freed until this number reaches 0.
+ The last one, please close the door! :-)
+ The result set objects can determine by inspecting
+ 'quit_sent' whether the connection is still valid.
+ */
+ unsigned int refcount;
+
+ /* Temporal storage for mysql_query */
+ unsigned int field_count;
+
+ /* persistent connection */
+ zend_bool persistent;
+
+ /* options */
+ MYSQLND_OPTIONS * options;
+ MYSQLND_OPTIONS options_impl;
+
+ /* stats */
+ MYSQLND_STATS * stats;
+
+ struct st_mysqlnd_conn_data_methods * m;
+};
+
+
+struct st_mysqlnd_connection
+{
+ MYSQLND_CONN_DATA * data;
+ zend_bool persistent;
+ struct st_mysqlnd_conn_methods * m;
+};
+
+
+struct mysqlnd_field_hash_key
+{
+ zend_bool is_numeric;
+ unsigned long key;
+#if MYSQLND_UNICODE
+ zstr ustr;
+ unsigned int ulen;
+#endif
+};
+
+
+struct st_mysqlnd_result_metadata
+{
+ MYSQLND_FIELD *fields;
+ struct mysqlnd_field_hash_key *zend_hash_keys;
+ unsigned int current_field;
+ unsigned int field_count;
+ /* We need this to make fast allocs in rowp_read */
+ unsigned int bit_fields_count;
+ size_t bit_fields_total_len; /* trailing \0 not counted */
+ zend_bool persistent;
+
+ struct st_mysqlnd_res_meta_methods *m;
+};
+
+
+struct st_mysqlnd_buffered_result
+{
+ zval **data;
+ zval **data_cursor;
+ MYSQLND_MEMORY_POOL_CHUNK **row_buffers;
+ uint64_t row_count;
+ uint64_t initialized_rows;
+
+ unsigned int references;
+
+ MYSQLND_ERROR_INFO error_info;
+};
+
+
+struct st_mysqlnd_unbuffered_result
+{
+ /* For unbuffered (both normal and PS) */
+ zval **last_row_data;
+ MYSQLND_MEMORY_POOL_CHUNK *last_row_buffer;
+
+ uint64_t row_count;
+ zend_bool eof_reached;
+};
+
+
+struct st_mysqlnd_res
+{
+ MYSQLND_CONN_DATA *conn;
+ enum_mysqlnd_res_type type;
+ unsigned int field_count;
+
+ /* For metadata functions */
+ MYSQLND_RES_METADATA *meta;
+
+ /* To be used with store_result() - both normal and PS */
+ MYSQLND_RES_BUFFERED *stored_data;
+ MYSQLND_RES_UNBUFFERED *unbuf;
+
+ /*
+ Column lengths of current row - both buffered and unbuffered.
+ For buffered results it duplicates the data found in **data
+ */
+ unsigned long *lengths;
+
+ struct st_mysqlnd_packet_row * row_packet;
+
+ MYSQLND_MEMORY_POOL * result_set_memory_pool;
+ zend_bool persistent;
+
+ struct st_mysqlnd_res_methods m;
+};
+
+
+struct st_mysqlnd_param_bind
+{
+ zval *zv;
+ zend_uchar type;
+ enum_param_bind_flags flags;
+};
+
+struct st_mysqlnd_result_bind
+{
+ zval *zv;
+ zend_bool bound;
+};
+
+
+struct st_mysqlnd_stmt_data
+{
+ MYSQLND_CONN_DATA *conn;
+ unsigned long stmt_id;
+ unsigned long flags;/* cursor is set here */
+ enum_mysqlnd_stmt_state state;
+ unsigned int warning_count;
+ MYSQLND_RES *result;
+ unsigned int field_count;
+ unsigned int param_count;
+ unsigned char send_types_to_server;
+ MYSQLND_PARAM_BIND *param_bind;
+ MYSQLND_RESULT_BIND *result_bind;
+ zend_bool result_zvals_separated_once;
+ zend_bool persistent;
+
+ MYSQLND_UPSERT_STATUS * upsert_status;
+ MYSQLND_UPSERT_STATUS upsert_status_impl;
+
+ MYSQLND_ERROR_INFO * error_info;
+ MYSQLND_ERROR_INFO error_info_impl;
+
+ zend_bool update_max_length;
+ unsigned long prefetch_rows;
+
+ zend_bool cursor_exists;
+ mysqlnd_stmt_use_or_store_func default_rset_handler;
+
+ MYSQLND_CMD_BUFFER execute_cmd_buffer;
+ unsigned int execute_count;/* count how many times the stmt was executed */
+};
+
+
+struct st_mysqlnd_stmt
+{
+ MYSQLND_STMT_DATA * data;
+ struct st_mysqlnd_stmt_methods *m;
+ zend_bool persistent;
+};
+
+
+typedef struct st_mysqlnd_string
+{
+ char *s;
+ size_t l;
+} MYSQLND_STRING;
+
+
+struct st_mysqlnd_plugin_header
+{
+ unsigned int plugin_api_version;
+ const char * plugin_name;
+ unsigned long plugin_version;
+ const char * plugin_string_version;
+ const char * plugin_license;
+ const char * plugin_author;
+ struct
+ {
+ MYSQLND_STATS * values;
+ const MYSQLND_STRING * names;
+ } plugin_stats;
+
+ struct
+ {
+ enum_func_status (*plugin_shutdown)(void * plugin TSRMLS_DC);
+ } m;
+};
+
+
+struct st_mysqlnd_plugin_core
+{
+ struct st_mysqlnd_plugin_header plugin_header;
+};
+
+
+struct st_mysqlnd_typeii_plugin_example
+{
+ struct st_mysqlnd_plugin_header plugin_header;
+ void * methods;
+ unsigned int counter;
+};
+
+struct st_mysqlnd_authentication_plugin;
+
+typedef zend_uchar * (*func_auth_plugin__get_auth_data)(struct st_mysqlnd_authentication_plugin * self,
+ size_t * auth_data_len,
+ MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
+ const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
+ const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
+ TSRMLS_DC);
+
+struct st_mysqlnd_authentication_plugin
+{
+ struct st_mysqlnd_plugin_header plugin_header;
+ struct {
+ func_auth_plugin__get_auth_data get_auth_data;
+ } methods;
+};
+
+
+#endif /* MYSQLND_STRUCTS_H */
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
new file mode 100644
index 0000000..d0ab9fe
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -0,0 +1,2416 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "php_globals.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "zend_ini.h"
+
+#define MYSQLND_SILENT 1
+
+#define MYSQLND_DUMP_HEADER_N_BODY
+
+#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
+ { \
+ DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
+ if (FAIL == mysqlnd_read_header((conn)->net, &((packet)->header), (conn)->stats, ((conn)->error_info) TSRMLS_CC)) {\
+ CONN_SET_STATE(conn, CONN_QUIT_SENT); \
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
+ DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
+ DBG_RETURN(FAIL);\
+ }\
+ if ((buf_size) < (packet)->header.size) { \
+ DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
+ (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
+ DBG_RETURN(FAIL); \
+ }\
+ if (FAIL == conn->net->m.receive_ex((conn)->net, (buf), (packet)->header.size, (conn)->stats, ((conn)->error_info) TSRMLS_CC)) { \
+ CONN_SET_STATE(conn, CONN_QUIT_SENT); \
+ SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
+ DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
+ DBG_RETURN(FAIL);\
+ } \
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
+ MYSQLND_HEADER_SIZE + (packet)->header.size, \
+ packet_type_to_statistic_packet_count[packet_type], \
+ 1); \
+ }
+
+
+#define BAIL_IF_NO_MORE_DATA \
+ if ((size_t)(p - begin) > packet->header.size) { \
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
+ goto premature_end; \
+ } \
+
+
+static const char *unknown_sqlstate= "HY000";
+
+const char * const mysqlnd_empty_string = "";
+
+/* Used in mysqlnd_debug.c */
+const char mysqlnd_read_header_name[] = "mysqlnd_read_header";
+const char mysqlnd_read_body_name[] = "mysqlnd_read_body";
+
+
+#define ERROR_MARKER 0xFF
+#define EODATA_MARKER 0xFE
+
+/* {{{ mysqlnd_command_to_text
+ */
+const char * const mysqlnd_command_to_text[COM_END] =
+{
+ "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
+ "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
+ "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
+ "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
+ "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
+ "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
+ "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
+};
+/* }}} */
+
+
+
+static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
+{
+ STAT_LAST,
+ STAT_LAST,
+ STAT_BYTES_RECEIVED_OK,
+ STAT_BYTES_RECEIVED_EOF,
+ STAT_LAST,
+ STAT_BYTES_RECEIVED_RSET_HEADER,
+ STAT_BYTES_RECEIVED_RSET_FIELD_META,
+ STAT_BYTES_RECEIVED_RSET_ROW,
+ STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
+ STAT_BYTES_RECEIVED_CHANGE_USER,
+};
+
+static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
+{
+ STAT_LAST,
+ STAT_LAST,
+ STAT_PACKETS_RECEIVED_OK,
+ STAT_PACKETS_RECEIVED_EOF,
+ STAT_LAST,
+ STAT_PACKETS_RECEIVED_RSET_HEADER,
+ STAT_PACKETS_RECEIVED_RSET_FIELD_META,
+ STAT_PACKETS_RECEIVED_RSET_ROW,
+ STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
+ STAT_PACKETS_RECEIVED_CHANGE_USER,
+};
+
+
+/* {{{ php_mysqlnd_net_field_length
+ Get next field's length */
+unsigned long
+php_mysqlnd_net_field_length(zend_uchar **packet)
+{
+ register zend_uchar *p= (zend_uchar *)*packet;
+
+ if (*p < 251) {
+ (*packet)++;
+ return (unsigned long) *p;
+ }
+
+ switch (*p) {
+ case 251:
+ (*packet)++;
+ return MYSQLND_NULL_LENGTH;
+ case 252:
+ (*packet) += 3;
+ return (unsigned long) uint2korr(p+1);
+ case 253:
+ (*packet) += 4;
+ return (unsigned long) uint3korr(p+1);
+ default:
+ (*packet) += 9;
+ return (unsigned long) uint4korr(p+1);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_net_field_length_ll
+ Get next field's length */
+uint64_t
+php_mysqlnd_net_field_length_ll(zend_uchar **packet)
+{
+ register zend_uchar *p= (zend_uchar *)*packet;
+
+ if (*p < 251) {
+ (*packet)++;
+ return (uint64_t) *p;
+ }
+
+ switch (*p) {
+ case 251:
+ (*packet)++;
+ return (uint64_t) MYSQLND_NULL_LENGTH;
+ case 252:
+ (*packet) += 3;
+ return (uint64_t) uint2korr(p + 1);
+ case 253:
+ (*packet) += 4;
+ return (uint64_t) uint3korr(p + 1);
+ default:
+ (*packet) += 9;
+ return (uint64_t) uint8korr(p + 1);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_net_store_length */
+zend_uchar *
+php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
+{
+ if (length < (uint64_t) L64(251)) {
+ *packet = (zend_uchar) length;
+ return packet + 1;
+ }
+
+ if (length < (uint64_t) L64(65536)) {
+ *packet++ = 252;
+ int2store(packet,(unsigned int) length);
+ return packet + 2;
+ }
+
+ if (length < (uint64_t) L64(16777216)) {
+ *packet++ = 253;
+ int3store(packet,(ulong) length);
+ return packet + 3;
+ }
+ *packet++ = 254;
+ int8store(packet, length);
+ return packet + 8;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_read_error_from_line */
+static enum_func_status
+php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
+ char *error, int error_buf_len,
+ unsigned int *error_no, char *sqlstate TSRMLS_DC)
+{
+ zend_uchar *p = buf;
+ int error_msg_len= 0;
+
+ DBG_ENTER("php_mysqlnd_read_error_from_line");
+
+ *error_no = CR_UNKNOWN_ERROR;
+ memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
+
+ if (buf_len > 2) {
+ *error_no = uint2korr(p);
+ p+= 2;
+ /*
+ sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
+ if it was >=2 then we would need a check
+ */
+ if (*p == '#') {
+ ++p;
+ if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
+ memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
+ p+= MYSQLND_SQLSTATE_LENGTH;
+ } else {
+ goto end;
+ }
+ }
+ if ((buf_len - (p - buf)) > 0) {
+ error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
+ memcpy(error, p, error_msg_len);
+ }
+ }
+end:
+ sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
+ error[error_msg_len]= '\0';
+
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_header */
+static enum_func_status
+mysqlnd_read_header(MYSQLND_NET * net, MYSQLND_PACKET_HEADER * header,
+ MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
+{
+ zend_uchar buffer[MYSQLND_HEADER_SIZE];
+
+ DBG_ENTER(mysqlnd_read_header_name);
+ DBG_INF_FMT("compressed=%u", net->compressed);
+ if (FAIL == net->m.receive_ex(net, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) {
+ DBG_RETURN(FAIL);
+ }
+
+ header->size = uint3korr(buffer);
+ header->packet_no = uint1korr(buffer + 3);
+
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
+#endif
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats,
+ STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
+ STAT_PACKETS_RECEIVED, 1);
+
+ if (net->compressed || net->packet_no == header->packet_no) {
+ /*
+ Have to increase the number, so we can send correct number back. It will
+ round at 255 as this is unsigned char. The server needs this for simple
+ flow control checking.
+ */
+ net->packet_no++;
+ DBG_RETURN(PASS);
+ }
+
+ DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ net->packet_no, header->packet_no, header->size);
+
+ php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ net->packet_no, header->packet_no, header->size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_greet_read */
+static enum_func_status
+php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_uchar buf[2048];
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ zend_uchar *pad_start = NULL;
+ MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
+
+ DBG_ENTER("php_mysqlnd_greet_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->auth_plugin_data = packet->intern_auth_plugin_data;
+ packet->auth_plugin_data_len = sizeof(packet->intern_auth_plugin_data);
+
+ if (packet->header.size < sizeof(buf)) {
+ /*
+ Null-terminate the string, so strdup can work even if the packets have a string at the end,
+ which is not ASCIIZ
+ */
+ buf[packet->header.size] = '\0';
+ }
+
+ packet->protocol_version = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->protocol_version) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error, sizeof(packet->error),
+ &packet->error_no, packet->sqlstate
+ TSRMLS_CC);
+ /*
+ The server doesn't send sqlstate in the greet packet.
+ It's a bug#26426 , so we have to set it correctly ourselves.
+ It's probably "Too many connections, which has SQL state 08004".
+ */
+ if (packet->error_no == 1040) {
+ memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
+ }
+ DBG_RETURN(PASS);
+ }
+
+ packet->server_version = estrdup((char *)p);
+ p+= strlen(packet->server_version) + 1; /* eat the '\0' */
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->thread_id = uint4korr(p);
+ p+=4;
+ BAIL_IF_NO_MORE_DATA;
+
+ memcpy(packet->auth_plugin_data, p, SCRAMBLE_LENGTH_323);
+ p+= SCRAMBLE_LENGTH_323;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* pad1 */
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_capabilities = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->charset_no = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* pad2 */
+ pad_start = p;
+ p+= 13;
+ BAIL_IF_NO_MORE_DATA;
+
+ if ((size_t) (p - buf) < packet->header.size) {
+ /* auth_plugin_data is split into two parts */
+ memcpy(packet->auth_plugin_data + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ p+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323;
+ p++; /* 0x0 at the end of the scramble and thus last byte in the packet in 5.1 and previous */
+ } else {
+ packet->pre41 = TRUE;
+ }
+
+ /* Is this a 5.5+ server ? */
+ if ((size_t) (p - buf) < packet->header.size) {
+ /* backtrack one byte, the 0x0 at the end of the scramble in 5.1 and previous */
+ p--;
+
+ /* Additional 16 bits for server capabilities */
+ packet->server_capabilities |= uint2korr(pad_start) << 16;
+ /* And a length of the server scramble in one byte */
+ packet->auth_plugin_data_len = uint1korr(pad_start + 2);
+ if (packet->auth_plugin_data_len > SCRAMBLE_LENGTH) {
+ /* more data*/
+ zend_uchar * new_auth_plugin_data = emalloc(packet->auth_plugin_data_len);
+ if (!new_auth_plugin_data) {
+ goto premature_end;
+ }
+ /* copy what we already have */
+ memcpy(new_auth_plugin_data, packet->auth_plugin_data, SCRAMBLE_LENGTH);
+ /* add additional scramble data 5.5+ sent us */
+ memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
+ p+= (packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
+ packet->auth_plugin_data = new_auth_plugin_data;
+ }
+ }
+
+ if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) {
+ BAIL_IF_NO_MORE_DATA;
+ /* The server is 5.5.x and supports authentication plugins */
+ packet->auth_protocol = estrdup((char *)p);
+ p+= strlen(packet->auth_protocol) + 1; /* eat the '\0' */
+ }
+
+ DBG_INF_FMT("proto=%u server=%s thread_id=%u",
+ packet->protocol_version, packet->server_version, packet->thread_id);
+
+ DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i auth_protocol=%s scramble_length=%u",
+ packet->server_capabilities, packet->charset_no, packet->server_status,
+ packet->auth_protocol? packet->auth_protocol:"n/a", packet->auth_plugin_data_len);
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_greet_free_mem */
+static
+void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
+ if (p->server_version) {
+ efree(p->server_version);
+ p->server_version = NULL;
+ }
+ if (p->auth_plugin_data && p->auth_plugin_data != p->intern_auth_plugin_data) {
+ efree(p->auth_plugin_data);
+ p->auth_plugin_data = NULL;
+ }
+ if (p->auth_protocol) {
+ efree(p->auth_protocol);
+ p->auth_protocol = NULL;
+ }
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 1024)
+
+/* {{{ php_mysqlnd_auth_write */
+static
+size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_uchar buffer[AUTH_WRITE_BUFFER_LEN];
+ zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
+ int len;
+ MYSQLND_PACKET_AUTH * packet= (MYSQLND_PACKET_AUTH *) _packet;
+
+ DBG_ENTER("php_mysqlnd_auth_write");
+
+ if (!packet->is_change_user_packet) {
+ int4store(p, packet->client_flags);
+ p+= 4;
+
+ int4store(p, packet->max_packet_size);
+ p+= 4;
+
+ int1store(p, packet->charset_no);
+ p++;
+
+ memset(p, 0, 23); /* filler */
+ p+= 23;
+ }
+
+ if (packet->send_auth_data || packet->is_change_user_packet) {
+ len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
+ memcpy(p, packet->user, len);
+ p+= len;
+ *p++ = '\0';
+
+ /* defensive coding */
+ if (packet->auth_data == NULL) {
+ packet->auth_data_len = 0;
+ }
+ if (packet->auth_data_len > 0xFF) {
+ const char * const msg = "Authentication data too long. "
+ "Won't fit into the buffer and will be truncated. Authentication will thus fail";
+ SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", msg);
+ DBG_RETURN(0);
+ }
+
+ int1store(p, packet->auth_data_len);
+ ++p;
+/*!!!!! is the buffer big enough ??? */
+ if ((sizeof(buffer) - (p - buffer)) < packet->auth_data_len) {
+ DBG_ERR("the stack buffer was not enough!!");
+ DBG_RETURN(0);
+ }
+ if (packet->auth_data_len) {
+ memcpy(p, packet->auth_data, packet->auth_data_len);
+ p+= packet->auth_data_len;
+ }
+
+ if (packet->db) {
+ /* CLIENT_CONNECT_WITH_DB should have been set */
+ size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
+ memcpy(p, packet->db, real_db_len);
+ p+= real_db_len;
+ *p++= '\0';
+ } else if (packet->is_change_user_packet) {
+ *p++= '\0';
+ }
+ /* no \0 for no DB */
+
+ if (packet->is_change_user_packet) {
+ if (packet->charset_no) {
+ int2store(p, packet->charset_no);
+ p+= 2;
+ }
+ }
+
+ if (packet->auth_plugin_name) {
+ size_t len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
+ memcpy(p, packet->auth_plugin_name, len);
+ p+= len;
+ *p++= '\0';
+ }
+ }
+ 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,
+ PROT_LAST /* the caller will handle the OK packet */,
+ packet->silent, TRUE TSRMLS_CC)) {
+ DBG_RETURN(0);
+ }
+ DBG_RETURN(p - buffer - MYSQLND_HEADER_SIZE);
+ } else {
+ size_t sent = conn->net->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info TSRMLS_CC);
+ if (!sent) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ }
+ DBG_RETURN(sent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_auth_free_mem */
+static
+void php_mysqlnd_auth_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ if (!stack_allocation) {
+ MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet;
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+#define AUTH_RESP_BUFFER_SIZE 2048
+
+/* {{{ php_mysqlnd_auth_response_read */
+static enum_func_status
+php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_uchar local_buf[AUTH_RESP_BUFFER_SIZE];
+ size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length: AUTH_RESP_BUFFER_SIZE;
+ zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ unsigned long i;
+ register MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
+
+ DBG_ENTER("php_mysqlnd_auth_response_read");
+
+ /* leave space for terminating safety \0 */
+ buf_len--;
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ /*
+ zero-terminate the buffer for safety. We are sure there is place for the \0
+ because buf_len is -1 the size of the buffer pointed
+ */
+ buf[packet->header.size] = '\0';
+
+ /* Should be always 0x0 or ERROR_MARKER for error */
+ packet->response_code = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->response_code) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error, sizeof(packet->error),
+ &packet->error_no, packet->sqlstate
+ TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+ if (0xFE == packet->response_code) {
+ /* Authentication Switch Response */
+ if (packet->header.size > (size_t) (p - buf)) {
+ packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
+ packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
+ p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
+
+ packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
+ if (packet->new_auth_protocol_data_len) {
+ packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
+ memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
+ }
+ DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
+ DBG_INF_FMT("Server salt : [%*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
+ }
+ } else {
+ /* Everything was fine! */
+ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->warning_count = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* There is a message */
+ if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(i, buf_len - (p - begin));
+ packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
+ } else {
+ packet->message = NULL;
+ packet->message_len = 0;
+ }
+
+ DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
+ packet->affected_rows, packet->last_insert_id, packet->server_status,
+ packet->warning_count);
+ }
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "AUTH_RESPONSE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_auth_response_free_mem */
+static void
+php_mysqlnd_auth_response_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_AUTH_RESPONSE * p = (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
+ if (p->message) {
+ mnd_efree(p->message);
+ p->message = NULL;
+ }
+ if (p->new_auth_protocol) {
+ mnd_efree(p->new_auth_protocol);
+ p->new_auth_protocol = NULL;
+ }
+ p->new_auth_protocol_len = 0;
+
+ if (p->new_auth_protocol_data) {
+ mnd_efree(p->new_auth_protocol_data);
+ p->new_auth_protocol_data = NULL;
+ }
+ p->new_auth_protocol_data_len = 0;
+
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_change_auth_response_write */
+static size_t
+php_mysqlnd_change_auth_response_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *packet= (MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *) _packet;
+ zend_uchar * buffer = conn->net->cmd_buffer.length >= packet->auth_data_len? conn->net->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
+ zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
+
+ DBG_ENTER("php_mysqlnd_change_auth_response_write");
+
+ if (packet->auth_data_len) {
+ memcpy(p, packet->auth_data, packet->auth_data_len);
+ p+= packet->auth_data_len;
+ }
+
+ {
+ size_t sent = conn->net->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info TSRMLS_CC);
+ if (buffer != conn->net->cmd_buffer.buffer) {
+ mnd_efree(buffer);
+ }
+ if (!sent) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ }
+ DBG_RETURN(sent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_change_auth_response_free_mem */
+static void
+php_mysqlnd_change_auth_response_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ if (!stack_allocation) {
+ MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * p = (MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *) _packet;
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+#define OK_BUFFER_SIZE 2048
+
+/* {{{ php_mysqlnd_ok_read */
+static enum_func_status
+php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ zend_uchar local_buf[OK_BUFFER_SIZE];
+ size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
+ zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ unsigned long i;
+ register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
+
+ DBG_ENTER("php_mysqlnd_ok_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ /* Should be always 0x0 or ERROR_MARKER for error */
+ packet->field_count = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->field_count) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error, sizeof(packet->error),
+ &packet->error_no, packet->sqlstate
+ TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+ /* Everything was fine! */
+ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->warning_count = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* There is a message */
+ if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(i, buf_len - (p - begin));
+ packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
+ } else {
+ packet->message = NULL;
+ packet->message_len = 0;
+ }
+
+ DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
+ packet->affected_rows, packet->last_insert_id, packet->server_status,
+ packet->warning_count);
+
+ BAIL_IF_NO_MORE_DATA;
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_ok_free_mem */
+static void
+php_mysqlnd_ok_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
+ if (p->message) {
+ mnd_efree(p->message);
+ p->message = NULL;
+ }
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_eof_read */
+static enum_func_status
+php_mysqlnd_eof_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ /*
+ EOF packet is since 4.1 five bytes long,
+ but we can get also an error, make it bigger.
+
+ Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
+ */
+ MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
+ size_t buf_len = conn->net->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+
+ DBG_ENTER("php_mysqlnd_eof_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ /* Should be always EODATA_MARKER */
+ packet->field_count = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->field_count) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error, sizeof(packet->error),
+ &packet->error_no, packet->sqlstate
+ TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+
+ /*
+ 4.1 sends 1 byte EOF packet after metadata of
+ PREPARE/EXECUTE but 5 bytes after the result. This is not
+ according to the Docs@Forge!!!
+ */
+ if (packet->header.size > 1) {
+ packet->warning_count = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+ BAIL_IF_NO_MORE_DATA;
+ } else {
+ packet->warning_count = 0;
+ packet->server_status = 0;
+ }
+
+ BAIL_IF_NO_MORE_DATA;
+
+ DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
+ packet->field_count, packet->server_status, packet->warning_count);
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_eof_free_mem */
+static
+void php_mysqlnd_eof_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ if (!stack_allocation) {
+ mnd_pefree(_packet, ((MYSQLND_PACKET_EOF *)_packet)->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_cmd_write */
+size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
+ MYSQLND_PACKET_COMMAND * packet= (MYSQLND_PACKET_COMMAND *) _packet;
+ MYSQLND_NET * net = conn->net;
+ unsigned int error_reporting = EG(error_reporting);
+ size_t sent = 0;
+
+ DBG_ENTER("php_mysqlnd_cmd_write");
+ /*
+ Reset packet_no, or we will get bad handshake!
+ Every command starts a new TX and packet numbers are reset to 0.
+ */
+ net->packet_no = 0;
+ net->compressed_envelope_packet_no = 0; /* this is for the response */
+
+ if (error_reporting) {
+ EG(error_reporting) = 0;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
+
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ net->m.consume_uneaten_data(net, packet->command TSRMLS_CC);
+#endif
+
+ if (!packet->argument || !packet->arg_len) {
+ zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
+
+ int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
+ sent = net->m.send_ex(net, buffer, 1, conn->stats, conn->error_info TSRMLS_CC);
+ } else {
+ size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE;
+ zend_uchar *tmp, *p;
+ tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
+ if (!tmp) {
+ goto end;
+ }
+ p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
+
+ int1store(p, packet->command);
+ p++;
+
+ memcpy(p, packet->argument, packet->arg_len);
+
+ sent = net->m.send_ex(net, tmp, tmp_len - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info TSRMLS_CC);
+ if (tmp != net->cmd_buffer.buffer) {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
+ mnd_efree(tmp);
+ }
+ }
+end:
+ if (error_reporting) {
+ /* restore error reporting */
+ EG(error_reporting) = error_reporting;
+ }
+ if (!sent) {
+ CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ }
+ DBG_RETURN(sent);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_cmd_free_mem */
+static
+void php_mysqlnd_cmd_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ if (!stack_allocation) {
+ MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet;
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_header_read */
+static enum_func_status
+php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ size_t buf_len = conn->net->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ size_t len;
+ MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
+
+ DBG_ENTER("php_mysqlnd_rset_header_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ /*
+ Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
+ of encoded sequence for length.
+ */
+ if (ERROR_MARKER == *p) {
+ /* Error */
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error_info.error, sizeof(packet->error_info.error),
+ &packet->error_info.error_no, packet->error_info.sqlstate
+ TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+
+ packet->field_count = php_mysqlnd_net_field_length(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ switch (packet->field_count) {
+ case MYSQLND_NULL_LENGTH:
+ DBG_INF("LOAD LOCAL");
+ /*
+ First byte in the packet is the field count.
+ Thus, the name is size - 1. And we add 1 for a trailing \0.
+ Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
+ that packet->header.size is > 0. Which means that len can't underflow, that
+ would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
+ */
+ len = packet->header.size - 1;
+ packet->info_or_local_file = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file) {
+ memcpy(packet->info_or_local_file, p, len);
+ packet->info_or_local_file[len] = '\0';
+ packet->info_or_local_file_len = len;
+ } else {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ }
+ break;
+ case 0x00:
+ DBG_INF("UPSERT");
+ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->server_status = uint2korr(p);
+ p+=2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->warning_count = uint2korr(p);
+ p+=2;
+ BAIL_IF_NO_MORE_DATA;
+ /* Check for additional textual data */
+ if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
+ packet->info_or_local_file = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file) {
+ memcpy(packet->info_or_local_file, p, len);
+ packet->info_or_local_file[len] = '\0';
+ packet->info_or_local_file_len = len;
+ } else {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ }
+ }
+ DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u",
+ packet->affected_rows, packet->last_insert_id,
+ packet->server_status, packet->warning_count);
+ break;
+ default:
+ DBG_INF("SELECT");
+ /* Result set */
+ break;
+ }
+ BAIL_IF_NO_MORE_DATA;
+
+ DBG_RETURN(ret);
+premature_end:
+ DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_header_free_mem */
+static
+void php_mysqlnd_rset_header_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
+ DBG_ENTER("php_mysqlnd_rset_header_free_mem");
+ if (p->info_or_local_file) {
+ mnd_efree(p->info_or_local_file);
+ p->info_or_local_file = NULL;
+ }
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+static size_t rset_field_offsets[] =
+{
+ STRUCT_OFFSET(MYSQLND_FIELD, catalog),
+ STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
+ STRUCT_OFFSET(MYSQLND_FIELD, db),
+ STRUCT_OFFSET(MYSQLND_FIELD, db_length),
+ STRUCT_OFFSET(MYSQLND_FIELD, table),
+ STRUCT_OFFSET(MYSQLND_FIELD, table_length),
+ STRUCT_OFFSET(MYSQLND_FIELD, org_table),
+ STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
+ STRUCT_OFFSET(MYSQLND_FIELD, name),
+ STRUCT_OFFSET(MYSQLND_FIELD, name_length),
+ STRUCT_OFFSET(MYSQLND_FIELD, org_name),
+ STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
+};
+
+
+/* {{{ php_mysqlnd_rset_field_read */
+static enum_func_status
+php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ /* Should be enough for the metadata of a single row */
+ MYSQLND_PACKET_RES_FIELD *packet= (MYSQLND_PACKET_RES_FIELD *) _packet;
+ size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ char *root_ptr;
+ unsigned long len;
+ MYSQLND_FIELD *meta;
+ unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
+
+ DBG_ENTER("php_mysqlnd_rset_field_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
+
+ if (packet->skip_parsing) {
+ DBG_RETURN(PASS);
+ }
+
+ BAIL_IF_NO_MORE_DATA;
+ if (ERROR_MARKER == *p) {
+ /* Error */
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error_info.error, sizeof(packet->error_info.error),
+ &packet->error_info.error_no, packet->error_info.sqlstate
+ TSRMLS_CC);
+ DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
+ DBG_RETURN(PASS);
+ } else if (EODATA_MARKER == *p && packet->header.size < 8) {
+ /* Premature EOF. That should be COM_FIELD_LIST */
+ DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
+ packet->stupid_list_fields_eof = TRUE;
+ DBG_RETURN(PASS);
+ }
+
+ meta = packet->metadata;
+
+ for (i = 0; i < field_count; i += 2) {
+ len = php_mysqlnd_net_field_length(&p);
+ BAIL_IF_NO_MORE_DATA;
+ switch ((len)) {
+ case 0:
+ *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
+ *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
+ break;
+ case MYSQLND_NULL_LENGTH:
+ goto faulty_or_fake;
+ default:
+ *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p;
+ *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
+ p += len;
+ total_len += len + 1;
+ break;
+ }
+ BAIL_IF_NO_MORE_DATA;
+ }
+
+ /* 1 byte length */
+ if (12 != *p) {
+ DBG_ERR_FMT("Protocol error. Server sent false length. Expected 12 got %d", (int) *p);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent false length. Expected 12");
+ }
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ meta->charsetnr = uint2korr(p);
+ p += 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ meta->length = uint4korr(p);
+ p += 4;
+ BAIL_IF_NO_MORE_DATA;
+
+ meta->type = uint1korr(p);
+ p += 1;
+ BAIL_IF_NO_MORE_DATA;
+
+ meta->flags = uint2korr(p);
+ p += 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ meta->decimals = uint1korr(p);
+ p += 1;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* 2 byte filler */
+ p +=2;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* Should we set NUM_FLAG (libmysql does it) ? */
+ if (
+ (meta->type <= MYSQL_TYPE_INT24 &&
+ (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
+ ) || meta->type == MYSQL_TYPE_YEAR)
+ {
+ meta->flags |= NUM_FLAG;
+ }
+
+
+ /*
+ def could be empty, thus don't allocate on the root.
+ NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
+ Otherwise the string is length encoded.
+ */
+ if (packet->header.size > (size_t) (p - buf) &&
+ (len = php_mysqlnd_net_field_length(&p)) &&
+ len != MYSQLND_NULL_LENGTH)
+ {
+ BAIL_IF_NO_MORE_DATA;
+ DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
+ meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
+ if (!meta->def) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+ memcpy(meta->def, p, len);
+ meta->def[len] = '\0';
+ meta->def_length = len;
+ p += len;
+ }
+
+ DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc);
+ root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
+ if (!root_ptr) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ meta->root_len = total_len;
+ /* Now do allocs */
+ if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
+ len = meta->catalog_length;
+ meta->catalog = memcpy(root_ptr, meta->catalog, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ if (meta->db && meta->db != mysqlnd_empty_string) {
+ len = meta->db_length;
+ meta->db = memcpy(root_ptr, meta->db, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ if (meta->table && meta->table != mysqlnd_empty_string) {
+ len = meta->table_length;
+ meta->table = memcpy(root_ptr, meta->table, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
+ len = meta->org_table_length;
+ meta->org_table = memcpy(root_ptr, meta->org_table, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ if (meta->name && meta->name != mysqlnd_empty_string) {
+ len = meta->name_length;
+ meta->name = memcpy(root_ptr, meta->name, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
+ len = meta->org_name_length;
+ meta->org_name = memcpy(root_ptr, meta->org_name, len);
+ *(root_ptr +=len) = '\0';
+ root_ptr++;
+ }
+
+ DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
+ meta->name? meta->name:"*NA*");
+
+ DBG_RETURN(PASS);
+
+faulty_or_fake:
+ DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
+ " The server is faulty");
+ DBG_RETURN(FAIL);
+premature_end:
+ DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
+ "shorter than expected", p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_field_free_mem */
+static
+void php_mysqlnd_rset_field_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet;
+ /* p->metadata was passed to us as temporal buffer */
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_read_row_ex */
+static enum_func_status
+php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
+ MYSQLND_MEMORY_POOL_CHUNK ** buffer,
+ size_t * data_size, zend_bool persistent_alloc,
+ unsigned int prealloc_more_bytes TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ MYSQLND_PACKET_HEADER header;
+ zend_uchar * p = NULL;
+ zend_bool first_iteration = TRUE;
+
+ DBG_ENTER("php_mysqlnd_read_row_ex");
+
+ /*
+ To ease the process the server splits everything in packets up to 2^24 - 1.
+ Even in the case the payload is evenly divisible by this value, the last
+ packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
+ for next one if they have 2^24 - 1 sizes. But just read the header of a
+ zero-length byte, don't read the body, there is no such.
+ */
+
+ *data_size = prealloc_more_bytes;
+ while (1) {
+ if (FAIL == mysqlnd_read_header(conn->net, &header, conn->stats, conn->error_info TSRMLS_CC)) {
+ ret = FAIL;
+ break;
+ }
+
+ *data_size += header.size;
+
+ if (first_iteration) {
+ first_iteration = FALSE;
+ /*
+ We need a trailing \0 for the last string, in case of text-mode,
+ to be able to implement read-only variables. Thus, we add + 1.
+ */
+ *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
+ if (!*buffer) {
+ ret = FAIL;
+ break;
+ }
+ p = (*buffer)->ptr;
+ } else if (!first_iteration) {
+ /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
+ if (!header.size) {
+ break;
+ }
+
+ /*
+ We have to realloc the buffer.
+
+ We need a trailing \0 for the last string, in case of text-mode,
+ to be able to implement read-only variables.
+ */
+ if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) {
+ SET_OOM_ERROR(*conn->error_info);
+ ret = FAIL;
+ break;
+ }
+ /* The position could have changed, recalculate */
+ p = (*buffer)->ptr + (*data_size - header.size);
+ }
+
+ if (PASS != (ret = conn->net->m.receive_ex(conn->net, p, header.size, conn->stats, conn->error_info TSRMLS_CC))) {
+ DBG_ERR("Empty row packet body");
+ php_error(E_WARNING, "Empty row packet body");
+ break;
+ }
+
+ if (header.size < MYSQLND_MAX_PACKET_SIZE) {
+ break;
+ }
+ }
+ if (ret == FAIL && *buffer) {
+ (*buffer)->free_chunk((*buffer) TSRMLS_CC);
+ *buffer = NULL;
+ }
+ *data_size -= prealloc_more_bytes;
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_read_binary_protocol */
+enum_func_status
+php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, MYSQLND_FIELD *fields_metadata,
+ zend_bool as_unicode, zend_bool as_int_or_float,
+ MYSQLND_STATS * stats TSRMLS_DC)
+{
+ unsigned int i;
+ zend_uchar * p = row_buffer->ptr;
+ zend_uchar * null_ptr, bit;
+ zval **current_field, **end_field, **start_field;
+
+ DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
+
+ if (!fields) {
+ DBG_RETURN(FAIL);
+ }
+
+ end_field = (start_field = fields) + field_count;
+
+ /* skip the first byte, not EODATA_MARKER -> 0x0, status */
+ p++;
+ null_ptr= p;
+ p += (field_count + 9)/8; /* skip null bits */
+ bit = 4; /* first 2 bits are reserved */
+
+ for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
+ DBG_INF("Directly creating zval");
+ MAKE_STD_ZVAL(*current_field);
+ if (!*current_field) {
+ DBG_RETURN(FAIL);
+ }
+ }
+
+ for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
+ enum_mysqlnd_collected_stats statistic;
+ zend_uchar * orig_p = p;
+
+ DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u",
+ *current_field, i,
+ fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
+ fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
+ if (*null_ptr & bit) {
+ DBG_INF("It's null");
+ ZVAL_NULL(*current_field);
+ statistic = STAT_BINARY_TYPE_FETCHED_NULL;
+ } else {
+ enum_mysqlnd_field_types type = fields_metadata[i].type;
+ mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC);
+
+ if (MYSQLND_G(collect_statistics)) {
+ switch (fields_metadata[i].type) {
+ case MYSQL_TYPE_DECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
+ case MYSQL_TYPE_TINY: statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
+ case MYSQL_TYPE_SHORT: statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
+ case MYSQL_TYPE_LONG: statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
+ case MYSQL_TYPE_FLOAT: statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
+ case MYSQL_TYPE_DOUBLE: statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
+ case MYSQL_TYPE_NULL: statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
+ case MYSQL_TYPE_TIMESTAMP: statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
+ case MYSQL_TYPE_LONGLONG: statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
+ case MYSQL_TYPE_INT24: statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
+ case MYSQL_TYPE_DATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
+ case MYSQL_TYPE_TIME: statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
+ case MYSQL_TYPE_DATETIME: statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
+ case MYSQL_TYPE_YEAR: statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
+ case MYSQL_TYPE_NEWDATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
+ case MYSQL_TYPE_VARCHAR: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_BIT: statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
+ case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
+ case MYSQL_TYPE_ENUM: statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
+ case MYSQL_TYPE_SET: statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
+ case MYSQL_TYPE_TINY_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_LONG_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_GEOMETRY: statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
+ default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
+ }
+ }
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
+ STAT_BYTES_RECEIVED_PURE_DATA_PS,
+ (Z_TYPE_PP(current_field) == IS_STRING)?
+ Z_STRLEN_PP(current_field) : (p - orig_p));
+
+ if (!((bit<<=1) & 255)) {
+ bit = 1; /* to the following byte */
+ null_ptr++;
+ }
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_read_text_protocol */
+enum_func_status
+php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, MYSQLND_FIELD *fields_metadata,
+ zend_bool as_unicode, zend_bool as_int_or_float,
+ MYSQLND_STATS * stats TSRMLS_DC)
+{
+ unsigned int i;
+ zend_bool last_field_was_string = FALSE;
+ zval **current_field, **end_field, **start_field;
+ zend_uchar * p = row_buffer->ptr;
+ size_t data_size = row_buffer->app;
+ zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
+
+ DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
+
+ if (!fields) {
+ DBG_RETURN(FAIL);
+ }
+
+ end_field = (start_field = fields) + field_count;
+
+ for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
+ DBG_INF("Directly creating zval");
+ MAKE_STD_ZVAL(*current_field);
+ if (!*current_field) {
+ DBG_RETURN(FAIL);
+ }
+ }
+
+ for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
+ /* Don't reverse the order. It is significant!*/
+ zend_uchar *this_field_len_pos = p;
+ /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
+ unsigned long len = php_mysqlnd_net_field_length(&p);
+
+ if (current_field > start_field && last_field_was_string) {
+ /*
+ Normal queries:
+ We have to put \0 now to the end of the previous field, if it was
+ a string. IS_NULL doesn't matter. Because we have already read our
+ length, then we can overwrite it in the row buffer.
+ This statement terminates the previous field, not the current one.
+
+ NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
+ Any string's length is encoded in at least one byte, so we can stick
+ a \0 there.
+ */
+
+ *this_field_len_pos = '\0';
+ }
+
+ /* NULL or NOT NULL, this is the question! */
+ if (len == MYSQLND_NULL_LENGTH) {
+ ZVAL_NULL(*current_field);
+ last_field_was_string = FALSE;
+ } else {
+#if MYSQLND_UNICODE || defined(MYSQLND_STRING_TO_INT_CONVERSION)
+ struct st_mysqlnd_perm_bind perm_bind =
+ mysqlnd_ps_fetch_functions[fields_metadata[i].type];
+#endif
+ if (MYSQLND_G(collect_statistics)) {
+ enum_mysqlnd_collected_stats statistic;
+ switch (fields_metadata[i].type) {
+ case MYSQL_TYPE_DECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
+ case MYSQL_TYPE_TINY: statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
+ case MYSQL_TYPE_SHORT: statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
+ case MYSQL_TYPE_LONG: statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
+ case MYSQL_TYPE_FLOAT: statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
+ case MYSQL_TYPE_DOUBLE: statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
+ case MYSQL_TYPE_NULL: statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
+ case MYSQL_TYPE_TIMESTAMP: statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
+ case MYSQL_TYPE_LONGLONG: statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
+ case MYSQL_TYPE_INT24: statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
+ case MYSQL_TYPE_DATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
+ case MYSQL_TYPE_TIME: statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
+ case MYSQL_TYPE_DATETIME: statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
+ case MYSQL_TYPE_YEAR: statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
+ case MYSQL_TYPE_NEWDATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
+ case MYSQL_TYPE_VARCHAR: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_BIT: statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
+ case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
+ case MYSQL_TYPE_ENUM: statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
+ case MYSQL_TYPE_SET: statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
+ case MYSQL_TYPE_TINY_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_LONG_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
+ case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
+ case MYSQL_TYPE_GEOMETRY: statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
+ default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
+ }
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ if (as_int_or_float && perm_bind.php_type == IS_LONG) {
+ zend_uchar save = *(p + len);
+ /* We have to make it ASCIIZ temporarily */
+ *(p + len) = '\0';
+ if (perm_bind.pack_len < SIZEOF_LONG) {
+ /* direct conversion */
+ int64_t v =
+#ifndef PHP_WIN32
+ atoll((char *) p);
+#else
+ _atoi64((char *) p);
+#endif
+ ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
+ } else {
+ uint64_t v =
+#ifndef PHP_WIN32
+ (uint64_t) atoll((char *) p);
+#else
+ (uint64_t) _atoi64((char *) p);
+#endif
+ zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
+ /* We have to make it ASCIIZ temporarily */
+#if SIZEOF_LONG==8
+ if (uns == TRUE && v > 9223372036854775807L)
+#elif SIZEOF_LONG==4
+ if ((uns == TRUE && v > L64(2147483647)) ||
+ (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
+ (L64(-2147483648) > (int64_t) v))))
+#else
+#error Need fix for this architecture
+#endif /* SIZEOF */
+ {
+ ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ } else {
+ ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
+ }
+ }
+ *(p + len) = save;
+ } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
+ zend_uchar save = *(p + len);
+ /* We have to make it ASCIIZ temporarily */
+ *(p + len) = '\0';
+ ZVAL_DOUBLE(*current_field, atof((char *) p));
+ *(p + len) = save;
+ } else
+#endif /* MYSQLND_STRING_TO_INT_CONVERSION */
+ if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
+ /*
+ BIT fields are specially handled. As they come as bit mask, we have
+ to convert it to human-readable representation. As the bits take
+ less space in the protocol than the numbers they represent, we don't
+ have enough space in the packet buffer to overwrite inside.
+ Thus, a bit more space is pre-allocated at the end of the buffer,
+ see php_mysqlnd_rowp_read(). And we add the strings at the end.
+ Definitely not nice, _hackish_ :(, but works.
+ */
+ zend_uchar *start = bit_area;
+ ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC);
+ /*
+ We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
+ later in this function there will be an advancement.
+ */
+ p -= len;
+ if (Z_TYPE_PP(current_field) == IS_LONG) {
+ bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
+#if MYSQLND_UNICODE
+ if (as_unicode) {
+ ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
+ } else
+#endif
+ {
+ ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
+ }
+ } else if (Z_TYPE_PP(current_field) == IS_STRING){
+ memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
+ bit_area += Z_STRLEN_PP(current_field);
+ *bit_area++ = '\0';
+ zval_dtor(*current_field);
+#if MYSQLND_UNICODE
+ if (as_unicode) {
+ ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
+ } else
+#endif
+ {
+ ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
+ }
+ }
+ /*
+ IS_UNICODE should not be specially handled. In unicode mode
+ the buffers are not referenced - everything is copied.
+ */
+ } else
+#if MYSQLND_UNICODE == 0
+ {
+ ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ }
+#else
+ /*
+ Here we have to convert to UTF16, which means not reusing the buffer.
+ Which in turn means that we can free the buffers once we have
+ stored the result set, if we use store_result().
+
+ Also the destruction of the zvals should not call zval_copy_ctor()
+ because then we will leak.
+
+ XXX: Keep in mind that up there there is an open `else` in
+ #ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ which will make with this `if` an `else if`.
+ */
+ if ((perm_bind.is_possibly_blob == TRUE &&
+ fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
+ (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
+ {
+ /* BLOB - no conversion please */
+ ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ } else {
+ ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
+ }
+#endif
+ p += len;
+ last_field_was_string = TRUE;
+ }
+ }
+ if (last_field_was_string) {
+ /* Normal queries: The buffer has one more byte at the end, because we need it */
+ row_buffer->ptr[data_size] = '\0';
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_read */
+/*
+ if normal statements => packet->fields is created by this function,
+ if PS => packet->fields is passed from outside
+*/
+static enum_func_status
+php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ MYSQLND_NET * net = conn->net;
+ zend_uchar *p;
+ enum_func_status ret = PASS;
+ size_t old_chunk_size = net->stream->chunk_size;
+ MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
+ size_t post_alloc_for_bit_fields = 0;
+ size_t data_size = 0;
+
+ DBG_ENTER("php_mysqlnd_rowp_read");
+
+ if (!packet->binary_protocol && packet->bit_fields_count) {
+ /* For every field we need terminating \0 */
+ post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
+ }
+
+ ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
+ packet->persistent_alloc, post_alloc_for_bit_fields
+ TSRMLS_CC);
+ if (FAIL == ret) {
+ goto end;
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
+ MYSQLND_HEADER_SIZE + packet->header.size,
+ packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
+ 1);
+
+ /* packet->row_buffer->ptr is of size 'data_size + 1' */
+ packet->header.size = data_size;
+ packet->row_buffer->app = data_size;
+
+ if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
+ /*
+ Error message as part of the result set,
+ not good but we should not hang. See:
+ Bug #27876 : SF with cyrillic variable name fails during execution
+ */
+ ret = FAIL;
+ php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
+ packet->error_info.error,
+ sizeof(packet->error_info.error),
+ &packet->error_info.error_no,
+ packet->error_info.sqlstate
+ TSRMLS_CC);
+ } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
+ packet->eof = TRUE;
+ p++;
+ if (data_size > 1) {
+ packet->warning_count = uint2korr(p);
+ p += 2;
+ packet->server_status = uint2korr(p);
+ /* Seems we have 3 bytes reserved for future use */
+ DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
+ }
+ } else {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
+ STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
+
+ packet->eof = FALSE;
+ /* packet->field_count is set by the user of the packet */
+
+ if (!packet->skip_extraction) {
+ if (!packet->fields) {
+ DBG_INF("Allocating packet->fields");
+ /*
+ old-API will probably set packet->fields to NULL every time, though for
+ unbuffered sets it makes not much sense as the zvals in this buffer matter,
+ not the buffer. Constantly allocating and deallocating brings nothing.
+
+ For PS - if stmt_store() is performed, thus we don't have a cursor, it will
+ behave just like old-API buffered. Cursors will behave like a bit different,
+ but mostly like old-API unbuffered and thus will populate this array with
+ value.
+ */
+ packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
+ packet->persistent_alloc);
+ }
+ } else {
+ MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
+ STAT_ROWS_SKIPPED_NORMAL);
+ }
+ }
+
+end:
+ net->stream->chunk_size = old_chunk_size;
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_free_mem */
+static void
+php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_ROW *p;
+
+ DBG_ENTER("php_mysqlnd_rowp_free_mem");
+ p = (MYSQLND_PACKET_ROW *) _packet;
+ if (p->row_buffer) {
+ p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC);
+ p->row_buffer = NULL;
+ }
+ DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
+ /*
+ Don't free packet->fields :
+ - normal queries -> store_result() | fetch_row_unbuffered() will transfer
+ the ownership and NULL it.
+ - PS will pass in it the bound variables, we have to use them! and of course
+ not free the array. As it is passed to us, we should not clean it ourselves.
+ */
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_stats_read */
+static enum_func_status
+php_mysqlnd_stats_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
+ size_t buf_len = conn->net->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+
+ DBG_ENTER("php_mysqlnd_stats_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
+
+ packet->message = mnd_emalloc(packet->header.size + 1);
+ memcpy(packet->message, buf, packet->header.size);
+ packet->message[packet->header.size] = '\0';
+ packet->message_len = packet->header.size;
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_stats_free_mem */
+static
+void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
+ if (p->message) {
+ mnd_efree(p->message);
+ p->message = NULL;
+ }
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
+#define PREPARE_RESPONSE_SIZE_41 9
+#define PREPARE_RESPONSE_SIZE_50 12
+
+/* {{{ php_mysqlnd_prepare_read */
+static enum_func_status
+php_mysqlnd_prepare_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ /* In case of an error, we should have place to put it */
+ size_t buf_len = conn->net->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ unsigned int data_size;
+ MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
+
+ DBG_ENTER("php_mysqlnd_prepare_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ data_size = packet->header.size;
+ packet->error_code = uint1korr(p);
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (ERROR_MARKER == packet->error_code) {
+ php_mysqlnd_read_error_from_line(p, data_size - 1,
+ packet->error_info.error,
+ sizeof(packet->error_info.error),
+ &packet->error_info.error_no,
+ packet->error_info.sqlstate
+ TSRMLS_CC);
+ DBG_RETURN(PASS);
+ }
+
+ if (data_size != PREPARE_RESPONSE_SIZE_41 &&
+ data_size != PREPARE_RESPONSE_SIZE_50 &&
+ !(data_size > PREPARE_RESPONSE_SIZE_50)) {
+ DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
+ php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
+ DBG_RETURN(FAIL);
+ }
+
+ packet->stmt_id = uint4korr(p);
+ p += 4;
+ BAIL_IF_NO_MORE_DATA;
+
+ /* Number of columns in result set */
+ packet->field_count = uint2korr(p);
+ p += 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->param_count = uint2korr(p);
+ p += 2;
+ BAIL_IF_NO_MORE_DATA;
+
+ if (data_size > 9) {
+ /* 0x0 filler sent by the server for 5.0+ clients */
+ p++;
+ BAIL_IF_NO_MORE_DATA;
+
+ packet->warning_count = uint2korr(p);
+ }
+
+ DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u",
+ packet->stmt_id, packet->field_count, packet->param_count);
+
+ BAIL_IF_NO_MORE_DATA;
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_prepare_free_mem */
+static void
+php_mysqlnd_prepare_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_chg_user_read */
+static enum_func_status
+php_mysqlnd_chg_user_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
+{
+ /* There could be an error message */
+ size_t buf_len = conn->net->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+
+ DBG_ENTER("php_mysqlnd_chg_user_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
+ BAIL_IF_NO_MORE_DATA;
+
+ /*
+ Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
+ of encoded sequence for length.
+ */
+
+ /* Should be always 0x0 or ERROR_MARKER for error */
+ packet->response_code = uint1korr(p);
+ p++;
+
+ if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
+ /* We don't handle 3.23 authentication */
+ packet->server_asked_323_auth = TRUE;
+ DBG_RETURN(FAIL);
+ }
+
+ if (ERROR_MARKER == packet->response_code) {
+ php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
+ packet->error_info.error,
+ sizeof(packet->error_info.error),
+ &packet->error_info.error_no,
+ packet->error_info.sqlstate
+ TSRMLS_CC);
+ }
+ BAIL_IF_NO_MORE_DATA;
+ if (packet->response_code == 0xFE && packet->header.size > (size_t) (p - buf)) {
+ packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
+ packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
+ p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
+ packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
+ if (packet->new_auth_protocol_data_len) {
+ packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
+ memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
+ }
+ DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
+ DBG_INF_FMT("Server salt : [%*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
+ }
+
+ DBG_RETURN(PASS);
+premature_end:
+ DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
+ p - begin - packet->header.size);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_chg_user_free_mem */
+static void
+php_mysqlnd_chg_user_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
+{
+ MYSQLND_PACKET_CHG_USER_RESPONSE * p = (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+
+ if (p->new_auth_protocol) {
+ mnd_efree(p->new_auth_protocol);
+ p->new_auth_protocol = NULL;
+ }
+ p->new_auth_protocol_len = 0;
+
+ if (p->new_auth_protocol_data) {
+ mnd_efree(p->new_auth_protocol_data);
+ p->new_auth_protocol_data = NULL;
+ }
+ p->new_auth_protocol_data_len = 0;
+
+ if (!stack_allocation) {
+ mnd_pefree(p, p->header.persistent);
+ }
+}
+/* }}} */
+
+
+/* {{{ packet_methods */
+static
+mysqlnd_packet_methods packet_methods[PROT_LAST] =
+{
+ {
+ sizeof(MYSQLND_PACKET_GREET),
+ php_mysqlnd_greet_read,
+ NULL, /* write */
+ php_mysqlnd_greet_free_mem,
+ }, /* PROT_GREET_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_AUTH),
+ NULL, /* read */
+ php_mysqlnd_auth_write,
+ php_mysqlnd_auth_free_mem,
+ }, /* PROT_AUTH_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_AUTH_RESPONSE),
+ php_mysqlnd_auth_response_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_auth_response_free_mem,
+ }, /* PROT_AUTH_RESP_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_CHANGE_AUTH_RESPONSE),
+ NULL, /* read */
+ php_mysqlnd_change_auth_response_write, /* write */
+ php_mysqlnd_change_auth_response_free_mem,
+ }, /* PROT_CHANGE_AUTH_RESP_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_OK),
+ php_mysqlnd_ok_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_ok_free_mem,
+ }, /* PROT_OK_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_EOF),
+ php_mysqlnd_eof_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_eof_free_mem,
+ }, /* PROT_EOF_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_COMMAND),
+ NULL, /* read */
+ php_mysqlnd_cmd_write, /* write */
+ php_mysqlnd_cmd_free_mem,
+ }, /* PROT_CMD_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_RSET_HEADER),
+ php_mysqlnd_rset_header_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rset_header_free_mem,
+ }, /* PROT_RSET_HEADER_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_RES_FIELD),
+ php_mysqlnd_rset_field_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rset_field_free_mem,
+ }, /* PROT_RSET_FLD_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_ROW),
+ php_mysqlnd_rowp_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rowp_free_mem,
+ }, /* PROT_ROW_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_STATS),
+ php_mysqlnd_stats_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_stats_free_mem,
+ }, /* PROT_STATS_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_PREPARE_RESPONSE),
+ php_mysqlnd_prepare_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_prepare_free_mem,
+ }, /* PROT_PREPARE_RESP_PACKET */
+ {
+ sizeof(MYSQLND_PACKET_CHG_USER_RESPONSE),
+ php_mysqlnd_chg_user_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_chg_user_free_mem,
+ } /* PROT_CHG_USER_RESP_PACKET */
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_greet_packet */
+static struct st_mysqlnd_packet_greet *
+MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_greet_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_GREET_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_auth_packet */
+static struct st_mysqlnd_packet_auth *
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_auth_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_AUTH_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_auth_response_packet */
+static struct st_mysqlnd_packet_auth_response *
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_RESP_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_auth_response_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_AUTH_RESP_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_change_auth_response_packet */
+static struct st_mysqlnd_packet_change_auth_response *
+MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_change_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_CHANGE_AUTH_RESP_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_change_auth_response_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_CHANGE_AUTH_RESP_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_ok_packet */
+static struct st_mysqlnd_packet_ok *
+MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_ok_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_OK_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_eof_packet */
+static struct st_mysqlnd_packet_eof *
+MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_eof_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_EOF_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_command_packet */
+static struct st_mysqlnd_packet_command *
+MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_command_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_CMD_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_rset_packet */
+static struct st_mysqlnd_packet_rset_header *
+MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_result_field_packet */
+static struct st_mysqlnd_packet_res_field *
+MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_row_packet */
+static struct st_mysqlnd_packet_row *
+MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_row_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_ROW_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_stats_packet */
+static struct st_mysqlnd_packet_stats *
+MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_stats_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_STATS_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_prepare_response_packet */
+static struct st_mysqlnd_packet_prepare_response *
+MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::get_change_user_response_packet */
+static struct st_mysqlnd_packet_chg_user_resp*
+MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
+{
+ struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
+ DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
+ if (packet) {
+ packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
+ packet->header.persistent = persistent;
+ }
+ DBG_RETURN(packet);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
+ MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_command_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_row_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
+ MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_protocol_init */
+PHPAPI MYSQLND_PROTOCOL *
+mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)
+{
+ MYSQLND_PROTOCOL * ret;
+ DBG_ENTER("mysqlnd_protocol_init");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_decoder(persistent TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol_free */
+PHPAPI void
+mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_protocol_free");
+
+ if (protocol) {
+ zend_bool pers = protocol->persistent;
+ mnd_pefree(protocol, pers);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h
new file mode 100644
index 0000000..253784c
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -0,0 +1,321 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef MYSQLND_WIREPROTOCOL_H
+#define MYSQLND_WIREPROTOCOL_H
+
+#include "mysqlnd_net.h"
+
+#define MYSQLND_HEADER_SIZE 4
+#define COMPRESSED_HEADER_SIZE 3
+
+#define MYSQLND_NULL_LENGTH (unsigned long) ~0
+
+/* Used in mysqlnd_debug.c */
+PHPAPI extern const char mysqlnd_read_header_name[];
+PHPAPI extern const char mysqlnd_read_body_name[];
+
+
+/* Packet handling */
+#define PACKET_WRITE(packet, conn) ((packet)->header.m->write_to_net((packet), (conn) TSRMLS_CC))
+#define PACKET_READ(packet, conn) ((packet)->header.m->read_from_net((packet), (conn) TSRMLS_CC))
+#define PACKET_FREE(packet) \
+ do { \
+ DBG_INF_FMT("PACKET_FREE(%p)", packet); \
+ if ((packet)) { \
+ ((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \
+ } \
+ } while (0);
+
+PHPAPI extern const char * const mysqlnd_command_to_text[COM_END];
+
+/* Low-level extraction functionality */
+typedef struct st_mysqlnd_packet_methods {
+ size_t struct_size;
+ enum_func_status (*read_from_net)(void * packet, MYSQLND_CONN_DATA * conn TSRMLS_DC);
+ size_t (*write_to_net)(void * packet, MYSQLND_CONN_DATA * conn TSRMLS_DC);
+ void (*free_mem)(void *packet, zend_bool stack_allocation TSRMLS_DC);
+} mysqlnd_packet_methods;
+
+
+typedef struct st_mysqlnd_packet_header {
+ size_t size;
+ mysqlnd_packet_methods *m;
+ zend_uchar packet_no;
+ zend_bool persistent;
+} MYSQLND_PACKET_HEADER;
+
+/* Server greets the client */
+typedef struct st_mysqlnd_packet_greet {
+ MYSQLND_PACKET_HEADER header;
+ uint8_t protocol_version;
+ char *server_version;
+ uint32_t thread_id;
+ zend_uchar intern_auth_plugin_data[SCRAMBLE_LENGTH];
+ zend_uchar * auth_plugin_data;
+ size_t auth_plugin_data_len;
+ /* 1 byte pad */
+ uint32_t server_capabilities;
+ uint8_t charset_no;
+ uint16_t server_status;
+ /* 13 byte pad, in 5.5 first 2 bytes are more capabilities followed by 1 byte scramble_length */
+ zend_bool pre41;
+ /* If error packet, we use these */
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+ char *auth_protocol;
+} MYSQLND_PACKET_GREET;
+
+
+/* Client authenticates */
+typedef struct st_mysqlnd_packet_auth {
+ MYSQLND_PACKET_HEADER header;
+ uint32_t client_flags;
+ uint32_t max_packet_size;
+ uint8_t charset_no;
+ const char *user;
+ const zend_uchar *auth_data;
+ size_t auth_data_len;
+ const char *db;
+ const char *auth_plugin_name;
+ /* Here the packet ends. This is user supplied data */
+ size_t db_len;
+ zend_bool send_auth_data;
+ zend_bool is_change_user_packet;
+ zend_bool silent;
+
+} MYSQLND_PACKET_AUTH;
+
+/* Auth response packet */
+typedef struct st_mysqlnd_packet_auth_response {
+ MYSQLND_PACKET_HEADER header;
+ uint8_t response_code;
+ uint64_t affected_rows;
+ uint64_t last_insert_id;
+ uint16_t server_status;
+ uint16_t warning_count;
+ char *message;
+ size_t message_len;
+ /* If error packet, we use these */
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+
+ char *new_auth_protocol;
+ size_t new_auth_protocol_len;
+ zend_uchar *new_auth_protocol_data;
+ size_t new_auth_protocol_data_len;
+} MYSQLND_PACKET_AUTH_RESPONSE;
+
+
+/* Auth response packet */
+typedef struct st_mysqlnd_packet_change_auth_response {
+ MYSQLND_PACKET_HEADER header;
+ const zend_uchar *auth_data;
+ size_t auth_data_len;
+} MYSQLND_PACKET_CHANGE_AUTH_RESPONSE;
+
+
+/* OK packet */
+typedef struct st_mysqlnd_packet_ok {
+ MYSQLND_PACKET_HEADER header;
+ uint8_t field_count; /* always 0x0 */
+ uint64_t affected_rows;
+ uint64_t last_insert_id;
+ uint16_t server_status;
+ uint16_t warning_count;
+ char *message;
+ size_t message_len;
+ /* If error packet, we use these */
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+} MYSQLND_PACKET_OK;
+
+
+/* Command packet */
+typedef struct st_mysqlnd_packet_command {
+ MYSQLND_PACKET_HEADER header;
+ enum php_mysqlnd_server_command command;
+ const zend_uchar *argument;
+ size_t arg_len;
+} MYSQLND_PACKET_COMMAND;
+
+
+/* EOF packet */
+typedef struct st_mysqlnd_packet_eof {
+ MYSQLND_PACKET_HEADER header;
+ uint8_t field_count; /* 0xFE */
+ uint16_t warning_count;
+ uint16_t server_status;
+ /* If error packet, we use these */
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+} MYSQLND_PACKET_EOF;
+/* EOF packet */
+
+
+/* Result Set header*/
+typedef struct st_mysqlnd_packet_rset_header {
+ MYSQLND_PACKET_HEADER header;
+ /*
+ 0x00 => ok
+ ~0 => LOAD DATA LOCAL
+ error_no != 0 => error
+ others => result set -> Read res_field packets up to field_count
+ */
+ unsigned long field_count;
+ /*
+ These are filled if no SELECT query. For SELECT warning_count
+ and server status are in the last row packet, the EOF packet.
+ */
+ uint16_t warning_count;
+ uint16_t server_status;
+ uint64_t affected_rows;
+ uint64_t last_insert_id;
+ /* This is for both LOAD DATA or info, when no result set */
+ char *info_or_local_file;
+ size_t info_or_local_file_len;
+ /* If error packet, we use these */
+ MYSQLND_ERROR_INFO error_info;
+} MYSQLND_PACKET_RSET_HEADER;
+
+
+/* Result set field packet */
+typedef struct st_mysqlnd_packet_res_field {
+ MYSQLND_PACKET_HEADER header;
+ MYSQLND_FIELD *metadata;
+ /* For table definitions, empty for result sets */
+ zend_bool skip_parsing;
+ zend_bool stupid_list_fields_eof;
+ zend_bool persistent_alloc;
+
+ MYSQLND_ERROR_INFO error_info;
+} MYSQLND_PACKET_RES_FIELD;
+
+
+/* Row packet */
+typedef struct st_mysqlnd_packet_row {
+ MYSQLND_PACKET_HEADER header;
+ zval **fields;
+ uint32_t field_count;
+ zend_bool eof;
+ /*
+ These are, of course, only for SELECT in the EOF packet,
+ which is detected by this packet
+ */
+ uint16_t warning_count;
+ uint16_t server_status;
+
+ struct st_mysqlnd_memory_pool_chunk *row_buffer;
+ MYSQLND_MEMORY_POOL * result_set_memory_pool;
+
+ zend_bool skip_extraction;
+ zend_bool binary_protocol;
+ zend_bool persistent_alloc;
+ MYSQLND_FIELD *fields_metadata;
+ /* We need this to alloc bigger bufs in non-PS mode */
+ unsigned int bit_fields_count;
+ size_t bit_fields_total_len; /* trailing \0 not counted */
+
+ /* If error packet, we use these */
+ MYSQLND_ERROR_INFO error_info;
+} MYSQLND_PACKET_ROW;
+
+
+/* Statistics packet */
+typedef struct st_mysqlnd_packet_stats {
+ MYSQLND_PACKET_HEADER header;
+ char *message;
+ /* message_len is not part of the packet*/
+ size_t message_len;
+} MYSQLND_PACKET_STATS;
+
+
+/* COM_PREPARE response packet */
+typedef struct st_mysqlnd_packet_prepare_response {
+ MYSQLND_PACKET_HEADER header;
+ /* also known as field_count 0x00=OK , 0xFF=error */
+ unsigned char error_code;
+ unsigned long stmt_id;
+ unsigned int field_count;
+ unsigned int param_count;
+ unsigned int warning_count;
+
+ /* present in case of error */
+ MYSQLND_ERROR_INFO error_info;
+} MYSQLND_PACKET_PREPARE_RESPONSE;
+
+
+/* Statistics packet */
+typedef struct st_mysqlnd_packet_chg_user_resp {
+ MYSQLND_PACKET_HEADER header;
+ uint32_t response_code;
+
+ /* message_len is not part of the packet*/
+ uint16_t server_capabilities;
+ /* If error packet, we use these */
+ MYSQLND_ERROR_INFO error_info;
+ zend_bool server_asked_323_auth;
+
+ char *new_auth_protocol;
+ size_t new_auth_protocol_len;
+ zend_uchar *new_auth_protocol_data;
+ size_t new_auth_protocol_data_len;
+} MYSQLND_PACKET_CHG_USER_RESPONSE;
+
+
+PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass, size_t pass_len);
+
+unsigned long php_mysqlnd_net_field_length(zend_uchar **packet);
+zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length);
+
+PHPAPI const extern char * const mysqlnd_empty_string;
+
+
+enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, MYSQLND_FIELD *fields_metadata,
+ zend_bool as_unicode, zend_bool as_int_or_float,
+ MYSQLND_STATS * stats TSRMLS_DC);
+
+
+enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, MYSQLND_FIELD *fields_metadata,
+ zend_bool as_unicode, zend_bool as_int_or_float,
+ MYSQLND_STATS * stats TSRMLS_DC);
+
+
+PHPAPI MYSQLND_PROTOCOL * mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC);
+PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC);
+
+#endif /* MYSQLND_WIREPROTOCOL_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
new file mode 100644
index 0000000..0021355
--- /dev/null
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -0,0 +1,389 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+#include "php.h"
+#include "php_ini.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_reverse_api.h"
+#include "ext/standard/info.h"
+#include "ext/standard/php_smart_str.h"
+
+/* {{{ mysqlnd_functions[]
+ *
+ * Every user visible function must have an entry in mysqlnd_functions[].
+ */
+static zend_function_entry mysqlnd_functions[] = {
+ PHP_FE_END
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_minfo_print_hash */
+#if MYSQLND_UNICODE
+PHPAPI void mysqlnd_minfo_print_hash(zval *values)
+{
+ zval **values_entry;
+ HashPosition pos_values;
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values),
+ (void **)&values_entry, &pos_values) == SUCCESS) {
+ zstr string_key;
+ uint string_key_len;
+ ulong num_key;
+ int s_len;
+ char *s = NULL;
+
+ TSRMLS_FETCH();
+ zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values);
+
+ convert_to_string(*values_entry);
+
+ if (zend_unicode_to_string(ZEND_U_CONVERTER(UG(runtime_encoding_conv)),
+ &s, &s_len, string_key.u, string_key_len TSRMLS_CC) == SUCCESS) {
+ php_info_print_table_row(2, s, Z_STRVAL_PP(values_entry));
+ }
+ if (s) {
+ mnd_efree(s);
+ }
+
+ zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
+ }
+}
+#else
+PHPAPI void mysqlnd_minfo_print_hash(zval *values)
+{
+ zval **values_entry;
+ HashPosition pos_values;
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&values_entry, &pos_values) == SUCCESS) {
+ char *string_key;
+ uint string_key_len;
+ ulong num_key;
+
+ zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &string_key, &string_key_len, &num_key, 0, &pos_values);
+
+ convert_to_string(*values_entry);
+ php_info_print_table_row(2, string_key, Z_STRVAL_PP(values_entry));
+
+ zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
+ }
+}
+#endif
+/* }}} */
+
+
+/* {{{ mysqlnd_minfo_dump_plugin_stats */
+static int
+mysqlnd_minfo_dump_plugin_stats(void *pDest, void * argument TSRMLS_DC)
+{
+ struct st_mysqlnd_plugin_header * plugin_header = *(struct st_mysqlnd_plugin_header **) pDest;
+ if (plugin_header->plugin_stats.values) {
+ char buf[64];
+ zval values;
+ snprintf(buf, sizeof(buf), "%s statistics", plugin_header->plugin_name);
+
+ mysqlnd_fill_stats_hash(plugin_header->plugin_stats.values, plugin_header->plugin_stats.names, &values TSRMLS_CC ZEND_FILE_LINE_CC);
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, buf, "");
+ mysqlnd_minfo_print_hash(&values);
+ php_info_print_table_end();
+ zval_dtor(&values);
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_minfo_dump_loaded_plugins */
+static int
+mysqlnd_minfo_dump_loaded_plugins(void *pDest, void * buf TSRMLS_DC)
+{
+ smart_str * buffer = (smart_str *) buf;
+ struct st_mysqlnd_plugin_header * plugin_header = *(struct st_mysqlnd_plugin_header **) pDest;
+ if (plugin_header->plugin_name) {
+ if (buffer->len) {
+ smart_str_appendc(buffer, ',');
+ }
+ smart_str_appends(buffer, plugin_header->plugin_name);
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+/* {{{ mysqlnd_minfo_dump_api_plugins */
+static void
+mysqlnd_minfo_dump_api_plugins(smart_str * buffer TSRMLS_DC)
+{
+ HashTable *ht = mysqlnd_reverse_api_get_api_list(TSRMLS_C);
+ Bucket *p;
+
+ p = ht->pListHead;
+ while(p != NULL) {
+ MYSQLND_REVERSE_API * ext = *(MYSQLND_REVERSE_API **) p->pData;
+ if (buffer->len) {
+ smart_str_appendc(buffer, ',');
+ }
+ smart_str_appends(buffer, ext->module->name);
+
+ p = p->pListNext;
+ }
+}
+/* }}} */
+
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(mysqlnd)
+{
+ char buf[32];
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, "mysqlnd", "enabled");
+ php_info_print_table_row(2, "Version", mysqlnd_get_client_info());
+ php_info_print_table_row(2, "Compression",
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ "supported");
+#else
+ "not supported");
+#endif
+ php_info_print_table_row(2, "SSL",
+#ifdef MYSQLND_SSL_SUPPORTED
+ "supported");
+#else
+ "not supported");
+#endif
+ snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_cmd_buffer_size));
+ php_info_print_table_row(2, "Command buffer size", buf);
+ snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size));
+ php_info_print_table_row(2, "Read buffer size", buf);
+ snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_timeout));
+ php_info_print_table_row(2, "Read timeout", buf);
+ php_info_print_table_row(2, "Collecting statistics", MYSQLND_G(collect_statistics)? "Yes":"No");
+ php_info_print_table_row(2, "Collecting memory statistics", MYSQLND_G(collect_memory_statistics)? "Yes":"No");
+
+ php_info_print_table_row(2, "Tracing", MYSQLND_G(debug)? MYSQLND_G(debug):"n/a");
+
+ /* loaded plugins */
+ {
+ smart_str tmp_str = {0, 0, 0};
+ mysqlnd_plugin_apply_with_argument(mysqlnd_minfo_dump_loaded_plugins, &tmp_str);
+ smart_str_0(&tmp_str);
+ php_info_print_table_row(2, "Loaded plugins", tmp_str.c);
+ smart_str_free(&tmp_str);
+
+ mysqlnd_minfo_dump_api_plugins(&tmp_str TSRMLS_CC);
+ smart_str_0(&tmp_str);
+ php_info_print_table_row(2, "API Extensions", tmp_str.c);
+ smart_str_free(&tmp_str);
+ }
+
+ php_info_print_table_end();
+
+
+ /* Print client stats */
+ mysqlnd_plugin_apply_with_argument(mysqlnd_minfo_dump_plugin_stats, NULL);
+}
+/* }}} */
+
+
+PHPAPI ZEND_DECLARE_MODULE_GLOBALS(mysqlnd)
+
+
+/* {{{ PHP_GINIT_FUNCTION
+ */
+static PHP_GINIT_FUNCTION(mysqlnd)
+{
+ mysqlnd_globals->collect_statistics = TRUE;
+ mysqlnd_globals->collect_memory_statistics = FALSE;
+ mysqlnd_globals->debug = NULL; /* The actual string */
+ mysqlnd_globals->dbg = NULL; /* The DBG object*/
+ mysqlnd_globals->net_cmd_buffer_size = MYSQLND_NET_CMD_BUFFER_MIN_SIZE;
+ mysqlnd_globals->net_read_buffer_size = 32768;
+ mysqlnd_globals->net_read_timeout = 31536000;
+ mysqlnd_globals->log_mask = 0;
+ mysqlnd_globals->mempool_default_size = 16000;
+ mysqlnd_globals->debug_emalloc_fail_threshold = -1;
+ mysqlnd_globals->debug_ecalloc_fail_threshold = -1;
+ mysqlnd_globals->debug_erealloc_fail_threshold = -1;
+ mysqlnd_globals->debug_malloc_fail_threshold = -1;
+ mysqlnd_globals->debug_calloc_fail_threshold = -1;
+ mysqlnd_globals->debug_realloc_fail_threshold = -1;
+}
+/* }}} */
+
+
+static PHP_INI_MH(OnUpdateNetCmdBufferSize)
+{
+ long long_value = atol(new_value);
+ if (long_value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
+ return FAILURE;
+ }
+ MYSQLND_G(net_cmd_buffer_size) = long_value;
+
+ return SUCCESS;
+}
+
+/* {{{ PHP_INI_BEGIN
+*/
+PHP_INI_BEGIN()
+ STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics", "0", PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size", MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR, PHP_INI_ALL, OnUpdateNetCmdBufferSize, net_cmd_buffer_size, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.net_read_buffer_size", "32768",PHP_INI_ALL, OnUpdateLong, net_read_buffer_size, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.net_read_timeout", "31536000", PHP_INI_SYSTEM, OnUpdateLong, net_read_timeout, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.log_mask", "0", PHP_INI_ALL, OnUpdateLong, log_mask, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.mempool_default_size","16000", PHP_INI_ALL, OnUpdateLong, mempool_default_size, zend_mysqlnd_globals, mysqlnd_globals)
+
+#if PHP_DEBUG
+ STD_PHP_INI_ENTRY("mysqlnd.debug_emalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_emalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.debug_ecalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_ecalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.debug_erealloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_erealloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+
+ STD_PHP_INI_ENTRY("mysqlnd.debug_malloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_malloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.debug_calloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_calloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.debug_realloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_realloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
+#endif
+PHP_INI_END()
+/* }}} */
+
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(mysqlnd)
+{
+ REGISTER_INI_ENTRIES();
+
+ mysqlnd_library_init(TSRMLS_C);
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(mysqlnd)
+{
+ mysqlnd_library_end(TSRMLS_C);
+
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+
+#if PHP_DEBUG
+/* {{{ PHP_RINIT_FUNCTION
+ */
+static PHP_RINIT_FUNCTION(mysqlnd)
+{
+ if (MYSQLND_G(debug)) {
+ struct st_mysqlnd_plugin_trace_log * trace_log_plugin = mysqlnd_plugin_find("debug_trace");
+ MYSQLND_G(dbg) = NULL;
+ if (trace_log_plugin) {
+ MYSQLND_DEBUG * dbg = trace_log_plugin->methods.trace_instance_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
+ if (!dbg) {
+ return FAILURE;
+ }
+ dbg->m->set_mode(dbg, MYSQLND_G(debug));
+ MYSQLND_G(dbg) = dbg;
+ }
+ }
+ return SUCCESS;
+}
+/* }}} */
+#endif
+
+
+#if PHP_DEBUG
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+static PHP_RSHUTDOWN_FUNCTION(mysqlnd)
+{
+ MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
+ DBG_ENTER("RSHUTDOWN");
+ if (dbg) {
+ dbg->m->close(dbg);
+ dbg->m->free_handle(dbg);
+ MYSQLND_G(dbg) = NULL;
+ }
+ return SUCCESS;
+}
+/* }}} */
+#endif
+
+
+
+static const zend_module_dep mysqlnd_deps[] = {
+ ZEND_MOD_REQUIRED("standard")
+ ZEND_MOD_END
+};
+
+/* {{{ mysqlnd_module_entry
+ */
+zend_module_entry mysqlnd_module_entry = {
+ STANDARD_MODULE_HEADER_EX,
+ NULL,
+ mysqlnd_deps,
+ "mysqlnd",
+ mysqlnd_functions,
+ PHP_MINIT(mysqlnd),
+ PHP_MSHUTDOWN(mysqlnd),
+#if PHP_DEBUG
+ PHP_RINIT(mysqlnd),
+#else
+ NULL,
+#endif
+#if PHP_DEBUG
+ PHP_RSHUTDOWN(mysqlnd),
+#else
+ NULL,
+#endif
+ PHP_MINFO(mysqlnd),
+ MYSQLND_VERSION,
+ PHP_MODULE_GLOBALS(mysqlnd),
+ PHP_GINIT(mysqlnd),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+/* {{{ COMPILE_DL_MYSQLND */
+#ifdef COMPILE_DL_MYSQLND
+ZEND_GET_MODULE(mysqlnd)
+#endif
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/php_mysqlnd.h b/ext/mysqlnd/php_mysqlnd.h
new file mode 100644
index 0000000..14ad234
--- /dev/null
+++ b/ext/mysqlnd/php_mysqlnd.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ | Georg Richter <georg@mysql.com> |
+ +----------------------------------------------------------------------+
+
+ $Id$
+*/
+
+#ifndef PHP_MYSQLND_H
+#define PHP_MYSQLND_H
+
+#define phpext_mysqlnd_ptr &mysqlnd_module_entry
+extern zend_module_entry mysqlnd_module_entry;
+
+#endif /* PHP_MYSQLND_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */