summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/mysql/config.m423
-rw-r--r--ext/mysql/config.w3215
-rw-r--r--ext/mysql/mysql_mysqlnd.h41
-rw-r--r--ext/mysql/php_mysql.c543
-rw-r--r--ext/mysql/php_mysql.h28
-rw-r--r--ext/mysqli/config.m437
-rw-r--r--ext/mysqli/config.w3238
-rw-r--r--ext/mysqli/mysqli.c356
-rw-r--r--ext/mysqli/mysqli_api.c1043
-rw-r--r--ext/mysqli/mysqli_driver.c16
-rw-r--r--ext/mysqli/mysqli_embedded.c2
-rw-r--r--ext/mysqli/mysqli_exception.c2
-rw-r--r--ext/mysqli/mysqli_fe.c71
-rw-r--r--ext/mysqli/mysqli_libmysql.h36
-rw-r--r--ext/mysqli/mysqli_mysqlnd.h41
-rw-r--r--ext/mysqli/mysqli_nonapi.c429
-rw-r--r--ext/mysqli/mysqli_prop.c19
-rw-r--r--ext/mysqli/mysqli_repl.c2
-rw-r--r--ext/mysqli/mysqli_report.c9
-rw-r--r--ext/mysqli/mysqli_warning.c162
-rw-r--r--ext/mysqli/php_mysqli.h343
-rw-r--r--ext/mysqli/php_mysqli_structs.h396
-rw-r--r--ext/mysqlnd/CREDITS2
-rw-r--r--ext/mysqlnd/config-win.h98
-rw-r--r--ext/mysqlnd/config.w3220
-rw-r--r--ext/mysqlnd/config9.m429
-rw-r--r--ext/mysqlnd/mysqlnd.c2078
-rw-r--r--ext/mysqlnd/mysqlnd.h354
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.c285
-rw-r--r--ext/mysqlnd/mysqlnd_charset.c603
-rw-r--r--ext/mysqlnd/mysqlnd_charset.h35
-rw-r--r--ext/mysqlnd/mysqlnd_debug.c1345
-rw-r--r--ext/mysqlnd/mysqlnd_debug.h145
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h367
-rw-r--r--ext/mysqlnd/mysqlnd_libmysql_compat.h121
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c263
-rw-r--r--ext/mysqlnd/mysqlnd_palloc.c565
-rw-r--r--ext/mysqlnd/mysqlnd_palloc.h114
-rw-r--r--ext/mysqlnd/mysqlnd_portability.h515
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h191
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c1740
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c854
-rw-r--r--ext/mysqlnd/mysqlnd_qcache.c141
-rw-r--r--ext/mysqlnd/mysqlnd_result.c1194
-rw-r--r--ext/mysqlnd/mysqlnd_result.h48
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c440
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.h40
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.c155
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.h209
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h540
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c1956
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h335
-rw-r--r--ext/mysqlnd/php_mysqlnd.h29
53 files changed, 17238 insertions, 1225 deletions
diff --git a/ext/mysql/config.m4 b/ext/mysql/config.m4
index 5870be02e1..f5ed71c6c5 100644
--- a/ext/mysql/config.m4
+++ b/ext/mysql/config.m4
@@ -40,7 +40,8 @@ AC_DEFUN([PHP_MYSQL_SOCKET_SEARCH], [
PHP_ARG_WITH(mysql, for MySQL support,
-[ --with-mysql[=DIR] Include MySQL support. DIR is the MySQL base directory])
+[ --with-mysql[=DIR] Include MySQL support. DIR is the MySQL base directory.
+ If mysqlnd is passed as DIR, the MySQL native driver will be used])
PHP_ARG_WITH(mysql-sock, for specified location of the MySQL UNIX socket,
[ --with-mysql-sock[=DIR] MySQL: Location of the MySQL unix socket pointer.
@@ -51,9 +52,11 @@ if test -z "$PHP_ZLIB_DIR"; then
[ --with-zlib-dir[=DIR] MySQL: Set the path to libz install prefix], no, no)
fi
+if test "$PHP_MYSQL" = "mysqlnd"; then
+ dnl enables build of mysqnd library
+ PHP_MYSQLND_ENABLED=yes
-if test "$PHP_MYSQL" != "no"; then
- AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL])
+elif test "$PHP_MYSQL" != "no"; then
AC_MSG_CHECKING([for MySQL UNIX socket location])
if test "$PHP_MYSQL_SOCK" != "no" && test "$PHP_MYSQL_SOCK" != "yes"; then
@@ -137,14 +140,22 @@ Note that the MySQL client library is not bundled anymore!])
PHP_ADD_LIBRARY_WITH_PATH($MYSQL_LIBNAME, $MYSQL_LIB_DIR, MYSQL_SHARED_LIBADD)
PHP_ADD_INCLUDE($MYSQL_INC_DIR)
- PHP_NEW_EXTENSION(mysql, php_mysql.c, $ext_shared)
-
MYSQL_MODULE_TYPE=external
MYSQL_LIBS="-L$MYSQL_LIB_DIR -l$MYSQL_LIBNAME $MYSQL_LIBS"
MYSQL_INCLUDE=-I$MYSQL_INC_DIR
- PHP_SUBST(MYSQL_SHARED_LIBADD)
PHP_SUBST_OLD(MYSQL_MODULE_TYPE)
PHP_SUBST_OLD(MYSQL_LIBS)
PHP_SUBST_OLD(MYSQL_INCLUDE)
fi
+
+dnl Enable extension
+if test "$PHP_MYSQL" != "no"; then
+ AC_DEFINE(HAVE_MYSQL, 1, [Whether you have MySQL])
+ PHP_NEW_EXTENSION(mysql, php_mysql.c, $ext_shared)
+ PHP_SUBST(MYSQL_SHARED_LIBADD)
+
+ if test "$PHP_MYSQLI" = "mysqlnd"; then
+ PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd)
+ fi
+fi
diff --git a/ext/mysql/config.w32 b/ext/mysql/config.w32
index 95f689e1d6..45fc13c8d1 100644
--- a/ext/mysql/config.w32
+++ b/ext/mysql/config.w32
@@ -4,12 +4,17 @@
ARG_WITH("mysql", "MySQL support", "no");
if (PHP_MYSQL != "no") {
- if (CHECK_LIB("libmysql.lib", "mysql", PHP_MYSQL) &&
+ if (PHP_MYSQLI != "mysqlnd") {
+ if (CHECK_LIB("libmysql.lib", "mysql", PHP_MYSQL) &&
CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQL",
- PHP_MYSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQL)) {
- EXTENSION("mysql", "php_mysql.c");
- AC_DEFINE('HAVE_MYSQL', 1, 'Have MySQL library');
+ PHP_MYSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQL)) {
+ } else {
+ WARNING("mysql not enabled; libraries and headers not found");
+ }
} else {
- WARNING("mysql not enabled; libraries and headers not found");
+ AC_DEFINE('HAVE_MYSQLND', 1, 'MySQL native driver support enabled');
+ ADD_EXTENSION_DEP('mysql', 'mysqlnd', true);
}
+ EXTENSION("mysql", "php_mysql.c");
+ AC_DEFINE('HAVE_MYSQL', 1, 'Have MySQL library');
}
diff --git a/ext/mysql/mysql_mysqlnd.h b/ext/mysql/mysql_mysqlnd.h
new file mode 100644
index 0000000000..cc8d162a90
--- /dev/null
+++ b/ext/mysql/mysql_mysqlnd.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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 MYSQL_MYSQLND_H
+#define MYSQL_MYSQLND_H
+
+#include "ext/mysqlnd/mysqlnd_libmysql_compat.h"
+
+/* Here comes non-libmysql API to have less ifdefs in mysqli*/
+#define MYSQLI_CLOSE_EXPLICIT MYSQLND_CLOSE_EXPLICIT
+#define MYSQLI_CLOSE_IMPLICIT MYSQLND_CLOSE_IMPLICIT
+#define MYSQLI_CLOSE_DISCONNECTED MYSQLND_CLOSE_DISCONNECTED
+
+#define mysqli_result_is_unbuffered(r) ((r)->unbuf)
+#define mysqli_server_status(c) (c)->upsert_status.server_status
+#define mysqli_stmt_warning_count(s) mysqlnd_stmt_warning_count((s))
+#define mysqli_stmt_server_status(s) (s)->upsert_status.server_status
+#define mysqli_stmt_get_connection(s) (s)->conn
+#define mysqli_close(c, how) mysqlnd_close((c), (how))
+#define mysqli_stmt_close(c, implicit) mysqlnd_stmt_close((c), (implicit))
+#define mysqli_free_result(r, implicit) mysqlnd_free_result((r), (implicit))
+
+#endif
diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c
index 3edf6b9cb5..41a90c622d 100644
--- a/ext/mysql/php_mysql.c
+++ b/ext/mysql/php_mysql.c
@@ -33,6 +33,7 @@
#include "php_globals.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
+#include "ext/standard/basic_functions.h"
#ifdef ZEND_ENGINE_2
# include "zend_exceptions.h"
@@ -64,7 +65,6 @@
# endif
#endif
-#include <mysql.h>
#include "php_ini.h"
#include "php_mysql.h"
@@ -79,7 +79,7 @@ static int le_result, le_link, le_plink;
#define SAFE_STRING(s) ((s)?(s):"")
-#if MYSQL_VERSION_ID > 32199
+#if MYSQL_VERSION_ID > 32199 || defined(HAVE_MYSQLND)
# define mysql_row_length_type unsigned long
# define HAVE_MYSQL_ERRNO
#else
@@ -89,7 +89,7 @@ static int le_result, le_link, le_plink;
# endif
#endif
-#if MYSQL_VERSION_ID >= 32032
+#if MYSQL_VERSION_ID >= 32032 || defined(HAVE_MYSQLND)
#define HAVE_GETINFO_FUNCS
#endif
@@ -101,10 +101,6 @@ static int le_result, le_link, le_plink;
#define MYSQL_HAS_YEAR
#endif
-#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
-#define MYSQL_HAS_SET_CHARSET
-#endif
-
#define MYSQL_ASSOC 1<<0
#define MYSQL_NUM 1<<1
#define MYSQL_BOTH (MYSQL_ASSOC|MYSQL_NUM)
@@ -124,13 +120,24 @@ ZEND_DECLARE_MODULE_GLOBALS(mysql)
static PHP_GINIT_FUNCTION(mysql);
typedef struct _php_mysql_conn {
- MYSQL conn;
+ MYSQL *conn;
int active_result_id;
+ int multi_query;
} php_mysql_conn;
+#ifdef HAVE_MYSQLND
+static MYSQLND_ZVAL_PCACHE *mysql_mysqlnd_zval_cache;
+static MYSQLND_QCACHE *mysql_mysqlnd_qcache;
+#endif
+
+#define MYSQL_DISABLE_MQ if (mysql->multi_query) { \
+ mysql_set_server_option(mysql->conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \
+ mysql->multi_query = 0; \
+}
+
/* {{{ mysql_functions[]
*/
-const zend_function_entry mysql_functions[] = {
+static const zend_function_entry mysql_functions[] = {
PHP_FE(mysql_connect, NULL)
PHP_FE(mysql_pconnect, NULL)
PHP_FE(mysql_close, NULL)
@@ -184,10 +191,8 @@ const zend_function_entry mysql_functions[] = {
PHP_FE(mysql_get_server_info, NULL)
#endif
- PHP_FE(mysql_info, NULL)
-#ifdef MYSQL_HAS_SET_CHARSET
- PHP_FE(mysql_set_charset, NULL)
-#endif
+ PHP_FE(mysql_info, NULL)
+
/* for downwards compatability */
PHP_FALIAS(mysql, mysql_db_query, NULL)
PHP_FALIAS(mysql_fieldname, mysql_field_name, NULL)
@@ -216,10 +221,23 @@ const zend_function_entry mysql_functions[] = {
};
/* }}} */
+/* Dependancies */
+static const zend_module_dep mysql_deps[] = {
+#if defined(HAVE_MYSQLND)
+ ZEND_MOD_REQUIRED("mysqlnd")
+#endif
+ {NULL, NULL, NULL}
+};
+
/* {{{ mysql_module_entry
*/
zend_module_entry mysql_module_entry = {
- STANDARD_MODULE_HEADER,
+#if ZEND_MODULE_API_NO >= 20050922
+ STANDARD_MODULE_HEADER_EX, NULL,
+ mysql_deps,
+#elif ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
"mysql",
mysql_functions,
ZEND_MODULE_STARTUP_N(mysql),
@@ -244,6 +262,26 @@ void timeout(int sig);
#define CHECK_LINK(link) { if (link==-1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "A link to the server could not be established"); RETURN_FALSE; } }
+#if defined(HAVE_MYSQLND)
+#define PHPMY_UNBUFFERED_QUERY_CHECK() \
+{\
+ if (mysql->active_result_id) { \
+ do { \
+ int type; \
+ MYSQL_RES *mysql_result; \
+ \
+ mysql_result = (MYSQL_RES *) zend_list_find(mysql->active_result_id, &type); \
+ if (mysql_result && type==le_result) { \
+ if (mysqli_result_is_unbuffered(mysql_result) && !mysql_eof(mysql_result)) { \
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Function called without first fetching all rows from a previous unbuffered query"); \
+ } \
+ zend_list_delete(mysql->active_result_id); \
+ mysql->active_result_id = 0; \
+ } \
+ } while(0); \
+ }\
+}
+#else
#define PHPMY_UNBUFFERED_QUERY_CHECK() \
{ \
if (mysql->active_result_id) { \
@@ -262,7 +300,8 @@ void timeout(int sig);
} \
} while(0); \
} \
-} \
+}
+#endif
/* {{{ _free_mysql_result
* This wrapper is required since mysql_free_result() returns an integer, and
@@ -295,7 +334,7 @@ static int php_mysql_select_db(php_mysql_conn *mysql, char *db TSRMLS_DC)
{
PHPMY_UNBUFFERED_QUERY_CHECK();
- if (mysql_select_db(&mysql->conn, db) != 0) {
+ if (mysql_select_db(mysql->conn, db) != 0) {
return 0;
} else {
return 1;
@@ -311,7 +350,7 @@ static void _close_mysql_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)
void (*handler) (int);
handler = signal(SIGPIPE, SIG_IGN);
- mysql_close(&link->conn);
+ mysql_close(link->conn);
signal(SIGPIPE, handler);
efree(link);
MySG(num_links)--;
@@ -326,7 +365,7 @@ static void _close_mysql_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC)
void (*handler) (int);
handler = signal(SIGPIPE, SIG_IGN);
- mysql_close(&link->conn);
+ mysql_close(link->conn);
signal(SIGPIPE, handler);
free(link);
@@ -361,6 +400,10 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("mysql.default_socket", NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_socket, zend_mysql_globals, mysql_globals)
STD_PHP_INI_ENTRY("mysql.connect_timeout", "60", PHP_INI_ALL, OnUpdateLong, connect_timeout, zend_mysql_globals, mysql_globals)
STD_PHP_INI_BOOLEAN("mysql.trace_mode", "0", PHP_INI_ALL, OnUpdateLong, trace_mode, zend_mysql_globals, mysql_globals)
+ STD_PHP_INI_BOOLEAN("mysql.allow_local_infile", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysql_globals, mysql_globals)
+#ifdef HAVE_MYSQLND
+ STD_PHP_INI_ENTRY("mysql.cache_size", "2000", PHP_INI_SYSTEM, OnUpdateLong, cache_size, zend_mysql_globals, mysql_globals)
+#endif
PHP_INI_END()
/* }}} */
@@ -377,7 +420,12 @@ static PHP_GINIT_FUNCTION(mysql)
mysql_globals->connect_error = NULL;
mysql_globals->connect_timeout = 0;
mysql_globals->trace_mode = 0;
+ mysql_globals->allow_local_infile = 1;
mysql_globals->result_allocated = 0;
+#ifdef HAVE_MYSQLND
+ mysql_globals->cache_size = 0;
+ mysql_globals->mysqlnd_thd_zval_cache = NULL;
+#endif
}
/* }}} */
@@ -401,11 +449,16 @@ ZEND_MODULE_STARTUP_D(mysql)
REGISTER_LONG_CONSTANT("MYSQL_CLIENT_INTERACTIVE", CLIENT_INTERACTIVE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQL_CLIENT_IGNORE_SPACE", CLIENT_IGNORE_SPACE, CONST_CS | CONST_PERSISTENT);
+#ifndef HAVE_MYSQLND
#if MYSQL_VERSION_ID >= 40000
if (mysql_server_init(0, NULL, NULL)) {
return FAILURE;
}
#endif
+#else
+ mysql_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MySG(cache_size));
+ mysql_mysqlnd_qcache = mysqlnd_qcache_init_cache();
+#endif
return SUCCESS;
}
@@ -415,17 +468,25 @@ ZEND_MODULE_STARTUP_D(mysql)
*/
PHP_MSHUTDOWN_FUNCTION(mysql)
{
+#ifndef HAVE_MYSQLND
#if MYSQL_VERSION_ID >= 40000
#ifdef PHP_WIN32
unsigned long client_ver = mysql_get_client_version();
- /* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */
- if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) {
+ /*
+ Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows.
+ PHP bug#41350 MySQL bug#25621
+ */
+ if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) {
mysql_server_end();
}
#else
mysql_server_end();
#endif
#endif
+#else
+ mysqlnd_palloc_free_cache(mysql_mysqlnd_zval_cache);
+ mysqlnd_qcache_free_cache_reference(&mysql_mysqlnd_qcache);
+#endif
UNREGISTER_INI_ENTRIES();
return SUCCESS;
@@ -436,7 +497,7 @@ PHP_MSHUTDOWN_FUNCTION(mysql)
*/
PHP_RINIT_FUNCTION(mysql)
{
-#if defined(ZTS) && MYSQL_VERSION_ID >= 40000
+#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000
if (mysql_thread_init()) {
return FAILURE;
}
@@ -448,6 +509,10 @@ PHP_RINIT_FUNCTION(mysql)
MySG(connect_errno) =0;
MySG(result_allocated) = 0;
+#ifdef HAVE_MYSQLND
+ MySG(mysqlnd_thd_zval_cache) = mysqlnd_palloc_rinit(mysql_mysqlnd_zval_cache);
+#endif
+
return SUCCESS;
}
/* }}} */
@@ -456,7 +521,7 @@ PHP_RINIT_FUNCTION(mysql)
*/
PHP_RSHUTDOWN_FUNCTION(mysql)
{
-#if defined(ZTS) && MYSQL_VERSION_ID >= 40000
+#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000
mysql_thread_end();
#endif
@@ -469,6 +534,10 @@ PHP_RSHUTDOWN_FUNCTION(mysql)
if (MySG(connect_error)!=NULL) {
efree(MySG(connect_error));
}
+#ifdef HAVE_MYSQLND
+ mysqlnd_palloc_rshutdown(MySG(mysqlnd_thd_zval_cache));
+#endif
+
return SUCCESS;
}
/* }}} */
@@ -486,12 +555,26 @@ PHP_MINFO_FUNCTION(mysql)
snprintf(buf, sizeof(buf), "%ld", MySG(num_links));
php_info_print_table_row(2, "Active Links", buf);
php_info_print_table_row(2, "Client API version", mysql_get_client_info());
-#if !defined (PHP_WIN32) && !defined (NETWARE)
+#if !defined (PHP_WIN32) && !defined (NETWARE) && !defined(HAVE_MYSQLND)
php_info_print_table_row(2, "MYSQL_MODULE_TYPE", PHP_MYSQL_TYPE);
php_info_print_table_row(2, "MYSQL_SOCKET", MYSQL_UNIX_ADDR);
php_info_print_table_row(2, "MYSQL_INCLUDE", PHP_MYSQL_INCLUDE);
php_info_print_table_row(2, "MYSQL_LIBS", PHP_MYSQL_LIBS);
#endif
+#if defined(HAVE_MYSQLND)
+ {
+ zval values;
+
+ php_info_print_table_header(2, "Persistent cache", mysql_mysqlnd_zval_cache? "enabled":"disabled");
+
+ if (mysql_mysqlnd_zval_cache) {
+ /* Now report cache status */
+ mysqlnd_palloc_stats(mysql_mysqlnd_zval_cache, &values);
+ mysqlnd_minfo_print_hash(&values);
+ zval_dtor(&values);
+ }
+ }
+#endif
php_info_print_table_end();
@@ -511,9 +594,14 @@ PHP_MINFO_FUNCTION(mysql)
MYSQL_DO_CONNECT_CLEANUP(); \
RETURN_FALSE;
+#ifdef HAVE_MYSQLND
+#define MYSQL_PORT 0
+#endif
+
static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
{
char *user=NULL, *passwd=NULL, *host_and_port=NULL, *socket=NULL, *tmp=NULL, *host=NULL;
+ int user_len, passwd_len, host_len;
char *hashed_details=NULL;
int hashed_details_length, port = MYSQL_PORT;
int client_flags = 0;
@@ -521,7 +609,6 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
#if MYSQL_VERSION_ID <= 32230
void (*handler) (int);
#endif
- zval **z_host=NULL, **z_user=NULL, **z_passwd=NULL, **z_new_link=NULL, **z_client_flags=NULL;
zend_bool free_host=0, new_link=0;
long connect_timeout;
@@ -560,93 +647,20 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
user = MySG(default_user);
passwd = MySG(default_password);
- switch(ZEND_NUM_ARGS()) {
- case 0: /* defaults */
- break;
- case 1: {
- if (zend_get_parameters_ex(1, &z_host)==FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- }
- break;
- case 2: {
- if (zend_get_parameters_ex(2, &z_host, &z_user)==FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- convert_to_string_ex(z_user);
- user = Z_STRVAL_PP(z_user);
- }
- break;
- case 3: {
- if (zend_get_parameters_ex(3, &z_host, &z_user, &z_passwd) == FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- convert_to_string_ex(z_user);
- convert_to_string_ex(z_passwd);
- user = Z_STRVAL_PP(z_user);
- passwd = Z_STRVAL_PP(z_passwd);
- }
- break;
- case 4: {
- if (!persistent) {
- if (zend_get_parameters_ex(4, &z_host, &z_user, &z_passwd, &z_new_link) == FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- convert_to_string_ex(z_user);
- convert_to_string_ex(z_passwd);
- convert_to_boolean_ex(z_new_link);
- user = Z_STRVAL_PP(z_user);
- passwd = Z_STRVAL_PP(z_passwd);
- new_link = Z_BVAL_PP(z_new_link);
- }
- else {
- if (zend_get_parameters_ex(4, &z_host, &z_user, &z_passwd, &z_client_flags) == FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- convert_to_string_ex(z_user);
- convert_to_string_ex(z_passwd);
- convert_to_long_ex(z_client_flags);
- user = Z_STRVAL_PP(z_user);
- passwd = Z_STRVAL_PP(z_passwd);
- client_flags = Z_LVAL_PP(z_client_flags);
- }
- }
- break;
- case 5: {
- if (zend_get_parameters_ex(5, &z_host, &z_user, &z_passwd, &z_new_link, &z_client_flags) == FAILURE) {
- MYSQL_DO_CONNECT_RETURN_FALSE();
- }
- convert_to_string_ex(z_user);
- convert_to_string_ex(z_passwd);
- convert_to_boolean_ex(z_new_link);
- convert_to_long_ex(z_client_flags);
- user = Z_STRVAL_PP(z_user);
- passwd = Z_STRVAL_PP(z_passwd);
- new_link = Z_BVAL_PP(z_new_link);
- client_flags = Z_LVAL_PP(z_client_flags);
- }
- break;
- default:
- WRONG_PARAM_COUNT;
- break;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sssll", &host_and_port, &host_len,
+ &user, &user_len, &passwd, &passwd_len,
+ &new_link, &client_flags)==FAILURE) {
+ WRONG_PARAM_COUNT;
}
- /* disable local infile option for open_basedir */
- if (((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) && (client_flags & CLIENT_LOCAL_FILES)) {
- client_flags ^= CLIENT_LOCAL_FILES;
+
+ /* mysql_pconnect does not support new_link parameter */
+ if (persistent) {
+ client_flags= new_link;
}
- if (z_host) {
- SEPARATE_ZVAL(z_host); /* We may modify z_host if it contains a port, separate */
- convert_to_string_ex(z_host);
- host_and_port = Z_STRVAL_PP(z_host);
- if (z_user) {
- convert_to_string_ex(z_user);
- user = Z_STRVAL_PP(z_user);
- if (z_passwd) {
- convert_to_string_ex(z_passwd);
- passwd = Z_STRVAL_PP(z_passwd);
- }
- }
+ /* disable local infile option for open_basedir */
+ if (((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) && (client_flags & CLIENT_LOCAL_FILES)) {
+ client_flags ^= CLIENT_LOCAL_FILES;
}
hashed_details_length = spprintf(&hashed_details, 0, "mysql_%s_%s_%s_%d", SAFE_STRING(host_and_port), SAFE_STRING(user), SAFE_STRING(passwd), client_flags);
@@ -687,12 +701,12 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) { /* we don't */
zend_rsrc_list_entry new_le;
- if (MySG(max_links)!=-1 && MySG(num_links)>=MySG(max_links)) {
+ if (MySG(max_links) != -1 && MySG(num_links) >= MySG(max_links)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MySG(num_links));
efree(hashed_details);
MYSQL_DO_CONNECT_RETURN_FALSE();
}
- if (MySG(max_persistent)!=-1 && MySG(num_persistent)>=MySG(max_persistent)) {
+ if (MySG(max_persistent) != -1 && MySG(num_persistent) >= MySG(max_persistent)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open persistent links (%ld)", MySG(num_persistent));
efree(hashed_details);
MYSQL_DO_CONNECT_RETURN_FALSE();
@@ -700,28 +714,36 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
/* create the link */
mysql = (php_mysql_conn *) malloc(sizeof(php_mysql_conn));
mysql->active_result_id = 0;
-#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */
- mysql_init(&mysql->conn);
-
- if (connect_timeout != -1) {
- mysql_options(&mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout);
- }
+ mysql->multi_query = 1;
+#ifndef HAVE_MYSQLND
+ mysql->conn = mysql_init(NULL);
+#else
+ mysql->conn = mysql_init(persistent);
+#endif
- if (mysql_real_connect(&mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) {
+ if (connect_timeout != -1)
+ mysql_options(mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout);
+#ifndef HAVE_MYSQLND
+ if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
#else
- if (mysql_connect(&mysql->conn, host, user, passwd)==NULL) {
+ if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,
+ port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
#endif
+ {
/* Populate connect error globals so that the error functions can read them */
- if (MySG(connect_error)!=NULL) efree(MySG(connect_error));
- MySG(connect_error)=estrdup(mysql_error(&mysql->conn));
+ if (MySG(connect_error) != NULL) {
+ efree(MySG(connect_error));
+ }
+ MySG(connect_error) = estrdup(mysql_error(mysql->conn));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", MySG(connect_error));
#if defined(HAVE_MYSQL_ERRNO)
- MySG(connect_errno)=mysql_errno(&mysql->conn);
+ MySG(connect_errno) = mysql_errno(mysql->conn);
#endif
free(mysql);
efree(hashed_details);
MYSQL_DO_CONNECT_RETURN_FALSE();
}
+ mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));
/* hash it up */
Z_TYPE(new_le) = le_plink;
@@ -737,36 +759,31 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
if (Z_TYPE_P(le) != le_plink) {
MYSQL_DO_CONNECT_RETURN_FALSE();
}
+ mysql = (php_mysql_conn *) le->ptr;
+ mysql->active_result_id = 0;
+ mysql->multi_query = 1;
/* ensure that the link did not die */
-#if MYSQL_VERSION_ID > 32230 /* Use mysql_ping to ensure link is alive (and to reconnect if needed) */
- if (mysql_ping(le->ptr)) {
-#else /* Use mysql_stat() to check if server is alive */
- handler=signal(SIGPIPE, SIG_IGN);
-#if defined(HAVE_MYSQL_ERRNO) && defined(CR_SERVER_GONE_ERROR)
- mysql_stat(le->ptr);
- if (mysql_errno(&((php_mysql_conn *) le->ptr)->conn) == CR_SERVER_GONE_ERROR) {
-#else
- if (!strcasecmp(mysql_stat(le->ptr), "mysql server has gone away")) { /* the link died */
-#endif
- signal(SIGPIPE, handler);
-#endif /* end mysql_ping */
-#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */
- if (mysql_real_connect(le->ptr, host, user, passwd, NULL, port, socket, client_flags)==NULL) {
+ if (mysql_ping(mysql->conn)) {
+ if (mysql_errno(mysql->conn) == 2006) {
+#ifndef HAVE_MYSQLND
+ if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
#else
- if (mysql_connect(le->ptr, host, user, passwd)==NULL) {
+ if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,
+ port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
#endif
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect");
- zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1);
- efree(hashed_details);
- MYSQL_DO_CONNECT_RETURN_FALSE();
+ {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect");
+ zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1);
+ efree(hashed_details);
+ MYSQL_DO_CONNECT_RETURN_FALSE();
+ }
+ mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));
}
- }
-#if MYSQL_VERSION_ID < 32231
- signal(SIGPIPE, handler);
+ } else {
+#ifdef HAVE_MYSQLND
+ mysqlnd_restart_psession(mysql->conn);
#endif
-
- mysql = (php_mysql_conn *) le->ptr;
- mysql->active_result_id = 0;
+ }
}
ZEND_REGISTER_RESOURCE(return_value, mysql, le_plink);
} else { /* non persistent */
@@ -799,7 +816,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);
}
}
- if (MySG(max_links)!=-1 && MySG(num_links)>=MySG(max_links)) {
+ if (MySG(max_links) != -1 && MySG(num_links) >= MySG(max_links)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MySG(num_links));
efree(hashed_details);
MYSQL_DO_CONNECT_RETURN_FALSE();
@@ -807,28 +824,41 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
mysql = (php_mysql_conn *) emalloc(sizeof(php_mysql_conn));
mysql->active_result_id = 0;
-#if MYSQL_VERSION_ID > 32199 /* this lets us set the port number */
- mysql_init(&mysql->conn);
+ mysql->multi_query = 1;
+#ifndef HAVE_MYSQLND
+ mysql->conn = mysql_init(NULL);
+#else
+ mysql->conn = mysql_init(persistent);
+#endif
- if (connect_timeout != -1) {
- mysql_options(&mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout);
- }
+ if (connect_timeout != -1)
+ mysql_options(mysql->conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout);
- if (mysql_real_connect(&mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) {
+#ifndef HAVE_MYSQLND
+ if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
#else
- if (mysql_connect(&mysql->conn, host, user, passwd)==NULL) {
+ if (mysqlnd_connect(mysql->conn, host, user, passwd, 0, NULL, 0,
+ port, socket, client_flags, MySG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
#endif
+ {
/* Populate connect error globals so that the error functions can read them */
- if (MySG(connect_error)!=NULL) efree(MySG(connect_error));
- MySG(connect_error)=estrdup(mysql_error(&mysql->conn));
+ if (MySG(connect_error) != NULL) {
+ efree(MySG(connect_error));
+ }
+ MySG(connect_error) = estrdup(mysql_error(mysql->conn));
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", MySG(connect_error));
#if defined(HAVE_MYSQL_ERRNO)
- MySG(connect_errno)=mysql_errno(&mysql->conn);
+ MySG(connect_errno) = mysql_errno(mysql->conn);
+#endif
+ /* free mysql structure */
+#ifdef HAVE_MYSQLND
+ mysqli_close(mysql->conn, MYSQLI_CLOSE_DISCONNECTED);
#endif
efree(hashed_details);
efree(mysql);
MYSQL_DO_CONNECT_RETURN_FALSE();
}
+ mysql_options(mysql->conn, MYSQL_OPT_LOCAL_INFILE, (char *)&MySG(allow_local_infile));
/* add it to the list */
ZEND_REGISTER_RESOURCE(return_value, mysql, le_link);
@@ -997,7 +1027,7 @@ PHP_FUNCTION(mysql_get_host_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_STRING((char *)mysql_get_host_info(&mysql->conn),1);
+ RETURN_STRING((char *)mysql_get_host_info(mysql->conn),1);
}
/* }}} */
@@ -1027,7 +1057,7 @@ PHP_FUNCTION(mysql_get_proto_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_LONG(mysql_get_proto_info(&mysql->conn));
+ RETURN_LONG(mysql_get_proto_info(mysql->conn));
}
/* }}} */
@@ -1057,7 +1087,7 @@ PHP_FUNCTION(mysql_get_server_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_STRING((char *)mysql_get_server_info(&mysql->conn),1);
+ RETURN_STRING((char *)mysql_get_server_info(mysql->conn),1);
}
/* }}} */
@@ -1081,7 +1111,7 @@ PHP_FUNCTION(mysql_info)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
- if ((str = (char *)mysql_info(&mysql->conn))) {
+ if ((str = (char *)mysql_info(mysql->conn))) {
RETURN_STRING(str,1);
} else {
RETURN_FALSE;
@@ -1107,7 +1137,7 @@ PHP_FUNCTION(mysql_thread_id)
}
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_LONG(mysql_thread_id(&mysql->conn));
+ RETURN_LONG(mysql_thread_id(mysql->conn));
}
/* }}} */
@@ -1118,6 +1148,10 @@ PHP_FUNCTION(mysql_stat)
zval *mysql_link = NULL;
int id = -1;
php_mysql_conn *mysql;
+ char *stat;
+#ifdef HAVE_MYSQLND
+ uint stat_len;
+#endif
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &mysql_link) == FAILURE) {
return;
@@ -1130,8 +1164,16 @@ PHP_FUNCTION(mysql_stat)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
PHPMY_UNBUFFERED_QUERY_CHECK();
-
- RETURN_STRING((char *)mysql_stat(&mysql->conn), 1);
+#ifndef HAVE_MYSQLND
+ if ((stat = (char *)mysql_stat(mysql->conn))) {
+ RETURN_STRING(stat, 1);
+#else
+ if (mysqlnd_stat(mysql->conn, &stat, &stat_len) == PASS) {
+ RETURN_STRINGL(stat, stat_len, 0);
+#endif
+ } else {
+ RETURN_FALSE;
+ }
}
/* }}} */
@@ -1153,38 +1195,7 @@ PHP_FUNCTION(mysql_client_encoding)
}
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
-
- RETURN_STRING((char *)mysql_character_set_name(&mysql->conn), 1);
-}
-/* }}} */
-#endif
-
-#ifdef MYSQL_HAS_SET_CHARSET
-/* {{{ proto bool mysql_set_charset(string csname [, int link_identifier])
- sets client character set */
-PHP_FUNCTION(mysql_set_charset)
-{
- zval *mysql_link = NULL;
- char *csname;
- int id = -1, csname_len;
- php_mysql_conn *mysql;
-
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &csname, &csname_len, &mysql_link) == FAILURE) {
- return;
- }
-
- if (ZEND_NUM_ARGS() == 1) {
- id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
- CHECK_LINK(id);
- }
-
- ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
-
- if (!mysql_set_character_set(&mysql->conn, csname)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_STRING((char *)mysql_character_set_name(mysql->conn), 1);
}
/* }}} */
#endif
@@ -1224,7 +1235,7 @@ PHP_FUNCTION(mysql_create_db)
convert_to_string_ex(db);
- if (mysql_create_db(&mysql->conn, Z_STRVAL_PP(db))==0) {
+ if (mysql_create_db(mysql->conn, Z_STRVAL_PP(db))==0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -1263,7 +1274,7 @@ PHP_FUNCTION(mysql_drop_db)
convert_to_string_ex(db);
- if (mysql_drop_db(&mysql->conn, Z_STRVAL_PP(db))==0) {
+ if (mysql_drop_db(mysql->conn, Z_STRVAL_PP(db))==0) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -1291,8 +1302,11 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link
PHPMY_UNBUFFERED_QUERY_CHECK();
+ MYSQL_DISABLE_MQ;
+
convert_to_string_ex(query);
+#ifndef HAVE_MYSQLND
/* check explain */
if (MySG(trace_mode)) {
if (!strncasecmp("select", Z_STRVAL_PP(query), 6)){
@@ -1300,14 +1314,14 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link
char *newquery;
int newql = spprintf (&newquery, 0, "EXPLAIN %s", Z_STRVAL_PP(query));
- mysql_real_query(&mysql->conn, newquery, newql);
+ mysql_real_query(mysql->conn, newquery, newql);
efree (newquery);
- if (mysql_errno(&mysql->conn)) {
- php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn));
+ if (mysql_errno(mysql->conn)) {
+ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));
RETURN_FALSE;
}
else {
- mysql_result = mysql_use_result(&mysql->conn);
+ mysql_result = mysql_use_result(mysql->conn);
while ((row = mysql_fetch_row(mysql_result))) {
if (!strcmp("ALL", row[1])) {
php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "Your query requires a full tablescan (table %s, %s rows affected). Use EXPLAIN to optimize your query.", row[0], row[6]);
@@ -1319,36 +1333,37 @@ static void php_mysql_do_query_general(zval **query, zval **mysql_link, int link
}
}
} /* end explain */
+#endif
/* mysql_query is binary unsafe, use mysql_real_query */
#if MYSQL_VERSION_ID > 32199
- if (mysql_real_query(&mysql->conn, Z_STRVAL_PP(query), Z_STRLEN_PP(query))!=0) {
+ if (mysql_real_query(mysql->conn, Z_STRVAL_PP(query), Z_STRLEN_PP(query))!=0) {
/* check possible error */
if (MySG(trace_mode)){
- if (mysql_errno(&mysql->conn)){
- php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn));
+ if (mysql_errno(mysql->conn)){
+ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));
}
}
RETURN_FALSE;
}
#else
- if (mysql_query(&mysql->conn, Z_STRVAL_PP(query))!=0) {
+ if (mysql_query(mysql->conn, Z_STRVAL_PP(query))!=0) {
/* check possible error */
if (MySG(trace_mode)){
- if (mysql_errno(&mysql->conn)){
- php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(&mysql->conn));
+ if (mysql_errno(mysql->conn)){
+ php_error_docref("http://www.mysql.com/doc" TSRMLS_CC, E_WARNING, "%s", mysql_error(mysql->conn));
}
}
RETURN_FALSE;
}
#endif
if(use_store == MYSQL_USE_RESULT) {
- mysql_result=mysql_use_result(&mysql->conn);
+ mysql_result=mysql_use_result(mysql->conn);
} else {
- mysql_result=mysql_store_result(&mysql->conn);
+ mysql_result=mysql_store_result(mysql->conn);
}
if (!mysql_result) {
- if (PHP_MYSQL_VALID_RESULT(&mysql->conn)) { /* query should have returned rows */
+ if (PHP_MYSQL_VALID_RESULT(mysql->conn)) { /* query should have returned rows */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save result set");
RETURN_FALSE;
} else {
@@ -1474,7 +1489,7 @@ PHP_FUNCTION(mysql_list_dbs)
PHPMY_UNBUFFERED_QUERY_CHECK();
- if ((mysql_result=mysql_list_dbs(&mysql->conn, NULL))==NULL) {
+ if ((mysql_result=mysql_list_dbs(mysql->conn, NULL))==NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");
RETURN_FALSE;
}
@@ -1519,7 +1534,7 @@ PHP_FUNCTION(mysql_list_tables)
PHPMY_UNBUFFERED_QUERY_CHECK();
- if ((mysql_result=mysql_list_tables(&mysql->conn, NULL))==NULL) {
+ if ((mysql_result=mysql_list_tables(mysql->conn, NULL))==NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");
RETURN_FALSE;
}
@@ -1566,7 +1581,7 @@ PHP_FUNCTION(mysql_list_fields)
PHPMY_UNBUFFERED_QUERY_CHECK();
convert_to_string_ex(table);
- if ((mysql_result=mysql_list_fields(&mysql->conn, Z_STRVAL_PP(table), NULL))==NULL) {
+ if ((mysql_result=mysql_list_fields(mysql->conn, Z_STRVAL_PP(table), NULL))==NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");
RETURN_FALSE;
}
@@ -1596,7 +1611,7 @@ PHP_FUNCTION(mysql_list_processes)
PHPMY_UNBUFFERED_QUERY_CHECK();
- mysql_result = mysql_list_processes(&mysql->conn);
+ mysql_result = mysql_list_processes(mysql->conn);
if (mysql_result == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to save MySQL query result");
RETURN_FALSE;
@@ -1639,7 +1654,7 @@ PHP_FUNCTION(mysql_error)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_STRING((char *)mysql_error(&mysql->conn), 1);
+ RETURN_STRING((char *)mysql_error(mysql->conn), 1);
}
/* }}} */
@@ -1677,7 +1692,7 @@ PHP_FUNCTION(mysql_errno)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
- RETURN_LONG(mysql_errno(&mysql->conn));
+ RETURN_LONG(mysql_errno(mysql->conn));
}
#endif
/* }}} */
@@ -1710,7 +1725,7 @@ PHP_FUNCTION(mysql_affected_rows)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
/* conversion from int64 to long happing here */
- Z_LVAL_P(return_value) = (long) mysql_affected_rows(&mysql->conn);
+ Z_LVAL_P(return_value) = (long) mysql_affected_rows(mysql->conn);
Z_TYPE_P(return_value) = IS_LONG;
}
/* }}} */
@@ -1722,8 +1737,9 @@ PHP_FUNCTION(mysql_escape_string)
{
zval **str;
+
if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &str) == FAILURE) {
- ZEND_WRONG_PARAM_COUNT();
+ WRONG_PARAM_COUNT;
}
convert_to_string_ex(str);
/* assume worst case situation, which is 2x of the original string.
@@ -1738,7 +1754,6 @@ PHP_FUNCTION(mysql_escape_string)
if (MySG(trace_mode)){
php_error_docref("function.mysql-real-escape-string" TSRMLS_CC, E_WARNING, "This function is deprecated; use mysql_real_escape_string() instead.");
}
-
}
/* }}} */
@@ -1765,7 +1780,7 @@ PHP_FUNCTION(mysql_real_escape_string)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
new_str = safe_emalloc(str_len, 2, 1);
- new_str_len = mysql_real_escape_string(&mysql->conn, new_str, str, str_len);
+ new_str_len = mysql_real_escape_string(mysql->conn, new_str, str, str_len);
new_str = erealloc(new_str, new_str_len + 1);
RETURN_STRINGL(new_str, new_str_len, 0);
@@ -1799,7 +1814,7 @@ PHP_FUNCTION(mysql_insert_id)
ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, mysql_link, id, "MySQL-Link", le_link, le_plink);
/* conversion from int64 to long happing here */
- Z_LVAL_P(return_value) = (long) mysql_insert_id(&mysql->conn);
+ Z_LVAL_P(return_value) = (long) mysql_insert_id(mysql->conn);
Z_TYPE_P(return_value) = IS_LONG;
}
/* }}} */
@@ -1811,8 +1826,10 @@ PHP_FUNCTION(mysql_result)
{
zval **result, **row, **field=NULL;
MYSQL_RES *mysql_result;
+#ifndef HAVE_MYSQLND
MYSQL_ROW sql_row;
mysql_row_length_type *sql_row_lengths;
+#endif
int field_offset=0;
switch (ZEND_NUM_ARGS()) {
@@ -1839,10 +1856,6 @@ PHP_FUNCTION(mysql_result)
RETURN_FALSE;
}
mysql_data_seek(mysql_result, Z_LVAL_PP(row));
- if ((sql_row=mysql_fetch_row(mysql_result))==NULL
- || (sql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) { /* shouldn't happen? */
- RETURN_FALSE;
- }
if (field) {
switch(Z_TYPE_PP(field)) {
@@ -1892,6 +1905,11 @@ PHP_FUNCTION(mysql_result)
}
}
+#ifndef HAVE_MYSQLND
+ if ((sql_row=mysql_fetch_row(mysql_result))==NULL
+ || (sql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) { /* shouldn't happen? */
+ RETURN_FALSE;
+ }
if (sql_row[field_offset]) {
Z_TYPE_P(return_value) = IS_STRING;
@@ -1904,6 +1922,9 @@ PHP_FUNCTION(mysql_result)
} else {
Z_TYPE_P(return_value) = IS_NULL;
}
+#else
+ mysqlnd_result_fetch_field_data(mysql_result, field_offset, return_value);
+#endif
}
/* }}} */
@@ -1951,12 +1972,14 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,
{
zval **result, **arg2;
MYSQL_RES *mysql_result;
- MYSQL_ROW mysql_row;
- MYSQL_FIELD *mysql_field;
- mysql_row_length_type *mysql_row_lengths;
- int i;
zval *res, *ctor_params = NULL;
zend_class_entry *ce = NULL;
+#ifndef HAVE_MYSQLND
+ int i;
+ MYSQL_FIELD *mysql_field;
+ MYSQL_ROW mysql_row;
+ mysql_row_length_type *mysql_row_lengths;
+#endif
#ifdef ZEND_ENGINE_2
if (into_object) {
@@ -2007,20 +2030,24 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,
}
if ((result_type & MYSQL_BOTH) == 0) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQL_NUM, MYSQL_ASSOC or MYSQL_BOTH.");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQL_NUM, MYSQL_ASSOC or MYSQL_BOTH");
}
ZEND_FETCH_RESOURCE(mysql_result, MYSQL_RES *, result, -1, "MySQL result", le_result);
- if ((mysql_row=mysql_fetch_row(mysql_result))==NULL
- || (mysql_row_lengths=mysql_fetch_lengths(mysql_result))==NULL) {
+#ifndef HAVE_MYSQLND
+ if ((mysql_row = mysql_fetch_row(mysql_result)) == NULL ||
+ (mysql_row_lengths = mysql_fetch_lengths(mysql_result)) == NULL) {
RETURN_FALSE;
}
array_init(return_value);
mysql_field_seek(mysql_result, 0);
- for (mysql_field=mysql_fetch_field(mysql_result), i=0; mysql_field; mysql_field=mysql_fetch_field(mysql_result), i++) {
+ for (mysql_field = mysql_fetch_field(mysql_result), i = 0;
+ mysql_field;
+ mysql_field = mysql_fetch_field(mysql_result), i++)
+ {
if (mysql_row[i]) {
zval *data;
@@ -2053,9 +2080,13 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,
}
}
}
+#else
+ mysqlnd_fetch_into(mysql_result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQL);
+#endif
#ifdef ZEND_ENGINE_2
- if (into_object) {
+ /* mysqlnd might return FALSE if no more rows */
+ if (into_object && Z_TYPE_P(return_value) != IS_BOOL) {
zval dataset = *return_value;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@@ -2127,7 +2158,19 @@ static void php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type,
Gets a result row as an enumerated array */
PHP_FUNCTION(mysql_fetch_row)
{
+#ifdef HAVE_MYSQLND
+ MYSQL_RES *result;
+ zval *mysql_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &mysql_result) == FAILURE) {
+ return;
+ }
+ ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result);
+
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, return_value, MYSQLND_MYSQL);
+#else
php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_NUM, 1, 0);
+#endif
}
/* }}} */
@@ -2149,7 +2192,20 @@ PHP_FUNCTION(mysql_fetch_object)
Fetch a result row as an array (associative, numeric or both) */
PHP_FUNCTION(mysql_fetch_array)
{
+#ifndef HAVE_MYSQLND
php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 2, 0);
+#else
+ MYSQL_RES *result;
+ zval *mysql_result;
+ long mode = MYSQLND_FETCH_BOTH;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &mysql_result, &mode) == FAILURE) {
+ return;
+ }
+ ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result);
+
+ mysqlnd_fetch_into(result, mode, return_value, MYSQLND_MYSQL);
+#endif
}
/* }}} */
@@ -2158,7 +2214,19 @@ PHP_FUNCTION(mysql_fetch_array)
Fetch a result row as an associative array */
PHP_FUNCTION(mysql_fetch_assoc)
{
+#ifndef HAVE_MYSQLND
php_mysql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_ASSOC, 1, 0);
+#else
+ MYSQL_RES *result;
+ zval *mysql_result;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &mysql_result) == FAILURE) {
+ return;
+ }
+ ZEND_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, -1, "MySQL result", le_result);
+
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQL);
+#endif
}
/* }}} */
@@ -2223,6 +2291,9 @@ static char *php_mysql_get_field_name(int field_type)
case FIELD_TYPE_VAR_STRING:
return "string";
break;
+#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)
+ case MYSQL_TYPE_BIT:
+#endif
#ifdef MYSQL_HAS_TINY
case FIELD_TYPE_TINY:
#endif
@@ -2585,7 +2656,7 @@ PHP_FUNCTION(mysql_ping)
PHPMY_UNBUFFERED_QUERY_CHECK();
- RETURN_BOOL(! mysql_ping(&mysql->conn));
+ RETURN_BOOL(! mysql_ping(mysql->conn));
}
/* }}} */
diff --git a/ext/mysql/php_mysql.h b/ext/mysql/php_mysql.h
index a088208a4b..fe21af15c5 100644
--- a/ext/mysql/php_mysql.h
+++ b/ext/mysql/php_mysql.h
@@ -34,6 +34,25 @@
#include "TSRM.h"
#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#if defined(HAVE_MYSQLND)
+#include "ext/mysqlnd/mysqlnd.h"
+#include "ext/mysql/mysql_mysqlnd.h"
+#else
+#include <mysql.h>
+#endif
+
+#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007 || HAVE_MYSQLND
+#define MYSQL_HAS_SET_CHARSET
+#endif
+
extern zend_module_entry mysql_module_entry;
#define mysql_module_ptr &mysql_module_entry
@@ -91,9 +110,6 @@ PHP_FUNCTION(mysql_stat);
PHP_FUNCTION(mysql_thread_id);
PHP_FUNCTION(mysql_client_encoding);
PHP_FUNCTION(mysql_ping);
-#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
-PHP_FUNCTION(mysql_set_charset);
-#endif
ZEND_BEGIN_MODULE_GLOBALS(mysql)
long default_link;
@@ -108,6 +124,12 @@ ZEND_BEGIN_MODULE_GLOBALS(mysql)
long connect_timeout;
long result_allocated;
long trace_mode;
+ long allow_local_infile;
+#ifdef HAVE_MYSQLND
+ MYSQLND_THD_ZVAL_PCACHE *mysqlnd_thd_zval_cache;
+ MYSQLND_QCACHE *mysqlnd_qcache;
+ long cache_size;
+#endif
ZEND_END_MODULE_GLOBALS(mysql)
#ifdef ZTS
diff --git a/ext/mysqli/config.m4 b/ext/mysqli/config.m4
index 19bc252bb0..7302e66c42 100644
--- a/ext/mysqli/config.m4
+++ b/ext/mysqli/config.m4
@@ -3,18 +3,17 @@ dnl $Id$
dnl config.m4 for extension mysqli
PHP_ARG_WITH(mysqli, for MySQLi support,
-[ --with-mysqli[=FILE] Include MySQLi support. FILE is the optional pathname
- to mysql_config [mysql_config]])
+[ --with-mysqli[=FILE] Include MySQLi support. FILE is the optional pathname to mysql_config [mysql_config].
+ If mysqlnd is passed as FILE, the MySQL native driver will be used])
PHP_ARG_ENABLE(embedded_mysqli, whether to enable embedded MySQLi support,
[ --enable-embedded-mysqli MYSQLi: Enable embedded support], no, no)
-if test "$PHP_MYSQLI" != "no"; then
+if test "$PHP_MYSQLI" = "mysqlnd"; then
+ dnl This needs to be set in any extension which wishes to use mysqlnd
+ PHP_MYSQLND_ENABLED=yes
-dnl there are no mysql libs currently bundled with PHP.. --Jani
-dnl if test "$PHP_MYSQL" = "yes"; then
-dnl AC_MSG_ERROR([--with-mysql (using bundled libs) can not be used together with --with-mysqli.])
-dnl fi
+elif test "$PHP_MYSQLI" != "no"; then
if test "$PHP_MYSQLI" = "yes"; then
MYSQL_CONFIG=`$php_shtool path mysql_config`
@@ -26,6 +25,8 @@ dnl fi
if test "$PHP_EMBEDDED_MYSQLI" = "yes"; then
AC_DEFINE(HAVE_EMBEDDED_MYSQLI, 1, [embedded MySQL support enabled])
MYSQL_LIB_CFG='--libmysqld-libs'
+ dnl mysqlnd doesn't support embedded, so we have to add some extra stuff
+ mysqli_extra_sources="mysqli_embedded.c"
elif test "$enable_maintainer_zts" = "yes"; then
MYSQL_LIB_CFG='--libs_r'
MYSQL_LIB_NAME='mysqlclient_r'
@@ -48,17 +49,29 @@ dnl fi
[
PHP_EVAL_INCLINE($MYSQLI_INCLINE)
PHP_EVAL_LIBLINE($MYSQLI_LIBLINE, MYSQLI_SHARED_LIBADD)
- AC_DEFINE(HAVE_MYSQLILIB,1,[ ])
- PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_stmt_field_count,
+ AC_DEFINE(HAVE_MYSQLILIB, 1, [ ])
+ PHP_CHECK_LIBRARY($MYSQL_LIB_NAME, mysql_set_character_set,
[ ],[
- AC_MSG_ERROR([MySQLI doesn't support versions < 4.1.3 (for MySQL 4.1.x) and < 5.0.1 for (MySQL 5.0.x) anymore. Please update your libraries.])
- ],[$MYSQLI_LIBLINE])
+ AC_MSG_ERROR([MySQLI doesn't support versions < 4.1.13 (for MySQL 4.1.x) and < 5.0.7 for (MySQL 5.0.x) anymore. Please update your libraries.])
+ ],[$MYSQLI_LIBLINE])
],[
AC_MSG_ERROR([wrong mysql library version or lib not found. Check config.log for more information.])
],[
$MYSQLI_LIBLINE
])
- PHP_NEW_EXTENSION(mysqli, mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c mysqli_fe.c mysqli_report.c mysqli_repl.c mysqli_driver.c mysqli_warning.c mysqli_exception.c mysqli_embedded.c, $ext_shared)
+ mysqli_extra_sources="$mysqli_extra_sources mysqli_repl.c"
+fi
+
+dnl Build extension
+if test "$PHP_MYSQLI" != "no"; then
+ mysqli_sources="mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c \
+ mysqli_fe.c mysqli_report.c mysqli_driver.c mysqli_warning.c \
+ mysqli_exception.c $mysqli_extra_sources"
+ PHP_NEW_EXTENSION(mysqli, $mysqli_sources, $ext_shared)
PHP_SUBST(MYSQLI_SHARED_LIBADD)
+
+ if test "$PHP_MYSQLI" = "mysqlnd"; then
+ PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd)
+ fi
fi
diff --git a/ext/mysqli/config.w32 b/ext/mysqli/config.w32
index 789112ea14..0f418f0cba 100644
--- a/ext/mysqli/config.w32
+++ b/ext/mysqli/config.w32
@@ -1,14 +1,42 @@
// $Id$
// vim:ft=javascript
+// Note: The extension name is "mysqli", you enable it with "--with-mysqli".
+// Passing value "mysqlnd" to it enables the bundled
+// client library to connect to the MySQL server, i.e. no external MySQL
+// client library is needed to perform the build.
+
ARG_WITH("mysqli", "MySQLi support", "no");
if (PHP_MYSQLI != "no") {
- if (CHECK_LIB("libmysql.lib", "mysqli", PHP_MYSQLI) &&
- CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQLI", PHP_MYSQLI + "\\include;" + PHP_PHP_BUILD + "\\include\\mysql;" + PHP_MYSQLI)) {
- EXTENSION("mysqli", "mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c mysqli_fe.c mysqli_report.c mysqli_repl.c mysqli_driver.c mysqli_warning.c mysqli_exception.c mysqli_embedded.c");
- AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library');
+ mysqli_source =
+ "mysqli.c " +
+ "mysqli_api.c " +
+ "mysqli_driver.c " +
+ "mysqli_embedded.c " +
+ "mysqli_exception.c " +
+ "mysqli_fe.c " +
+ "mysqli_nonapi.c " +
+ "mysqli_prop.c " +
+ "mysqli_report.c " +
+ "mysqli_warning.c";
+
+ if (PHP_MYSQLI != "mysqlnd") {
+ if (CHECK_LIB("libmysql.lib", "mysqli", PHP_MYSQLI) &&
+ CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_MYSQLI", PHP_MYSQLI +
+ "\\include;" + PHP_PHP_BUILD +
+ "\\include\\mysql;" + PHP_MYSQLI)) {
+ // No "mysqli_repl.c" when using "mysqlnd"
+ mysqli_extra_sources = "mysqli_repl.c";
+ EXTENSION("mysqli", mysqli_source + " " + mysqli_extra_sources);
+ AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library');
+ } else {
+ WARNING("mysqli not enabled; libraries and headers not found");
+ }
} else {
- WARNING("mysqli not enabled; libraries and headers not found");
+ EXTENSION("mysqli", mysqli_source);
+ AC_DEFINE('HAVE_MYSQLND', 1, 'MySQLi with native driver support enabled');
+ AC_DEFINE('HAVE_MYSQLILIB', 1, 'Have MySQLi library');
+ ADD_EXTENSION_DEP('mysqli', 'mysqlnd', true);
}
}
diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c
index ff4f206b7a..8de80f5e05 100644
--- a/ext/mysqli/mysqli.c
+++ b/ext/mysqli/mysqli.c
@@ -28,7 +28,7 @@
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
#include "zend_exceptions.h"
#define MYSQLI_STORE_RESULT 0
@@ -52,6 +52,12 @@ zend_class_entry *mysqli_driver_class_entry;
zend_class_entry *mysqli_warning_class_entry;
zend_class_entry *mysqli_exception_class_entry;
+#ifdef HAVE_MYSQLND
+MYSQLND_ZVAL_PCACHE *mysqli_mysqlnd_zval_cache;
+MYSQLND_QCACHE *mysqli_mysqlnd_qcache;
+#endif
+
+
extern void php_mysqli_connect(INTERNAL_FUNCTION_PARAMETERS);
typedef int (*mysqli_read_t)(mysqli_object *obj, zval **retval TSRMLS_DC);
@@ -62,6 +68,63 @@ typedef struct _mysqli_prop_handler {
mysqli_write_t write_func;
} mysqli_prop_handler;
+static int le_pmysqli;
+
+static int php_mysqli_persistent_on_rshut(zend_rsrc_list_entry *le TSRMLS_DC)
+{
+ if (le->type == le_pmysqli) {
+ mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr;
+ HashPosition pos;
+ MYSQL **mysql;
+ ulong idx;
+ dtor_func_t pDestructor = plist->used_links.pDestructor;
+ plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */
+
+ zend_hash_internal_pointer_reset_ex(&plist->used_links, &pos);
+ while (SUCCESS == zend_hash_get_current_data_ex(&plist->used_links, (void **)&mysql, &pos)) {
+ zend_hash_get_current_key_ex(&plist->used_links, NULL, NULL, &idx, FALSE, &pos);
+ /* Make it free */
+ zend_hash_next_index_insert(&plist->free_links, mysql, sizeof(MYSQL *), NULL);
+ /* First move forward */
+ zend_hash_move_forward_ex(&plist->used_links, &pos);
+ /* The delete, because del will free memory, but we need it's ->nextItem */
+ zend_hash_index_del(&plist->used_links, idx);
+ }
+
+ /* restore pDestructor, which should be php_mysqli_dtor_p_elements() */
+ plist->used_links.pDestructor = pDestructor;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+/* Destructor for mysqli entries in free_links/used_links */
+void php_mysqli_dtor_p_elements(void *data)
+{
+ MYSQL **mysql = (MYSQL **) data;
+ TSRMLS_FETCH();
+#if defined(HAVE_MYSQLND)
+ mysqlnd_end_psession(*mysql);
+#endif
+ mysqli_close(*mysql, MYSQLI_CLOSE_IMPLICIT);
+}
+
+ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor)
+{
+ if (rsrc->ptr) {
+ mysqli_plist_entry *plist = (mysqli_plist_entry *) rsrc->ptr;
+ zend_hash_destroy(&plist->free_links);
+ zend_hash_destroy(&plist->used_links);
+ free(plist);
+ }
+}
+
+
+int php_le_pmysqli(void)
+{
+ return le_pmysqli;
+}
+
+#ifndef HAVE_MYSQLND
/* {{{ php_free_stmt_bind_buffer */
void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)
{
@@ -80,7 +143,7 @@ void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)
if (bbuf.vars[i]) {
zval_ptr_dtor(&bbuf.vars[i]);
- }
+ }
}
if (bbuf.vars) {
@@ -100,30 +163,44 @@ void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type)
}
bbuf.var_cnt = 0;
- return;
}
/* }}} */
+#endif
/* {{{ php_clear_stmt_bind */
-void php_clear_stmt_bind(MY_STMT *stmt)
+void php_clear_stmt_bind(MY_STMT *stmt TSRMLS_DC)
{
if (stmt->stmt) {
- mysql_stmt_close(stmt->stmt);
+ if (mysqli_stmt_close(stmt->stmt, TRUE)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error occured while closing statement");
+ return;
+ }
}
+ /*
+ mysqlnd keeps track of the binding and has freed its
+ structures in stmt_close() above
+ */
+#ifndef HAVE_MYSQLND
+ /* Clean param bind */
php_free_stmt_bind_buffer(stmt->param, FETCH_SIMPLE);
+ /* Clean output bind */
php_free_stmt_bind_buffer(stmt->result, FETCH_RESULT);
+#endif
if (stmt->query) {
efree(stmt->query);
}
efree(stmt);
- return;
}
/* }}} */
/* {{{ php_clear_mysql */
void php_clear_mysql(MY_MYSQL *mysql) {
+ if (mysql->hash_key) {
+ efree(mysql->hash_key);
+ mysql->hash_key = NULL;
+ }
if (mysql->li_read) {
efree(Z_STRVAL_P(mysql->li_read));
FREE_ZVAL(mysql->li_read);
@@ -140,7 +217,7 @@ static void mysqli_objects_free_storage(void *object TSRMLS_DC)
mysqli_object *intern = (mysqli_object *)zo;
MYSQLI_RESOURCE *my_res = (MYSQLI_RESOURCE *)intern->ptr;
- my_efree(my_res);
+ my_efree(my_res);
zend_object_std_dtor(&intern->zo TSRMLS_CC);
efree(intern);
}
@@ -157,7 +234,9 @@ static void mysqli_link_free_storage(void *object TSRMLS_DC)
if (my_res && my_res->ptr) {
MY_MYSQL *mysql = (MY_MYSQL *)my_res->ptr;
if (mysql->mysql) {
- mysql_close(mysql->mysql);
+ if (!mysql->persistent) {
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT);
+ }
}
php_clear_mysql(mysql);
efree(mysql);
@@ -166,6 +245,13 @@ static void mysqli_link_free_storage(void *object TSRMLS_DC)
}
/* }}} */
+/* {{{ mysql_driver_free_storage */
+static void mysqli_driver_free_storage(void *object TSRMLS_DC)
+{
+ mysqli_objects_free_storage(object TSRMLS_CC);
+}
+/* }}} */
+
/* {{{ mysqli_stmt_free_storage
*/
static void mysqli_stmt_free_storage(void *object TSRMLS_DC)
@@ -176,7 +262,7 @@ static void mysqli_stmt_free_storage(void *object TSRMLS_DC)
if (my_res && my_res->ptr) {
MY_STMT *stmt = (MY_STMT *)my_res->ptr;
- php_clear_stmt_bind(stmt);
+ php_clear_stmt_bind(stmt TSRMLS_CC);
}
mysqli_objects_free_storage(object TSRMLS_CC);
}
@@ -243,7 +329,7 @@ zval *mysqli_read_property(zval *object, zval *member, int type TSRMLS_DC)
ret = FAILURE;
obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC);
- if (member->type != IS_STRING) {
+ if (member->type != IS_STRING) {
tmp_member = *member;
zval_copy_ctor(&tmp_member);
convert_to_string(&tmp_member);
@@ -256,7 +342,8 @@ zval *mysqli_read_property(zval *object, zval *member, int type TSRMLS_DC)
if (ret == SUCCESS) {
if (strcmp(obj->zo.ce->name, "mysqli_driver") &&
- (!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED)) {
+ (!obj->ptr || ((MYSQLI_RESOURCE *)(obj->ptr))->status < MYSQLI_STATUS_INITIALIZED))
+ {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", obj->zo.ce->name );
retval = EG(uninitialized_zval_ptr);
return(retval);
@@ -290,7 +377,7 @@ void mysqli_write_property(zval *object, zval *member, zval *value TSRMLS_DC)
zend_object_handlers *std_hnd;
int ret;
- if (member->type != IS_STRING) {
+ if (member->type != IS_STRING) {
tmp_member = *member;
zval_copy_ctor(&tmp_member);
convert_to_string(&tmp_member);
@@ -333,7 +420,6 @@ void mysqli_add_property(HashTable *h, char *pname, mysqli_read_t r_func, mysqli
static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)
{
- mysqli_object *obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC);
zend_class_entry * ce = Z_OBJCE_P(object);
if (ce != mysqli_link_class_entry && ce != mysqli_stmt_class_entry &&
@@ -342,6 +428,7 @@ static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)
return zend_std_get_constructor(object TSRMLS_CC);
} else {
static zend_internal_function f;
+ mysqli_object *obj = (mysqli_object *)zend_objects_get_address(object TSRMLS_CC);
f.function_name = obj->zo.ce->name;
f.scope = obj->zo.ce;
@@ -361,7 +448,7 @@ static union _zend_function *php_mysqli_constructor_get(zval *object TSRMLS_DC)
} else if (obj->zo.ce == mysqli_warning_class_entry) {
f.handler = ZEND_MN(mysqli_warning___construct);
}
-
+
return (union _zend_function*)&f;
}
}
@@ -382,8 +469,7 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_
intern->prop_handler = NULL;
mysqli_base_class = class_type;
- while (mysqli_base_class->type != ZEND_INTERNAL_CLASS && mysqli_base_class->parent != NULL)
- {
+ while (mysqli_base_class->type != ZEND_INTERNAL_CLASS && mysqli_base_class->parent != NULL) {
mysqli_base_class = mysqli_base_class->parent;
}
zend_hash_find(&classes, mysqli_base_class->name, mysqli_base_class->name_length + 1,
@@ -396,6 +482,8 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_
/* link object */
if (instanceof_function(class_type, mysqli_link_class_entry TSRMLS_CC)) {
free_storage = mysqli_link_free_storage;
+ } else if (instanceof_function(class_type, mysqli_driver_class_entry TSRMLS_CC)) { /* driver object */
+ free_storage = mysqli_driver_free_storage;
} else if (instanceof_function(class_type, mysqli_stmt_class_entry TSRMLS_CC)) { /* stmt object */
free_storage = mysqli_stmt_free_storage;
} else if (instanceof_function(class_type, mysqli_result_class_entry TSRMLS_CC)) { /* result object */
@@ -412,17 +500,21 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_
return retval;
}
/* }}} */
-
-/* {{{ mysqli_module_entry
- */
+
+
/* Dependancies */
-static const zend_module_dep mysqli_deps[] = {
+const static zend_module_dep mysqli_deps[] = {
#if defined(HAVE_SPL) && ((PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 1))
ZEND_MOD_REQUIRED("spl")
#endif
+#if defined(HAVE_MYSQLND)
+ ZEND_MOD_REQUIRED("mysqlnd")
+#endif
{NULL, NULL, NULL}
};
+/* {{{ mysqli_module_entry
+ */
zend_module_entry mysqli_module_entry = {
#if ZEND_MODULE_API_NO >= 20050922
STANDARD_MODULE_HEADER_EX, NULL,
@@ -454,22 +546,33 @@ ZEND_GET_MODULE(mysqli)
*/
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY_EX("mysqli.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_mysqli_globals, mysqli_globals, display_link_numbers)
+ STD_PHP_INI_ENTRY_EX("mysqli.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_mysqli_globals, mysqli_globals, display_link_numbers)
+ STD_PHP_INI_BOOLEAN("mysqli.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_persistent, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_ENTRY("mysqli.default_host", NULL, PHP_INI_ALL, OnUpdateString, default_host, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_ENTRY("mysqli.default_user", NULL, PHP_INI_ALL, OnUpdateString, default_user, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_ENTRY("mysqli.default_pw", NULL, PHP_INI_ALL, OnUpdateString, default_pw, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_ENTRY("mysqli.default_port", "3306", PHP_INI_ALL, OnUpdateLong, default_port, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_ENTRY("mysqli.default_socket", NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_socket, zend_mysqli_globals, mysqli_globals)
STD_PHP_INI_BOOLEAN("mysqli.reconnect", "0", PHP_INI_SYSTEM, OnUpdateLong, reconnect, zend_mysqli_globals, mysqli_globals)
+ STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals)
+#ifdef HAVE_MYSQLND
+ STD_PHP_INI_ENTRY("mysqli.cache_size", "2000", PHP_INI_SYSTEM, OnUpdateLong, cache_size, zend_mysqli_globals, mysqli_globals)
+#endif
PHP_INI_END()
-
/* }}} */
+
/* {{{ PHP_GINIT_FUNCTION
*/
static PHP_GINIT_FUNCTION(mysqli)
{
mysqli_globals->num_links = 0;
- mysqli_globals->max_links = 0;
+ mysqli_globals->num_active_persistent = 0;
+ mysqli_globals->num_inactive_persistent = 0;
+ mysqli_globals->max_links = -1;
+ mysqli_globals->max_links = -1;
+ mysqli_globals->max_persistent = -1;
+ mysqli_globals->allow_persistent = 1;
mysqli_globals->default_port = 0;
mysqli_globals->default_host = NULL;
mysqli_globals->default_user = NULL;
@@ -478,11 +581,16 @@ static PHP_GINIT_FUNCTION(mysqli)
mysqli_globals->reconnect = 0;
mysqli_globals->report_mode = 0;
mysqli_globals->report_ht = 0;
+ mysqli_globals->allow_local_infile = 1;
#ifdef HAVE_EMBEDDED_MYSQLI
mysqli_globals->embedded = 1;
#else
mysqli_globals->embedded = 0;
#endif
+#ifdef HAVE_MYSQLND
+ mysqli_globals->cache_size = 0;
+ mysqli_globals->mysqlnd_thd_zval_cache = NULL;
+#endif
}
/* }}} */
@@ -494,6 +602,16 @@ PHP_MINIT_FUNCTION(mysqli)
zend_object_handlers *std_hnd = zend_get_std_object_handlers();
REGISTER_INI_ENTRIES();
+#ifndef HAVE_MYSQLND
+#if MYSQL_VERSION_ID >= 40000
+ if (mysql_server_init(0, NULL, NULL)) {
+ return FAILURE;
+ }
+#endif
+#else
+ mysqli_mysqlnd_zval_cache = mysqlnd_palloc_init_cache(MyG(cache_size));
+ mysqli_mysqlnd_qcache = mysqlnd_qcache_init_cache();
+#endif
memcpy(&mysqli_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
mysqli_object_handlers.clone_obj = NULL;
@@ -504,6 +622,10 @@ PHP_MINIT_FUNCTION(mysqli)
zend_hash_init(&classes, 0, NULL, NULL, 1);
+ /* persistent connections */
+ le_pmysqli = zend_register_list_destructors_ex(NULL, php_mysqli_dtor,
+ "MySqli persistent connection", module_number);
+
INIT_CLASS_ENTRY(cex, "mysqli_sql_exception", mysqli_exception_methods);
#ifdef HAVE_SPL
mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException, NULL TSRMLS_CC);
@@ -552,6 +674,13 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_LONG_CONSTANT("MYSQLI_OPT_CONNECT_TIMEOUT", MYSQL_OPT_CONNECT_TIMEOUT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_OPT_LOCAL_INFILE", MYSQL_OPT_LOCAL_INFILE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_INIT_COMMAND", MYSQL_INIT_COMMAND, CONST_CS | CONST_PERSISTENT);
+#if defined(HAVE_MYSQLND)
+ REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_CMD_BUFFER_SIZE", MYSQLND_OPT_NET_CMD_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("MYSQLI_OPT_NET_READ_BUFFER_SIZE", MYSQLND_OPT_NET_READ_BUFFER_SIZE, CONST_CS | CONST_PERSISTENT);
+#endif
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ REGISTER_LONG_CONSTANT("MYSQLI_OPT_INT_AND_YEAR_AS_INT", MYSQLND_OPT_INT_AND_YEAR_AS_INT, CONST_CS | CONST_PERSISTENT);
+#endif
/* mysqli_real_connect flags */
REGISTER_LONG_CONSTANT("MYSQLI_CLIENT_SSL", CLIENT_SSL, CONST_CS | CONST_PERSISTENT);
@@ -573,7 +702,7 @@ PHP_MINIT_FUNCTION(mysqli)
/* for mysqli_stmt_set_attr */
REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH", STMT_ATTR_UPDATE_MAX_LENGTH, CONST_CS | CONST_PERSISTENT);
-#if MYSQL_VERSION_ID > 50003
+#if MYSQL_VERSION_ID > 50003 || defined(HAVE_MYSQLND)
REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_CURSOR_TYPE", STMT_ATTR_CURSOR_TYPE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_NO_CURSOR", CURSOR_TYPE_NO_CURSOR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_READ_ONLY", CURSOR_TYPE_READ_ONLY, CONST_CS | CONST_PERSISTENT);
@@ -581,7 +710,7 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_LONG_CONSTANT("MYSQLI_CURSOR_TYPE_SCROLLABLE", CURSOR_TYPE_SCROLLABLE, CONST_CS | CONST_PERSISTENT);
#endif
-#if MYSQL_VERSION_ID > 50007
+#if MYSQL_VERSION_ID > 50007 || defined(HAVE_MYSQLND)
REGISTER_LONG_CONSTANT("MYSQLI_STMT_ATTR_PREFETCH_ROWS", STMT_ATTR_PREFETCH_ROWS, CONST_CS | CONST_PERSISTENT);
#endif
@@ -627,17 +756,19 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_LONG_CONSTANT("MYSQLI_TYPE_INTERVAL", FIELD_TYPE_INTERVAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_TYPE_GEOMETRY", FIELD_TYPE_GEOMETRY, CONST_CS | CONST_PERSISTENT);
-#if MYSQL_VERSION_ID > 50002
+#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)
REGISTER_LONG_CONSTANT("MYSQLI_TYPE_NEWDECIMAL", FIELD_TYPE_NEWDECIMAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_TYPE_BIT", FIELD_TYPE_BIT, CONST_CS | CONST_PERSISTENT);
#endif
-
+ REGISTER_LONG_CONSTANT("MYSQLI_SET_CHARSET_NAME", MYSQL_SET_CHARSET_NAME, CONST_CS | CONST_PERSISTENT);
/* replication */
+#if !defined(HAVE_MYSQLND)
REGISTER_LONG_CONSTANT("MYSQLI_RPL_MASTER", MYSQL_RPL_MASTER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_RPL_SLAVE", MYSQL_RPL_SLAVE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_RPL_ADMIN", MYSQL_RPL_ADMIN, CONST_CS | CONST_PERSISTENT);
+#endif
/* bind support */
REGISTER_LONG_CONSTANT("MYSQLI_NO_DATA", MYSQL_NO_DATA, CONST_CS | CONST_PERSISTENT);
@@ -652,10 +783,6 @@ PHP_MINIT_FUNCTION(mysqli)
REGISTER_LONG_CONSTANT("MYSQLI_REPORT_ALL", MYSQLI_REPORT_ALL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MYSQLI_REPORT_OFF", 0, CONST_CS | CONST_PERSISTENT);
- if (mysql_server_init(0, NULL, NULL)) {
- return FAILURE;
- }
-
return SUCCESS;
}
/* }}} */
@@ -664,15 +791,25 @@ PHP_MINIT_FUNCTION(mysqli)
*/
PHP_MSHUTDOWN_FUNCTION(mysqli)
{
+#ifndef HAVE_MYSQLND
+#if MYSQL_VERSION_ID >= 40000
#ifdef PHP_WIN32
unsigned long client_ver = mysql_get_client_version();
- /* Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows */
- if ((client_ver > 50042 && client_ver < 50100) || client_ver > 50122) {
+ /*
+ Can't call mysql_server_end() multiple times prior to 5.0.42 on Windows.
+ PHP bug#41350 MySQL bug#25621
+ */
+ if ((client_ver >= 50042 && client_ver < 50100) || client_ver > 50122) {
mysql_server_end();
}
#else
mysql_server_end();
#endif
+#endif
+#else
+ mysqlnd_palloc_free_cache(mysqli_mysqlnd_zval_cache);
+ mysqlnd_qcache_free_cache_reference(&mysqli_mysqlnd_qcache);
+#endif
zend_hash_destroy(&mysqli_driver_properties);
zend_hash_destroy(&mysqli_result_properties);
@@ -690,13 +827,16 @@ PHP_MSHUTDOWN_FUNCTION(mysqli)
*/
PHP_RINIT_FUNCTION(mysqli)
{
-#ifdef ZTS
+#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000
if (mysql_thread_init()) {
return FAILURE;
}
#endif
MyG(error_msg) = NULL;
MyG(error_no) = 0;
+#ifdef HAVE_MYSQLND
+ MyG(mysqlnd_thd_zval_cache) = mysqlnd_palloc_rinit(mysqli_mysqlnd_zval_cache);
+#endif
return SUCCESS;
}
@@ -706,27 +846,55 @@ PHP_RINIT_FUNCTION(mysqli)
*/
PHP_RSHUTDOWN_FUNCTION(mysqli)
{
-#ifdef ZTS
+ /* check persistent connections, move used to free */
+ zend_hash_apply(&EG(persistent_list), (apply_func_t) php_mysqli_persistent_on_rshut TSRMLS_CC);
+
+#if !defined(HAVE_MYSQLND) && defined(ZTS) && MYSQL_VERSION_ID >= 40000
mysql_thread_end();
#endif
if (MyG(error_msg)) {
efree(MyG(error_msg));
}
+#ifdef HAVE_MYSQLND
+ mysqlnd_palloc_rshutdown(MyG(mysqlnd_thd_zval_cache));
+#endif
return SUCCESS;
}
/* }}} */
+
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(mysqli)
{
+ char buf[32];
+
php_info_print_table_start();
php_info_print_table_header(2, "MysqlI Support", "enabled");
php_info_print_table_row(2, "Client API library version", mysql_get_client_info());
+ snprintf(buf, sizeof(buf), "%ld", MyG(num_active_persistent));
+ php_info_print_table_row(2, "Active Persistent Links", buf);
+ snprintf(buf, sizeof(buf), "%ld", MyG(num_inactive_persistent));
+ php_info_print_table_row(2, "Inactive Persistent Links", buf);
+ snprintf(buf, sizeof(buf), "%ld", MyG(num_links));
+ php_info_print_table_row(2, "Active Links", buf);
+#if !defined(HAVE_MYSQLND)
php_info_print_table_row(2, "Client API header version", MYSQL_SERVER_VERSION);
php_info_print_table_row(2, "MYSQLI_SOCKET", MYSQL_UNIX_ADDR);
-
-
+#else
+ {
+ zval values;
+
+ php_info_print_table_header(2, "Persistent cache", mysqli_mysqlnd_zval_cache? "enabled":"disabled");
+
+ if (mysqli_mysqlnd_zval_cache) {
+ /* Now report cache status */
+ mysqlnd_palloc_stats(mysqli_mysqlnd_zval_cache, &values);
+ mysqlnd_minfo_print_hash(&values);
+ zval_dtor(&values);
+ }
+ }
+#endif
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
@@ -742,16 +910,16 @@ Parameters:
ZEND_FUNCTION(mysqli_stmt_construct)
{
MY_MYSQL *mysql;
- zval *mysql_link;
+ zval *mysql_link;
MY_STMT *stmt;
- MYSQLI_RESOURCE *mysqli_resource;
+ MYSQLI_RESOURCE *mysqli_resource;
char *statement;
- int stmt_len;
+ int statement_len;
switch (ZEND_NUM_ARGS())
{
case 1: /* mysql_stmt_init */
- if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {
+ if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
@@ -761,15 +929,15 @@ ZEND_FUNCTION(mysqli_stmt_construct)
stmt->stmt = mysql_stmt_init(mysql->mysql);
break;
case 2:
- if (zend_parse_parameters(2 TSRMLS_CC, "Os", &mysql_link, mysqli_link_class_entry, &statement, &stmt_len)==FAILURE) {
+ if (zend_parse_parameters(2 TSRMLS_CC, "Os", &mysql_link, mysqli_link_class_entry, &statement, &statement_len)==FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
stmt = (MY_STMT *)ecalloc(1,sizeof(MY_STMT));
-
+
if ((stmt->stmt = mysql_stmt_init(mysql->mysql))) {
- mysql_stmt_prepare(stmt->stmt, statement, stmt_len);
+ mysql_stmt_prepare(stmt->stmt, statement, statement_len);
}
break;
default:
@@ -800,20 +968,24 @@ ZEND_FUNCTION(mysqli_result_construct)
MY_MYSQL *mysql;
MYSQL_RES *result;
zval *mysql_link;
- MYSQLI_RESOURCE *mysqli_resource;
+ MYSQLI_RESOURCE *mysqli_resource;
long resmode = MYSQLI_STORE_RESULT;
switch (ZEND_NUM_ARGS()) {
case 1:
- if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {
+ if (zend_parse_parameters(1 TSRMLS_CC, "O", &mysql_link, mysqli_link_class_entry)==FAILURE) {
return;
}
- break;
+ break;
case 2:
- if (zend_parse_parameters(2 TSRMLS_CC, "Ol", &mysql_link, mysqli_link_class_entry, &resmode)==FAILURE) {
+ if (zend_parse_parameters(2 TSRMLS_CC, "Ol", &mysql_link, mysqli_link_class_entry, &resmode)==FAILURE) {
return;
}
- break;
+ if (resmode != MYSQLI_USE_RESULT && resmode != MYSQLI_STORE_RESULT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
+ RETURN_FALSE;
+ }
+ break;
default:
WRONG_PARAM_COUNT;
}
@@ -830,7 +1002,7 @@ ZEND_FUNCTION(mysqli_result_construct)
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)result;
mysqli_resource->status = MYSQLI_STATUS_VALID;
-
+
((mysqli_object *) zend_object_store_get_object(getThis() TSRMLS_CC))->ptr = mysqli_resource;
}
@@ -843,12 +1015,14 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
MYSQL_RES *result;
zval *mysql_result;
long fetchtype;
+ zval *ctor_params = NULL;
+ zend_class_entry *ce = NULL;
+#if !defined(HAVE_MYSQLND)
unsigned int i;
MYSQL_FIELD *fields;
MYSQL_ROW row;
unsigned long *field_len;
- zval *ctor_params = NULL;
- zend_class_entry *ce = NULL;
+#endif
if (into_object) {
char *class_name;
@@ -882,11 +1056,12 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
}
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
- if ((fetchtype & MYSQLI_BOTH) == 0) {
+ if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH");
RETURN_FALSE;
}
+#if !defined(HAVE_MYSQLND)
if (!(row = mysql_fetch_row(result))) {
RETURN_NULL();
}
@@ -930,16 +1105,19 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
}
}
}
+#else
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQLI);
+#endif
- if (into_object) {
+ if (into_object && Z_TYPE_P(return_value) != IS_NULL) {
zval dataset = *return_value;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zval *retval_ptr;
-
+
object_and_properties_init(return_value, ce, NULL);
zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC);
-
+
if (ce->constructor) {
fci.size = sizeof(fci);
fci.function_table = &ce->function_table;
@@ -951,7 +1129,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
if (Z_TYPE_P(ctor_params) == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(ctor_params);
Bucket *p;
-
+
fci.param_count = 0;
fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);
p = ht->pListHead;
@@ -979,7 +1157,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
fcc.function_handler = ce->constructor;
fcc.calling_scope = EG(scope);
fcc.object_pp = &return_value;
-
+
if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
} else {
@@ -1009,6 +1187,8 @@ PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRML
}
/* }}} */
+#if !defined(HAVE_MYSQLND)
+
#define ALLOC_CALLBACK_ARGS(a, b, c)\
if (c) {\
a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\
@@ -1029,7 +1209,7 @@ if (a) {\
#define LOCAL_INFILE_ERROR_MSG(source,dest)\
memset(source, 0, LOCAL_INFILE_ERROR_LEN);\
-memcpy(source, dest, LOCAL_INFILE_ERROR_LEN-1);
+memcpy(source, dest, MIN(strlen(dest), LOCAL_INFILE_ERROR_LEN-1));
/* {{{ void php_set_local_infile_handler_default
*/
@@ -1037,7 +1217,10 @@ void php_set_local_infile_handler_default(MY_MYSQL *mysql) {
/* register internal callback functions */
mysql_set_local_infile_handler(mysql->mysql, &php_local_infile_init, &php_local_infile_read,
&php_local_infile_end, &php_local_infile_error, (void *)mysql);
- mysql->li_read = NULL;
+ if (mysql->li_read) {
+ zval_ptr_dtor(&mysql->li_read);
+ mysql->li_read = NULL;
+ }
}
/* }}} */
@@ -1047,7 +1230,7 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)
{
mysqli_local_infile *data;
MY_MYSQL *mysql;
- php_stream_context *context = NULL;
+ php_stream_context *context = NULL;
TSRMLS_FETCH();
@@ -1086,7 +1269,7 @@ int php_local_infile_init(void **ptr, const char *filename, void *userdata)
int php_local_infile_read(void *ptr, char *buf, uint buf_len)
{
mysqli_local_infile *data;
- MY_MYSQL *mysql;
+ MY_MYSQL *mysql;
zval ***callback_args;
zval *retval;
zval *fp;
@@ -1101,9 +1284,7 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)
/* default processing */
if (!mysql->li_read) {
- int count;
-
- count = (int)php_stream_read(mysql->li_stream, buf, buf_len);
+ int count = (int)php_stream_read(mysql->li_stream, buf, buf_len);
if (count < 0) {
LOCAL_INFILE_ERROR_MSG(data->error_msg, ER(2));
@@ -1113,21 +1294,21 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)
}
ALLOC_CALLBACK_ARGS(callback_args, 1, argc);
-
+
/* set parameters: filepointer, buffer, buffer_len, errormsg */
MAKE_STD_ZVAL(fp);
php_stream_to_zval(mysql->li_stream, fp);
callback_args[0] = &fp;
- ZVAL_STRING(*callback_args[1], "", 1);
- ZVAL_LONG(*callback_args[2], buf_len);
- ZVAL_STRING(*callback_args[3], "", 1);
+ ZVAL_STRING(*callback_args[1], "", 1);
+ ZVAL_LONG(*callback_args[2], buf_len);
+ ZVAL_STRING(*callback_args[3], "", 1);
if (call_user_function_ex(EG(function_table),
NULL,
mysql->li_read,
&retval,
- argc,
+ argc,
callback_args,
0,
NULL TSRMLS_CC) == SUCCESS) {
@@ -1136,22 +1317,36 @@ int php_local_infile_read(void *ptr, char *buf, uint buf_len)
zval_ptr_dtor(&retval);
if (rc > 0) {
- if (rc > buf_len) {
+ if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) {
+ LOCAL_INFILE_ERROR_MSG(data->error_msg,
+ "Mismatch between the return value of the callback and the content "
+ "length of the buffer.");
+ rc = -1;
+ } else if (rc > buf_len) {
/* check buffer overflow */
- LOCAL_INFILE_ERROR_MSG(data->error_msg, "Read buffer too large");
+ LOCAL_INFILE_ERROR_MSG(data->error_msg, "Too much data returned");
rc = -1;
} else {
- memcpy(buf, Z_STRVAL_P(*callback_args[1]), rc);
+ memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1])));
}
- }
- if (rc < 0) {
+ } else if (rc < 0) {
LOCAL_INFILE_ERROR_MSG(data->error_msg, Z_STRVAL_P(*callback_args[3]));
}
} else {
LOCAL_INFILE_ERROR_MSG(data->error_msg, "Can't execute load data local init callback function");
rc = -1;
}
-
+ /*
+ If the (ab)user has closed the file handle we should
+ not try to use it anymore or even close it
+ */
+ if (!zend_rsrc_list_get_rsrc_type(Z_LVAL_P(fp) TSRMLS_CC)) {
+ LOCAL_INFILE_ERROR_MSG(data->error_msg, "File handle closed");
+ rc = -1;
+ /* Thus the end handler won't try to free already freed memory */
+ mysql->li_stream = NULL;
+ }
+
FREE_CALLBACK_ARGS(callback_args, 1, argc);
efree(fp);
return rc;
@@ -1167,7 +1362,7 @@ int php_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)
if (data) {
strlcpy(error_msg, data->error_msg, error_msg_len);
return 2000;
- }
+ }
strlcpy(error_msg, ER(CR_OUT_OF_MEMORY), error_msg_len);
return CR_OUT_OF_MEMORY;
}
@@ -1175,10 +1370,10 @@ int php_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)
/* {{{ php_local_infile_end
*/
-void php_local_infile_end(void *ptr)
+void php_local_infile_end(void *ptr)
{
- mysqli_local_infile *data;
- MY_MYSQL *mysql;
+ mysqli_local_infile *data;
+ MY_MYSQL *mysql;
TSRMLS_FETCH();
@@ -1193,9 +1388,10 @@ void php_local_infile_end(void *ptr)
php_stream_close(mysql->li_stream);
free(data);
- return;
+ return;
}
/* }}} */
+#endif
/*
* Local variables:
diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c
index fd01ba9ffa..5e55be7d51 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -12,7 +12,9 @@
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
- | Author: Georg Richter <georg@php.net> |
+ | Authors: Georg Richter <georg@php.net> |
+ | Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+----------------------------------------------------------------------+
$Id$
@@ -26,8 +28,9 @@
#include "php.h"
#include "php_ini.h"
+#include "php_globals.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
/* {{{ proto mixed mysqli_affected_rows(object link)
Get number of affected rows in previous MySQL operation */
@@ -51,16 +54,17 @@ PHP_FUNCTION(mysqli_affected_rows)
}
/* }}} */
+
/* {{{ proto bool mysqli_autocommit(object link, bool mode)
Turn auto commit on or of */
PHP_FUNCTION(mysqli_autocommit)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
- zend_bool automode;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
+ zend_bool automode;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ob", &mysql_link, mysqli_link_class_entry, &automode) == FAILURE) {
- return;
+ return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
@@ -71,83 +75,38 @@ PHP_FUNCTION(mysqli_autocommit)
}
/* }}} */
-/* {{{ proto bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....])
- Bind variables to a prepared statement as parameters */
-PHP_FUNCTION(mysqli_stmt_bind_param)
+/* {{{ mysqli_stmt_bind_param_do_bind */
+#ifndef HAVE_MYSQLND
+static
+int mysqli_stmt_bind_param_do_bind(MY_STMT *stmt, unsigned int argc, unsigned int num_vars,
+ zval ***args, unsigned int start, const char * const types TSRMLS_DC)
{
- zval ***args;
- int argc = ZEND_NUM_ARGS();
- int i;
- int num_vars;
- int start = 2;
- int ofs;
- MY_STMT *stmt;
- zval *mysql_stmt;
+ int i, ofs;
MYSQL_BIND *bind;
- char *types;
- int typelen;
unsigned long rc;
- /* calculate and check number of parameters */
- if (argc < 2) {
- /* there has to be at least one pair */
- WRONG_PARAM_COUNT;
- }
-
- if (zend_parse_method_parameters((getThis()) ? 1:2 TSRMLS_CC, getThis(), "Os", &mysql_stmt, mysqli_stmt_class_entry, &types, &typelen) == FAILURE) {
- return;
- }
-
- MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
-
- num_vars = argc - 1;
- if (getThis()) {
- start = 1;
- } else {
- /* ignore handle parameter in procedural interface*/
- --num_vars;
- }
-
- if (typelen != argc - start) {
- /* number of bind variables doesn't match number of elements in type definition string */
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements in type definition string doesn't match number of bind variables");
- RETURN_FALSE;
- }
-
- if (typelen != stmt->stmt->param_count) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of variables doesn't match number of parameters in prepared statement");
- RETURN_FALSE;
- }
-
/* prevent leak if variables are already bound */
if (stmt->param.var_cnt) {
php_free_stmt_bind_buffer(stmt->param, FETCH_SIMPLE);
}
- args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
-
- if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
- efree(args);
- WRONG_PARAM_COUNT;
- }
-
stmt->param.is_null = ecalloc(num_vars, sizeof(char));
- bind = (MYSQL_BIND *)ecalloc(num_vars, sizeof(MYSQL_BIND));
+ bind = (MYSQL_BIND *) ecalloc(num_vars, sizeof(MYSQL_BIND));
ofs = 0;
- for (i=start; i < argc; i++) {
+ for (i = start; i < argc; i++) {
/* set specified type */
switch (types[ofs]) {
case 'd': /* Double */
bind[ofs].buffer_type = MYSQL_TYPE_DOUBLE;
- bind[ofs].buffer = (char*)&Z_DVAL_PP(args[i]);
+ bind[ofs].buffer = &Z_DVAL_PP(args[i]);
bind[ofs].is_null = &stmt->param.is_null[ofs];
break;
case 'i': /* Integer */
- bind[ofs].buffer_type = MYSQL_TYPE_LONG;
- bind[ofs].buffer = (char*)&Z_LVAL_PP(args[i]);
+ bind[ofs].buffer_type = (sizeof(long) > 4) ? MYSQL_TYPE_LONGLONG : MYSQL_TYPE_LONG;
+ bind[ofs].buffer = &Z_LVAL_PP(args[i]);
bind[ofs].is_null = &stmt->param.is_null[ofs];
break;
@@ -164,85 +123,162 @@ PHP_FUNCTION(mysqli_stmt_bind_param)
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Undefined fieldtype %c (parameter %d)", types[ofs], i+1);
- RETVAL_FALSE;
- goto end;
+ rc = 1;
+ goto end_1;
}
ofs++;
}
rc = mysql_stmt_bind_param(stmt->stmt, bind);
- MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
+end_1:
if (rc) {
- RETVAL_FALSE;
- goto end;
+ efree(stmt->param.is_null);
+ } else {
+ stmt->param.var_cnt = num_vars;
+ stmt->param.vars = (zval **)safe_emalloc(num_vars, sizeof(zval), 0);
+ for (i = 0; i < num_vars; i++) {
+ if (bind[i].buffer_type != MYSQL_TYPE_LONG_BLOB) {
+ ZVAL_ADDREF(*args[i+start]);
+ stmt->param.vars[i] = *args[i+start];
+ } else {
+ stmt->param.vars[i] = NULL;
+ }
+ }
}
+ efree(bind);
- stmt->param.var_cnt = num_vars;
- stmt->param.vars = (zval **)safe_emalloc(num_vars, sizeof(zval), 0);
- for (i = 0; i < num_vars; i++) {
- if (bind[i].buffer_type != MYSQL_TYPE_LONG_BLOB) {
- ZVAL_ADDREF(*args[i+start]);
- stmt->param.vars[i] = *args[i+start];
- } else {
- stmt->param.vars[i] = NULL;
+ return rc;
+}
+#else
+static
+int mysqli_stmt_bind_param_do_bind(MY_STMT *stmt, unsigned int argc, unsigned int num_vars,
+ zval ***args, unsigned int start, const char * const types TSRMLS_DC)
+{
+ int i;
+ MYSQLND_PARAM_BIND *params;
+ enum_func_status ret = FAIL;
+
+ /* If no params -> skip binding and return directly */
+ if (argc == start) {
+ return PASS;
+ }
+ params = emalloc((argc - start) * sizeof(MYSQLND_PARAM_BIND));
+ for (i = 0; i < (argc - start); i++) {
+ zend_uchar type;
+ switch (types[i]) {
+ case 'd': /* Double */
+ type = MYSQL_TYPE_DOUBLE;
+ break;
+ case 'i': /* Integer */
+#if SIZEOF_LONG==8
+ type = MYSQL_TYPE_LONGLONG;
+#elif SIZEOF_LONG==4
+ type = MYSQL_TYPE_LONG;
+#endif
+ break;
+ case 'b': /* Blob (send data) */
+ type = MYSQL_TYPE_LONG_BLOB;
+ break;
+ case 's': /* string */
+ type = MYSQL_TYPE_VAR_STRING;
+ break;
+ default:
+ /* We count parameters from 1 */
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Undefined fieldtype %c (parameter %d)", types[i], i + start + 1);
+ ret = FAIL;
+ efree(params);
+ goto end;
}
+ params[i].zv = *(args[i + start]);
+ params[i].type = type;
}
- RETVAL_TRUE;
+ ret = mysqlnd_stmt_bind_param(stmt->stmt, params);
+
end:
- efree(args);
- efree(bind);
+ return ret;
}
+#endif
/* }}} */
-/* {{{ proto bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...])
- Bind variables to a prepared statement for result storage */
-
-/* TODO:
- do_alloca, free_alloca
-*/
-
-PHP_FUNCTION(mysqli_stmt_bind_result)
+/* {{{ proto bool mysqli_stmt_bind_param(object stmt, string types, mixed variable [,mixed,....]) U
+ Bind variables to a prepared statement as parameters */
+PHP_FUNCTION(mysqli_stmt_bind_param)
{
- zval ***args;
- int argc = ZEND_NUM_ARGS();
- int i;
- int start = 1;
- int var_cnt;
- int ofs;
- long col_type;
- ulong rc;
- MY_STMT *stmt;
- zval *mysql_stmt;
- MYSQL_BIND *bind;
+ zval ***args;
+ int argc = ZEND_NUM_ARGS();
+ int num_vars;
+ int start = 2;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ char *types;
+ int types_len;
+ unsigned long rc;
- if (getThis()) {
- start = 0;
+ /* calculate and check number of parameters */
+ if (argc < 2) {
+ /* there has to be at least one pair */
+ WRONG_PARAM_COUNT;
}
- if (zend_parse_method_parameters((getThis()) ? 0:1 TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
+ if (zend_parse_method_parameters((getThis()) ? 1:2 TSRMLS_CC, getThis(), "Os", &mysql_stmt, mysqli_stmt_class_entry,
+ &types, &types_len) == FAILURE) {
return;
}
- MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
- if (argc < (getThis() ? 1 : 2)) {
- WRONG_PARAM_COUNT;
+ num_vars = argc - 1;
+ if (getThis()) {
+ start = 1;
+ } else {
+ /* ignore handle parameter in procedural interface*/
+ --num_vars;
+ }
+ if (!types_len) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type or no types specified");
+ RETURN_FALSE;
+ }
+
+ if (types_len != argc - start) {
+ /* number of bind variables doesn't match number of elements in type definition string */
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements in type definition string doesn't match number of bind variables");
+ RETURN_FALSE;
+ }
+
+ if (types_len != mysql_stmt_param_count(stmt->stmt)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of variables doesn't match number of parameters in prepared statement");
+ RETURN_FALSE;
}
args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
- efree(args);
- WRONG_PARAM_COUNT;
+ zend_wrong_param_count(TSRMLS_C);
+ rc = 1;
+ } else {
+ rc = mysqli_stmt_bind_param_do_bind(stmt, argc, num_vars, args, start, types TSRMLS_CC);
+ MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
}
- var_cnt = argc - start;
+ efree(args);
- if (var_cnt != mysql_stmt_field_count(stmt->stmt)) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of bind variables doesn't match number of fields in prepared statement");
- efree(args);
- RETURN_FALSE;
- }
+ RETURN_BOOL(!rc);
+}
+/* }}} */
+
+/* {{{ mysqli_stmt_bind_result_do_bind */
+#ifndef HAVE_MYSQLND
+/* TODO:
+ do_alloca, free_alloca
+*/
+static int
+mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc, unsigned int start TSRMLS_DC)
+{
+ MYSQL_BIND *bind;
+ int i, ofs;
+ int var_cnt = argc - start;
+ long col_type;
+ ulong rc;
/* prevent leak if variables are already bound */
if (stmt->result.var_cnt) {
@@ -268,7 +304,7 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
convert_to_double_ex(args[i]);
stmt->result.buf[ofs].type = IS_DOUBLE;
stmt->result.buf[ofs].buflen = sizeof(double);
-
+
/* allocate buffer for double */
stmt->result.buf[ofs].val = (char *)emalloc(sizeof(double));
bind[ofs].buffer_type = MYSQL_TYPE_DOUBLE;
@@ -305,7 +341,7 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
break;
case MYSQL_TYPE_LONGLONG:
-#if MYSQL_VERSION_ID > 50002
+#if MYSQL_VERSION_ID > 50002 || defined(HAVE_MYSQLND)
case MYSQL_TYPE_BIT:
#endif
stmt->result.buf[ofs].type = IS_STRING;
@@ -324,8 +360,8 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_TIMESTAMP:
@@ -385,7 +421,6 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
}
/* Don't free stmt->result.is_null because is_null & buf are one block of memory */
efree(stmt->result.buf);
- RETVAL_FALSE;
} else {
stmt->result.var_cnt = var_cnt;
stmt->result.vars = (zval **)safe_emalloc((var_cnt), sizeof(zval), 0);
@@ -394,10 +429,69 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
ZVAL_ADDREF(*args[i]);
stmt->result.vars[ofs] = *args[i];
}
- RETVAL_TRUE;
}
- efree(args);
efree(bind);
+
+ return rc;
+}
+#else
+static int
+mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc, unsigned int start TSRMLS_DC)
+{
+ unsigned int i;
+ MYSQLND_RESULT_BIND *params;
+
+ params = emalloc((argc - start) * sizeof(MYSQLND_RESULT_BIND));
+ for (i = 0; i < (argc - start); i++) {
+ params[i].zv = *(args[i + start]);
+ }
+ return mysqlnd_stmt_bind_result(stmt->stmt, params);
+}
+#endif
+/* }}} */
+
+/* {{{ proto bool mysqli_stmt_bind_result(object stmt, mixed var, [,mixed, ...]) U
+ Bind variables to a prepared statement for result storage */
+PHP_FUNCTION(mysqli_stmt_bind_result)
+{
+ zval ***args;
+ int argc = ZEND_NUM_ARGS();
+ int start = 1;
+ ulong rc;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+
+ if (getThis()) {
+ start = 0;
+ }
+
+ if (zend_parse_method_parameters((getThis()) ? 0:1 TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
+ return;
+ }
+
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+
+ if (argc < (getThis() ? 1 : 2)) {
+ WRONG_PARAM_COUNT;
+ }
+
+ if ((argc - start) != mysql_stmt_field_count(stmt->stmt)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of bind variables doesn't match number of fields in prepared statement");
+ RETURN_FALSE;
+ }
+
+ args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
+
+ if (zend_get_parameters_array_ex(argc, args) == FAILURE) {
+ efree(args);
+ WRONG_PARAM_COUNT;
+ }
+
+ rc = mysqli_stmt_bind_result_do_bind(stmt, args, argc, start TSRMLS_CC);
+
+ efree(args);
+
+ RETURN_BOOL(!rc);
}
/* }}} */
@@ -406,9 +500,9 @@ PHP_FUNCTION(mysqli_stmt_bind_result)
PHP_FUNCTION(mysqli_change_user)
{
MY_MYSQL *mysql;
- zval *mysql_link = NULL;
- char *user, *password, *dbname;
- int user_len, password_len, dbname_len;
+ zval *mysql_link = NULL;
+ char *user, *password, *dbname;
+ int user_len, password_len, dbname_len;
ulong rc;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osss", &mysql_link, mysqli_link_class_entry, &user, &user_len, &password, &password_len, &dbname, &dbname_len) == FAILURE) {
@@ -431,8 +525,8 @@ PHP_FUNCTION(mysqli_change_user)
Returns the name of the character set used for this connection */
PHP_FUNCTION(mysqli_character_set_name)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -440,7 +534,7 @@ PHP_FUNCTION(mysqli_character_set_name)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- RETURN_STRING((char *) mysql_character_set_name(mysql->mysql), 1);
+ RETURN_STRING((char *)mysql_character_set_name(mysql->mysql), 1);
}
/* }}} */
@@ -448,8 +542,8 @@ PHP_FUNCTION(mysqli_character_set_name)
Close connection */
PHP_FUNCTION(mysqli_close)
{
- zval *mysql_link;
- MY_MYSQL *mysql;
+ zval *mysql_link;
+ MY_MYSQL *mysql;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -457,11 +551,32 @@ PHP_FUNCTION(mysqli_close)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED);
- mysql_close(mysql->mysql);
- mysql->mysql = NULL;
+ if (!mysql->persistent) {
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT);
+ mysql->mysql = NULL;
+ } else {
+ zend_rsrc_list_entry *le;
+ if (zend_hash_find(&EG(persistent_list), mysql->hash_key, strlen(mysql->hash_key) + 1, (void **)&le) == SUCCESS) {
+ if (Z_TYPE_P(le) == php_le_pmysqli()) {
+ mysqli_plist_entry *plist = (mysqli_plist_entry *) le->ptr;
+ dtor_func_t pDestructor = plist->used_links.pDestructor;
+
+ plist->used_links.pDestructor = NULL; /* Don't call pDestructor now */
+ zend_hash_index_del(&plist->used_links, mysql->hash_index);
+ plist->used_links.pDestructor = pDestructor; /* Restore the destructor */
+
+ zend_hash_next_index_insert(&plist->free_links, &mysql->mysql, sizeof(MYSQL *), NULL);
+ MyG(num_links)--;
+ MyG(num_active_persistent)--;
+ MyG(num_inactive_persistent)++;
+ }
+ }
+ }
+
php_clear_mysql(mysql);
+
+ MYSQLI_CLEAR_RESOURCE(&mysql_link);
efree(mysql);
- MYSQLI_CLEAR_RESOURCE(&mysql_link);
RETURN_TRUE;
}
/* }}} */
@@ -470,8 +585,8 @@ PHP_FUNCTION(mysqli_close)
Commit outstanding actions and close transaction */
PHP_FUNCTION(mysqli_commit)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -489,8 +604,8 @@ PHP_FUNCTION(mysqli_commit)
PHP_FUNCTION(mysqli_data_seek)
{
MYSQL_RES *result;
- zval *mysql_result;
- long offset;
+ zval *mysql_result;
+ long offset;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_result, mysqli_result_class_entry, &offset) == FAILURE) {
return;
@@ -498,13 +613,13 @@ PHP_FUNCTION(mysqli_data_seek)
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
- if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) {
+ if (mysqli_result_is_unbuffered(result)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function cannot be used with MYSQL_USE_RESULT");
RETURN_FALSE;
}
- if (offset < 0 || offset >= result->row_count) {
- RETURN_FALSE;
+ if (offset < 0 || offset >= mysql_num_rows(result)) {
+ RETURN_FALSE;
}
mysql_data_seek(result, offset);
@@ -512,12 +627,12 @@ PHP_FUNCTION(mysqli_data_seek)
}
/* }}} */
-/* {{{ proto void mysqli_debug(string debug)
+/* {{{ proto void mysqli_debug(string debug) U
*/
PHP_FUNCTION(mysqli_debug)
{
- char *debug;
- int debug_len;
+ char *debug;
+ int debug_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &debug, &debug_len) == FAILURE) {
return;
@@ -528,25 +643,20 @@ PHP_FUNCTION(mysqli_debug)
}
/* }}} */
+
/* {{{ proto bool mysqli_dump_debug_info(object link)
*/
PHP_FUNCTION(mysqli_dump_debug_info)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
- ulong rc;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- rc = mysql_dump_debug_info(mysql->mysql);
-
- if (rc) {
- RETURN_FALSE;
- }
- RETURN_TRUE;
+ RETURN_BOOL(!mysql_dump_debug_info(mysql->mysql))
}
/* }}} */
@@ -554,8 +664,8 @@ PHP_FUNCTION(mysqli_dump_debug_info)
Returns the numerical value of the error message from previous MySQL operation */
PHP_FUNCTION(mysqli_errno)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -569,8 +679,8 @@ PHP_FUNCTION(mysqli_errno)
Returns the text of the error message from previous MySQL operation */
PHP_FUNCTION(mysqli_error)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -584,16 +694,19 @@ PHP_FUNCTION(mysqli_error)
Execute a prepared statement */
PHP_FUNCTION(mysqli_stmt_execute)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
- unsigned int i;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+#ifndef HAVE_MYSQLND
+ unsigned int i;
+#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
-
- for (i = 0; i < stmt->param.var_cnt; i++) {
+
+#ifndef HAVE_MYSQLND
+ for (i = 0; i < stmt->param.var_cnt; i++) {
if (stmt->param.vars[i]) {
if ( !(stmt->param.is_null[i] = (stmt->param.vars[i]->type == IS_NULL)) ) {
switch (stmt->stmt->params[i].buffer_type) {
@@ -604,40 +717,43 @@ PHP_FUNCTION(mysqli_stmt_execute)
break;
case MYSQL_TYPE_DOUBLE:
convert_to_double_ex(&stmt->param.vars[i]);
- stmt->stmt->params[i].buffer = (char*)&Z_LVAL_PP(&stmt->param.vars[i]);
+ stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);
break;
case MYSQL_TYPE_LONG:
convert_to_long_ex(&stmt->param.vars[i]);
- stmt->stmt->params[i].buffer = (char*)&Z_LVAL_PP(&stmt->param.vars[i]);
+ stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);
break;
default:
break;
}
- }
+ }
}
}
+#endif
+
if (mysql_stmt_execute(stmt->stmt)) {
MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
- RETURN_FALSE;
+ RETVAL_FALSE;
+ } else {
+ RETVAL_TRUE;
}
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index(stmt->query, stmt->stmt->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index(stmt->query, mysqli_stmt_server_status(stmt->stmt) TSRMLS_CC);
}
-
- RETURN_TRUE;
}
/* }}} */
-/* {{{ proto mixed mysqli_stmt_fetch(object stmt)
+#ifndef HAVE_MYSQLND
+/* {{{ void mysqli_stmt_fetch_libmysql
Fetch results from a prepared statement into the bound variables */
-PHP_FUNCTION(mysqli_stmt_fetch)
+void mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAMETERS)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
- unsigned int i;
- ulong ret;
- unsigned int uval;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ unsigned int i;
+ ulong ret;
+ unsigned int uval;
my_ulonglong llval;
@@ -647,8 +763,6 @@ PHP_FUNCTION(mysqli_stmt_fetch)
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
/* reset buffers */
-
-
for (i = 0; i < stmt->result.var_cnt; i++) {
if (stmt->result.buf[i].type == IS_STRING) {
memset(stmt->result.buf[i].val, 0, stmt->result.buf[i].buflen);
@@ -661,6 +775,11 @@ PHP_FUNCTION(mysqli_stmt_fetch)
if (!ret) {
#endif
for (i = 0; i < stmt->result.var_cnt; i++) {
+ /*
+ QQ: Isn't it quite better to call zval_dtor(). What if the user has
+ assigned a resource, or an array to the bound variable? We are going
+ to leak probably. zval_dtor() will handle also Unicode/Non-unicode mode.
+ */
/* Even if the string is of length zero there is one byte alloced so efree() in all cases */
if (Z_TYPE_P(stmt->result.vars[i]) == IS_STRING) {
efree(stmt->result.vars[i]->value.str.val);
@@ -669,11 +788,11 @@ PHP_FUNCTION(mysqli_stmt_fetch)
switch (stmt->result.buf[i].type) {
case IS_LONG:
if ((stmt->stmt->fields[i].type == MYSQL_TYPE_LONG)
- && (stmt->stmt->fields[i].flags & UNSIGNED_FLAG))
+ && (stmt->stmt->fields[i].flags & UNSIGNED_FLAG))
{
/* unsigned int (11) */
uval= *(unsigned int *) stmt->result.buf[i].val;
-
+#if SIZEOF_LONG==4
if (uval > INT_MAX) {
char *tmp, *p;
int j=10;
@@ -681,13 +800,14 @@ PHP_FUNCTION(mysqli_stmt_fetch)
p= &tmp[9];
do {
*p-- = (uval % 10) + 48;
- uval = uval / 10;
+ uval = uval / 10;
} while (--j > 0);
tmp[10]= '\0';
- /* unsigned int > INT_MAX is 10 digis - ALWAYS */
+ /* unsigned int > INT_MAX is 10 digits - ALWAYS */
ZVAL_STRINGL(stmt->result.vars[i], tmp, 10, 0);
break;
}
+#endif
}
if (stmt->stmt->fields[i].flags & UNSIGNED_FLAG) {
ZVAL_LONG(stmt->result.vars[i], *(unsigned int *)stmt->result.buf[i].val);
@@ -702,11 +822,12 @@ PHP_FUNCTION(mysqli_stmt_fetch)
if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_LONGLONG) {
my_bool uns= (stmt->stmt->fields[i].flags & UNSIGNED_FLAG)? 1:0;
llval= *(my_ulonglong *) stmt->result.buf[i].val;
-#if SIZEOF_LONG==8
+#if SIZEOF_LONG==8
if (uns && llval > 9223372036854775807L) {
#elif SIZEOF_LONG==4
if ((uns && llval > L64(2147483647)) ||
- (!uns && (( L64(2147483647) < (my_longlong) llval) || (L64(-2147483648) > (my_longlong) llval))))
+ (!uns && (( L64(2147483647) < (my_longlong) llval) ||
+ (L64(-2147483648) > (my_longlong) llval))))
{
#endif
char tmp[22];
@@ -719,7 +840,7 @@ PHP_FUNCTION(mysqli_stmt_fetch)
} else {
ZVAL_LONG(stmt->result.vars[i], llval);
}
- }
+ }
#if MYSQL_VERSION_ID > 50002
else if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_BIT) {
llval = *(my_ulonglong *)stmt->result.buf[i].val;
@@ -728,19 +849,21 @@ PHP_FUNCTION(mysqli_stmt_fetch)
#endif
else {
#if defined(MYSQL_DATA_TRUNCATED) && MYSQL_VERSION_ID > 50002
- if(ret == MYSQL_DATA_TRUNCATED && *(stmt->stmt->bind[i].error) != 0) {
+ if (ret == MYSQL_DATA_TRUNCATED && *(stmt->stmt->bind[i].error) != 0) {
/* result was truncated */
- ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->stmt->bind[i].buffer_length, 1);
+ ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val,
+ stmt->stmt->bind[i].buffer_length, 1);
} else {
#else
{
#endif
- ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val, stmt->result.buf[i].buflen, 1);
+ ZVAL_STRINGL(stmt->result.vars[i], stmt->result.buf[i].val,
+ stmt->result.buf[i].buflen, 1);
}
}
break;
default:
- break;
+ break;
}
} else {
ZVAL_NULL(stmt->result.vars[i]);
@@ -770,14 +893,68 @@ PHP_FUNCTION(mysqli_stmt_fetch)
}
}
/* }}} */
+#else
+/* {{{ mixed mysqli_stmt_fetch_mysqlnd */
+void mysqli_stmt_fetch_mysqlnd(INTERNAL_FUNCTION_PARAMETERS)
+{
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ zend_bool fetched_anything;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+
+ if (FAIL == mysqlnd_stmt_fetch(stmt->stmt, &fetched_anything)) {
+ RETURN_BOOL(FALSE);
+ } else if (fetched_anything == TRUE) {
+ RETURN_BOOL(TRUE);
+ } else {
+ RETURN_NULL();
+ }
+}
+#endif
+/* }}} */
+
+
+/* {{{ proto mixed mysqli_stmt_fetch(object stmt) U
+ Fetch results from a prepared statement into the bound variables */
+PHP_FUNCTION(mysqli_stmt_fetch)
+{
+#if !defined(HAVE_MYSQLND)
+ mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+#else
+ mysqli_stmt_fetch_mysqlnd(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+#endif
+}
+/* }}} */
+
+/* {{{ php_add_field_properties */
+static void php_add_field_properties(zval *value, MYSQL_FIELD *field TSRMLS_DC)
+{
+ add_property_string(value, "name",(field->name ? field->name : ""), 1);
+ add_property_string(value, "orgname",(field->org_name ? field->org_name : ""), 1);
+ add_property_string(value, "table",(field->table ? field->table : ""), 1);
+ add_property_string(value, "orgtable",(field->org_table ? field->org_table : ""), 1);
+ add_property_string(value, "def",(field->def ? field->def : ""), 1);
+
+ add_property_long(value, "max_length", field->max_length);
+ add_property_long(value, "length", field->length);
+ add_property_long(value, "charsetnr", field->charsetnr);
+ add_property_long(value, "flags", field->flags);
+ add_property_long(value, "type", field->type);
+ add_property_long(value, "decimals", field->decimals);
+}
+/* }}} */
/* {{{ proto mixed mysqli_fetch_field (object result)
Get column information from a result and return as an object */
PHP_FUNCTION(mysqli_fetch_field)
{
- MYSQL_RES *result;
- zval *mysql_result;
- MYSQL_FIELD *field;
+ MYSQL_RES *result;
+ zval *mysql_result;
+ MYSQL_FIELD *field;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
return;
@@ -790,18 +967,7 @@ PHP_FUNCTION(mysqli_fetch_field)
}
object_init(return_value);
-
- add_property_string(return_value, "name",(field->name ? field->name : ""), 1);
- add_property_string(return_value, "orgname",(field->org_name ? field->org_name : ""), 1);
- add_property_string(return_value, "table",(field->table ? field->table : ""), 1);
- add_property_string(return_value, "orgtable",(field->org_table ? field->org_table : ""), 1);
- add_property_string(return_value, "def",(field->def ? field->def : ""), 1);
- add_property_long(return_value, "max_length", field->max_length);
- add_property_long(return_value, "length", field->length);
- add_property_long(return_value, "charsetnr", field->charsetnr);
- add_property_long(return_value, "flags", field->flags);
- add_property_long(return_value, "type", field->type);
- add_property_long(return_value, "decimals", field->decimals);
+ php_add_field_properties(return_value, field TSRMLS_CC);
}
/* }}} */
@@ -810,9 +976,9 @@ PHP_FUNCTION(mysqli_fetch_field)
PHP_FUNCTION(mysqli_fetch_fields)
{
MYSQL_RES *result;
- zval *mysql_result;
+ zval *mysql_result;
MYSQL_FIELD *field;
- zval *obj;
+ zval *obj;
unsigned int i;
@@ -827,22 +993,10 @@ PHP_FUNCTION(mysqli_fetch_fields)
for (i = 0; i < mysql_num_fields(result); i++) {
field = mysql_fetch_field_direct(result, i);
-
MAKE_STD_ZVAL(obj);
object_init(obj);
- add_property_string(obj, "name",(field->name ? field->name : ""), 1);
- add_property_string(obj, "orgname",(field->org_name ? field->org_name : ""), 1);
- add_property_string(obj, "table",(field->table ? field->table : ""), 1);
- add_property_string(obj, "orgtable",(field->org_table ? field->org_table : ""), 1);
- add_property_string(obj, "def",(field->def ? field->def : ""), 1);
- add_property_long(obj, "max_length", field->max_length);
- add_property_long(obj, "length", field->length);
- add_property_long(obj, "charsetnr", field->charsetnr);
- add_property_long(obj, "flags", field->flags);
- add_property_long(obj, "type", field->type);
- add_property_long(obj, "decimals", field->decimals);
-
+ php_add_field_properties(obj, field TSRMLS_CC);
add_index_zval(return_value, i, obj);
}
}
@@ -854,8 +1008,8 @@ PHP_FUNCTION(mysqli_fetch_field_direct)
{
MYSQL_RES *result;
zval *mysql_result;
- MYSQL_FIELD *field;
- long offset;
+ MYSQL_FIELD *field;
+ long offset;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_result, mysqli_result_class_entry, &offset) == FAILURE) {
return;
@@ -873,18 +1027,7 @@ PHP_FUNCTION(mysqli_fetch_field_direct)
}
object_init(return_value);
-
- add_property_string(return_value, "name",(field->name ? field->name : ""), 1);
- add_property_string(return_value, "orgname",(field->org_name ? field->org_name : ""), 1);
- add_property_string(return_value, "table",(field->table ? field->table : ""), 1);
- add_property_string(return_value, "orgtable",(field->org_table ? field->org_table : ""), 1);
- add_property_string(return_value, "def",(field->def ? field->def : ""), 1);
- add_property_long(return_value, "max_length", field->max_length);
- add_property_long(return_value, "length", field->length);
- add_property_long(return_value, "charsetnr", field->charsetnr);
- add_property_long(return_value, "flags", field->flags);
- add_property_long(return_value, "type", field->type);
- add_property_long(return_value, "decimals", field->decimals);
+ php_add_field_properties(return_value, field TSRMLS_CC);
}
/* }}} */
@@ -896,7 +1039,7 @@ PHP_FUNCTION(mysqli_fetch_lengths)
zval *mysql_result;
unsigned int i;
unsigned long *ret;
-
+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
return;
}
@@ -910,7 +1053,7 @@ PHP_FUNCTION(mysqli_fetch_lengths)
array_init(return_value);
for (i = 0; i < mysql_num_fields(result); i++) {
- add_index_long(return_value, i, ret[i]);
+ add_index_long(return_value, i, ret[i]);
}
}
/* }}} */
@@ -919,17 +1062,28 @@ PHP_FUNCTION(mysqli_fetch_lengths)
Get a result row as an enumerated array */
PHP_FUNCTION(mysqli_fetch_row)
{
+#if !defined(HAVE_MYSQLND)
php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_NUM, 0);
+#else
+ MYSQL_RES *result;
+ zval *mysql_result;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, return_value, MYSQLND_MYSQLI);
+#endif
}
/* }}} */
/* {{{ proto int mysqli_field_count(object link)
Fetch the number of fields returned by the last query for the given link
*/
-PHP_FUNCTION(mysqli_field_count)
+PHP_FUNCTION(mysqli_field_count)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -958,7 +1112,7 @@ PHP_FUNCTION(mysqli_field_seek)
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid field offset");
RETURN_FALSE;
}
-
+
mysql_field_seek(result, fieldnr);
RETURN_TRUE;
}
@@ -975,7 +1129,7 @@ PHP_FUNCTION(mysqli_field_tell)
return;
}
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
-
+
RETURN_LONG(mysql_field_tell(result));
}
/* }}} */
@@ -992,12 +1146,12 @@ PHP_FUNCTION(mysqli_free_result)
}
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
- mysql_free_result(result);
- MYSQLI_CLEAR_RESOURCE(&mysql_result);
+ mysqli_free_result(result, FALSE);
+ MYSQLI_CLEAR_RESOURCE(&mysql_result);
}
/* }}} */
-/* {{{ proto string mysqli_get_client_info(void)
+/* {{{ proto string mysqli_get_client_info(void)
Get MySQL client info */
PHP_FUNCTION(mysqli_get_client_info)
{
@@ -1005,7 +1159,7 @@ PHP_FUNCTION(mysqli_get_client_info)
}
/* }}} */
-/* {{{ proto int mysqli_get_client_version(void)
+/* {{{ proto int mysqli_get_client_version(void)
Get MySQL client info */
PHP_FUNCTION(mysqli_get_client_version)
{
@@ -1018,7 +1172,7 @@ PHP_FUNCTION(mysqli_get_client_version)
PHP_FUNCTION(mysqli_get_host_info)
{
MY_MYSQL *mysql;
- zval *mysql_link = NULL;
+ zval *mysql_link = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1033,24 +1187,23 @@ PHP_FUNCTION(mysqli_get_host_info)
Get MySQL protocol information */
PHP_FUNCTION(mysqli_get_proto_info)
{
- MY_MYSQL *mysql;
- zval *mysql_link = NULL;
+ MY_MYSQL *mysql;
+ zval *mysql_link = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
-
RETURN_LONG(mysql_get_proto_info(mysql->mysql));
}
/* }}} */
-/* {{{ proto string mysqli_get_server_info(object link)
+/* {{{ proto string mysqli_get_server_info(object link)
Get MySQL server info */
PHP_FUNCTION(mysqli_get_server_info)
{
MY_MYSQL *mysql;
- zval *mysql_link = NULL;
+ zval *mysql_link = NULL;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1083,14 +1236,16 @@ PHP_FUNCTION(mysqli_get_server_version)
PHP_FUNCTION(mysqli_info)
{
MY_MYSQL *mysql;
- zval *mysql_link = NULL;
+ zval *mysql_link = NULL;
+ const char *info;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- RETURN_STRING((mysql->mysql->info) ? mysql->mysql->info : "", 1);
+ info = mysql_info(mysql->mysql);
+ RETURN_STRING((info) ? (char *)info : "", 1);
}
/* }}} */
@@ -1101,7 +1256,12 @@ PHP_FUNCTION(mysqli_init)
MYSQLI_RESOURCE *mysqli_resource;
MY_MYSQL *mysql = (MY_MYSQL *)ecalloc(1, sizeof(MY_MYSQL));
- if (!(mysql->mysql = mysql_init(NULL))) {
+#if !defined(HAVE_MYSQLND)
+ if (!(mysql->mysql = mysql_init(NULL)))
+#else
+ if (!(mysql->mysql = mysql_init(FALSE)))
+#endif
+ {
efree(mysql);
RETURN_FALSE;
}
@@ -1123,8 +1283,8 @@ PHP_FUNCTION(mysqli_init)
PHP_FUNCTION(mysqli_insert_id)
{
MY_MYSQL *mysql;
- my_ulonglong rc;
- zval *mysql_link;
+ my_ulonglong rc;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1139,15 +1299,20 @@ PHP_FUNCTION(mysqli_insert_id)
Kill a mysql process on the server */
PHP_FUNCTION(mysqli_kill)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
- long processid;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
+ long processid;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_link, mysqli_link_class_entry, &processid) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
-
+
+ if (processid <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "processid should have positive value");
+ RETURN_FALSE;
+ }
+
if (mysql_kill(mysql->mysql, processid)) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
RETURN_FALSE;
@@ -1158,6 +1323,7 @@ PHP_FUNCTION(mysqli_kill)
/* {{{ proto void mysqli_set_local_infile_default(object link)
unsets user defined handler for load local infile command */
+#if !defined(HAVE_MYSQLND)
PHP_FUNCTION(mysqli_set_local_infile_default)
{
MY_MYSQL *mysql;
@@ -1182,7 +1348,7 @@ PHP_FUNCTION(mysqli_set_local_infile_default)
PHP_FUNCTION(mysqli_set_local_infile_handler)
{
MY_MYSQL *mysql;
- zval *mysql_link;
+ zval *mysql_link;
char *callback_name;
zval *callback_func;
@@ -1197,24 +1363,29 @@ PHP_FUNCTION(mysqli_set_local_infile_handler)
if (!zend_is_callable(callback_func, 0, &callback_name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not a valid callback function %s", callback_name);
efree(callback_name);
- RETURN_FALSE;
+ RETURN_FALSE;
}
efree(callback_name);
/* save callback function */
- ALLOC_ZVAL(mysql->li_read);
- ZVAL_STRING(mysql->li_read, callback_func->value.str.val, 1);
+ if (!mysql->li_read) {
+ MAKE_STD_ZVAL(mysql->li_read);
+ } else {
+ zval_dtor(mysql->li_read);
+ }
+ ZVAL_STRINGL(mysql->li_read, Z_STRVAL_P(callback_func), Z_STRLEN_P(callback_func), 1);
RETURN_TRUE;
}
+#endif
/* }}} */
/* {{{ proto bool mysqli_more_results(object link)
check if there any more query results from a multi query */
PHP_FUNCTION(mysqli_more_results)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1228,14 +1399,20 @@ PHP_FUNCTION(mysqli_more_results)
/* {{{ proto bool mysqli_next_result(object link)
read next result from multi_query */
PHP_FUNCTION(mysqli_next_result) {
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+ if (!mysql_more_results(mysql->mysql)) {
+ php_error_docref(NULL TSRMLS_CC, E_STRICT, "There is no next result set. "
+ "Please, call mysqli_more_results()/mysqli::more_results() to check "
+ "whether to call this function/method");
+ }
+
RETURN_BOOL(!mysql_next_result(mysql->mysql));
}
/* }}} */
@@ -1268,7 +1445,7 @@ PHP_FUNCTION(mysqli_num_rows)
}
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
- if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) {
+ if (mysqli_result_is_unbuffered(result)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function cannot be used with MYSQL_USE_RESULT");
RETURN_LONG(0);
}
@@ -1281,12 +1458,12 @@ PHP_FUNCTION(mysqli_num_rows)
Set options */
PHP_FUNCTION(mysqli_options)
{
- MY_MYSQL *mysql;
- zval *mysql_link = NULL;
- zval *mysql_value;
- long mysql_option;
- unsigned int l_value;
- long ret;
+ MY_MYSQL *mysql;
+ zval *mysql_link = NULL;
+ zval *mysql_value;
+ long mysql_option;
+ unsigned int l_value;
+ long ret;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olz", &mysql_link, mysqli_link_class_entry, &mysql_option, &mysql_value) == FAILURE) {
return;
@@ -1311,7 +1488,7 @@ PHP_FUNCTION(mysqli_options)
}
RETURN_BOOL(!ret);
-}
+}
/* }}} */
@@ -1319,8 +1496,8 @@ PHP_FUNCTION(mysqli_options)
Ping a server connection or reconnect if there is no connection */
PHP_FUNCTION(mysqli_ping)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
long rc;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
@@ -1339,60 +1516,71 @@ PHP_FUNCTION(mysqli_ping)
PHP_FUNCTION(mysqli_prepare)
{
MY_MYSQL *mysql;
- MY_STMT *stmt;
+ MY_STMT *stmt;
char *query = NULL;
unsigned int query_len;
zval *mysql_link;
- MYSQLI_RESOURCE *mysqli_resource;
+ MYSQLI_RESOURCE *mysqli_resource;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",&mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+
+#if !defined(HAVE_MYSQLND)
if (mysql->mysql->status == MYSQL_STATUS_GET_RESULT) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "All data must be fetched before a new statement prepare takes place");
RETURN_FALSE;
}
+#endif
stmt = (MY_STMT *)ecalloc(1,sizeof(MY_STMT));
if ((stmt->stmt = mysql_stmt_init(mysql->mysql))) {
if (mysql_stmt_prepare(stmt->stmt, query, query_len)) {
- char last_error[MYSQL_ERRMSG_SIZE];
- char sqlstate[SQLSTATE_LENGTH+1];
+ /* mysql_stmt_close() clears errors, so we have to store them temporarily */
+#if !defined(HAVE_MYSQLND)
+ char last_error[MYSQL_ERRMSG_SIZE];
+ char sqlstate[SQLSTATE_LENGTH+1];
unsigned int last_errno;
- /* mysql_stmt_close clears errors, so we have to store them temporarily */
last_errno = stmt->stmt->last_errno;
memcpy(last_error, stmt->stmt->last_error, MYSQL_ERRMSG_SIZE);
memcpy(sqlstate, mysql->mysql->net.sqlstate, SQLSTATE_LENGTH+1);
-
- mysql_stmt_close(stmt->stmt);
+#else
+ mysqlnd_error_info error_info = mysql->mysql->error_info;
+#endif
+ mysqli_stmt_close(stmt->stmt, FALSE);
stmt->stmt = NULL;
/* restore error messages */
+#if !defined(HAVE_MYSQLND)
mysql->mysql->net.last_errno = last_errno;
memcpy(mysql->mysql->net.last_error, last_error, MYSQL_ERRMSG_SIZE);
memcpy(mysql->mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH+1);
+#else
+ mysql->mysql->error_info = error_info;
+#endif
}
}
- /* don't joing to the previous if because it won't work if mysql_stmt_prepare_fails */
+
+ /* don't initialize stmt->query with NULL, we ecalloc()-ed the memory */
+ /* Get performance boost if reporting is switched off */
+ if (stmt->stmt && query_len && (MyG(report_mode) & MYSQLI_REPORT_INDEX)) {
+ stmt->query = (char *)emalloc(query_len + 1);
+ memcpy(stmt->query, query, query_len);
+ stmt->query[query_len] = '\0';
+ }
+
+ /* don't join to the previous if because it won't work if mysql_stmt_prepare_fails */
if (!stmt->stmt) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
efree(stmt);
RETURN_FALSE;
}
-
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)stmt;
- /* don't initialize stmt->query with NULL, we ecalloc()-ed the memory */
- /* Get performance boost if reporting is switched off */
- if (query_len && (MyG(report_mode) & MYSQLI_REPORT_INDEX)) {
- stmt->query = (char *)emalloc(query_len + 1);
- memcpy(stmt->query, query, query_len);
- stmt->query[query_len] = '\0';
- }
/* change status */
mysqli_resource->status = MYSQLI_STATUS_VALID;
@@ -1404,10 +1592,10 @@ PHP_FUNCTION(mysqli_prepare)
Open a connection to a mysql server */
PHP_FUNCTION(mysqli_real_connect)
{
- MY_MYSQL *mysql;
- char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL;
- unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0;
- unsigned long port=0, flags=0;
+ MY_MYSQL *mysql;
+ char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL;
+ unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0;
+ unsigned long port=0, flags=0;
zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sssslsl", &mysql_link, mysqli_link_class_entry,
@@ -1419,37 +1607,38 @@ PHP_FUNCTION(mysqli_real_connect)
if (!socket_len) {
socket = NULL;
}
-
- /* TODO: safe mode handling */
- if (PG(sql_safe_mode)) {
- } else {
- if (!passwd) {
- passwd = MyG(default_pw);
- if (!username){
- username = MyG(default_user);
- if (!hostname) {
- hostname = MyG(default_host);
- }
- }
- }
+ if (!socket) {
+ socket = MyG(default_socket);
}
+ if (!passwd) {
+ passwd = MyG(default_pw);
+ passwd_len = strlen(passwd);
+ }
+ if (!username){
+ username = MyG(default_user);
+ }
+ if (!hostname) {
+ hostname = MyG(default_host);
+ }
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED);
/* remove some insecure options */
flags &= ~CLIENT_MULTI_STATEMENTS; /* don't allow multi_queries via connect parameter */
- if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) {
- flags &= ~CLIENT_LOCAL_FILES;
- }
-
- if (!socket) {
- socket = MyG(default_socket);
+ if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
+ flags ^= CLIENT_LOCAL_FILES;
}
- if (mysql_real_connect(mysql->mysql,hostname,username,passwd,dbname,port,socket,flags) == NULL) {
+#if !defined(HAVE_MYSQLND)
+ if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname ,port, socket ,flags) == NULL)
+#else
+ if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
+ port, socket, flags, MyG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
+#endif
+ {
php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC);
- php_mysqli_throw_sql_exception( mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC,
- "%s", mysql->mysql->net.last_error);
+ php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC,
+ "%s", mysql_error(mysql->mysql));
/* change status */
MYSQLI_SET_STATUS(&mysql_link, MYSQLI_STATUS_INITIALIZED);
@@ -1458,10 +1647,14 @@ PHP_FUNCTION(mysqli_real_connect)
php_mysqli_set_error(mysql_errno(mysql->mysql), (char *)mysql_error(mysql->mysql) TSRMLS_CC);
+#if !defined(HAVE_MYSQLND)
mysql->mysql->reconnect = MyG(reconnect);
/* set our own local_infile handler */
php_set_local_infile_handler_default(mysql);
+#endif
+
+ mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&MyG(allow_local_infile));
/* change status */
MYSQLI_SET_STATUS(&mysql_link, MYSQLI_STATUS_VALID);
@@ -1477,7 +1670,7 @@ PHP_FUNCTION(mysqli_real_query)
MY_MYSQL *mysql;
zval *mysql_link;
char *query = NULL;
- unsigned int query_len;
+ unsigned int query_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) {
return;
@@ -1493,7 +1686,7 @@ PHP_FUNCTION(mysqli_real_query)
if (!mysql_field_count(mysql->mysql)) {
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
}
@@ -1517,7 +1710,7 @@ PHP_FUNCTION(mysqli_real_escape_string) {
newstr = safe_emalloc(2, escapestr_len, 1);
newstr_len = mysql_real_escape_string(mysql->mysql, newstr, escapestr, escapestr_len);
newstr = erealloc(newstr, newstr_len + 1);
-
+
RETURN_STRINGL(newstr, newstr_len, 0);
}
/* }}} */
@@ -1527,7 +1720,7 @@ PHP_FUNCTION(mysqli_real_escape_string) {
PHP_FUNCTION(mysqli_rollback)
{
MY_MYSQL *mysql;
- zval *mysql_link;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1546,12 +1739,11 @@ PHP_FUNCTION(mysqli_rollback)
PHP_FUNCTION(mysqli_stmt_send_long_data)
{
MY_STMT *stmt;
- zval *mysql_stmt;
+ zval *mysql_stmt;
char *data;
long param_nr;
int data_len;
-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols", &mysql_stmt, mysqli_stmt_class_entry, &param_nr, &data, &data_len) == FAILURE) {
return;
}
@@ -1573,8 +1765,8 @@ PHP_FUNCTION(mysqli_stmt_send_long_data)
Return the number of rows affected in the last query for the given link */
PHP_FUNCTION(mysqli_stmt_affected_rows)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
my_ulonglong rc;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
@@ -1590,21 +1782,21 @@ PHP_FUNCTION(mysqli_stmt_affected_rows)
}
/* }}} */
-/* {{{ proto bool mysqli_stmt_close(object stmt)
+/* {{{ proto bool mysqli_stmt_close(object stmt)
Close statement */
PHP_FUNCTION(mysqli_stmt_close)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
- mysql_stmt_close(stmt->stmt);
+ mysqli_stmt_close(stmt->stmt, FALSE);
stmt->stmt = NULL;
- php_clear_stmt_bind(stmt);
+ php_clear_stmt_bind(stmt TSRMLS_CC);
MYSQLI_CLEAR_RESOURCE(&mysql_stmt);
RETURN_TRUE;
}
@@ -1614,9 +1806,9 @@ PHP_FUNCTION(mysqli_stmt_close)
Move internal result pointer */
PHP_FUNCTION(mysqli_stmt_data_seek)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
- long offset;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+ long offset;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &mysql_stmt, mysqli_stmt_class_entry, &offset) == FAILURE) {
return;
@@ -1636,7 +1828,7 @@ PHP_FUNCTION(mysqli_stmt_data_seek)
Return the number of result columns for the given statement */
PHP_FUNCTION(mysqli_stmt_field_count)
{
- MY_STMT *stmt;
+ MY_STMT *stmt;
zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
@@ -1652,8 +1844,8 @@ PHP_FUNCTION(mysqli_stmt_field_count)
Free stored result memory for the given statement handle */
PHP_FUNCTION(mysqli_stmt_free_result)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
@@ -1669,9 +1861,9 @@ PHP_FUNCTION(mysqli_stmt_free_result)
Get the ID generated from the previous INSERT operation */
PHP_FUNCTION(mysqli_stmt_insert_id)
{
- MY_STMT *stmt;
- my_ulonglong rc;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ my_ulonglong rc;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
@@ -1682,13 +1874,13 @@ PHP_FUNCTION(mysqli_stmt_insert_id)
}
/* }}} */
-/* {{{ proto int mysqli_stmt_param_count(object stmt) {
+/* {{{ proto int mysqli_stmt_param_count(object stmt)
Return the number of parameter for the given statement */
PHP_FUNCTION(mysqli_stmt_param_count)
{
- MY_STMT *stmt;
+ MY_STMT *stmt;
zval *mysql_stmt;
-
+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
}
@@ -1700,10 +1892,10 @@ PHP_FUNCTION(mysqli_stmt_param_count)
/* {{{ proto bool mysqli_stmt_reset(object stmt)
reset a prepared statement */
-PHP_FUNCTION(mysqli_stmt_reset)
+PHP_FUNCTION(mysqli_stmt_reset)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
@@ -1722,8 +1914,8 @@ PHP_FUNCTION(mysqli_stmt_reset)
Return the number of rows in statements result set */
PHP_FUNCTION(mysqli_stmt_num_rows)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
my_ulonglong rc;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
@@ -1737,27 +1929,25 @@ PHP_FUNCTION(mysqli_stmt_num_rows)
}
/* }}} */
-/* {{{ proto string mysqli_select_db(object link, string dbname)
+/* {{{ proto bool mysqli_select_db(object link, string dbname)
Select a MySQL database */
PHP_FUNCTION(mysqli_select_db)
{
MY_MYSQL *mysql;
- zval *mysql_link;
- char *dbname;
- int dbname_len;
-
+ zval *mysql_link;
+ char *dbname;
+ int dbname_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &mysql_link, mysqli_link_class_entry, &dbname, &dbname_len) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
-
- if (!mysql_select_db(mysql->mysql, dbname)) {
- RETURN_TRUE;
+
+ if (mysql_select_db(mysql->mysql, dbname)) {
+ MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
+ RETURN_FALSE;
}
-
- MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
- RETURN_FALSE;
+ RETURN_TRUE;
}
/* }}} */
@@ -1765,8 +1955,8 @@ PHP_FUNCTION(mysqli_select_db)
Returns the SQLSTATE error from previous MySQL operation */
PHP_FUNCTION(mysqli_sqlstate)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1776,21 +1966,22 @@ PHP_FUNCTION(mysqli_sqlstate)
}
/* }}} */
-/* {{{ proto bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher])
+/* {{{ proto bool mysqli_ssl_set(object link ,string key ,string cert ,string ca ,string capath ,string cipher]) U
*/
+#if !defined(HAVE_MYSQLND)
PHP_FUNCTION(mysqli_ssl_set)
{
MY_MYSQL *mysql;
- zval *mysql_link;
- char *ssl_parm[5];
+ zval *mysql_link;
+ char *ssl_parm[5];
int ssl_parm_len[5], i;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osssss", &mysql_link, mysqli_link_class_entry, &ssl_parm[0], &ssl_parm_len[0], &ssl_parm[1], &ssl_parm_len[1], &ssl_parm[2], &ssl_parm_len[2], &ssl_parm[3], &ssl_parm_len[3], &ssl_parm[4], &ssl_parm_len[4]) == FAILURE) {
return;
}
- MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_INITIALIZED);
+ MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- for (i=0; i < 5; i++) {
+ for (i = 0; i < 5; i++) {
if (!ssl_parm_len[i]) {
ssl_parm[i] = NULL;
}
@@ -1800,25 +1991,37 @@ PHP_FUNCTION(mysqli_ssl_set)
RETURN_TRUE;
}
+#endif
/* }}} */
/* {{{ proto mixed mysqli_stat(object link)
Get current system status */
PHP_FUNCTION(mysqli_stat)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
char *stat;
+#if defined(HAVE_MYSQLND)
+ uint stat_len;
+#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
- if ((stat = (char *)mysql_stat(mysql->mysql))) {
+#if !defined(HAVE_MYSQLND)
+ if ((stat = (char *)mysql_stat(mysql->mysql)))
+ {
RETURN_STRING(stat, 1);
+#else
+ if (mysqlnd_stat(mysql->mysql, &stat, &stat_len) == PASS)
+ {
+ RETURN_STRINGL(stat, stat_len, 0);
+#endif
+ } else {
+ RETURN_FALSE;
}
- RETURN_FALSE;
}
/* }}} */
@@ -1828,16 +2031,23 @@ PHP_FUNCTION(mysqli_stat)
PHP_FUNCTION(mysqli_stmt_attr_set)
{
MY_STMT *stmt;
- zval *mysql_stmt;
- ulong mode;
+ zval *mysql_stmt;
+ long mode_in;
+ ulong mode;
ulong attr;
int rc;
- if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", &mysql_stmt, mysqli_stmt_class_entry, &attr, &mode) == FAILURE) {
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", &mysql_stmt, mysqli_stmt_class_entry, &attr, &mode_in) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+ if (mode_in < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "mode should be non-negative, %ld passed", mode_in);
+ RETURN_FALSE;
+ }
+
+ mode = mode_in;
if ((rc = mysql_stmt_attr_set(stmt->stmt, attr, (void *)&mode))) {
RETURN_FALSE;
}
@@ -1850,8 +2060,8 @@ PHP_FUNCTION(mysqli_stmt_attr_set)
PHP_FUNCTION(mysqli_stmt_attr_get)
{
MY_STMT *stmt;
- zval *mysql_stmt;
-#if MYSQL_VERSION_ID > 50099
+ zval *mysql_stmt;
+#if !defined(HAVE_MYSQLND) && MYSQL_VERSION_ID > 50099
my_bool value;
#else
ulong value = 0;
@@ -1876,7 +2086,7 @@ PHP_FUNCTION(mysqli_stmt_attr_get)
PHP_FUNCTION(mysqli_stmt_errno)
{
MY_STMT *stmt;
- zval *mysql_stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
@@ -1909,9 +2119,9 @@ PHP_FUNCTION(mysqli_stmt_error)
PHP_FUNCTION(mysqli_stmt_init)
{
MY_MYSQL *mysql;
- MY_STMT *stmt;
+ MY_STMT *stmt;
zval *mysql_link;
- MYSQLI_RESOURCE *mysqli_resource;
+ MYSQLI_RESOURCE *mysqli_resource;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",&mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -1961,9 +2171,9 @@ PHP_FUNCTION(mysqli_stmt_prepare)
return result set from statement */
PHP_FUNCTION(mysqli_stmt_result_metadata)
{
- MY_STMT *stmt;
+ MY_STMT *stmt;
MYSQL_RES *result;
- zval *mysql_stmt;
+ zval *mysql_stmt;
MYSQLI_RESOURCE *mysqli_resource;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
@@ -1979,7 +2189,7 @@ PHP_FUNCTION(mysqli_stmt_result_metadata)
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)result;
mysqli_resource->status = MYSQLI_STATUS_VALID;
- MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
+ MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
}
/* }}} */
@@ -1987,32 +2197,37 @@ PHP_FUNCTION(mysqli_stmt_result_metadata)
*/
PHP_FUNCTION(mysqli_stmt_store_result)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
- int i=0;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
- /*
- If the user wants to store the data and we have BLOBs/TEXTs we try to allocate
- not the maximal length of the type (which is 16MB even for LONGBLOB) but
- the maximal length of the field in the result set. If he/she has quite big
- BLOB/TEXT columns after calling store_result() the memory usage of PHP will
- double - but this is a known problem of the simple MySQL API ;)
- */
- for (i = mysql_stmt_field_count(stmt->stmt) - 1; i >=0; --i) {
- if (stmt->stmt->fields && (stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB ||
- stmt->stmt->fields[i].type == MYSQL_TYPE_MEDIUM_BLOB ||
- stmt->stmt->fields[i].type == MYSQL_TYPE_LONG_BLOB))
- {
- my_bool tmp=1;
- mysql_stmt_attr_set(stmt->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &tmp);
- break;
+#if !defined(HAVE_MYSQLND)
+ {
+ /*
+ If the user wants to store the data and we have BLOBs/TEXTs we try to allocate
+ not the maximal length of the type (which is 16MB even for LONGBLOB) but
+ the maximal length of the field in the result set. If he/she has quite big
+ BLOB/TEXT columns after calling store_result() the memory usage of PHP will
+ double - but this is a known problem of the simple MySQL API ;)
+ */
+ int i = 0;
+
+ for (i = mysql_stmt_field_count(stmt->stmt) - 1; i >=0; --i) {
+ if (stmt->stmt->fields && (stmt->stmt->fields[i].type == MYSQL_TYPE_BLOB ||
+ stmt->stmt->fields[i].type == MYSQL_TYPE_MEDIUM_BLOB ||
+ stmt->stmt->fields[i].type == MYSQL_TYPE_LONG_BLOB))
+ {
+ my_bool tmp=1;
+ mysql_stmt_attr_set(stmt->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &tmp);
+ break;
+ }
}
}
+#endif
if (mysql_stmt_store_result(stmt->stmt)){
MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
@@ -2024,16 +2239,16 @@ PHP_FUNCTION(mysqli_stmt_store_result)
/* {{{ proto string mysqli_stmt_sqlstate(object stmt)
*/
-PHP_FUNCTION(mysqli_stmt_sqlstate)
+PHP_FUNCTION(mysqli_stmt_sqlstate)
{
- MY_STMT *stmt;
- zval *mysql_stmt;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
return;
}
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
-
+
RETURN_STRING((char *)mysql_stmt_sqlstate(stmt->stmt),1);
}
/* }}} */
@@ -2042,9 +2257,9 @@ PHP_FUNCTION(mysqli_stmt_sqlstate)
Buffer result set on client */
PHP_FUNCTION(mysqli_store_result)
{
- MY_MYSQL *mysql;
- MYSQL_RES *result;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ MYSQL_RES *result;
+ zval *mysql_link;
MYSQLI_RESOURCE *mysqli_resource;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
@@ -2057,22 +2272,23 @@ PHP_FUNCTION(mysqli_store_result)
RETURN_FALSE;
}
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index("from previous query", mysql->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index("from previous query", mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)result;
mysqli_resource->status = MYSQLI_STATUS_VALID;
- MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
+ MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
}
/* }}} */
+
/* {{{ proto int mysqli_thread_id(object link)
Return the current thread ID */
PHP_FUNCTION(mysqli_thread_id)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -2089,17 +2305,16 @@ PHP_FUNCTION(mysqli_thread_safe)
{
RETURN_BOOL(mysql_thread_safe());
}
-
/* }}} */
/* {{{ proto mixed mysqli_use_result(object link)
Directly retrieve query results - do not buffer results on client side */
PHP_FUNCTION(mysqli_use_result)
{
- MY_MYSQL *mysql;
- MYSQL_RES *result;
- zval *mysql_link;
- MYSQLI_RESOURCE *mysqli_resource;
+ MY_MYSQL *mysql;
+ MYSQL_RES *result;
+ zval *mysql_link;
+ MYSQLI_RESOURCE *mysqli_resource;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -2112,12 +2327,12 @@ PHP_FUNCTION(mysqli_use_result)
}
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index("from previous query", mysql->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index("from previous query", mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)result;
mysqli_resource->status = MYSQLI_STATUS_VALID;
- MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
+ MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
}
/* }}} */
@@ -2125,8 +2340,8 @@ PHP_FUNCTION(mysqli_use_result)
Return number of warnings from the last query for the given link */
PHP_FUNCTION(mysqli_warning_count)
{
- MY_MYSQL *mysql;
- zval *mysql_link;
+ MY_MYSQL *mysql;
+ zval *mysql_link;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
diff --git a/ext/mysqli/mysqli_driver.c b/ext/mysqli/mysqli_driver.c
index 494c2a554b..e8a9f71806 100644
--- a/ext/mysqli/mysqli_driver.c
+++ b/ext/mysqli/mysqli_driver.c
@@ -25,7 +25,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
#include "zend_exceptions.h"
@@ -110,7 +110,7 @@ static int driver_client_version_read(mysqli_object *obj, zval **retval TSRMLS_D
static int driver_client_info_read(mysqli_object *obj, zval **retval TSRMLS_DC)
{
ALLOC_ZVAL(*retval);
- ZVAL_STRING(*retval, MYSQL_SERVER_VERSION, 1);
+ ZVAL_STRING(*retval, (char *)mysql_get_client_info(), 1);
return SUCCESS;
}
/* }}} */
@@ -130,9 +130,17 @@ MAP_PROPERTY_MYG_LONG_READ(driver_report_read, report_mode);
ZEND_FUNCTION(mysqli_driver_construct)
{
+#if G0
+ MYSQLI_RESOURCE *mysqli_resource;
+
+ mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
+ mysqli_resource->ptr = 1;
+ mysqli_resource->status = (ZEND_NUM_ARGS() == 1) ? MYSQLI_STATUS_INITIALIZED : MYSQLI_STATUS_VALID;
+ ((mysqli_object *) zend_object_store_get_object(getThis() TSRMLS_CC))->ptr = mysqli_resource;
+#endif
}
-mysqli_property_entry mysqli_driver_property_entries[] = {
+const mysqli_property_entry mysqli_driver_property_entries[] = {
{"client_info", driver_client_info_read, NULL},
{"client_version", driver_client_version_read, NULL},
{"driver_version", driver_driver_version_read, NULL},
@@ -145,8 +153,10 @@ mysqli_property_entry mysqli_driver_property_entries[] = {
/* {{{ mysqli_driver_methods[]
*/
const zend_function_entry mysqli_driver_methods[] = {
+#if defined(HAVE_EMBEDDED_MYSQLI)
PHP_FALIAS(embedded_server_start, mysqli_embedded_server_start, NULL)
PHP_FALIAS(embedded_server_end, mysqli_embedded_server_end, NULL)
+#endif
{NULL, NULL, NULL}
};
/* }}} */
diff --git a/ext/mysqli/mysqli_embedded.c b/ext/mysqli/mysqli_embedded.c
index 12affc87c2..328025af21 100644
--- a/ext/mysqli/mysqli_embedded.c
+++ b/ext/mysqli/mysqli_embedded.c
@@ -25,7 +25,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
/* {{{ proto bool mysqli_embedded_server_start(bool start, array arguments, array groups)
initialize and start embedded server */
diff --git a/ext/mysqli/mysqli_exception.c b/ext/mysqli/mysqli_exception.c
index b6b1d8903e..7b8a1d63e1 100644
--- a/ext/mysqli/mysqli_exception.c
+++ b/ext/mysqli/mysqli_exception.c
@@ -25,7 +25,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
#include "zend_exceptions.h"
/* {{{ mysqli_exception_methods[]
diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c
index f82d31c405..623042e576 100644
--- a/ext/mysqli/mysqli_fe.c
+++ b/ext/mysqli/mysqli_fe.c
@@ -27,7 +27,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
static
@@ -61,14 +61,24 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_connect_errno, NULL)
PHP_FE(mysqli_connect_error, NULL)
PHP_FE(mysqli_data_seek, NULL)
+ PHP_FE(mysqli_dump_debug_info, NULL)
PHP_FE(mysqli_debug, NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FE(mysqli_disable_reads_from_master, NULL)
PHP_FE(mysqli_disable_rpl_parse, NULL)
- PHP_FE(mysqli_dump_debug_info, NULL)
PHP_FE(mysqli_enable_reads_from_master, NULL)
PHP_FE(mysqli_enable_rpl_parse, NULL)
+ PHP_FE(mysqli_send_query, NULL)
+ PHP_FE(mysqli_slave_query, NULL)
+ PHP_FE(mysqli_master_query, NULL)
+ PHP_FE(mysqli_rpl_parse_enabled, NULL)
+ PHP_FE(mysqli_rpl_probe, NULL)
+ PHP_FE(mysqli_rpl_query_type, NULL)
+#endif
+#if defined(HAVE_EMBEDDED_MYSQLI)
PHP_FE(mysqli_embedded_server_end, NULL)
PHP_FE(mysqli_embedded_server_start, NULL)
+#endif
PHP_FE(mysqli_errno, NULL)
PHP_FE(mysqli_error, NULL)
PHP_FE(mysqli_stmt_execute, NULL)
@@ -77,14 +87,22 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_fetch_fields, NULL)
PHP_FE(mysqli_fetch_field_direct, NULL)
PHP_FE(mysqli_fetch_lengths, NULL)
+#ifdef HAVE_MYSQLND
+ PHP_FE(mysqli_fetch_all, NULL)
+#endif
PHP_FE(mysqli_fetch_array, NULL)
PHP_FE(mysqli_fetch_assoc, NULL)
- PHP_FE(mysqli_fetch_object, NULL)
+ PHP_FE(mysqli_fetch_object, NULL)
PHP_FE(mysqli_fetch_row, NULL)
PHP_FE(mysqli_field_count, NULL)
PHP_FE(mysqli_field_seek, NULL)
PHP_FE(mysqli_field_tell, NULL)
PHP_FE(mysqli_free_result, NULL)
+#if defined(HAVE_MYSQLND)
+ PHP_FE(mysqli_get_cache_stats, NULL)
+ PHP_FE(mysqli_get_connection_stats, NULL)
+ PHP_FE(mysqli_get_client_stats, NULL)
+#endif
#ifdef HAVE_MYSQLI_GET_CHARSET
PHP_FE(mysqli_get_charset, NULL)
#endif
@@ -99,9 +117,10 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_info, NULL)
PHP_FE(mysqli_insert_id, NULL)
PHP_FE(mysqli_kill, NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FE(mysqli_set_local_infile_default, NULL)
PHP_FE(mysqli_set_local_infile_handler, NULL)
- PHP_FE(mysqli_master_query, NULL)
+#endif
PHP_FE(mysqli_more_results, NULL)
PHP_FE(mysqli_multi_query, NULL)
PHP_FE(mysqli_next_result, NULL)
@@ -116,9 +135,6 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_real_escape_string, NULL)
PHP_FE(mysqli_real_query, NULL)
PHP_FE(mysqli_rollback, NULL)
- PHP_FE(mysqli_rpl_parse_enabled, NULL)
- PHP_FE(mysqli_rpl_probe, NULL)
- PHP_FE(mysqli_rpl_query_type, NULL)
PHP_FE(mysqli_select_db, NULL)
#ifdef HAVE_MYSQLI_SET_CHARSET
PHP_FE(mysqli_set_charset, NULL)
@@ -134,14 +150,17 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_stmt_bind_result, second_arg_force_by_ref_rest)
PHP_FE(mysqli_stmt_fetch, NULL)
PHP_FE(mysqli_stmt_free_result, NULL)
+#if defined(HAVE_MYSQLND)
+ PHP_FE(mysqli_stmt_get_result, NULL)
+#endif
PHP_FE(mysqli_stmt_get_warnings, NULL)
PHP_FE(mysqli_stmt_insert_id, NULL)
PHP_FE(mysqli_stmt_reset, NULL)
PHP_FE(mysqli_stmt_param_count, NULL)
- PHP_FE(mysqli_send_query, NULL)
- PHP_FE(mysqli_slave_query, NULL)
PHP_FE(mysqli_sqlstate, NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FE(mysqli_ssl_set, NULL)
+#endif
PHP_FE(mysqli_stat, NULL)
PHP_FE(mysqli_stmt_affected_rows, NULL)
PHP_FE(mysqli_stmt_close, NULL)
@@ -150,8 +169,8 @@ const zend_function_entry mysqli_functions[] = {
PHP_FE(mysqli_stmt_error, NULL)
PHP_FE(mysqli_stmt_num_rows, NULL)
PHP_FE(mysqli_stmt_sqlstate, NULL)
- PHP_FE(mysqli_store_result, NULL)
PHP_FE(mysqli_stmt_store_result, NULL)
+ PHP_FE(mysqli_store_result, NULL)
PHP_FE(mysqli_thread_id, NULL)
PHP_FE(mysqli_thread_safe, NULL)
PHP_FE(mysqli_use_result, NULL)
@@ -184,23 +203,34 @@ const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(close,mysqli_close,NULL)
PHP_FALIAS(commit,mysqli_commit,NULL)
PHP_FALIAS(connect,mysqli_connect,NULL)
+ PHP_FALIAS(dump_debug_info,mysqli_dump_debug_info,NULL)
PHP_FALIAS(debug,mysqli_debug,NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FALIAS(disable_reads_from_master,mysqli_disable_reads_from_master,NULL)
PHP_FALIAS(disable_rpl_parse,mysqli_disable_rpl_parse,NULL)
- PHP_FALIAS(dump_debug_info,mysqli_dump_debug_info,NULL)
PHP_FALIAS(enable_reads_from_master,mysqli_enable_reads_from_master,NULL)
PHP_FALIAS(enable_rpl_parse,mysqli_enable_rpl_parse,NULL)
+ PHP_FALIAS(rpl_parse_enabled,mysqli_rpl_parse_enabled,NULL)
+ PHP_FALIAS(rpl_probe,mysqli_rpl_probe,NULL)
+ PHP_FALIAS(rpl_query_type,mysqli_rpl_query_type,NULL)
+ PHP_FALIAS(master_query,mysqli_master_query,NULL)
+ PHP_FALIAS(slave_query,mysqli_slave_query,NULL)
+#endif
#ifdef HAVE_MYSQLI_GET_CHARSET
PHP_FALIAS(get_charset,mysqli_get_charset,NULL)
#endif
PHP_FALIAS(get_client_info,mysqli_get_client_info,NULL)
+#if defined(HAVE_MYSQLND)
+ PHP_FALIAS(get_connection_stats,mysqli_get_connection_stats,NULL)
+#endif
PHP_FALIAS(get_server_info,mysqli_get_server_info,NULL)
PHP_FALIAS(get_warnings, mysqli_get_warnings, NULL)
PHP_FALIAS(init,mysqli_init,NULL)
PHP_FALIAS(kill,mysqli_kill,NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FALIAS(set_local_infile_default,mysqli_set_local_infile_default,NULL)
PHP_FALIAS(set_local_infile_handler,mysqli_set_local_infile_handler,NULL)
- PHP_FALIAS(master_query,mysqli_master_query,NULL)
+#endif
PHP_FALIAS(multi_query,mysqli_multi_query,NULL)
PHP_FALIAS(mysqli,mysqli_connect,NULL)
PHP_FALIAS(more_results,mysqli_more_results, NULL)
@@ -214,16 +244,14 @@ const zend_function_entry mysqli_link_methods[] = {
PHP_FALIAS(escape_string, mysqli_real_escape_string,NULL)
PHP_FALIAS(real_query,mysqli_real_query,NULL)
PHP_FALIAS(rollback,mysqli_rollback,NULL)
- PHP_FALIAS(rpl_parse_enabled,mysqli_rpl_parse_enabled,NULL)
- PHP_FALIAS(rpl_probe,mysqli_rpl_probe,NULL)
- PHP_FALIAS(rpl_query_type,mysqli_rpl_query_type,NULL)
PHP_FALIAS(select_db,mysqli_select_db,NULL)
#ifdef HAVE_MYSQLI_SET_CHARSET
PHP_FALIAS(set_charset,mysqli_set_charset,NULL)
#endif
PHP_FALIAS(set_opt, mysqli_options,NULL)
- PHP_FALIAS(slave_query,mysqli_slave_query,NULL)
+#if !defined(HAVE_MYSQLND)
PHP_FALIAS(ssl_set,mysqli_ssl_set,NULL)
+#endif
PHP_FALIAS(stat,mysqli_stat,NULL)
PHP_FALIAS(stmt_init,mysqli_stmt_init, NULL)
PHP_FALIAS(store_result,mysqli_store_result,NULL)
@@ -238,18 +266,20 @@ const zend_function_entry mysqli_link_methods[] = {
* Every user visible function must have an entry in mysqli_result_functions[].
*/
const zend_function_entry mysqli_result_methods[] = {
- PHP_FALIAS(mysqli_result, mysqli_result_construct, NULL)
+ PHP_FALIAS(__construct, mysqli_result_construct, NULL)
PHP_FALIAS(close,mysqli_free_result,NULL)
PHP_FALIAS(free,mysqli_free_result,NULL)
PHP_FALIAS(data_seek,mysqli_data_seek,NULL)
PHP_FALIAS(fetch_field,mysqli_fetch_field,NULL)
PHP_FALIAS(fetch_fields,mysqli_fetch_fields,NULL)
PHP_FALIAS(fetch_field_direct,mysqli_fetch_field_direct,NULL)
+#if defined(HAVE_MYSQLND)
+ PHP_FALIAS(fetch_all,mysqli_fetch_all,NULL)
+#endif
PHP_FALIAS(fetch_array,mysqli_fetch_array,NULL)
PHP_FALIAS(fetch_assoc,mysqli_fetch_assoc,NULL)
PHP_FALIAS(fetch_object,mysqli_fetch_object,NULL)
PHP_FALIAS(fetch_row,mysqli_fetch_row,NULL)
- PHP_FALIAS(field_count,mysqli_field_count,NULL)
PHP_FALIAS(field_seek,mysqli_field_seek,NULL)
PHP_FALIAS(free_result,mysqli_free_result,NULL)
{NULL, NULL, NULL}
@@ -261,7 +291,7 @@ const zend_function_entry mysqli_result_methods[] = {
* Every user visible function must have an entry in mysqli_stmt_functions[].
*/
const zend_function_entry mysqli_stmt_methods[] = {
- PHP_FALIAS(mysqli_stmt, mysqli_stmt_construct, NULL)
+ PHP_FALIAS(__construct, mysqli_stmt_construct, NULL)
PHP_FALIAS(attr_get,mysqli_stmt_attr_get,NULL)
PHP_FALIAS(attr_set,mysqli_stmt_attr_set,NULL)
PHP_FALIAS(bind_param,mysqli_stmt_bind_param,second_arg_force_by_ref_rest)
@@ -279,6 +309,9 @@ const zend_function_entry mysqli_stmt_methods[] = {
PHP_FALIAS(reset,mysqli_stmt_reset,NULL)
PHP_FALIAS(prepare,mysqli_stmt_prepare, NULL)
PHP_FALIAS(store_result,mysqli_stmt_store_result,NULL)
+#if defined(HAVE_MYSQLND)
+ PHP_FALIAS(get_result,mysqli_stmt_get_result,NULL)
+#endif
{NULL, NULL, NULL}
};
/* }}} */
diff --git a/ext/mysqli/mysqli_libmysql.h b/ext/mysqli/mysqli_libmysql.h
new file mode 100644
index 0000000000..1ea1fc3bf4
--- /dev/null
+++ b/ext/mysqli/mysqli_libmysql.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2007 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> |
+ +----------------------------------------------------------------------+
+
+*/
+
+/* These are unused */
+#define MYSQLI_CLOSE_EXPLICIT
+#define MYSQLI_CLOSE_IMPLICIT
+#define MYSQLI_CLOSE_DISCONNECTED
+#define MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE 200
+#define MYSQLND_OPT_INT_AND_YEAR_AS_INT 201
+
+#define mysqli_result_is_unbuffered(r) ((r)->handle && (r)->handle->status == MYSQL_STATUS_USE_RESULT)
+#define mysqli_server_status(c) (c)->server_status
+#define mysqli_stmt_warning_count(s) mysql_warning_count((s)->mysql)
+#define mysqli_stmt_server_status(s) (s)->mysql->server_status
+#define mysqli_stmt_get_connection(s) (s)->mysql
+#define mysqli_close(c, is_forced) mysql_close((c))
+#define mysqli_stmt_close(c, implicit) mysql_stmt_close((c))
+#define mysqli_free_result(r, is_forced) mysql_free_result((r))
diff --git a/ext/mysqli/mysqli_mysqlnd.h b/ext/mysqli/mysqli_mysqlnd.h
new file mode 100644
index 0000000000..27032f15c8
--- /dev/null
+++ b/ext/mysqli/mysqli_mysqlnd.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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 MYSQLI_MYSQLND_H
+#define MYSQLI_MYSQLND_H
+
+#include "ext/mysqlnd/mysqlnd_libmysql_compat.h"
+
+/* Here comes non-libmysql API to have less ifdefs in mysqli*/
+#define MYSQLI_CLOSE_EXPLICIT MYSQLND_CLOSE_EXPLICIT
+#define MYSQLI_CLOSE_IMPLICIT MYSQLND_CLOSE_IMPLICIT
+#define MYSQLI_CLOSE_DISCONNECTED MYSQLND_CLOSE_DISCONNECTED
+
+#define mysqli_result_is_unbuffered(r) ((r)->unbuf)
+#define mysqli_server_status(c) (c)->upsert_status.server_status
+#define mysqli_stmt_warning_count(s) mysqlnd_stmt_warning_count((s))
+#define mysqli_stmt_server_status(s) (s)->upsert_status.server_status
+#define mysqli_stmt_get_connection(s) (s)->conn
+#define mysqli_close(c, how) mysqlnd_close((c), (how))
+#define mysqli_stmt_close(c, implicit) mysqlnd_stmt_close((c), (implicit))
+#define mysqli_free_result(r, implicit) mysqlnd_free_result((r), (implicit))
+
+#endif
diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c
index 2f9ba5325b..918594ad76 100644
--- a/ext/mysqli/mysqli_nonapi.c
+++ b/ext/mysqli/mysqli_nonapi.c
@@ -12,7 +12,9 @@
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
- | Author: Georg Richter <georg@php.net> |
+ | Authors: Georg Richter <georg@php.net> |
+ | Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+----------------------------------------------------------------------+
$Id$
@@ -27,46 +29,54 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
+
+#define SAFE_STR(a) ((a)?a:"")
+
/* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]])
Open a connection to a mysql server */
PHP_FUNCTION(mysqli_connect)
{
- MY_MYSQL *mysql = NULL;
- MYSQLI_RESOURCE *mysqli_resource = NULL;
- zval *object = getThis();
- char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL;
- unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0;
- long port=0;
+ MY_MYSQL *mysql = NULL;
+ MYSQLI_RESOURCE *mysqli_resource = NULL;
+ zval *object = getThis();
+ char *hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL;
+ unsigned int hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0;
+ zend_bool persistent = FALSE;
+ long port = 0;
+ uint hash_len;
+ char *hash_key = NULL;
+ zend_bool new_connection = FALSE;
+ zend_rsrc_list_entry *le;
+ mysqli_plist_entry *plist = NULL;
if (getThis() && !ZEND_NUM_ARGS()) {
RETURN_NULL();
}
+ hostname = username = dbname = passwd = socket = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssls", &hostname, &hostname_len, &username, &username_len,
&passwd, &passwd_len, &dbname, &dbname_len, &port, &socket, &socket_len) == FAILURE) {
return;
}
- if (!socket_len) {
- socket = NULL;
+ if (!socket_len || !socket) {
+ socket = MyG(default_socket);
}
-
- /* TODO: safe mode handling */
- if (PG(sql_safe_mode)){
- } else {
- if (!passwd) {
- passwd = MyG(default_pw);
- if (!username){
- username = MyG(default_user);
- if (!hostname) {
- hostname = MyG(default_host);
- }
- }
- }
+
+ if (!passwd) {
+ passwd = MyG(default_pw);
+ passwd_len = strlen(SAFE_STR(passwd));
+ }
+ if (!username){
+ username = MyG(default_user);
+ }
+ if (!hostname || !hostname_len) {
+ hostname = MyG(default_host);
}
+
if (object && instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {
mysqli_resource = ((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr;
if (mysqli_resource && mysqli_resource->ptr &&
@@ -75,7 +85,7 @@ PHP_FUNCTION(mysqli_connect)
mysql = (MY_MYSQL*)mysqli_resource->ptr;
php_clear_mysql(mysql);
if (mysql->mysql) {
- mysql_close(mysql->mysql);
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_EXPLICIT);
mysql->mysql = NULL;
}
}
@@ -84,61 +94,194 @@ PHP_FUNCTION(mysqli_connect)
mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL));
}
+ if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) {
+ hostname += 2;
+ if (!MyG(allow_persistent)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Persistent connections are disabled. Downgrading to normal");
+ } else {
+ mysql->persistent = persistent = TRUE;
+
+ if (!strlen(hostname)) {
+ hostname = MyG(default_host);
+ }
+
+ hash_len = spprintf(&hash_key, 0, "mysqli_%s%ld%s%s%s", SAFE_STR(hostname),
+ port, SAFE_STR(username), SAFE_STR(dbname),
+ SAFE_STR(passwd));
+
+ /* check if we can reuse exisiting connection ... */
+ if (zend_hash_find(&EG(persistent_list), hash_key, hash_len + 1, (void **)&le) == SUCCESS) {
+ if (Z_TYPE_P(le) == php_le_pmysqli()) {
+ plist = (mysqli_plist_entry *) le->ptr;
+
+ do {
+ if (zend_hash_num_elements(&plist->free_links)) {
+ HashPosition pos;
+ MYSQL **free_mysql;
+ ulong idx;
+ dtor_func_t pDestructor = plist->free_links.pDestructor;
+
+ zend_hash_internal_pointer_reset_ex(&plist->free_links, &pos);
+ if (SUCCESS != zend_hash_get_current_data_ex(&plist->free_links,
+ (void **)&free_mysql, &pos)) {
+ break;
+ }
+ if (HASH_KEY_IS_LONG != zend_hash_get_current_key_ex(&plist->free_links, NULL,
+ NULL, &idx, FALSE, &pos)) {
+ break;
+ }
+ plist->free_links.pDestructor = NULL; /* Don't call pDestructor now */
+ if (SUCCESS != zend_hash_index_del(&plist->free_links, idx)) {
+ plist->used_links.pDestructor = pDestructor; /* Restore the destructor */
+ break;
+ }
+ plist->free_links.pDestructor = pDestructor; /* Restore the destructor */
+ mysql->mysql = *free_mysql;
+
+ MyG(num_inactive_persistent)--;
+ MyG(num_active_persistent)++;
+ /* reset variables */
+ /* todo: option for ping or change_user */
+#if G0
+ if (!mysql_change_user(mysql->mysql, username, passwd, dbname)) {
+#else
+ if (!mysql_ping(mysql->mysql)) {
+#endif
+#ifdef HAVE_MYSQLND
+ mysqlnd_restart_psession(mysql->mysql);
+#endif
+ idx = zend_hash_next_free_element(&plist->used_links);
+ if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &free_mysql,
+ sizeof(MYSQL *), NULL)) {
+ php_mysqli_dtor_p_elements(free_mysql);
+ MyG(num_links)--;
+ break;
+ }
+ mysql->hash_index = idx;
+ mysql->hash_key = hash_key;
+ goto end;
+ }
+ }
+ } while (0);
+ }
+ } else {
+ zend_rsrc_list_entry le;
+ le.type = php_le_pmysqli();
+ le.ptr = plist = calloc(1, sizeof(mysqli_plist_entry));
+
+ zend_hash_init(&plist->free_links, MAX(2, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1);
+ zend_hash_init(&plist->used_links, MAX(2, MyG(max_persistent)), NULL, php_mysqli_dtor_p_elements, 1);
+ zend_hash_update(&EG(persistent_list), hash_key, hash_len + 1, (void *)&le, sizeof(le), NULL);
+ }
+ }
+ }
+
+ if (MyG(max_links) != -1 && MyG(num_links) >= MyG(max_links)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open links (%ld)", MyG(num_links));
+ goto err;
+ }
+ if (persistent && MyG(max_persistent) != -1 &&
+ (MyG(num_active_persistent) + MyG(num_inactive_persistent))>= MyG(max_persistent))
+ {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many open persistent links (%ld)",
+ MyG(num_active_persistent) + MyG(num_inactive_persistent));
+ goto err;
+ }
+
+#if !defined(HAVE_MYSQLND)
if (!(mysql->mysql = mysql_init(NULL))) {
- efree(mysql);
- RETURN_FALSE;
+#else
+ if (!(mysql->mysql = mysqlnd_init(persistent))) {
+#endif
+ goto err;
}
+ new_connection = TRUE;
#ifdef HAVE_EMBEDDED_MYSQLI
- if (strcmp(hostname, ":embedded")) {
+ if (hostname_len) {
unsigned int external=1;
mysql_options(mysql->mysql, MYSQL_OPT_USE_REMOTE_CONNECTION, (char *)&external);
} else {
- hostname[0] = '\0';
mysql_options(mysql->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, 0);
}
#endif
- if (!socket) {
- socket = MyG(default_socket);
- }
-
- if (mysql_real_connect(mysql->mysql,hostname,username,passwd,dbname,port,socket,CLIENT_MULTI_RESULTS) == NULL) {
+#if !defined(HAVE_MYSQLND)
+ if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname, port, socket, CLIENT_MULTI_RESULTS) == NULL)
+#else
+ if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
+ port, socket, CLIENT_MULTI_RESULTS, MyG(mysqlnd_thd_zval_cache) TSRMLS_CC) == NULL)
+#endif
+ {
/* Save error messages */
-
- php_mysqli_throw_sql_exception( mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC,
- "%s", mysql->mysql->net.last_error);
-
php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC);
+ php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC,
+ "%s", mysql_error(mysql->mysql));
/* free mysql structure */
- mysql_close(mysql->mysql);
- efree(mysql);
- RETURN_FALSE;
+ mysqli_close(mysql->mysql, MYSQLI_CLOSE_DISCONNECTED);
+ goto err;
}
/* clear error */
php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql) TSRMLS_CC);
+#if !defined(HAVE_MYSQLND)
mysql->mysql->reconnect = MyG(reconnect);
/* set our own local_infile handler */
php_set_local_infile_handler_default(mysql);
+#endif
+
+ mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&MyG(allow_local_infile));
+end:
if (!mysqli_resource) {
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)mysql;
}
mysqli_resource->status = MYSQLI_STATUS_VALID;
+ /* store persistent connection */
+ if (persistent && new_connection) {
+ /* save persistent connection */
+ ulong hash_index = zend_hash_next_free_element(&plist->used_links);
+ if (SUCCESS != zend_hash_next_index_insert(&plist->used_links, &mysql->mysql,
+ sizeof(MYSQL *), NULL)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't store persistent connection");
+ } else {
+ mysql->hash_index = hash_index;
+ }
+ MyG(num_active_persistent)++;
+ }
+
+ mysql->hash_key = hash_key;
+ MyG(num_links)++;
+
+#if !defined(HAVE_MYSQLND)
+ mysql->multi_query = 0;
+#else
+ mysql->multi_query = 1;
+#endif
+
+
if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry);
} else {
((mysqli_object *) zend_object_store_get_object(object TSRMLS_CC))->ptr = mysqli_resource;
}
+ return;
+
+err:
+ efree(mysql);
+ if (persistent) {
+ efree(hash_key);
+ }
+ RETVAL_FALSE;
}
/* }}} */
+
/* {{{ proto int mysqli_connect_errno(void)
Returns the numerical value of the error message from last connect command */
PHP_FUNCTION(mysqli_connect_errno)
@@ -159,11 +302,30 @@ PHP_FUNCTION(mysqli_connect_error)
}
/* }}} */
+
/* {{{ proto mixed mysqli_fetch_array (object result [,int resulttype])
Fetch a result row as an associative array, a numeric array, or both */
PHP_FUNCTION(mysqli_fetch_array)
{
+#if !defined(HAVE_MYSQLND)
php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
+#else
+ MYSQL_RES *result;
+ zval *mysql_result;
+ long mode = MYSQLND_FETCH_BOTH;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
+
+ if (mode < MYSQLI_ASSOC || mode > MYSQLI_BOTH) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH");
+ RETURN_FALSE;
+ }
+
+ mysqlnd_fetch_into(result, mode, return_value, MYSQLND_MYSQLI);
+#endif
}
/* }}} */
@@ -171,20 +333,102 @@ PHP_FUNCTION(mysqli_fetch_array)
Fetch a result row as an associative array */
PHP_FUNCTION(mysqli_fetch_assoc)
{
+#if !defined(HAVE_MYSQLND)
php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0);
+#else
+ MYSQL_RES *result;
+ zval *mysql_result;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQLI);
+
+#endif
}
/* }}} */
+
+/* {{{ proto mixed mysqli_fetch_all (object result [,int resulttype])
+ Fetches all result rows as an associative array, a numeric array, or both */
+#if defined(HAVE_MYSQLND)
+PHP_FUNCTION(mysqli_fetch_all)
+{
+ MYSQL_RES *result;
+ zval *mysql_result;
+ long mode = MYSQLND_FETCH_NUM;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
+
+ if (!mode || (mode & ~MYSQLND_FETCH_BOTH)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Mode can be only MYSQLI_FETCH_NUM, "
+ "MYSQLI_FETCH_ASSOC or MYSQLI_FETCH_BOTH");
+ RETURN_FALSE;
+ }
+
+ mysqlnd_fetch_all(result, mode, return_value);
+}
+/* }}} */
+
+
+/* {{{ proto array mysqli_cache_stats(void) U
+ Returns statistics about the zval cache */
+PHP_FUNCTION(mysqli_get_cache_stats)
+{
+ if (ZEND_NUM_ARGS()) {
+ WRONG_PARAM_COUNT;
+ }
+ mysqlnd_palloc_stats(mysqli_mysqlnd_zval_cache, return_value);
+}
+/* }}} */
+
+
+/* {{{ proto array mysqli_get_client_stats(void)
+ Returns statistics about the zval cache */
+PHP_FUNCTION(mysqli_get_client_stats)
+{
+ if (ZEND_NUM_ARGS()) {
+ WRONG_PARAM_COUNT;
+ }
+ mysqlnd_get_client_stats(return_value);
+}
+/* }}} */
+
+
+/* {{{ proto array mysqli_get_connection_stats(void)
+ Returns statistics about the zval cache */
+PHP_FUNCTION(mysqli_get_connection_stats)
+{
+ MY_MYSQL *mysql;
+ zval *mysql_link;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &mysql_link, mysqli_link_class_entry) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL *, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+
+ mysqlnd_get_connection_stats(mysql->mysql, return_value);
+}
+#endif
+/* }}} */
+
+
/* {{{ proto mixed mysqli_fetch_object (object result [, string class_name [, NULL|array ctor_params]])
Fetch a result row as an object */
PHP_FUNCTION(mysqli_fetch_object)
{
php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1);
+/* todo: mysqlnd support */
}
/* }}} */
/* {{{ proto bool mysqli_multi_query(object link, string query)
- Binary-safe version of mysql_query() */
+ allows to execute multiple queries */
PHP_FUNCTION(mysqli_multi_query)
{
MY_MYSQL *mysql;
@@ -199,25 +443,30 @@ PHP_FUNCTION(mysqli_multi_query)
MYSQLI_ENABLE_MQ;
if (mysql_real_query(mysql->mysql, query, query_len)) {
+#ifndef HAVE_MYSQLND
char s_error[MYSQL_ERRMSG_SIZE], s_sqlstate[SQLSTATE_LENGTH+1];
unsigned int s_errno;
- MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
-
/* we have to save error information, cause
MYSQLI_DISABLE_MQ will reset error information */
strcpy(s_error, mysql_error(mysql->mysql));
strcpy(s_sqlstate, mysql_sqlstate(mysql->mysql));
s_errno = mysql_errno(mysql->mysql);
-
+#else
+ mysqlnd_error_info error_info = mysql->mysql->error_info;
+#endif
+ MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
MYSQLI_DISABLE_MQ;
+#ifndef HAVE_MYSQLND
/* restore error information */
strcpy(mysql->mysql->net.last_error, s_error);
strcpy(mysql->mysql->net.sqlstate, s_sqlstate);
mysql->mysql->net.last_errno = s_errno;
-
+#else
+ mysql->mysql->error_info = error_info;
+#endif
RETURN_FALSE;
- }
+ }
RETURN_TRUE;
}
/* }}} */
@@ -258,7 +507,7 @@ PHP_FUNCTION(mysqli_query)
if (!mysql_field_count(mysql->mysql)) {
/* no result set - not a SELECT */
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
RETURN_TRUE;
}
@@ -266,13 +515,13 @@ PHP_FUNCTION(mysqli_query)
result = (resultmode == MYSQLI_USE_RESULT) ? mysql_use_result(mysql->mysql) : mysql_store_result(mysql->mysql);
if (!result) {
- php_mysqli_throw_sql_exception(mysql->mysql->net.sqlstate, mysql->mysql->net.last_errno TSRMLS_CC,
- "%s", mysql->mysql->net.last_error);
+ php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC,
+ "%s", mysql_error(mysql->mysql));
RETURN_FALSE;
}
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
- php_mysqli_report_index(query, mysql->mysql->server_status TSRMLS_CC);
+ php_mysqli_report_index(query, mysqli_server_status(mysql->mysql) TSRMLS_CC);
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
@@ -282,6 +531,36 @@ PHP_FUNCTION(mysqli_query)
}
/* }}} */
+
+#if defined(HAVE_MYSQLND)
+/* {{{ proto object mysqli_stmt_get_result(object link) U
+ Buffer result set on client */
+PHP_FUNCTION(mysqli_stmt_get_result)
+{
+ MYSQL_RES *result;
+ MYSQLI_RESOURCE *mysqli_resource;
+ MY_STMT *stmt;
+ zval *mysql_stmt;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
+ return;
+ }
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
+
+ if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) {
+ MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
+ RETURN_FALSE;
+ }
+
+ mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
+ mysqli_resource->ptr = (void *)result;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
+ MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
+}
+/* }}} */
+#endif
+
+
/* {{{ proto object mysqli_get_warnings(object link) */
PHP_FUNCTION(mysqli_get_warnings)
{
@@ -296,12 +575,13 @@ PHP_FUNCTION(mysqli_get_warnings)
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
if (mysql_warning_count(mysql->mysql)) {
- w = php_get_warnings(mysql->mysql);
+ w = php_get_warnings(mysql->mysql TSRMLS_CC);
} else {
RETURN_FALSE;
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = mysqli_resource->info = (void *)w;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
}
/* }}} */
@@ -317,15 +597,16 @@ PHP_FUNCTION(mysqli_stmt_get_warnings)
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) {
return;
}
- MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", 1);
+ MYSQLI_FETCH_RESOURCE(stmt, MY_STMT*, &stmt_link, "mysqli_stmt", MYSQLI_STATUS_VALID);
- if (mysql_warning_count(stmt->stmt->mysql)) {
- w = php_get_warnings(stmt->stmt->mysql);
+ if (mysqli_stmt_warning_count(stmt->stmt)) {
+ w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt) TSRMLS_CC);
} else {
RETURN_FALSE;
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = mysqli_resource->info = (void *)w;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
}
/* }}} */
@@ -354,13 +635,19 @@ PHP_FUNCTION(mysqli_set_charset)
#endif
#ifdef HAVE_MYSQLI_GET_CHARSET
-/* {{{ proto object mysqli_get_charset(object link)
+/* {{{ proto object mysqli_get_charset(object link) U
returns a character set object */
PHP_FUNCTION(mysqli_get_charset)
{
MY_MYSQL *mysql;
zval *mysql_link;
+ char *name = NULL, *collation = NULL, *dir = NULL;
+ uint minlength, maxlength, number, state;
+#if !defined(HAVE_MYSQLND)
MY_CHARSET_INFO cs;
+#else
+ const MYSQLND_CHARSET *cs;
+#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
return;
@@ -369,16 +656,32 @@ PHP_FUNCTION(mysqli_get_charset)
object_init(return_value);
+#if !defined(HAVE_MYSQLND)
mysql_get_character_set_info(mysql->mysql, &cs);
+ name = (char *)cs.csname;
+ collation = (char *)cs.name;
+ dir = (char *)cs.dir;
+ minlength = cs.mbminlen;
+ maxlength = cs.mbmaxlen;
+ number = cs.number;
+ state = cs.state;
+#else
+ cs = mysql->mysql->charset;
+ name = cs->name;
+ collation = cs->collation;
+ minlength = cs->char_minlen;
+ maxlength = cs->char_maxlen;
+ number = cs->nr;
+ state = 1; /* all charsets are compiled in */
+#endif
- add_property_string(return_value, "charset", (cs.name) ? (char *)cs.csname : "", 1);
- add_property_string(return_value, "collation",(cs.name) ? (char *)cs.name : "", 1);
- add_property_string(return_value, "comment", (cs.comment) ? (char *)cs.comment : "", 1);
- add_property_string(return_value, "dir", (cs.dir) ? (char *)cs.dir : "", 1);
- add_property_long(return_value, "min_length", cs.mbminlen);
- add_property_long(return_value, "max_length", cs.mbmaxlen);
- add_property_long(return_value, "number", cs.number);
- add_property_long(return_value, "state", cs.state);
+ add_property_string(return_value, "charset", (name) ? (char *)name : "", 1);
+ add_property_string(return_value, "collation",(collation) ? (char *)collation : "", 1);
+ add_property_string(return_value, "dir", (dir) ? (char *)dir : "", 1);
+ add_property_long(return_value, "min_length", minlength);
+ add_property_long(return_value, "max_length", maxlength);
+ add_property_long(return_value, "number", number);
+ add_property_long(return_value, "state", state);
}
/* }}} */
#endif
diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c
index fd0b8a21fa..fd8281b551 100644
--- a/ext/mysqli/mysqli_prop.c
+++ b/ext/mysqli/mysqli_prop.c
@@ -27,7 +27,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
#define CHECK_STATUS(value) \
if (((MYSQLI_RESOURCE *)obj->ptr)->status < value ) { \
@@ -221,24 +221,23 @@ static int result_type_read(mysqli_object *obj, zval **retval TSRMLS_DC)
static int result_lengths_read(mysqli_object *obj, zval **retval TSRMLS_DC)
{
MYSQL_RES *p;
+ ulong *ret;
ALLOC_ZVAL(*retval);
CHECK_STATUS(MYSQLI_STATUS_VALID);
p = (MYSQL_RES *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
- if (!p || !p->field_count) {
+ if (!p || !p->field_count || !(ret = mysql_fetch_lengths(p)))
+ {
ZVAL_NULL(*retval);
} else {
ulong i;
- zval *l;
array_init(*retval);
for (i=0; i < p->field_count; i++) {
- MAKE_STD_ZVAL(l);
- ZVAL_LONG(l, p->lengths[i]);
- add_index_zval(*retval, i, l);
- }
+ add_index_long(*retval, i, ret[i]);
+ }
}
return SUCCESS;
}
@@ -312,7 +311,7 @@ MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_error_read, mysql_stmt_error, MYSQLI_GET_ST
MYSQLI_MAP_PROPERTY_FUNC_STRING(stmt_sqlstate_read, mysql_stmt_sqlstate, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED));
/* }}} */
-mysqli_property_entry mysqli_link_property_entries[] = {
+const mysqli_property_entry mysqli_link_property_entries[] = {
{"affected_rows", link_affected_rows_read, NULL},
{"client_info", link_client_info_read, NULL},
{"client_version", link_client_version_read, NULL},
@@ -333,7 +332,7 @@ mysqli_property_entry mysqli_link_property_entries[] = {
{NULL, NULL, NULL}
};
-mysqli_property_entry mysqli_result_property_entries[] = {
+const mysqli_property_entry mysqli_result_property_entries[] = {
{"current_field", result_current_field_read, NULL},
{"field_count", result_field_count_read, NULL},
{"lengths", result_lengths_read, NULL},
@@ -342,7 +341,7 @@ mysqli_property_entry mysqli_result_property_entries[] = {
{NULL, NULL, NULL}
};
-mysqli_property_entry mysqli_stmt_property_entries[] = {
+const mysqli_property_entry mysqli_stmt_property_entries[] = {
{"affected_rows", stmt_affected_rows_read, NULL},
{"insert_id", stmt_insert_id_read, NULL},
{"num_rows", stmt_num_rows_read, NULL},
diff --git a/ext/mysqli/mysqli_repl.c b/ext/mysqli/mysqli_repl.c
index b12706fa6b..8096c0239a 100644
--- a/ext/mysqli/mysqli_repl.c
+++ b/ext/mysqli/mysqli_repl.c
@@ -27,7 +27,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
/* {{{ proto void mysqli_disable_reads_from_master(object link)
*/
diff --git a/ext/mysqli/mysqli_report.c b/ext/mysqli/mysqli_report.c
index be6e857226..dae2f9242d 100644
--- a/ext/mysqli/mysqli_report.c
+++ b/ext/mysqli/mysqli_report.c
@@ -25,7 +25,7 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
/* {{{ proto bool mysqli_report(int flags)
sets report level */
@@ -45,13 +45,14 @@ PHP_FUNCTION(mysqli_report)
/* }}} */
/* {{{ void php_mysqli_report_error(char *sqlstate, int errorno, char *error) */
-void php_mysqli_report_error(char *sqlstate, int errorno, char *error TSRMLS_DC) {
- php_mysqli_throw_sql_exception(sqlstate, errorno TSRMLS_CC, "%s", error);
+void php_mysqli_report_error(const char *sqlstate, int errorno, const char *error TSRMLS_DC)
+{
+ php_mysqli_throw_sql_exception((char *)sqlstate, errorno TSRMLS_CC, "%s", error);
}
/* }}} */
/* {{{ void php_mysqli_report_index() */
-void php_mysqli_report_index(char *query, unsigned int status TSRMLS_DC) {
+void php_mysqli_report_index(const char *query, unsigned int status TSRMLS_DC) {
char index[15];
if (status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c
index 92dc8ce327..7b02d9117a 100644
--- a/ext/mysqli/mysqli_warning.c
+++ b/ext/mysqli/mysqli_warning.c
@@ -25,55 +25,144 @@
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
-#include "php_mysqli.h"
+#include "php_mysqli_structs.h"
+
+/* Define these in the PHP5 tree to make merging easy process */
+#define ZSTR_DUPLICATE (1<<0)
+#define ZSTR_AUTOFREE (1<<1)
+
+#define ZVAL_UTF8_STRING(z, s, flags) ZVAL_STRING((z), (char*)(s), ((flags) & ZSTR_DUPLICATE))
+#define ZVAL_UTF8_STRINGL(z, s, l, flags) ZVAL_STRINGL((z), (char*)(s), (l), ((flags) & ZSTR_DUPLICATE))
+
/* {{{ void php_clear_warnings() */
void php_clear_warnings(MYSQLI_WARNING *w)
{
- MYSQLI_WARNING *n;
+ MYSQLI_WARNING *n;
while (w) {
n = w;
- efree(w->reason);
+ zval_dtor(&(w->reason));
+ zval_dtor(&(w->sqlstate));
w = w->next;
efree(n);
}
}
/* }}} */
+
+#ifndef HAVE_MYSQLND
/* {{{ MYSQLI_WARNING *php_new_warning */
-MYSQLI_WARNING *php_new_warning(char *reason, char *sqlstate, int errorno)
+static
+MYSQLI_WARNING *php_new_warning(const char *reason, int errorno TSRMLS_DC)
{
- MYSQLI_WARNING *w;
+ MYSQLI_WARNING *w;
w = (MYSQLI_WARNING *)ecalloc(1, sizeof(MYSQLI_WARNING));
- w->reason = safe_estrdup(reason);
- if (sqlstate) {
- strcpy(w->sqlstate, sqlstate);
- } else {
- strcpy(w->sqlstate, "00000");
- }
+ ZVAL_UTF8_STRING(&(w->reason), reason, ZSTR_DUPLICATE);
+
+ ZVAL_UTF8_STRINGL(&(w->sqlstate), "HY000", sizeof("HY000") - 1, ZSTR_DUPLICATE);
+
w->errorno = errorno;
return w;
}
/* }}} */
-/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql) */
-MYSQLI_WARNING *php_get_warnings(MYSQL *mysql)
+
+/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC) */
+MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC)
{
- MYSQLI_WARNING *w, *first = NULL, *prev = NULL;
+ MYSQLI_WARNING *w, *first = NULL, *prev = NULL;
MYSQL_RES *result;
MYSQL_ROW row;
- if (mysql_query(mysql, "SHOW WARNINGS")) {
+ if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) {
return NULL;
}
result = mysql_store_result(mysql);
+
while ((row = mysql_fetch_row(result))) {
- w = php_new_warning(row[2], "HY000", atoi(row[1]));
+ w = php_new_warning(row[2], atoi(row[1]) TSRMLS_CC);
+ if (!first) {
+ first = w;
+ }
+ if (prev) {
+ prev->next = w;
+ }
+ prev = w;
+ }
+ mysql_free_result(result);
+ return first;
+}
+/* }}} */
+#else
+/* {{{ MYSQLI_WARNING *php_new_warning */
+static
+MYSQLI_WARNING *php_new_warning(const zval *reason, int errorno TSRMLS_DC)
+{
+ MYSQLI_WARNING *w;
+
+ w = (MYSQLI_WARNING *)ecalloc(1, sizeof(MYSQLI_WARNING));
+
+ w->reason = *reason;
+ zval_copy_ctor(&(w->reason));
+
+ ZVAL_UTF8_STRINGL(&(w->reason), Z_STRVAL(w->reason), Z_STRLEN(w->reason), ZSTR_AUTOFREE);
+
+ ZVAL_UTF8_STRINGL(&(w->sqlstate), "HY000", sizeof("HY000") - 1, ZSTR_DUPLICATE);
+
+ w->errorno = errorno;
+
+ return w;
+}
+/* }}} */
+
+
+/* {{{ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC) */
+MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC)
+{
+ MYSQLI_WARNING *w, *first = NULL, *prev = NULL;
+ MYSQL_RES *result;
+ zval *row;
+
+ if (mysql_real_query(mysql, "SHOW WARNINGS", 13)) {
+ return NULL;
+ }
+
+ result = mysql_use_result(mysql);
+
+ for (;;) {
+ zval **entry;
+ int errno;
+
+ MAKE_STD_ZVAL(row);
+ mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, row, MYSQLND_MYSQLI);
+ if (Z_TYPE_P(row) != IS_ARRAY) {
+ zval_ptr_dtor(&row);
+ break;
+ }
+ zend_hash_internal_pointer_reset(Z_ARRVAL_P(row));
+ /* 0. we don't care about the first */
+ zend_hash_move_forward(Z_ARRVAL_P(row));
+
+ /* 1. Here comes the error no */
+ zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry);
+ convert_to_long_ex(entry);
+ errno = Z_LVAL_PP(entry);
+ zend_hash_move_forward(Z_ARRVAL_P(row));
+
+ /* 2. Here comes the reason */
+ zend_hash_get_current_data(Z_ARRVAL_P(row), (void **)&entry);
+
+ w = php_new_warning(*entry, errno TSRMLS_CC);
+ /*
+ Don't destroy entry, because the row destroy will decrease
+ the refcounter. Decreased twice then mysqlnd_free_result()
+ will crash, because it will try to access already freed memory.
+ */
if (!first) {
first = w;
}
@@ -81,11 +170,16 @@ MYSQLI_WARNING *php_get_warnings(MYSQL *mysql)
prev->next = (void *)w;
}
prev = w;
+
+ zval_ptr_dtor(&row);
}
+
mysql_free_result(result);
return first;
}
/* }}} */
+#endif
+
/* {{{ bool mysqli_warning::next() */
PHP_METHOD(mysqli_warning, next)
@@ -112,7 +206,9 @@ PHP_METHOD(mysqli_warning, next)
}
/* }}} */
+
/* {{{ property mysqli_warning_message */
+static
int mysqli_warning_message(mysqli_object *obj, zval **retval TSRMLS_DC)
{
MYSQLI_WARNING *w;
@@ -122,17 +218,16 @@ int mysqli_warning_message(mysqli_object *obj, zval **retval TSRMLS_DC)
}
w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
- ALLOC_ZVAL(*retval);
- if (w->reason) {
- ZVAL_STRING(*retval, w->reason, 1);
- } else {
- ZVAL_NULL(*retval);
- }
+ MAKE_STD_ZVAL(*retval);
+ **retval = w->reason;
+ zval_copy_ctor(*retval);
return SUCCESS;
}
/* }}} */
+
/* {{{ property mysqli_warning_sqlstate */
+static
int mysqli_warning_sqlstate(mysqli_object *obj, zval **retval TSRMLS_DC)
{
MYSQLI_WARNING *w;
@@ -142,13 +237,16 @@ int mysqli_warning_sqlstate(mysqli_object *obj, zval **retval TSRMLS_DC)
}
w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
- ALLOC_ZVAL(*retval);
- ZVAL_STRING(*retval, w->sqlstate, 1);
+ MAKE_STD_ZVAL(*retval);
+ **retval = w->sqlstate;
+ zval_copy_ctor(*retval);
return SUCCESS;
}
/* }}} */
+
/* {{{ property mysqli_warning_error */
+static
int mysqli_warning_errno(mysqli_object *obj, zval **retval TSRMLS_DC)
{
MYSQLI_WARNING *w;
@@ -157,7 +255,7 @@ int mysqli_warning_errno(mysqli_object *obj, zval **retval TSRMLS_DC)
return FAILURE;
}
w = (MYSQLI_WARNING *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
- ALLOC_ZVAL(*retval);
+ MAKE_STD_ZVAL(*retval);
ZVAL_LONG(*retval, w->errorno);
return SUCCESS;
}
@@ -187,20 +285,22 @@ PHP_METHOD(mysqli_warning, __construct)
} else if (obj->zo.ce == mysqli_stmt_class_entry) {
MY_STMT *stmt;
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &z, "mysqli_stmt", MYSQLI_STATUS_VALID);
- hdl = stmt->stmt->mysql;
+ hdl = mysqli_stmt_get_connection(stmt->stmt);
} else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid class argument");
RETURN_FALSE;
}
if (mysql_warning_count(hdl)) {
- w = php_get_warnings(hdl);
+ w = php_get_warnings(hdl TSRMLS_CC);
} else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "No warnings found");
RETURN_FALSE;
}
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
- mysqli_resource->status = MYSQLI_STATUS_VALID;
mysqli_resource->ptr = mysqli_resource->info = (void *)w;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
if (!getThis() || !instanceof_function(Z_OBJCE_P(getThis()), mysqli_warning_class_entry TSRMLS_CC)) {
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
@@ -211,18 +311,22 @@ PHP_METHOD(mysqli_warning, __construct)
}
/* }}} */
+/* {{{ mysqli_warning_methods */
const zend_function_entry mysqli_warning_methods[] = {
PHP_ME(mysqli_warning, __construct, NULL, ZEND_ACC_PROTECTED)
PHP_ME(mysqli_warning, next, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
+/* }}} */
-mysqli_property_entry mysqli_warning_property_entries[] = {
+/* {{{ mysqli_warning_property_entries */
+const mysqli_property_entry mysqli_warning_property_entries[] = {
{"message", mysqli_warning_message, NULL},
{"sqlstate", mysqli_warning_sqlstate, NULL},
{"errno", mysqli_warning_errno, NULL},
{NULL, NULL, NULL}
};
+/* }}} */
/*
* Local variables:
diff --git a/ext/mysqli/php_mysqli.h b/ext/mysqli/php_mysqli.h
index 965bec7082..d166d00f52 100644
--- a/ext/mysqli/php_mysqli.h
+++ b/ext/mysqli/php_mysqli.h
@@ -12,299 +12,17 @@
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
- | Author: Georg Richter <georg@php.net> |
+ | Authors: Georg Richter <georg@php.net> |
+ | Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+----------------------------------------------------------------------+
$Id$
*/
-/* A little hack to prevent build break, when mysql is used together with
- * c-client, which also defines LIST.
- */
-#ifdef LIST
-#undef LIST
-#endif
-
-#include <mysql.h>
-
-/* character set support */
-#if MYSQL_VERSION_ID > 50009
-#define HAVE_MYSQLI_GET_CHARSET
-#endif
-
-#if (MYSQL_VERSION_ID > 40112 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID > 50005
-#define HAVE_MYSQLI_SET_CHARSET
-#endif
-
-
-#include <errmsg.h>
-
#ifndef PHP_MYSQLI_H
#define PHP_MYSQLI_H
-#define MYSQLI_VERSION_ID 101009
-
-enum mysqli_status {
- MYSQLI_STATUS_UNKNOWN=0,
- MYSQLI_STATUS_CLEARED,
- MYSQLI_STATUS_INITIALIZED,
- MYSQLI_STATUS_VALID
-};
-
-typedef struct {
- ulong buflen;
- char *val;
- ulong type;
-} VAR_BUFFER;
-
-typedef struct {
- unsigned int var_cnt;
- VAR_BUFFER *buf;
- zval **vars;
- char *is_null;
-} BIND_BUFFER;
-
-typedef struct {
- MYSQL_STMT *stmt;
- BIND_BUFFER param;
- BIND_BUFFER result;
- char *query;
-} MY_STMT;
-
-typedef struct {
- MYSQL *mysql;
- zval *li_read;
- php_stream *li_stream;
- unsigned int multi_query;
-} MY_MYSQL;
-
-typedef struct {
- int mode;
- int socket;
- FILE *fp;
-} PROFILER;
-
-typedef struct {
- void *ptr; /* resource: (mysql, result, stmt) */
- void *info; /* additional buffer */
- enum mysqli_status status;
-} MYSQLI_RESOURCE;
-
-typedef struct _mysqli_object {
- zend_object zo;
- void *ptr;
- HashTable *prop_handler;
-} mysqli_object; /* extends zend_object */
-
-typedef struct {
- char *reason;
- char sqlstate[6];
- int errorno;
- void *next;
-} MYSQLI_WARNING;
-
-typedef struct _mysqli_property_entry {
- char *pname;
- int (*r_func)(mysqli_object *obj, zval **retval TSRMLS_DC);
- int (*w_func)(mysqli_object *obj, zval *value TSRMLS_DC);
-} mysqli_property_entry;
-
-typedef struct {
- char error_msg[LOCAL_INFILE_ERROR_LEN];
- void *userdata;
-} mysqli_local_infile;
-
-#define phpext_mysqli_ptr &mysqli_module_entry
-
-#ifdef PHP_WIN32
-#define PHP_MYSQLI_API __declspec(dllexport)
-#define MYSQLI_LLU_SPEC "%I64u"
-#define MYSQLI_LL_SPEC "%I64d"
-#else
-#define PHP_MYSQLI_API
-#define MYSQLI_LLU_SPEC "%llu"
-#define MYSQLI_LL_SPEC "%lld"
-#endif
-
-#ifdef ZTS
-#include "TSRM.h"
-#endif
-
-#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type
-
-extern zend_module_entry mysqli_module_entry;
-extern const zend_function_entry mysqli_functions[];
-extern const zend_function_entry mysqli_link_methods[];
-extern const zend_function_entry mysqli_stmt_methods[];
-extern const zend_function_entry mysqli_result_methods[];
-extern const zend_function_entry mysqli_driver_methods[];
-extern const zend_function_entry mysqli_warning_methods[];
-extern const zend_function_entry mysqli_exception_methods[];
-
-extern mysqli_property_entry mysqli_link_property_entries[];
-extern mysqli_property_entry mysqli_result_property_entries[];
-extern mysqli_property_entry mysqli_stmt_property_entries[];
-extern mysqli_property_entry mysqli_driver_property_entries[];
-extern mysqli_property_entry mysqli_warning_property_entries[];
-
-extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag, int into_object);
-extern void php_clear_stmt_bind(MY_STMT *stmt);
-extern void php_clear_mysql(MY_MYSQL *);
-extern MYSQLI_WARNING *php_get_warnings(MYSQL *mysql);
-extern void php_clear_warnings(MYSQLI_WARNING *w);
-extern void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type);
-extern void php_mysqli_report_error(char *sqlstate, int errorno, char *error TSRMLS_DC);
-extern void php_mysqli_report_index(char *query, unsigned int status TSRMLS_DC);
-extern int php_local_infile_init(void **, const char *, void *);
-extern int php_local_infile_read(void *, char *, uint);
-extern void php_local_infile_end(void *);
-extern int php_local_infile_error(void *, char *, uint);
-extern void php_set_local_infile_handler_default(MY_MYSQL *);
-extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno TSRMLS_DC, char *format, ...);
-extern zend_class_entry *mysqli_link_class_entry;
-extern zend_class_entry *mysqli_stmt_class_entry;
-extern zend_class_entry *mysqli_result_class_entry;
-extern zend_class_entry *mysqli_driver_class_entry;
-extern zend_class_entry *mysqli_warning_class_entry;
-extern zend_class_entry *mysqli_exception_class_entry;
-
-#ifdef HAVE_SPL
-extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
-#endif
-
-PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRMLS_DC);
-
-#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \
- mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \
- mysql->multi_query = 0; \
-}
-
-#define MYSQLI_ENABLE_MQ if (!mysql->multi_query) { \
- mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); \
- mysql->multi_query = 1; \
-}
-
-#define REGISTER_MYSQLI_CLASS_ENTRY(name, mysqli_entry, class_functions) { \
- zend_class_entry ce; \
- INIT_CLASS_ENTRY(ce, name,class_functions); \
- ce.create_object = mysqli_objects_new; \
- mysqli_entry = zend_register_internal_class(&ce TSRMLS_CC); \
-} \
-
-#define MYSQLI_REGISTER_RESOURCE_EX(__ptr, __zval) \
- ((mysqli_object *) zend_object_store_get_object(__zval TSRMLS_CC))->ptr = __ptr; \
-
-#define MYSQLI_RETURN_RESOURCE(__ptr, __ce) \
- Z_TYPE_P(return_value) = IS_OBJECT; \
- (return_value)->value.obj = mysqli_objects_new(__ce TSRMLS_CC); \
- MYSQLI_REGISTER_RESOURCE_EX(__ptr, return_value)
-
-#define MYSQLI_REGISTER_RESOURCE(__ptr, __ce) \
-{\
- zval *object = getThis();\
- if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {\
- object = return_value;\
- Z_TYPE_P(object) = IS_OBJECT;\
- (object)->value.obj = mysqli_objects_new(__ce TSRMLS_CC);\
- }\
- MYSQLI_REGISTER_RESOURCE_EX(__ptr, object)\
-}
-
-#define MYSQLI_FETCH_RESOURCE(__ptr, __type, __id, __name, __check) \
-{ \
- MYSQLI_RESOURCE *my_res; \
- mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
- if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {\
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", intern->zo.ce->name);\
- RETURN_NULL();\
- }\
- __ptr = (__type)my_res->ptr; \
- if (__check && my_res->status < __check) { \
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid object or resource %s\n", intern->zo.ce->name); \
- RETURN_NULL();\
- }\
-}
-
-#define MYSQLI_SET_STATUS(__id, __value) \
-{ \
- mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
- ((MYSQLI_RESOURCE *)intern->ptr)->status = __value; \
-} \
-
-#define MYSQLI_CLEAR_RESOURCE(__id) \
-{ \
- mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
- efree(intern->ptr); \
- intern->ptr = NULL; \
-}
-
-#define MYSQLI_RETURN_LONG_LONG(__val) \
-{ \
- if ((__val) < LONG_MAX) { \
- RETURN_LONG((long) (__val)); \
- } else { \
- char *ret; \
- /* always used with my_ulonglong -> %llu */ \
- int l = spprintf(&ret, 0, "%llu", (__val)); \
- RETURN_STRINGL(ret, l, 0); \
- } \
-}
-
-#define MYSQLI_ADD_PROPERTIES(a,b) \
-{ \
- int i = 0; \
- while (b[i].pname != NULL) { \
- mysqli_add_property(a, b[i].pname, (mysqli_read_t)b[i].r_func, (mysqli_write_t)b[i].w_func TSRMLS_CC); \
- i++; \
- }\
-}
-
-#if WIN32|WINNT
-#define SCLOSE(a) closesocket(a)
-#else
-#define SCLOSE(a) close(a)
-#endif
-
-#define MYSQLI_STORE_RESULT 0
-#define MYSQLI_USE_RESULT 1
-
-/* for mysqli_fetch_assoc */
-#define MYSQLI_ASSOC 1
-#define MYSQLI_NUM 2
-#define MYSQLI_BOTH 3
-
-/* for mysqli_bind_param */
-#define MYSQLI_BIND_INT 1
-#define MYSQLI_BIND_DOUBLE 2
-#define MYSQLI_BIND_STRING 3
-#define MYSQLI_BIND_SEND_DATA 4
-
-/* fetch types */
-#define FETCH_SIMPLE 1
-#define FETCH_RESULT 2
-
-/*** REPORT MODES ***/
-#define MYSQLI_REPORT_OFF 0
-#define MYSQLI_REPORT_ERROR 1
-#define MYSQLI_REPORT_STRICT 2
-#define MYSQLI_REPORT_INDEX 4
-#define MYSQLI_REPORT_CLOSE 8
-#define MYSQLI_REPORT_ALL 255
-
-#define MYSQLI_REPORT_MYSQL_ERROR(mysql) \
-if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql->net.last_errno) { \
- php_mysqli_report_error(mysql->net.sqlstate, mysql->net.last_errno, mysql->net.last_error TSRMLS_CC); \
-}
-
-#define MYSQLI_REPORT_STMT_ERROR(stmt) \
-if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && stmt->last_errno) { \
- php_mysqli_report_error(stmt->sqlstate, stmt->last_errno, stmt->last_error TSRMLS_CC); \
-}
-
-PHP_MYSQLI_API void mysqli_register_link(zval *return_value, void *link TSRMLS_DC);
-PHP_MYSQLI_API void mysqli_register_stmt(zval *return_value, void *stmt TSRMLS_DC);
-PHP_MYSQLI_API void mysqli_register_result(zval *return_value, void *result TSRMLS_DC);
-PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRMLS_DC);
PHP_MINIT_FUNCTION(mysqli);
PHP_MSHUTDOWN_FUNCTION(mysqli);
@@ -317,9 +35,7 @@ PHP_FUNCTION(mysqli_affected_rows);
PHP_FUNCTION(mysqli_autocommit);
PHP_FUNCTION(mysqli_change_user);
PHP_FUNCTION(mysqli_character_set_name);
-#ifdef HAVE_MYSQLI_SET_CHARSET
PHP_FUNCTION(mysqli_set_charset);
-#endif
PHP_FUNCTION(mysqli_close);
PHP_FUNCTION(mysqli_commit);
PHP_FUNCTION(mysqli_connect);
@@ -334,6 +50,7 @@ PHP_FUNCTION(mysqli_enable_reads_from_master);
PHP_FUNCTION(mysqli_enable_rpl_parse);
PHP_FUNCTION(mysqli_errno);
PHP_FUNCTION(mysqli_error);
+PHP_FUNCTION(mysqli_fetch_all);
PHP_FUNCTION(mysqli_fetch_array);
PHP_FUNCTION(mysqli_fetch_assoc);
PHP_FUNCTION(mysqli_fetch_object);
@@ -346,9 +63,10 @@ PHP_FUNCTION(mysqli_field_count);
PHP_FUNCTION(mysqli_field_seek);
PHP_FUNCTION(mysqli_field_tell);
PHP_FUNCTION(mysqli_free_result);
-#ifdef HAVE_MYSQLI_GET_CHARSET
+PHP_FUNCTION(mysqli_get_cache_stats);
+PHP_FUNCTION(mysqli_get_client_stats);
+PHP_FUNCTION(mysqli_get_connection_stats);
PHP_FUNCTION(mysqli_get_charset);
-#endif
PHP_FUNCTION(mysqli_get_client_info);
PHP_FUNCTION(mysqli_get_client_version);
PHP_FUNCTION(mysqli_get_host_info);
@@ -408,6 +126,7 @@ PHP_FUNCTION(mysqli_stmt_data_seek);
PHP_FUNCTION(mysqli_stmt_errno);
PHP_FUNCTION(mysqli_stmt_error);
PHP_FUNCTION(mysqli_stmt_free_result);
+PHP_FUNCTION(mysqli_stmt_get_result);
PHP_FUNCTION(mysqli_stmt_get_warnings);
PHP_FUNCTION(mysqli_stmt_reset);
PHP_FUNCTION(mysqli_stmt_insert_id);
@@ -425,53 +144,11 @@ ZEND_FUNCTION(mysqli_result_construct);
ZEND_FUNCTION(mysqli_driver_construct);
ZEND_METHOD(mysqli_warning,__construct);
-ZEND_BEGIN_MODULE_GLOBALS(mysqli)
- long default_link;
- long num_links;
- long max_links;
- unsigned int default_port;
- char *default_host;
- char *default_user;
- char *default_socket;
- char *default_pw;
- int reconnect;
- int strict;
- long error_no;
- char *error_msg;
- int report_mode;
- HashTable *report_ht;
- unsigned int multi_query;
- unsigned int embedded;
-ZEND_END_MODULE_GLOBALS(mysqli)
-
-
-#define MYSQLI_PROPERTY(a) extern int a(mysqli_object *obj, zval **retval TSRMLS_DC)
-
-MYSQLI_PROPERTY(my_prop_link_host);
-
-#ifdef ZTS
-#define MyG(v) TSRMG(mysqli_globals_id, zend_mysqli_globals *, v)
-#else
-#define MyG(v) (mysqli_globals.v)
-#endif
-
-#define my_estrdup(x) (x) ? estrdup(x) : NULL
-#define my_efree(x) if (x) efree(x)
-
-#ifdef PHP_WIN32
-#define L64(x) x##i64
-typedef __int64 my_longlong;
-#else
-#define L64(x) x##LL
-typedef long long my_longlong;
-#endif
-
-
-ZEND_EXTERN_MODULE_GLOBALS(mysqli)
+#define phpext_mysqli_ptr &mysqli_module_entry
+extern zend_module_entry mysqli_module_entry;
#endif /* PHP_MYSQLI.H */
-
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h
new file mode 100644
index 0000000000..5c6e23f4f3
--- /dev/null
+++ b/ext/mysqli/php_mysqli_structs.h
@@ -0,0 +1,396 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2007 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. |
+ +----------------------------------------------------------------------+
+ | Author: Georg Richter <georg@php.net> |
+ +----------------------------------------------------------------------+
+
+ $Id$
+*/
+
+#ifndef PHP_MYSQLI_STRUCTS_H
+#define PHP_MYSQLI_STRUCTS_H
+
+/* A little hack to prevent build break, when mysql is used together with
+ * c-client, which also defines LIST.
+ */
+#ifdef LIST
+#undef LIST
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef HAVE_MYSQLND
+#include "ext/mysqlnd/mysqlnd.h"
+#include "ext/mysqli/mysqli_mysqlnd.h"
+#else
+#include <mysql.h>
+#include <errmsg.h>
+#include "ext/mysqli/mysqli_libmysql.h"
+#endif
+
+#include "php_mysqli.h"
+
+/* character set support */
+#if defined(MYSQLND_VERSION_ID) || MYSQL_VERSION_ID > 50009
+#define HAVE_MYSQLI_GET_CHARSET
+#endif
+
+#if defined(MYSQLND_VERSION_ID) || (MYSQL_VERSION_ID > 40112 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID > 50005
+#define HAVE_MYSQLI_SET_CHARSET
+#endif
+
+#define MYSQLI_VERSION_ID 101009
+
+enum mysqli_status {
+ MYSQLI_STATUS_UNKNOWN=0,
+ MYSQLI_STATUS_CLEARED,
+ MYSQLI_STATUS_INITIALIZED,
+ MYSQLI_STATUS_VALID
+};
+
+typedef struct {
+ ulong buflen;
+ char *val;
+ ulong type;
+} VAR_BUFFER;
+
+typedef struct {
+ unsigned int var_cnt;
+ VAR_BUFFER *buf;
+ zval **vars;
+ char *is_null;
+} BIND_BUFFER;
+
+typedef struct {
+ MYSQL_STMT *stmt;
+ BIND_BUFFER param;
+ BIND_BUFFER result;
+ char *query;
+} MY_STMT;
+
+typedef struct {
+ MYSQL *mysql;
+ char *hash_key;
+ zval *li_read;
+ php_stream *li_stream;
+ zend_bool persistent;
+ unsigned long hash_index; /* Used when persistent, hold the index in plist->used_links */
+ unsigned int multi_query;
+} MY_MYSQL;
+
+typedef struct {
+ int mode;
+ int socket;
+ FILE *fp;
+} PROFILER;
+
+typedef struct {
+ void *ptr; /* resource: (mysql, result, stmt) */
+ void *info; /* additional buffer */
+ enum mysqli_status status; /* object status */
+} MYSQLI_RESOURCE;
+
+typedef struct _mysqli_object {
+ zend_object zo;
+ void *ptr;
+ HashTable *prop_handler;
+} mysqli_object; /* extends zend_object */
+
+typedef struct st_mysqli_warning MYSQLI_WARNING;
+
+struct st_mysqli_warning {
+ zval reason;
+ zval sqlstate;
+ int errorno;
+ MYSQLI_WARNING *next;
+};
+
+typedef struct _mysqli_property_entry {
+ char *pname;
+ int (*r_func)(mysqli_object *obj, zval **retval TSRMLS_DC);
+ int (*w_func)(mysqli_object *obj, zval *value TSRMLS_DC);
+} mysqli_property_entry;
+
+#if !defined(HAVE_MYSQLND)
+typedef struct {
+ char error_msg[LOCAL_INFILE_ERROR_LEN];
+ void *userdata;
+} mysqli_local_infile;
+#endif
+
+typedef struct {
+ HashTable free_links;
+ HashTable used_links;
+} mysqli_plist_entry;
+
+#ifdef PHP_WIN32
+#define PHP_MYSQLI_API __declspec(dllexport)
+#define MYSQLI_LLU_SPEC "%I64u"
+#define MYSQLI_LL_SPEC "%I64d"
+#define L64(x) x##i64
+typedef __int64 my_longlong;
+#else
+#define PHP_MYSQLI_API
+#define MYSQLI_LLU_SPEC "%llu"
+#define MYSQLI_LL_SPEC "%lld"
+#define L64(x) x##LL
+typedef long long my_longlong;
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type
+
+extern const zend_function_entry mysqli_functions[];
+extern const zend_function_entry mysqli_link_methods[];
+extern const zend_function_entry mysqli_stmt_methods[];
+extern const zend_function_entry mysqli_result_methods[];
+extern const zend_function_entry mysqli_driver_methods[];
+extern const zend_function_entry mysqli_warning_methods[];
+extern const zend_function_entry mysqli_exception_methods[];
+
+extern const mysqli_property_entry mysqli_link_property_entries[];
+extern const mysqli_property_entry mysqli_result_property_entries[];
+extern const mysqli_property_entry mysqli_stmt_property_entries[];
+extern const mysqli_property_entry mysqli_driver_property_entries[];
+extern const mysqli_property_entry mysqli_warning_property_entries[];
+
+#ifdef HAVE_MYSQLND
+extern MYSQLND_ZVAL_PCACHE *mysqli_mysqlnd_zval_cache;
+extern MYSQLND_QCACHE *mysqli_mysqlnd_qcache;
+#endif
+
+extern void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flag, int into_object);
+extern void php_clear_stmt_bind(MY_STMT *stmt TSRMLS_DC);
+extern void php_clear_mysql(MY_MYSQL *);
+extern MYSQLI_WARNING *php_get_warnings(MYSQL *mysql TSRMLS_DC);
+extern void php_clear_warnings(MYSQLI_WARNING *w);
+extern void php_free_stmt_bind_buffer(BIND_BUFFER bbuf, int type);
+extern void php_mysqli_report_error(const char *sqlstate, int errorno, const char *error TSRMLS_DC);
+extern void php_mysqli_report_index(const char *query, unsigned int status TSRMLS_DC);
+extern int php_local_infile_init(void **, const char *, void *);
+extern int php_local_infile_read(void *, char *, uint);
+extern void php_local_infile_end(void *);
+extern int php_local_infile_error(void *, char *, uint);
+extern void php_set_local_infile_handler_default(MY_MYSQL *);
+extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno TSRMLS_DC, char *format, ...);
+extern zend_class_entry *mysqli_link_class_entry;
+extern zend_class_entry *mysqli_stmt_class_entry;
+extern zend_class_entry *mysqli_result_class_entry;
+extern zend_class_entry *mysqli_driver_class_entry;
+extern zend_class_entry *mysqli_warning_class_entry;
+extern zend_class_entry *mysqli_exception_class_entry;
+extern int php_le_pmysqli(void);
+extern void php_mysqli_dtor_p_elements(void *data);
+
+#ifdef HAVE_SPL
+extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
+#endif
+
+PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRMLS_DC);
+
+#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \
+ mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \
+ mysql->multi_query = 0; \
+}
+
+#define MYSQLI_ENABLE_MQ if (!mysql->multi_query) { \
+ mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); \
+ mysql->multi_query = 1; \
+}
+
+#define REGISTER_MYSQLI_CLASS_ENTRY(name, mysqli_entry, class_functions) { \
+ zend_class_entry ce; \
+ INIT_CLASS_ENTRY(ce, name,class_functions); \
+ ce.create_object = mysqli_objects_new; \
+ mysqli_entry = zend_register_internal_class(&ce TSRMLS_CC); \
+} \
+
+#define MYSQLI_REGISTER_RESOURCE_EX(__ptr, __zval) \
+ ((mysqli_object *) zend_object_store_get_object(__zval TSRMLS_CC))->ptr = __ptr;
+
+#define MYSQLI_RETURN_RESOURCE(__ptr, __ce) \
+ Z_TYPE_P(return_value) = IS_OBJECT; \
+ (return_value)->value.obj = mysqli_objects_new(__ce TSRMLS_CC); \
+ MYSQLI_REGISTER_RESOURCE_EX(__ptr, return_value)
+
+#define MYSQLI_REGISTER_RESOURCE(__ptr, __ce) \
+{\
+ zval *object = getThis();\
+ if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry TSRMLS_CC)) {\
+ object = return_value;\
+ Z_TYPE_P(object) = IS_OBJECT;\
+ (object)->value.obj = mysqli_objects_new(__ce TSRMLS_CC);\
+ }\
+ MYSQLI_REGISTER_RESOURCE_EX(__ptr, object)\
+}
+
+#define MYSQLI_FETCH_RESOURCE(__ptr, __type, __id, __name, __check) \
+{ \
+ MYSQLI_RESOURCE *my_res; \
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
+ if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {\
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't fetch %s", intern->zo.ce->name);\
+ RETURN_NULL();\
+ }\
+ __ptr = (__type)my_res->ptr; \
+ if (__check && my_res->status < __check) { \
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid object or resource %s\n", intern->zo.ce->name); \
+ RETURN_NULL();\
+ }\
+}
+
+#define MYSQLI_SET_STATUS(__id, __value) \
+{ \
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
+ ((MYSQLI_RESOURCE *)intern->ptr)->status = __value; \
+} \
+
+#define MYSQLI_CLEAR_RESOURCE(__id) \
+{ \
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*(__id) TSRMLS_CC);\
+ efree(intern->ptr); \
+ intern->ptr = NULL; \
+}
+
+#define MYSQLI_RETURN_LONG_LONG(__val) \
+{ \
+ if ((__val) < LONG_MAX) { \
+ RETURN_LONG((__val)); \
+ } else { \
+ char *ret; \
+ int l = spprintf(&ret, 0, "%llu", (__val)); \
+ RETURN_STRINGL(ret, l, 0); \
+ } \
+}
+
+#define MYSQLI_ADD_PROPERTIES(a,b) \
+{ \
+ int i = 0; \
+ while (b[i].pname != NULL) { \
+ mysqli_add_property(a, b[i].pname, (mysqli_read_t)b[i].r_func, (mysqli_write_t)b[i].w_func TSRMLS_CC); \
+ i++; \
+ }\
+}
+
+#if WIN32|WINNT
+#define SCLOSE(a) closesocket(a)
+#else
+#define SCLOSE(a) close(a)
+#endif
+
+#define MYSQLI_STORE_RESULT 0
+#define MYSQLI_USE_RESULT 1
+
+/* for mysqli_fetch_assoc */
+#define MYSQLI_ASSOC 1
+#define MYSQLI_NUM 2
+#define MYSQLI_BOTH 3
+
+/* for mysqli_bind_param */
+#define MYSQLI_BIND_INT 1
+#define MYSQLI_BIND_DOUBLE 2
+#define MYSQLI_BIND_STRING 3
+#define MYSQLI_BIND_SEND_DATA 4
+
+/* fetch types */
+#define FETCH_SIMPLE 1
+#define FETCH_RESULT 2
+
+/*** REPORT MODES ***/
+#define MYSQLI_REPORT_OFF 0
+#define MYSQLI_REPORT_ERROR 1
+#define MYSQLI_REPORT_STRICT 2
+#define MYSQLI_REPORT_INDEX 4
+#define MYSQLI_REPORT_CLOSE 8
+#define MYSQLI_REPORT_ALL 255
+
+#define MYSQLI_REPORT_MYSQL_ERROR(mysql) \
+if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql_errno(mysql)) { \
+ php_mysqli_report_error(mysql_sqlstate(mysql), mysql_errno(mysql), mysql_error(mysql) TSRMLS_CC); \
+}
+
+#define MYSQLI_REPORT_STMT_ERROR(stmt) \
+if ((MyG(report_mode) & MYSQLI_REPORT_ERROR) && mysql_stmt_errno(stmt)) { \
+ php_mysqli_report_error(mysql_stmt_sqlstate(stmt), mysql_stmt_errno(stmt), mysql_stmt_error(stmt) TSRMLS_CC); \
+}
+
+PHP_MYSQLI_API void mysqli_register_link(zval *return_value, void *link TSRMLS_DC);
+PHP_MYSQLI_API void mysqli_register_stmt(zval *return_value, void *stmt TSRMLS_DC);
+PHP_MYSQLI_API void mysqli_register_result(zval *return_value, void *result TSRMLS_DC);
+PHP_MYSQLI_API void php_mysqli_set_error(long mysql_errno, char *mysql_err TSRMLS_DC);
+
+ZEND_BEGIN_MODULE_GLOBALS(mysqli)
+ long default_link;
+ long num_links;
+ long max_links;
+ long num_active_persistent;
+ long num_inactive_persistent;
+ long max_persistent;
+ long allow_persistent;
+ long cache_size;
+ unsigned long default_port;
+ char *default_host;
+ char *default_user;
+ char *default_socket;
+ char *default_pw;
+ long reconnect;
+ long allow_local_infile;
+ long strict;
+ long error_no;
+ char *error_msg;
+ long report_mode;
+ HashTable *report_ht;
+ unsigned long multi_query;
+ unsigned long embedded;
+#ifdef HAVE_MYSQLND
+ MYSQLND_THD_ZVAL_PCACHE *mysqlnd_thd_zval_cache;
+#endif
+ZEND_END_MODULE_GLOBALS(mysqli)
+
+#define MYSQLI_PROPERTY(a) extern int a(mysqli_object *obj, zval **retval TSRMLS_DC)
+
+MYSQLI_PROPERTY(my_prop_link_host);
+
+#ifdef ZTS
+#define MyG(v) TSRMG(mysqli_globals_id, zend_mysqli_globals *, v)
+#else
+#define MyG(v) (mysqli_globals.v)
+#endif
+
+#define my_estrdup(x) (x) ? estrdup(x) : NULL
+#define my_efree(x) if (x) efree(x)
+
+ZEND_EXTERN_MODULE_GLOBALS(mysqli)
+
+#endif /* PHP_MYSQLI_STRUCTS.H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/CREDITS b/ext/mysqlnd/CREDITS
new file mode 100644
index 0000000000..bac8d1a4a2
--- /dev/null
+++ b/ext/mysqlnd/CREDITS
@@ -0,0 +1,2 @@
+MySQLnd
+Georg Richter, Andrey Hristov, Ulf Wendel
diff --git a/ext/mysqlnd/config-win.h b/ext/mysqlnd/config-win.h
new file mode 100644
index 0000000000..86018221a6
--- /dev/null
+++ b/ext/mysqlnd/config-win.h
@@ -0,0 +1,98 @@
+/* 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 */
+
+#include <sys/locking.h>
+#include <windows.h>
+#include <math.h> /* Because of rint() */
+#include <fcntl.h>
+#include <io.h>
+#include <malloc.h>
+
+#if defined(__NT__)
+#define SYSTEM_TYPE "NT"
+#elif defined(__WIN2000__)
+#define SYSTEM_TYPE "WIN2000"
+#else
+#define SYSTEM_TYPE "Win95/Win98"
+#endif
+
+#ifdef _WIN64
+#define MACHINE_TYPE "ia64" /* Define to machine type name */
+#else
+#define MACHINE_TYPE "i32" /* Define to machine type name */
+#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
+
+#define LONGLONG_MIN ((__int64) 0x8000000000000000)
+#define LONGLONG_MAX ((__int64) 0x7FFFFFFFFFFFFFFF)
+#define LL(A) ((__int64) A)
+
+/* Type information */
+
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */
+typedef __int64 longlong;
+typedef int sigset_t;
+#define longlong_defined
+
+#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 *) (A)))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (*((long *) (A)))
+#define uint2korr(A) (*((uint16 *) (A)))
+#define uint3korr(A) (long) (*((unsigned long *) (A)) & 0xFFFFFF)
+#define uint4korr(A) (*((unsigned long *) (A)))
+#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 32))
+#define uint8korr(A) (*((ulonglong *) (A)))
+#define sint8korr(A) (*((longlong *) (A)))
+#define int2store(T,A) *((uint16*) (T))= (uint16) (A)
+#define int3store(T,A) { *(T)= (uchar) ((A));\
+ *(T+1)=(uchar) (((uint) (A) >> 8));\
+ *(T+2)=(uchar) (((A) >> 16)); }
+#define int4store(T,A) *((long *) (T))= (long) (A)
+#define int5store(T,A) { *(T)= (uchar)((A));\
+ *((T)+1)=(uchar) (((A) >> 8));\
+ *((T)+2)=(uchar) (((A) >> 16));\
+ *((T)+3)=(uchar) (((A) >> 24)); \
+ *((T)+4)=(uchar) (((A) >> 32)); }
+#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A)
+
+#define doubleget(V,M) { *((long *) &V) = *((long*) M); \
+ *(((long *) &V)+1) = *(((long*) M)+1); }
+#define doublestore(T,V) { *((long *) T) = *((long*) &V); \
+ *(((long *) T)+1) = *(((long*) &V)+1); }
+#define float4get(V,M) { *((long *) &(V)) = *((long*) (M)); }
+#define float8get(V,M) doubleget((V),(M))
+#define float4store(V,M) memcpy((byte*) V,(byte*) (&M),sizeof(float))
+#define float8store(V,M) doublestore((V),(M))
+
+#endif /* _WIN64 */
diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32
new file mode 100644
index 0000000000..99a702f1f0
--- /dev/null
+++ b/ext/mysqlnd/config.w32
@@ -0,0 +1,20 @@
+// $Id$
+// vim:ft=javascript
+
+mysqld_source = "";
+if (CHECK_LIB("ws2_32.lib", "mysqlnd")) {
+ mysqlnd_source =
+ "mysqlnd.c " +
+ "mysqlnd_debug.c " +
+ "mysqlnd_charset.c " +
+ "mysqlnd_loaddata.c " +
+ "mysqlnd_palloc.c " +
+ "mysqlnd_ps.c " +
+ "mysqlnd_ps_codec.c " +
+ "mysqlnd_qcache.c " +
+ "mysqlnd_result.c " +
+ "mysqlnd_result_meta.c " +
+ "mysqlnd_statistics.c " +
+ "mysqlnd_wireprotocol.c";
+ EXTENSION("mysqlnd", mysqlnd_source, false);
+}
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4
new file mode 100644
index 0000000000..9d09ba71da
--- /dev/null
+++ b/ext/mysqlnd/config9.m4
@@ -0,0 +1,29 @@
+dnl
+dnl $Id$
+dnl config.m4 for mysqlnd driver
+
+dnl If some extension uses mysqlnd it will get compiled in PHP core
+if test "$PHP_MYSQLND_ENABLED" = "yes"; then
+ mysqlnd_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
+ mysqlnd_ps.c mysqlnd_loaddata.c mysqlnd_palloc.c \
+ mysqlnd_ps_codec.c mysqlnd_statistics.c mysqlnd_qcache.c\
+ mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c"
+
+ PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, no)
+ PHP_ADD_BUILD_DIR([ext/mysqlnd], 1)
+ PHP_INSTALL_HEADERS([ext/mysqlnd])
+ PHP_INSTALL_HEADERS([$ext_builddir/php_mysqlnd_config.h])
+ AC_DEFINE([HAVE_MYSQLND], 1, [Whether mysqlnd is enabled])
+
+ 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_builddir/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 0000000000..98b3ab7dc9
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd.c
@@ -0,0 +1,2078 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+#include "php_ini.h"
+#include "ext/standard/basic_functions.h"
+#include "ext/standard/php_lcg.h"
+#include "ext/standard/info.h"
+
+/* the server doesn't support 4byte utf8, but let's make it forward compatible */
+#define MYSQLND_MAX_ALLOWED_USER_LEN 256 /* 64 char * 4byte */
+#define MYSQLND_MAX_ALLOWED_DB_LEN 256 /* 64 char * 4byte */
+/*
+ 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;
+
+
+
+
+const char * mysqlnd_server_gone = "MySQL server has gone away";
+const char * mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
+
+MYSQLND_STATS *mysqlnd_global_stats = NULL;
+static zend_bool mysqlnd_library_initted = FALSE;
+
+
+enum_func_status mysqlnd_send_close(MYSQLND * conn TSRMLS_DC);
+
+/* {{{ mysqlnd_library_init */
+static
+void mysqlnd_library_init()
+{
+ if (mysqlnd_library_initted == FALSE) {
+ mysqlnd_library_initted = TRUE;
+ _mysqlnd_init_ps_subsystem();
+ /* Should be calloc, as mnd_calloc will reference LOCK_access*/
+ mysqlnd_global_stats = calloc(1, sizeof(MYSQLND_STATS));
+#ifdef ZTS
+ mysqlnd_global_stats->LOCK_access = tsrm_mutex_alloc();
+#endif
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_library_end */
+static
+void mysqlnd_library_end()
+{
+ if (mysqlnd_library_initted == TRUE) {
+#ifdef ZTS
+ tsrm_mutex_free(mysqlnd_global_stats->LOCK_access);
+#endif
+ /* mnd_free will reference LOCK_access and crash...*/
+ free(mysqlnd_global_stats);
+ mysqlnd_global_stats = NULL;
+ mysqlnd_library_initted = FALSE;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND *conn TSRMLS_DC)
+{
+ zend_bool pers = conn->persistent;
+
+ DBG_ENTER("mysqlnd_conn::free_contents");
+
+ mysqlnd_local_infile_default(conn);
+ if (conn->current_result) {
+ conn->current_result->m.free_result_contents(conn->current_result TSRMLS_CC);
+ mnd_efree(conn->current_result);
+ conn->current_result = NULL;
+ }
+
+ if (conn->net.stream) {
+ DBG_INF_FMT("Freeing stream. abstract=%p", conn->net.stream->abstract);
+ if (pers) {
+ php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
+ } else {
+ php_stream_free(conn->net.stream, PHP_STREAM_FREE_CLOSE);
+
+ }
+ conn->net.stream = NULL;
+ }
+
+ DBG_INF("Freeing memory of members");
+ if (conn->host) {
+ DBG_INF("Freeing host");
+ mnd_pefree(conn->host, pers);
+ conn->host = NULL;
+ }
+ if (conn->user) {
+ DBG_INF("Freeing user");
+ mnd_pefree(conn->user, pers);
+ conn->user = NULL;
+ }
+ if (conn->passwd) {
+ DBG_INF("Freeing passwd");
+ mnd_pefree(conn->passwd, pers);
+ conn->passwd = NULL;
+ }
+ if (conn->unix_socket) {
+ DBG_INF("Freeing unix_socket");
+ mnd_pefree(conn->unix_socket, pers);
+ conn->unix_socket = NULL;
+ }
+ if (conn->scheme) {
+ DBG_INF("Freeing scheme");
+ mnd_pefree(conn->scheme, pers);
+ conn->scheme = NULL;
+ }
+ if (conn->server_version) {
+ DBG_INF("Freeing server_version");
+ mnd_pefree(conn->server_version, pers);
+ conn->server_version = NULL;
+ }
+ if (conn->host_info) {
+ DBG_INF("Freeing host_info");
+ mnd_pefree(conn->host_info, pers);
+ conn->host_info = NULL;
+ }
+ if (conn->scramble) {
+ DBG_INF("Freeing scramble");
+ mnd_pefree(conn->scramble, pers);
+ conn->scramble = NULL;
+ }
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, pers);
+ conn->last_message = NULL;
+ }
+ if (conn->options.charset_name) {
+ mnd_pefree(conn->options.charset_name, pers);
+ conn->options.charset_name = NULL;
+ }
+ if (conn->options.num_commands) {
+ unsigned int i;
+ for (i=0; i < conn->options.num_commands; i++) {
+ 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;
+ }
+ if (conn->options.ssl_key) {
+ mnd_pefree(conn->options.ssl_key, pers);
+ conn->options.ssl_key = NULL;
+ }
+ if (conn->options.ssl_cert) {
+ mnd_pefree(conn->options.ssl_cert, pers);
+ conn->options.ssl_cert = NULL;
+ }
+ if (conn->options.ssl_ca) {
+ mnd_pefree(conn->options.ssl_ca, pers);
+ conn->options.ssl_ca = NULL;
+ }
+ if (conn->options.ssl_capath) {
+ mnd_pefree(conn->options.ssl_capath, pers);
+ conn->options.ssl_capath = NULL;
+ }
+ if (conn->options.ssl_cipher) {
+ mnd_pefree(conn->options.ssl_cipher, pers);
+ conn->options.ssl_cipher = NULL;
+ }
+ if (conn->zval_cache) {
+ DBG_INF("Freeing zval cache reference");
+ mysqlnd_palloc_free_thd_cache_reference(&conn->zval_cache);
+ conn->zval_cache = NULL;
+ }
+ if (conn->qcache) {
+ DBG_INF("Freeing qcache reference");
+ mysqlnd_qcache_free_cache_reference(&conn->qcache);
+ conn->qcache = NULL;
+ }
+ if (conn->net.cmd_buffer.buffer) {
+ DBG_INF("Freeing cmd buffer");
+ mnd_pefree(conn->net.cmd_buffer.buffer, pers);
+ conn->net.cmd_buffer.buffer = NULL;
+ }
+ conn->charset = NULL;
+ conn->greet_charset = NULL;
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::dtor */
+static void
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND *conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::dtor");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ conn->m->free_contents(conn TSRMLS_CC);
+
+ mnd_pefree(conn, conn->persistent);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_simple_command_handle_response */
+enum_func_status
+mysqlnd_simple_command_handle_response(MYSQLND *conn, enum php_mysql_packet_type ok_packet,
+ zend_bool silent, enum php_mysqlnd_server_command command
+ TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_simple_command_handle_response");
+ DBG_INF_FMT("silent=%d packet=%d command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
+
+ switch (ok_packet) {
+ case PROT_OK_PACKET:{
+ php_mysql_packet_ok ok_response;
+ PACKET_INIT_ALLOCA(ok_response, PROT_OK_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(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=%d",
+ 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);
+
+ 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_ALLOCA(ok_response);
+ break;
+ }
+ case PROT_EOF_PACKET:{
+ php_mysql_packet_eof ok_response;
+ PACKET_INIT_ALLOCA(ok_response, PROT_EOF_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(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_ALLOCA(ok_response);
+ break;
+ }
+ default:
+ ret = FAIL;
+ SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
+ "Malformed packet");
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %d passed to the function",
+ ok_packet);
+ break;
+ }
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_simple_command */
+enum_func_status
+mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command,
+ const char * const arg, size_t arg_len,
+ enum php_mysql_packet_type ok_packet, zend_bool silent TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ php_mysql_packet_command cmd_packet;
+
+ DBG_ENTER("mysqlnd_simple_command");
+ DBG_INF_FMT("command=%s ok_packet=%d silent=%d", mysqlnd_command_to_text[command], ok_packet, silent);
+
+ switch (conn->state) {
+ 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("Command out of sync");
+ DBG_RETURN(FAIL);
+ }
+
+ /* clean UPSERT info */
+ memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
+ SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+
+ PACKET_INIT_ALLOCA(cmd_packet, PROT_CMD_PACKET);
+ cmd_packet.command = command;
+ if (arg && arg_len) {
+ cmd_packet.argument = arg;
+ cmd_packet.arg_len = arg_len;
+ }
+
+ if (! PACKET_WRITE_ALLOCA(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());
+ }
+ DBG_ERR("Server is gone");
+ ret = FAIL;
+ } else if (ok_packet != PROT_LAST) {
+ ret = mysqlnd_simple_command_handle_response(conn, ok_packet, silent, command TSRMLS_CC);
+ }
+
+ /*
+ There is no need to call FREE_ALLOCA on cmd_packet as the
+ only allocated string is cmd_packet.argument and it was passed
+ to us. We should not free it.
+ */
+
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::set_server_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, set_server_option)(MYSQLND * const conn,
+ enum_mysqlnd_server_option option TSRMLS_DC)
+{
+ enum_func_status ret;
+ char buffer[2];
+ DBG_ENTER("mysqlnd_conn::set_server_option");
+
+ int2store(buffer, (uint) option);
+ ret = mysqlnd_simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer),
+ PROT_EOF_PACKET, FALSE TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_restart_psession */
+PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_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_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_end_psession */
+PHPAPI void mysqlnd_end_psession(MYSQLND *conn)
+{
+
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect */
+PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
+ char *host, char *user,
+ char *passwd, unsigned int passwd_len,
+ char *db, unsigned int db_len,
+ unsigned int port,
+ char *socket,
+ unsigned int mysql_flags,
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache
+ TSRMLS_DC)
+{
+ char *transport = NULL, *errstr = NULL;
+ char *hashed_details = NULL;
+ int transport_len, hashed_details_len, errcode = 0;
+ unsigned int streams_options = ENFORCE_SAFE_MODE;
+ unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
+ zend_bool self_alloced = FALSE;
+ struct timeval tv;
+ zend_bool unix_socket = FALSE;
+ const MYSQLND_CHARSET * charset;
+ zend_bool reconnect = FALSE;
+
+ php_mysql_packet_greet greet_packet;
+ php_mysql_packet_auth *auth_packet;
+ php_mysql_packet_ok ok_packet;
+
+ DBG_ENTER("mysqlnd_connect");
+ DBG_INF_FMT("host=%s user=%s db=%s port=%d flags=%d persistent=%d state=%d",
+ host?host:"", user?user:"", db?db:"", port, mysql_flags,
+ conn? conn->persistent:0, conn? conn->state:-1);
+
+ DBG_INF_FMT("state=%d", conn->state);
+ if (conn && conn->state > CONN_ALLOCED && conn->state ) {
+ DBG_INF("Connecting on a connected handle.");
+
+ if (conn->state < CONN_QUIT_SENT) {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CLOSE_IMPLICIT);
+ reconnect = TRUE;
+ mysqlnd_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 (!host || !host[0]) {
+ host = "localhost";
+ }
+ if (!user || !user[0]) {
+ user = php_get_current_user();
+ }
+ if (!passwd) {
+ passwd = "";
+ passwd_len = 0;
+ }
+ if (!db) {
+ db = "";
+ db_len = 0;
+ }
+ if (!port && !socket) {
+ port = 3306;
+ }
+#ifndef PHP_WIN32
+ if (!strncasecmp(host, "localhost", sizeof("localhost") - 1)) {
+ if (!socket) {
+ socket = "/tmp/mysql.sock";
+ }
+ transport_len = spprintf(&transport, 0, "unix://%s", socket);
+ unix_socket = TRUE;
+ } else
+#endif
+ {
+ transport_len = spprintf(&transport, 0, "tcp://%s:%d", host, port);
+ }
+ DBG_INF_FMT("transport=%p", transport);
+
+ if (conn->persistent) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ /* We should generate something unique */
+ hashed_details_len = spprintf(&hashed_details, 0, "%s@%s@%s@%ld@%ld@%0.8F",
+ transport, user, db, tv.tv_sec, (long int)tv.tv_usec,
+ php_combined_lcg(TSRMLS_C) * 10);
+ DBG_INF_FMT("hashed_details=%s", hashed_details);
+ }
+
+ PACKET_INIT_ALLOCA(greet_packet, PROT_GREET_PACKET);
+ PACKET_INIT(auth_packet, PROT_AUTH_PACKET, php_mysql_packet_auth *);
+ PACKET_INIT_ALLOCA(ok_packet, PROT_OK_PACKET);
+
+ if (!conn) {
+ conn = mysqlnd_init(FALSE);
+ self_alloced = TRUE;
+ }
+
+ conn->state = CONN_ALLOCED;
+ conn->net.packet_no = 0;
+
+ if (conn->options.timeout_connect) {
+ tv.tv_sec = conn->options.timeout_connect;
+ tv.tv_usec = 0;
+ }
+ if (conn->persistent) {
+ conn->scheme = pestrndup(transport, transport_len, 1);
+ mnd_efree(transport);
+ } else {
+ conn->scheme = transport;
+ }
+ DBG_INF(conn->scheme);
+ conn->net.stream = php_stream_xport_create(conn->scheme, transport_len, streams_options, streams_flags,
+ hashed_details,
+ (conn->options.timeout_connect) ? &tv : NULL,
+ NULL /*ctx*/, &errstr, &errcode);
+ DBG_INF_FMT("stream=%p", conn->net.stream);
+
+ 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.
+ */
+ conn->net.stream->in_free = 1;
+ zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1);
+ conn->net.stream->in_free = 0;
+ }
+#if ZEND_DEBUG
+ /* Shut-up the streams, they don't know what they are doing */
+ conn->net.stream->__exposed = 1;
+#endif
+ mnd_efree(hashed_details);
+ }
+
+ if (errstr || !conn->net.stream) {
+ goto err;
+ }
+
+ if (conn->options.timeout_read)
+ {
+ tv.tv_sec = conn->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(conn->net.stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ if (!unix_socket) {
+ /* Set TCP_NODELAY */
+ mysqlnd_set_sock_no_delay(conn->net.stream);
+ }
+
+ if (FAIL == PACKET_READ_ALLOCA(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=%d 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 = greet_packet.server_version;
+ greet_packet.server_version = NULL; /* The string will be freed otherwise */
+
+ conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no);
+ /* we allow load data local infile by default */
+ mysql_flags |= CLIENT_LOCAL_FILES;
+
+ auth_packet->user = user;
+ auth_packet->password = passwd;
+
+ if (conn->options.charset_name &&
+ (charset = mysqlnd_find_charset_name(conn->options.charset_name)))
+ {
+ auth_packet->charset_no = charset->nr;
+#if PHP_MAJOR_VERSION >= 6
+ } else if (UG(unicode)) {
+ auth_packet->charset_no = 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
+#endif
+ } else {
+ auth_packet->charset_no = greet_packet.charset_no;
+ }
+ auth_packet->db = db;
+ auth_packet->db_len = db_len;
+ auth_packet->max_packet_size= 3UL*1024UL*1024UL*1024UL;
+ auth_packet->client_flags= mysql_flags;
+
+ conn->scramble = auth_packet->server_scramble_buf = mnd_pemalloc(SCRAMBLE_LENGTH, conn->persistent);
+ memcpy(auth_packet->server_scramble_buf, greet_packet.scramble_buf, SCRAMBLE_LENGTH);
+ if (!PACKET_WRITE(auth_packet, conn)) {
+ conn->state = CONN_QUIT_SENT;
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ goto err;
+ }
+
+ if (FAIL == PACKET_READ_ALLOCA(ok_packet, conn) || ok_packet.field_count >= 0xFE) {
+ if (ok_packet.field_count == 0xFE) {
+ /* old authentication with new server !*/
+ DBG_ERR("mysqlnd cannot connect to MySQL 4.1+ using old authentication");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd cannot connect to MySQL 4.1+ using old authentication");
+ } else if (ok_packet.field_count == 0xFF) {
+ if (ok_packet.sqlstate[0]) {
+ if (!self_alloced) {
+ strncpy(conn->error_info.sqlstate, ok_packet.sqlstate, sizeof(conn->error_info.sqlstate));
+ }
+ DBG_ERR_FMT("ERROR:%d [SQLSTATE:%s] %s",
+ ok_packet.error_no, ok_packet.sqlstate, ok_packet.error);
+ }
+ if (!self_alloced) {
+ conn->error_info.error_no = ok_packet.error_no;
+ strncpy(conn->error_info.error, ok_packet.error, sizeof(conn->error_info.error));
+ }
+ }
+ } else {
+ conn->state = CONN_READY;
+
+ conn->user = pestrdup(user, conn->persistent);
+ conn->passwd = pestrndup(passwd, passwd_len, conn->persistent);
+ conn->port = port;
+ if (host && !socket) {
+ char *p;
+
+ conn->host = pestrdup(host, conn->persistent);
+ spprintf(&p, 0, "MySQL host info: %s via TCP/IP", conn->host);
+ if (conn->persistent) {
+ conn->host_info = pestrdup(p, 1);
+ mnd_efree(p);
+ } else {
+ conn->host_info = p;
+ }
+ } else {
+ conn->unix_socket = pestrdup(socket, conn->persistent);
+ conn->host_info = pestrdup("MySQL host info: Localhost via UNIX socket", conn->persistent);
+ }
+ conn->client_flag = auth_packet->client_flags;
+ conn->max_packet_size = auth_packet->max_packet_size;
+ /* todo: check if charset is available */
+ conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
+ 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_NEW_MESSAGE(conn->last_message, conn->last_message_len,
+ ok_packet.message, ok_packet.message_len,
+ conn->persistent);
+
+ SET_EMPTY_ERROR(conn->error_info);
+
+ PACKET_FREE_ALLOCA(greet_packet);
+ PACKET_FREE(auth_packet);
+ PACKET_FREE_ALLOCA(ok_packet);
+
+ conn->zval_cache = mysqlnd_palloc_get_thd_cache_reference(zval_cache);
+ conn->net.cmd_buffer.length = 128L*1024L;
+ conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent);
+
+ mysqlnd_local_infile_default(conn);
+ {
+ uint buf_size;
+ buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to uint*/
+ conn->m->set_client_option(conn, MYSQLND_OPT_NET_READ_BUFFER_SIZE,
+ (char *)&buf_size TSRMLS_CC);
+
+ buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to uint*/
+ conn->m->set_client_option(conn, MYSQLND_OPT_NET_CMD_BUFFER_SIZE,
+ (char *)&buf_size TSRMLS_CC);
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_SUCCESS);
+ if (reconnect) {
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
+ }
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS);
+ if (conn->persistent) {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ }
+
+ DBG_INF_FMT("connection_id=%llu", conn->thread_id);
+#if PHP_MAJOR_VERSION >= 6
+ {
+ uint 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
+ DBG_RETURN(conn);
+ }
+err:
+ PACKET_FREE_ALLOCA(greet_packet);
+ PACKET_FREE(auth_packet);
+ PACKET_FREE_ALLOCA(ok_packet);
+
+ if (errstr) {
+ DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
+ SET_CLIENT_ERROR(conn->error_info, errcode, UNKNOWN_SQLSTATE, errstr);
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
+
+ mnd_efree(errstr);
+ }
+ if (conn->scheme) {
+ mnd_pefree(conn->scheme, conn->persistent);
+ conn->scheme = NULL;
+ }
+
+
+ /* This will also close conn->net.stream if it has been opened */
+ conn->m->free_contents(conn TSRMLS_CC);
+
+ if (self_alloced) {
+ /*
+ We have alloced, thus there are no references to this
+ object - we are free to kill it!
+ */
+ conn->m->dtor(conn TSRMLS_CC);
+ } else {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CONNECT_FAILURE);
+ }
+ DBG_RETURN(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::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, query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC)
+{
+ enum_func_status ret;
+ DBG_ENTER("mysqlnd_conn::query");
+ DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
+
+ if (PASS != mysqlnd_simple_command(conn, COM_QUERY, query, query_len,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE TSRMLS_CC)) {
+ DBG_RETURN(FAIL);
+ }
+
+ /*
+ Here read the result set. We don't do it in simple_command because it need
+ information from the ok packet. We will fetch it ourselves.
+ */
+ ret = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/*
+ 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::list_fields */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC)
+{
+ /* db + \0 + wild + \0 (for wild) */
+ char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 4 * 2 + 1 + 1], *p;
+ size_t table_len, wild_len;
+ MYSQLND_RES *result = NULL;
+ DBG_ENTER("mysqlnd_conn::list_fields");
+ DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
+
+ p = buff;
+ if (table && (table_len = strlen(table))) {
+ memcpy(p, table, MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4));
+ p += table_len;
+ *p++ = '\0';
+ }
+
+ if (achtung_wild && (wild_len = strlen(achtung_wild))) {
+ memcpy(p, achtung_wild, MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN * 4));
+ p += wild_len;
+ *p++ = '\0';
+ }
+
+ if (PASS != mysqlnd_simple_command(conn, COM_FIELD_LIST, buff, p - buff,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE TSRMLS_CC)) {
+ DBG_RETURN(NULL);
+ }
+ /*
+ Prepare for the worst case.
+ MyISAM goes to 2500 BIT columns, double it for safety.
+ */
+ result = mysqlnd_result_init(5000, mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC);
+
+
+ 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);
+ DBG_RETURN(NULL);
+ }
+
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ result->unbuf->eof_reached = TRUE;
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::list_method */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND *conn, const char *query,
+ const char *achtung_wild, char *par1 TSRMLS_DC)
+{
+ char *show_query = NULL;
+ size_t show_query_len;
+ MYSQLND_RES *result = NULL;
+
+ DBG_ENTER("mysqlnd_conn::list_method");
+ DBG_INF_FMT("conn=%llu query=%s wild=%d", conn->thread_id, query, achtung_wild);
+
+ if (par1) {
+ if (achtung_wild) {
+ show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild);
+ } else {
+ show_query_len = spprintf(&show_query, 0, query, par1);
+ }
+ } else {
+ if (achtung_wild) {
+ show_query_len = spprintf(&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_efree(show_query);
+ }
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::errno */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn)
+{
+ return conn->error_info.error_no;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::error */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn)
+{
+ return conn->error_info.error;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::sqlstate */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn)
+{
+ 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, int 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::escape_string */
+static ulong
+MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr,
+ const char *escapestr, int escapestr_len TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::escape_string");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+ if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
+ DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
+ }
+ DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::dump_debug_info */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::dump_debug_info");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+ DBG_RETURN(mysqlnd_simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::select_db */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn,
+ const char * const db,
+ unsigned int db_len TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_conn::select_db");
+ DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
+
+ ret = mysqlnd_simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE 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);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::ping */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_conn::ping");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ ret = mysqlnd_simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, 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);
+
+ DBG_INF_FMT("ret=%d", ret);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::stat */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, stat)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC)
+{
+ enum_func_status ret;
+ php_mysql_packet_stats stats_header;
+
+ DBG_ENTER("mysqlnd_conn::stat");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ ret = mysqlnd_simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE TSRMLS_CC);
+ if (FAIL == ret) {
+ DBG_RETURN(FAIL);
+ }
+ PACKET_INIT_ALLOCA(stats_header, PROT_STATS_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(stats_header, conn))) {
+ DBG_RETURN(FAIL);
+ }
+ *message = stats_header.message;
+ *message_len = stats_header.message_len;
+ /* Ownership transfer */
+ stats_header.message = NULL;
+ PACKET_FREE_ALLOCA(stats_header);
+
+ DBG_INF(*message);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::kill */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND *conn, unsigned int pid TSRMLS_DC)
+{
+ enum_func_status ret;
+ char buff[4];
+
+ DBG_ENTER("mysqlnd_conn::kill");
+ DBG_INF_FMT("conn=%llu pid=%lu", conn->thread_id, pid);
+
+ int4store(buff, pid);
+
+ /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
+ if (pid != conn->thread_id) {
+ ret = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE 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 = mysqlnd_simple_command(conn, COM_PROCESS_KILL, buff,
+ 4, PROT_LAST, FALSE TSRMLS_CC))) {
+ conn->state = CONN_QUIT_SENT;
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn::set_charset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ char *query;
+ size_t query_len;
+ const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
+
+ DBG_ENTER("mysqlnd_conn::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(FAIL);
+ }
+
+ query_len = spprintf(&query, 0, "SET NAMES %s", csname);
+
+ if (FAIL == 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_efree(query);
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::refresh */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, unsigned long options TSRMLS_DC)
+{
+ zend_uchar bits[1];
+ DBG_ENTER("mysqlnd_conn::refresh");
+ DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
+
+ int1store(bits, options);
+
+ DBG_RETURN(mysqlnd_simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::shutdown */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, unsigned long level TSRMLS_DC)
+{
+ zend_uchar bits[1];
+ DBG_ENTER("mysqlnd_conn::shutdown");
+ DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
+
+ int1store(bits, level);
+
+ DBG_RETURN(mysqlnd_simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE TSRMLS_CC));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_send_close */
+enum_func_status
+mysqlnd_send_close(MYSQLND * 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);
+
+ switch (conn->state) {
+ case CONN_READY:
+ DBG_INF("Connection clean, sending COM_QUIT");
+ ret = mysqlnd_simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST,
+ 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->state = CONN_QUIT_SENT;
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ static enum_mysqlnd_collected_stats
+ close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
+ STAT_CLOSE_EXPLICIT,
+ STAT_CLOSE_IMPLICIT,
+ STAT_CLOSE_DISCONNECT
+ };
+ enum_mysqlnd_collected_stats stat = close_type_to_stat_map[close_type];
+
+ DBG_ENTER("mysqlnd_conn::close");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
+ MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_CONNECTIONS);
+ if (conn->persistent) {
+ MYSQLND_DEC_CONN_STATISTIC(&conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ }
+
+ /*
+ Close now, free_reference will try,
+ if we are last, but that's not a problem.
+ */
+ ret = mysqlnd_send_close(conn TSRMLS_CC);
+
+ ret = conn->m->free_reference(conn TSRMLS_CC);
+
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::get_reference */
+static MYSQLND *
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn)
+{
+ ++conn->refcount;
+ return conn;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::free_reference */
+static enum_func_status
+MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ DBG_ENTER("mysqlnd_conn::free_reference");
+ DBG_INF_FMT("conn=%llu conn->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 = mysqlnd_send_close(conn TSRMLS_CC);
+ conn->m->dtor(conn TSRMLS_CC);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::field_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn)
+{
+ return conn->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::insert_id */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn)
+{
+ return conn->upsert_status.last_insert_id;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::affected_rows */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn)
+{
+ return conn->upsert_status.affected_rows;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::warning_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn)
+{
+ return conn->upsert_status.warning_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn)
+{
+ 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::get_server_info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn)
+{
+ return conn->server_version;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::get_host_info */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn)
+{
+ return conn->host_info;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::get_proto_info */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn)
+{
+ return conn->protocol_version;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::charset_name */
+static const char *
+MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn)
+{
+ return conn->charset->name;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::thread_id */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn)
+{
+ return conn->thread_id;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::get_server_version */
+static unsigned long
+MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn)
+{
+ 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::more_results */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_conn,more_results)(const MYSQLND * const conn)
+{
+ /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
+ return conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::next_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_conn::next_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (conn->state != CONN_NEXT_RESULT_PENDING) {
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(conn->error_info);
+ conn->upsert_status.affected_rows= ~(mynd_ulonglong) 0;
+ /*
+ 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 = mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC))) {
+ DBG_ERR_FMT("Serious error. %s::%d", __FILE__, __LINE__);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
+ conn->state = CONN_QUIT_SENT;
+ }
+
+ 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::change_user */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
+ const char *user,
+ const char *passwd,
+ const char *db TSRMLS_DC)
+{
+ /*
+ 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
+ stack overrungs.
+ */
+ size_t user_len;
+ enum_func_status ret;
+ php_mysql_packet_chg_user_resp chg_user_resp;
+ char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1];
+ char *p = buffer;
+
+ DBG_ENTER("mysqlnd_conn::change_user");
+ DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s",
+ conn->thread_id, user?user:"", passwd?"***":"null", db?db:"");
+
+ if (!user) {
+ user = "";
+ }
+ if (!passwd) {
+ passwd = "";
+ }
+ if (!db) {
+ db = "";
+ }
+
+ /* 1. user ASCIIZ */
+ user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_DB_LEN);
+ memcpy(p, user, user_len);
+ p += user_len;
+ *p++ = '\0';
+
+ /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */
+ if (passwd[0]) {
+ *p++ = SCRAMBLE_LENGTH;
+ php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd);
+ p += SCRAMBLE_LENGTH;
+ } else {
+ *p++ = '\0';
+ }
+
+ /* 3. db ASCIIZ */
+ if (db[0]) {
+ size_t db_len = strlen(db);
+ memcpy(p, db, MIN(db_len, MYSQLND_MAX_ALLOWED_DB_LEN));
+ p += db_len;
+ }
+ *p++ = '\0';
+
+ if (PASS != mysqlnd_simple_command(conn, COM_CHANGE_USER, buffer, p - buffer,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE TSRMLS_CC)) {
+ DBG_RETURN(FAIL);
+ }
+
+ PACKET_INIT_ALLOCA(chg_user_resp, PROT_CHG_USER_PACKET);
+ ret = PACKET_READ_ALLOCA(chg_user_resp, conn);
+ conn->error_info = chg_user_resp.error_info;
+ PACKET_FREE_ALLOCA(chg_user_resp);
+
+ 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 (mysqlnd_get_server_version(conn) > 50113L &&
+ mysqlnd_get_server_version(conn) < 50118L)
+ {
+ php_mysql_packet_ok redundant_error_packet;
+ PACKET_INIT_ALLOCA(redundant_error_packet, PROT_OK_PACKET);
+ PACKET_READ_ALLOCA(redundant_error_packet, conn);
+ PACKET_FREE_ALLOCA(redundant_error_packet);
+ DBG_INF_FMT("Server is %d, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
+ }
+ }
+ if (ret == PASS) {
+ mnd_pefree(conn->user, conn->persistent);
+ conn->user = pestrndup(user, user_len, conn->persistent);
+ mnd_pefree(conn->passwd, conn->persistent);
+ conn->passwd = pestrdup(passwd, conn->persistent);
+ if (conn->last_message) {
+ mnd_pefree(conn->last_message, conn->persistent);
+ conn->last_message = NULL;
+ }
+ conn->charset = conn->greet_charset;
+ memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
+ }
+
+ SET_ERROR_AFF_ROWS(conn);
+
+ /*
+ Here we should close all statements. Unbuffered queries should not be a
+ problem as we won't allow sending COM_CHANGE_USER.
+ */
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,
+ enum mysqlnd_option option,
+ const char * const value
+ TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::set_client_option");
+ DBG_INF_FMT("conn=%llu option=%d", conn->thread_id, option);
+ switch (option) {
+#if PHP_MAJOR_VERSION >= 6
+ case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
+ conn->options.numeric_and_datetime_as_unicode = *(uint*) value;
+ break;
+#endif
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ conn->net.cmd_buffer.length = *(uint*) value;
+ if (!conn->net.cmd_buffer.buffer) {
+ conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent);
+ } else {
+ conn->net.cmd_buffer.buffer = mnd_perealloc(conn->net.cmd_buffer.buffer,
+ conn->net.cmd_buffer.length,
+ conn->persistent);
+ }
+ break;
+ case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ conn->options.net_read_buffer_size = *(uint*) value;
+ break;
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ case MYSQLND_OPT_INT_AND_YEAR_AS_INT:
+ conn->options.int_and_year_as_int = *(uint*) value;
+ break;
+#endif
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ conn->options.timeout_connect = *(uint*) value;
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_READ_TIMEOUT:
+ conn->options.timeout_read = *(uint*) value;
+ break;
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ conn->options.timeout_write = *(uint*) value;
+ break;
+#endif
+ case MYSQL_OPT_LOCAL_INFILE:
+ if (!value || (*(uint*) value) ? 1 : 0) {
+ conn->options.flags |= CLIENT_LOCAL_FILES;
+ } else {
+ conn->options.flags &= ~CLIENT_LOCAL_FILES;
+ }
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_COMPRESS:
+#endif
+ case MYSQL_INIT_COMMAND:
+ 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:
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+#endif
+ /* currently not supported. Todo!! */
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ conn->options.charset_name = pestrdup(value, conn->persistent);
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_SET_CHARSET_DIR:
+ case MYSQL_OPT_RECONNECT:
+ case MYSQL_OPT_PROTOCOL:
+ /* 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
+
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_NAMED_PIPE:
+ case MYSQL_SHARED_MEMORY_BASE_NAME:
+ case MYSQL_OPT_USE_RESULT:
+ case MYSQL_SECURE_AUTH:
+ /* not sure, todo ? */
+#endif
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn::use_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_conn::use_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (!conn->current_result) {
+ DBG_RETURN(NULL);
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA */
+ if (conn->last_query_type != QUERY_SELECT || conn->state != 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");
+ DBG_RETURN(NULL);
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_UNBUFFERED_SETS);
+
+ result = conn->current_result;
+ conn->current_result = NULL;
+ result->conn = conn->m->get_reference(conn);
+
+ result = result->m.use_result(result, FALSE TSRMLS_CC);
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn::store_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_conn::store_result");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+
+ if (!conn->current_result) {
+ DBG_RETURN(NULL);
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (conn->last_query_type != QUERY_SELECT || conn->state != 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");
+ DBG_RETURN(NULL);
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
+
+ result = conn->current_result;
+ conn->current_result = NULL;
+
+ result = result->m.store_result(result, conn, FALSE TSRMLS_CC);
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_conn::get_connection_stats */
+void
+MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
+ zval *return_value
+ TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ DBG_ENTER("mysqlnd_conn::get_connection_stats");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+ mysqlnd_fill_stats_hash(&(conn->stats), return_value TSRMLS_CC ZEND_FILE_LINE_CC);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC);
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
+ MYSQLND_METHOD(mysqlnd_conn, escape_string),
+ MYSQLND_METHOD(mysqlnd_conn, set_charset),
+ MYSQLND_METHOD(mysqlnd_conn, query),
+ MYSQLND_METHOD(mysqlnd_conn, use_result),
+ MYSQLND_METHOD(mysqlnd_conn, store_result),
+ MYSQLND_METHOD(mysqlnd_conn, next_result),
+ MYSQLND_METHOD(mysqlnd_conn, more_results),
+
+ _mysqlnd_stmt_init,
+
+ MYSQLND_METHOD(mysqlnd_conn, shutdown),
+ MYSQLND_METHOD(mysqlnd_conn, refresh),
+
+ MYSQLND_METHOD(mysqlnd_conn, ping),
+ MYSQLND_METHOD(mysqlnd_conn, kill),
+ MYSQLND_METHOD(mysqlnd_conn, select_db),
+ MYSQLND_METHOD(mysqlnd_conn, dump_debug_info),
+ MYSQLND_METHOD(mysqlnd_conn, change_user),
+
+ MYSQLND_METHOD(mysqlnd_conn, errno),
+ MYSQLND_METHOD(mysqlnd_conn, error),
+ MYSQLND_METHOD(mysqlnd_conn, sqlstate),
+ MYSQLND_METHOD(mysqlnd_conn, thread_id),
+
+ MYSQLND_METHOD(mysqlnd_conn, get_connection_stats),
+
+ MYSQLND_METHOD(mysqlnd_conn, get_server_version),
+ MYSQLND_METHOD(mysqlnd_conn, get_server_info),
+ MYSQLND_METHOD(mysqlnd_conn, stat),
+ MYSQLND_METHOD(mysqlnd_conn, get_host_info),
+ MYSQLND_METHOD(mysqlnd_conn, get_proto_info),
+ MYSQLND_METHOD(mysqlnd_conn, info),
+ MYSQLND_METHOD(mysqlnd_conn, charset_name),
+ MYSQLND_METHOD(mysqlnd_conn, list_fields),
+ MYSQLND_METHOD(mysqlnd_conn, list_method),
+
+ MYSQLND_METHOD(mysqlnd_conn, insert_id),
+ MYSQLND_METHOD(mysqlnd_conn, affected_rows),
+ MYSQLND_METHOD(mysqlnd_conn, warning_count),
+ MYSQLND_METHOD(mysqlnd_conn, field_count),
+
+ MYSQLND_METHOD(mysqlnd_conn, set_server_option),
+ MYSQLND_METHOD(mysqlnd_conn, set_client_option),
+ MYSQLND_METHOD(mysqlnd_conn, free_contents),
+ MYSQLND_METHOD(mysqlnd_conn, close),
+
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
+
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_init */
+PHPAPI MYSQLND *_mysqlnd_init(zend_bool persistent TSRMLS_DC)
+{
+ MYSQLND *ret = mnd_pecalloc(1, sizeof(MYSQLND), persistent);
+
+ DBG_ENTER("mysqlnd_init");
+ DBG_INF_FMT("persistent=%d", persistent);
+
+ SET_ERROR_AFF_ROWS(ret);
+ ret->persistent = persistent;
+
+ ret->m = & mysqlnd_mysqlnd_conn_methods;
+ ret->m->get_reference(ret);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_functions[]
+ *
+ * Every user visible function must have an entry in mysqlnd_functions[].
+ */
+static zend_function_entry mysqlnd_functions[] = {
+ {NULL, NULL, NULL} /* Must be the last line in mysqlnd_functions[] */
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_minfo_print_hash */
+#if PHP_MAJOR_VERSION >= 6
+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) {
+ TSRMLS_FETCH();
+ zstr string_key;
+ uint string_key_len;
+ ulong num_key;
+ char *s = NULL;
+
+ 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 (UG(unicode)) {
+ int s_len;
+ 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);
+ }
+ } else {
+ php_info_print_table_row(2, string_key.s, Z_STRVAL_PP(values_entry));
+ }
+
+ zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values);
+ }
+}
+#else
+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
+/* }}} */
+
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(mysqlnd)
+{
+ char buf[32];
+ zval values;
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, "mysqlnd", "enabled");
+ php_info_print_table_row(2, "Version", mysqlnd_get_client_info());
+
+ /* Print client stats */
+ php_info_print_table_header(2, "Client statistics", "");
+ mysqlnd_get_client_stats(&values);
+ mysqlnd_minfo_print_hash(&values);
+ 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");
+
+ 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);
+
+ zval_dtor(&values);
+ php_info_print_table_end();
+}
+/* }}} */
+
+
+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 = 2048;
+ mysqlnd_globals->net_read_buffer_size = 32768;
+}
+/* }}} */
+
+
+/* {{{ 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", "2048", PHP_INI_ALL, OnUpdateLong, 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)
+PHP_INI_END()
+/* }}} */
+
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(mysqlnd)
+{
+ REGISTER_INI_ENTRIES();
+
+ mysqlnd_library_init();
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(mysqlnd)
+{
+ mysqlnd_library_end();
+
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+
+#if PHP_DEBUG
+/* {{{ PHP_RINIT_FUNCTION
+ */
+static PHP_RINIT_FUNCTION(mysqlnd)
+{
+#ifdef PHP_DEBUG
+ if (MYSQLND_G(debug)) {
+ MYSQLND_DEBUG *dbg = mysqlnd_debug_init(TSRMLS_C);
+ if (!dbg) {
+ return FAILURE;
+ }
+ dbg->m->set_mode(dbg, MYSQLND_G(debug));
+ MYSQLND_G(dbg) = dbg;
+ }
+#endif
+ return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+static PHP_RSHUTDOWN_FUNCTION(mysqlnd)
+{
+#ifdef PHP_DEBUG
+ MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
+ DBG_ENTER("RSHUTDOWN");
+ if (dbg) {
+ dbg->m->close(dbg);
+ dbg->m->free(dbg);
+ MYSQLND_G(dbg) = NULL;
+ }
+#endif
+ return SUCCESS;
+}
+/* }}} */
+#endif
+
+
+/* {{{ mysqlnd_module_entry
+ */
+zend_module_entry mysqlnd_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "mysqlnd",
+ mysqlnd_functions,
+ PHP_MINIT(mysqlnd),
+ PHP_MSHUTDOWN(mysqlnd),
+#if PHP_DEBUG
+ PHP_RINIT(mysqlnd),
+ PHP_RSHUTDOWN(mysqlnd),
+#else
+ NULL,
+ NULL,
+#endif
+ PHP_MINFO(mysqlnd),
+ MYSQLND_VERSION,
+ PHP_MODULE_GLOBALS(mysqlnd),
+ PHP_GINIT(mysqlnd),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+
+/*
+ * 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 0000000000..f648207fdc
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd.h
@@ -0,0 +1,354 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_H
+#define MYSQLND_H
+
+#define MYSQLND_VERSION "mysqlnd 5.0.2-dev - 070928 - $Revision$"
+#define MYSQLND_VERSION_ID 50002
+
+/* This forces inlining of some accessor functions */
+#define MYSQLND_USE_OPTIMISATIONS 0
+
+/* #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 PHP_DEBUG
+#define MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND 1
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#include "mysqlnd_portability.h"
+#include "mysqlnd_enum_n_def.h"
+#include "mysqlnd_structs.h"
+
+
+/* Library related */
+#define mysqlnd_restart_psession(conn) _mysqlnd_restart_psession((conn) TSRMLS_CC)
+PHPAPI void _mysqlnd_restart_psession(MYSQLND *conn TSRMLS_DC);
+PHPAPI void mysqlnd_end_psession(MYSQLND *conn);
+PHPAPI void mysqlnd_minfo_print_hash(zval *values);
+#define mysqlnd_thread_safe() TRUE
+
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint 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,
+ char *host, char *user,
+ char *passwd, unsigned int passwd_len,
+ char *db, unsigned int db_len,
+ unsigned int port,
+ char *socket,
+ unsigned int mysql_flags,
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache
+ TSRMLS_DC);
+
+#define mysqlnd_change_user(conn, user, passwd, db) (conn)->m->change_user((conn), (user), (passwd), (db) TSRMLS_CC)
+
+#define mysqlnd_debug(x) _mysqlnd_debug((x) TSRMLS_CC)
+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_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)->m->get_statistics((conn), (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)->m->query((conn), (query_str), (query_len) TSRMLS_CC)
+#define mysqlnd_unbuffered_skip_result(result) (result)->m.skip_result((result) TSRMLS_CC)
+
+
+#define mysqlnd_use_result(conn) (conn)->m->use_result((conn) TSRMLS_CC)
+#define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC)
+#define mysqlnd_next_result(conn) (conn)->m->next_result((conn) TSRMLS_CC)
+#define mysqlnd_more_results(conn) (conn)->m->more_results((conn))
+#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)
+
+/*****************************************************************************************************/
+#if defined(MYSQLND_USE_OPTIMISATIONS) && MYSQLND_USE_OPTIMISATIONS == 1
+
+/* Errors */
+#define mysqlnd_errno(conn) (conn)->error_info.error_no
+#define mysqlnd_error(conn) (conn)->error_info.error
+#define mysqlnd_sqlstate(conn) ((conn)->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL)
+
+/* Charset */
+#define mysqlnd_character_set_name(conn) (conn)->charset->name
+
+/* Simple metadata */
+#define mysqlnd_field_count(conn) (conn)->field_count
+#define mysqlnd_insert_id(conn) (conn)->upsert_status.last_insert_id
+#define mysqlnd_affected_rows(conn) (conn)->upsert_status.affected_rows
+#define mysqlnd_warning_count(conn) (conn)->upsert_status.warning_count
+#define mysqlnd_info(conn) (conn)->last_message
+#define mysqlnd_get_server_info(conn) (conn)->server_version
+#define mysqlnd_get_host_info(conn) (conn)->host_info
+#define mysqlnd_get_proto_info(conn) (conn)->protocol_version
+#define mysqlnd_thread_id(conn) (conn)->thread_id
+
+#define mysqlnd_num_rows(result) ((result)->data? (result)->data->row_count:0)
+#define mysqlnd_num_fields(result) (result)->field_count
+
+#define mysqlnd_fetch_lengths(result) ((result)->m.fetch_lengths? (result)->m.fetch_lengths((result)):NULL)
+
+#define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs))
+#define mysqlnd_field_tell(result) (result)->meta? (result)->meta->current_field:0)
+#define mysqlnd_fetch_field(result) (result)->m.fetch_field((result) TSRMLS_CC)
+#define mysqlnd_fetch_field_direct(result,fnr) ((result)->meta? &((result)->meta->fields[(fnr)]):NULL)
+
+/* mysqlnd metadata */
+#define mysqlnd_get_client_info() MYSQLND_VERSION
+#define mysqlnd_get_client_version() MYSQLND_VERSION_ID
+
+/* PS */
+#define mysqlnd_stmt_insert_id(stmt) (stmt)->upsert_status.last_insert_id
+#define mysqlnd_stmt_affected_rows(stmt) (stmt)->upsert_status.affected_rows
+#define mysqlnd_stmt_num_rows(stmt) (stmt)->result? mysqlnd_num_rows((stmt)->result):0
+#define mysqlnd_stmt_param_count(stmt) (stmt)->param_count
+#define mysqlnd_stmt_field_count(stmt) (stmt)->field_count
+#define mysqlnd_stmt_warning_count(stmt) (stmt)->upsert_status.warning_count
+#define mysqlnd_stmt_errno(stmt) (stmt)->error_info.error_no
+#define mysqlnd_stmt_error(stmt) (stmt)->error_info.error
+#define mysqlnd_stmt_sqlstate(stmt) ((stmt)->error_info.sqlstate[0] ? (stmt)->error_info.sqlstate:MYSQLND_SQLSTATE_NULL)
+
+
+
+/*****************************************************************************************************/
+#else /* Using plain functions */
+/*****************************************************************************************************/
+
+/* Errors */
+#define mysqlnd_errno(conn) (conn)->m->get_error_no((conn))
+#define mysqlnd_error(conn) (conn)->m->get_error_str((conn))
+#define mysqlnd_sqlstate(conn) (conn)->m->get_sqlstate((conn))
+
+/* Charset */
+#define mysqlnd_character_set_name(conn) (conn)->m->charset_name((conn))
+
+/* Simple metadata */
+#define mysqlnd_field_count(conn) (conn)->m->get_field_count((conn))
+#define mysqlnd_insert_id(conn) (conn)->m->get_last_insert_id((conn))
+#define mysqlnd_affected_rows(conn) (conn)->m->get_affected_rows((conn))
+#define mysqlnd_warning_count(conn) (conn)->m->get_warning_count((conn))
+#define mysqlnd_info(conn) (conn)->m->get_last_message((conn))
+#define mysqlnd_get_server_info(conn) (conn)->m->get_server_information((conn))
+#define mysqlnd_get_host_info(conn) (conn)->m->get_host_information((conn))
+#define mysqlnd_get_proto_info(conn) (conn)->m->get_protocol_information((conn))
+#define mysqlnd_thread_id(conn) (conn)->m->get_thread_id((conn))
+
+#define mysqlnd_num_rows(result) (result)->m.num_rows((result))
+#define mysqlnd_num_fields(result) (result)->m.num_fields((result))
+
+PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result);
+
+#define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs))
+#define mysqlnd_field_tell(result) (result)->m.field_tell((result))
+#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)
+
+/* mysqlnd metadata */
+PHPAPI const char * mysqlnd_get_client_info();
+PHPAPI unsigned int mysqlnd_get_client_version();
+
+/* PS */
+#define mysqlnd_stmt_insert_id(stmt) (stmt)->m->get_last_insert_id((stmt))
+#define mysqlnd_stmt_affected_rows(stmt) (stmt)->m->get_affected_rows((stmt))
+#define mysqlnd_stmt_num_rows(stmt) (stmt)->m->get_num_rows((stmt))
+#define mysqlnd_stmt_param_count(stmt) (stmt)->m->get_param_count((stmt))
+#define mysqlnd_stmt_field_count(stmt) (stmt)->m->get_field_count((stmt))
+#define mysqlnd_stmt_warning_count(stmt) (stmt)->m->get_warning_count((stmt))
+#define mysqlnd_stmt_errno(stmt) (stmt)->m->get_error_no((stmt))
+#define mysqlnd_stmt_error(stmt) (stmt)->m->get_error_str((stmt))
+#define mysqlnd_stmt_sqlstate(stmt) (stmt)->m->get_sqlstate((stmt))
+#endif /* MYSQLND_USE_OPTIMISATIONS */
+/*****************************************************************************************************/
+
+
+
+PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type);
+
+/* LOAD DATA LOCAL */
+PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn);
+PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname);
+
+/* Simple commands */
+#define mysqlnd_autocommit(conn, mode) (conn)->m->query((conn),(mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", 16 TSRMLS_CC)
+#define mysqlnd_commit(conn) (conn)->m->query((conn), "COMMIT", sizeof("COMMIT")-1 TSRMLS_CC)
+#define mysqlnd_rollback(conn) (conn)->m->query((conn), "ROLLBACK", sizeof("ROLLBACK")-1 TSRMLS_CC)
+#define mysqlnd_list_dbs(conn, wild) (conn)->m->list_method((conn), wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL TSRMLS_CC)
+#define mysqlnd_list_fields(conn, tab,wild) (conn)->m->list_fields((conn), (tab), (wild) TSRMLS_CC)
+#define mysqlnd_list_processes(conn) (conn)->m->list_method((conn), "SHOW PROCESSLIST", NULL, NULL TSRMLS_CC)
+#define mysqlnd_list_tables(conn, wild) (conn)->m->list_method((conn), wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL TSRMLS_CC)
+#define mysqlnd_dump_debug_info(conn) (conn)->m->server_dump_debug_information((conn) TSRMLS_CC)
+#define mysqlnd_select_db(conn, db, db_len) (conn)->m->select_db((conn), (db), (db_len) TSRMLS_CC)
+#define mysqlnd_ping(conn) (conn)->m->ping((conn) TSRMLS_CC)
+#define mysqlnd_kill(conn, pid) (conn)->m->kill_connection((conn), (pid) TSRMLS_CC)
+#define mysqlnd_refresh(conn, options) (conn)->m->refresh_server((conn), (options) TSRMLS_CC)
+#define mysqlnd_shutdown(conn, level) (conn)->m->shutdown_server((conn), (level) TSRMLS_CC)
+#define mysqlnd_get_server_version(conn) (conn)->m->get_server_version((conn))
+#define mysqlnd_set_character_set(conn, cs) (conn)->m->set_charset((conn), (cs) TSRMLS_CC)
+#define mysqlnd_stat(conn, msg, msg_len) (conn)->m->get_server_statistics((conn), (msg), (msg_len) TSRMLS_CC)
+#define mysqlnd_options(conn, opt, value) (conn)->m->set_client_option((conn), (opt), (value) TSRMLS_CC)
+#define mysqlnd_set_server_option(conn, op) (conn)->m->set_server_option((conn), (op) TSRMLS_CC)
+
+/* Escaping */
+#define mysqlnd_real_escape_string(conn, newstr, escapestr, escapestr_len) \
+ (conn)->m->escape_string((conn), (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, int escapestr_len TSRMLS_DC);
+
+
+/* PS */
+#define mysqlnd_stmt_init(conn) (conn)->m->stmt_init((conn) 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_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(s,p,d,l) (s)->m->send_long_data((s), (p), (d), (l) TSRMLS_CC)
+#define mysqlnd_stmt_bind_param(stmt,bind) (stmt)->m->bind_param((stmt), (bind) TSRMLS_CC)
+#define mysqlnd_stmt_bind_result(stmt,bind) (stmt)->m->bind_result((stmt), (bind) 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_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);
+
+/* Persistent caching zval allocator */
+#define mysqlnd_palloc_init_cache(size) _mysqlnd_palloc_init_cache((size) TSRMLS_CC)
+#define mysqlnd_palloc_free_cache(cache) _mysqlnd_palloc_free_cache((cache) TSRMLS_CC)
+PHPAPI MYSQLND_ZVAL_PCACHE* _mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC);
+PHPAPI void _mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC);
+PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache,
+ zval *return_value);
+
+#define mysqlnd_palloc_rinit(cache) _mysqlnd_palloc_rinit((cache) TSRMLS_CC)
+#define mysqlnd_palloc_rshutdown(cache) _mysqlnd_palloc_rshutdown((cache) TSRMLS_CC)
+PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC);
+PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * cache TSRMLS_DC);
+
+
+#define mysqlnd_palloc_init_thd_cache(cache) _mysqlnd_palloc_init_thd_cache((cache) TSRMLS_CC)
+#define mysqlnd_palloc_free_thd_cache_reference(cache) _mysqlnd_palloc_free_thd_cache_reference((cache) TSRMLS_CC)
+
+PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC);
+MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache);
+PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC);
+
+
+/* There two should not be used from outside */
+void * mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const cache, zend_bool *allocated TSRMLS_DC);
+void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const cache,
+ enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC);
+
+
+
+/* ---------------------- QUERY CACHE ---------------*/
+struct st_mysqlnd_qcache {
+ HashTable *ht;
+ unsigned int references;
+#ifdef ZTS
+ MUTEX_T LOCK_access;
+#endif
+};
+
+
+typedef struct st_mysqlnd_qcache_element {
+ MYSQLND_RES_BUFFERED *data;
+ MYSQLND_RES_METADATA *meta;
+ const char * query;
+ size_t query_len;
+} MYSQLND_QCACHE_ELEMENT;
+
+
+PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_init_cache();
+PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_get_cache_reference(MYSQLND_QCACHE * const cache);
+PHPAPI void mysqlnd_qcache_free_cache_reference(MYSQLND_QCACHE **cache);
+PHPAPI void mysqlnd_qcache_stats(const MYSQLND_QCACHE * const cache, zval *return_value);
+MYSQLND_RES * mysqlnd_qcache_get(MYSQLND_QCACHE * const cache, const char * query,
+ size_t query_len);
+void mysqlnd_qcache_put(MYSQLND_QCACHE * const cache, char * query, size_t query_len,
+ MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta);
+
+
+
+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;
+ZEND_END_MODULE_GLOBALS(mysqlnd)
+
+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
+
+
+#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 0000000000..9b3b819fcd
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_alloc.c
@@ -0,0 +1,285 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_palloc.h"
+
+
+#define MYSQLND_SILENT
+#define MYSQLND_DONT_DUMP_STATS
+
+#define MYSQLND_ZVALS_MAX_CACHE 5000
+
+
+#if A0
+/* Caching zval allocator */
+zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache);
+void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache);
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache();
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache);
+void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache);
+#endif
+
+
+/*
+ The cache line is a big contiguous array of zval pointers.
+ Because the CPU cache will cache starting from an address, and not
+ before it, then we have to organize our structure according to this.
+ Thus, if 'last_added' is valid pointer (not NULL) then last_added is
+ increased. When zval is cached, if there is room, last_added is decreased
+ and then the zval pointer will be assigned to it. This means that some
+ positions may become hot points and stay in the cache.
+ Imagine we have 5 pointers in a line
+ 1. last_added = list_item->ptr_line + cache->max_items;
+ 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ empty_position, always 0x0 <-- last_added
+
+ 5. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ (memory addresses increase downwards)
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+ 6. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 7. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0xC <-- last_added
+ 0xB
+ 0xA
+ ---
+ 0x0
+
+ 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ 0x0 <-- last_added
+
+*/
+
+
+zval * mysqlnd_alloc_get_zval(MYSQLND_ZVAL_CACHE * const cache)
+{
+ zval *ret = NULL;
+
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_get_zval %p] *last_added=%p free_items=%d ", cache, cache? cache->free_list->last_added:NULL, cache->free_items);
+#endif
+
+ if (cache) {
+ if ((ret = *cache->free_list->last_added)) {
+ *cache->free_list->last_added++ = NULL;
+
+ --cache->free_items;
+ ++cache->get_hits;
+ } else {
+ ++cache->get_misses;
+ }
+ }
+ if (!ret) {
+ ALLOC_ZVAL(ret);
+ }
+ INIT_PZVAL(ret);
+
+#ifndef MYSQLND_SILENT
+ php_printf("ret=%p\n", ret);
+#endif
+ return ret;
+}
+
+static
+void mysqlnd_alloc_cache_prealloc(MYSQLND_ZVAL_CACHE * const cache, unsigned int count)
+{
+ zval *data;
+ cache->free_items = count;
+ while (count--) {
+ MAKE_STD_ZVAL(data);
+ ZVAL_NULL(data);
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_prealloc %p] items=%d data=%p\n", cache, cache->free_items, data);
+#endif
+
+ *(--cache->free_list->last_added) = data;
+ }
+}
+
+void mysqlnd_alloc_zval_ptr_dtor(zval **zv, MYSQLND_ZVAL_CACHE * const cache)
+{
+ if (!cache || ZVAL_REFCOUNT(*zv) > 1 || cache->max_items == cache->free_items) {
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_zval_ptr_dtor %p]1 last_added-1=%p *zv=%p\n", cache->free_list->last_added, *zv);
+#endif
+ /* We can't cache zval's with refcount > 1 */
+ zval_ptr_dtor(zv);
+ if (cache) {
+ if (cache->max_items == cache->free_items) {
+ ++cache->put_full_misses;
+ } else {
+ ++cache->put_refcount_misses;
+ }
+ }
+ } else {
+ /* refcount is 1 and there is place. Go, cache it! */
+ ++cache->free_items;
+ zval_dtor(*zv);
+ ZVAL_NULL(*zv);
+ *(--cache->free_list->last_added) = *zv;
+ ++cache->put_hits;
+ }
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_zval_ptr_dtor %p] free_items=%d\n", cache, cache->free_items);
+#endif
+}
+
+
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_init_cache(void)
+{
+ MYSQLND_ZVAL_CACHE *ret = ecalloc(1, sizeof(MYSQLND_ZVAL_CACHE));
+
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_init_cache %p]\n", ret);
+#endif
+
+ ret->max_items = MYSQLND_ZVALS_MAX_CACHE;
+ ret->free_items = 0;
+ ret->references = 1;
+
+ /* Let's have always one, so we don't need to do a check in get_zval */
+ ret->free_list = ecalloc(1, sizeof(struct st_mysqlnd_zval_list));
+
+ /* One more for empty position of last_added */
+ ret->free_list->ptr_line = ecalloc(ret->max_items + 1, sizeof(zval *));
+ ret->free_list->last_added = ret->free_list->ptr_line + ret->max_items;
+
+ mysqlnd_alloc_cache_prealloc(ret, (ret->max_items / 100) * 100);
+
+ return ret;
+}
+
+
+MYSQLND_ZVAL_CACHE* mysqlnd_alloc_get_cache_reference(MYSQLND_ZVAL_CACHE *cache)
+{
+ if (cache) {
+ cache->references++;
+ }
+ return cache;
+}
+
+
+static
+void mysqlnd_alloc_free_cache(MYSQLND_ZVAL_CACHE *cache)
+{
+#ifndef MYSQLND_SILENT
+ uint i = 0;
+ php_printf("[mysqlnd_alloc_free_cache %p]\n", cache);
+#endif
+
+ while (*cache->free_list->last_added) {
+#ifndef MYSQLND_SILENT
+ php_printf("\t[free_item %d %p]\n", i++, *cache->free_list->last_added);
+#endif
+ zval_ptr_dtor(cache->free_list->last_added);
+ cache->free_list->last_added++;
+ }
+#ifndef MYSQLND_DONT_DUMP_STATS
+ php_printf("CACHE STATS:\n\tGET\n\t\tHITS:%lu\n\t\tMISSES=%lu\n\t\tHIT RATIO=%1.3f\n\t"
+ "PUT\n\t\tHITS:%lu\n\t\tFULL_MISS=%lu\n\t\tREFC_MISS=%lu\n\t\tHIT RATIO=%1.3f\n\n",
+ cache->get_hits, cache->get_misses, (1.0*cache->get_hits/(cache->get_hits + cache->get_misses)),
+ cache->put_hits, cache->put_full_misses, cache->put_refcount_misses,
+ (1.0 * cache->put_hits / (cache->put_hits + cache->put_full_misses + cache->put_refcount_misses)));
+#endif
+ efree(cache->free_list->ptr_line);
+ efree(cache->free_list);
+ efree(cache);
+}
+
+
+
+void mysqlnd_alloc_free_cache_reference(MYSQLND_ZVAL_CACHE **cache)
+{
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_alloc_free_cache_reference %p] refs=%d\n", *cache, (*cache)->references);
+#endif
+ if (*cache && --(*cache)->references == 0) {
+ mysqlnd_alloc_free_cache(*cache);
+ }
+ *cache = 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_charset.c b/ext/mysqlnd/mysqlnd_charset.c
new file mode 100644
index 0000000000..cd6112af9f
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_charset.c
@@ -0,0 +1,603 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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"
+
+/* {{{ utf8 functions */
+
+static uint 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;
+ }
+ return 0;
+}
+
+static uint check_mb_utf8_valid(const char *start, const char *end)
+{
+ uint len = check_mb_utf8_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+static uint mysqlnd_mbcharlen_utf8(uint 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 */
+ }
+ /* We still don't support characters out of the BMP */
+
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ big5 functions */
+#define valid_big5head(c) (0xA1 <= (uint)(c) && (uint)(c) <= 0xF9)
+#define valid_big5tail(c) ((0x40 <= (uint)(c) && (uint)(c) <= 0x7E) || \
+ (0xA1 <= (uint)(c) && (uint)(c) <= 0xFE))
+
+#define isbig5code(c,d) (isbig5head(c) && isbig5tail(d))
+
+static uint check_mb_big5(const char *start, const char *end)
+{
+ return (valid_big5head(*(start)) && (end - start) > 1 && valid_big5tail(*(start + 1)) ? 2 : 0);
+}
+
+
+static uint mysqlnd_mbcharlen_big5(uint 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 uint 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 uint mysqlnd_mbcharlen_cp932(uint cp932)
+{
+ return (valid_cp932head((zend_uchar)cp932)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ euckr functions */
+#define valid_euckr(c) ((0xA1 <= (zend_uchar)(c) && (zend_uchar)(c) <= 0xFE))
+
+static uint 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 uint mysqlnd_mbcharlen_euckr(uint 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 uint 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 uint mysqlnd_mbcharlen_eucjpms(uint 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 uint check_mb_gb2312(const char *start, const char *end)
+{
+ return (valid_gb2312_head((uint)start[0]) && end - start > 1 &&
+ valid_gb2312_tail((uint)start[1])) ? 2 : 0;
+}
+
+
+static uint mysqlnd_mbcharlen_gb2312(uint 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 uint 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 uint mysqlnd_mbcharlen_gbk(uint 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) <= 0x7C))
+
+
+static uint 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 uint mysqlnd_mbcharlen_sjis(uint sjis)
+{
+ return (valid_sjis_head((zend_uchar)sjis)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ ucs2 functions */
+static uint check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+
+static uint mysqlnd_mbcharlen_ucs2(uint 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 uint check_mb_ujis(const char *start, const char *end)
+{
+ if (*(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 uint mysqlnd_mbcharlen_ujis(uint ujis)
+{
+ return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1);
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_charsets */
+const MYSQLND_CHARSET mysqlnd_charsets[] =
+{
+ { 1, "big5","big5_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5},
+ { 3, "dec8", "dec8_swedisch_ci", 1, 1, 0, NULL, NULL},
+ { 4, "cp850", "cp850_general_ci", 1, 1, 0, NULL, NULL},
+ { 6, "hp8", "hp8_english_ci", 1, 1, 0, NULL, NULL},
+ { 7, "koi8r", "koi8r_general_ci", 1, 1, 0, NULL, NULL},
+ { 8, "latin1", "latin1_swedish_ci", 1, 1, 0, NULL, NULL},
+ { 9, "latin2", "latin2_general_ci", 1, 1, 0, NULL, NULL},
+ { 10, "swe7", "swe7_swedish_ci", 1, 1, 0, NULL, NULL},
+ { 11, "ascii", "ascii_general_ci", 1, 1, 0, NULL, NULL},
+ { 12, "ujis", "ujis_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis},
+ { 13, "sjis", "sjis_japanese_ci", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis},
+ { 16, "hebrew", "hebrew_general_ci", 1, 1, 0, NULL, NULL},
+ { 18, "tis620", "tis620_thai_ci", 1, 1, 0, NULL, NULL},
+ { 19, "euckr", "euckr_korean_ci", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr},
+ { 22, "koi8u", "koi8u_general_ci", 1, 1, 0, NULL, NULL},
+ { 24, "gb2312", "gb2312_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312},
+ { 25, "greek", "greek_general_ci", 1, 1, 0, NULL, NULL},
+ { 26, "cp1250", "cp1250_general_ci", 1, 1, 0, NULL, NULL},
+ { 28, "gbk", "gbk_chinese_ci", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk},
+ { 30, "latin5", "latin5_turkish_ci", 1, 1, 0, NULL, NULL},
+ { 32, "armscii8", "armscii8_general_ci", 1, 1, 0, NULL, NULL},
+ { 33, "utf8", "utf8_general_ci", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 35, "ucs2", "ucs2_general_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 36, "cp866", "cp866_general_ci", 1, 1, 0, NULL, NULL},
+ { 37, "keybcs2", "keybcs2_general_ci", 1, 1, 0, NULL, NULL},
+ { 38, "macce", "macce_general_ci", 1, 1, 0, NULL, NULL},
+ { 39, "macroman", "macroman_general_ci", 1, 1, 0, NULL, NULL},
+ { 40, "cp852", "cp852_general_ci", 1, 1, 0, NULL, NULL},
+ { 41, "latin7", "latin7_general_ci", 1, 1, 0, NULL, NULL},
+ { 51, "cp1251", "cp1251_general_ci", 1, 1, 0, NULL, NULL},
+ { 57, "cp1256", "cp1256_general_ci", 1, 1, 0, NULL, NULL},
+ { 59, "cp1257", "cp1257_general_ci", 1, 1, 0, NULL, NULL},
+ { 63, "binary", "binary", 1, 1, 0, NULL, NULL},
+ { 92, "geostd8", "geostd8_general_ci", 1, 1, 0, NULL, NULL},
+ { 95, "cp932", "cp932_japanese_ci", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932},
+ { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 2, "latin2", "latin2_czech_cs", 1, 1, 0, NULL, NULL},
+ { 5, "latin1", "latin1_german_ci", 1, 1, 0, NULL, NULL},
+ { 14, "cp1251", "cp1251_bulgarian_ci", 1, 1, 0, NULL, NULL},
+ { 15, "latin1", "latin1_danish_ci", 1, 1, 0, NULL, NULL},
+ { 17, "filename", "filename", 1, 5, 1, NULL, NULL},
+ { 20, "latin7", "latin7_estonian_cs", 1, 1, 0, NULL, NULL},
+ { 21, "latin2", "latin2_hungarian_ci", 1, 1, 0, NULL, NULL},
+ { 23, "cp1251", "cp1251_ukrainian_ci", 1, 1, 0, NULL, NULL},
+ { 27, "latin2", "latin2_croatian_ci", 1, 1, 0, NULL, NULL},
+ { 29, "cp1257", "cp1257_lithunian_ci", 1, 1, 0, NULL, NULL},
+ { 31, "latin1", "latin1_german2_ci", 1, 1, 0, NULL, NULL},
+ { 34, "cp1250", "cp1250_czech_cs", 1, 1, 0, NULL, NULL},
+ { 42, "latin7", "latin7_general_cs", 1, 1, 0, NULL, NULL},
+ { 43, "macce", "macce_bin", 1, 1, 0, NULL, NULL},
+ { 44, "cp1250", "cp1250_croatian_ci", 1, 1, 0, NULL, NULL},
+ { 47, "latin1", "latin1_bin", 1, 1, 0, NULL, NULL},
+ { 48, "latin1", "latin1_general_ci", 1, 1, 0, NULL, NULL},
+ { 49, "latin1", "latin1_general_cs", 1, 1, 0, NULL, NULL},
+ { 50, "cp1251", "cp1251_bin", 1, 1, 0, NULL, NULL},
+ { 52, "cp1251", "cp1251_general_cs", 1, 1, 0, NULL, NULL},
+ { 53, "macroman", "macroman_bin", 1, 1, 0, NULL, NULL},
+ { 58, "cp1257", "cp1257_bin", 1, 1, 0, NULL, NULL},
+ { 60, "armascii8", "armascii8_bin", 1, 1, 0, NULL, NULL},
+ { 65, "ascii", "ascii_bin", 1, 1, 0, NULL, NULL},
+ { 66, "cp1250", "cp1250_bin", 1, 1, 0, NULL, NULL},
+ { 67, "cp1256", "cp1256_bin", 1, 1, 0, NULL, NULL},
+ { 68, "cp866", "cp866_bin", 1, 1, 0, NULL, NULL},
+ { 69, "dec8", "dec8_bin", 1, 1, 0, NULL, NULL},
+ { 70, "greek", "greek_bin", 1, 1, 0, NULL, NULL},
+ { 71, "hebew", "hebrew_bin", 1, 1, 0, NULL, NULL},
+ { 72, "hp8", "hp8_bin", 1, 1, 0, NULL, NULL},
+ { 73, "keybcs2", "keybcs2_bin", 1, 1, 0, NULL, NULL},
+ { 74, "koi8r", "koi8r_bin", 1, 1, 0, NULL, NULL},
+ { 75, "koi8u", "koi8u_bin", 1, 1, 0, NULL, NULL},
+ { 77, "latin2", "latin2_bin", 1, 1, 0, NULL, NULL},
+ { 78, "latin5", "latin5_bin", 1, 1, 0, NULL, NULL},
+ { 79, "latin7", "latin7_bin", 1, 1, 0, NULL, NULL},
+ { 80, "cp850", "cp850_bin", 1, 1, 0, NULL, NULL},
+ { 81, "cp852", "cp852_bin", 1, 1, 0, NULL, NULL},
+ { 82, "swe7", "swe7_bin", 1, 1, 0, NULL, NULL},
+ { 93, "geostd8", "geostd8_bin", 1, 1, 0, NULL, NULL},
+ { 83, "utf8", "utf8_bin", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 84, "big5", "big5_bin", 1, 2, 0, mysqlnd_mbcharlen_big5, check_mb_big5},
+ { 85, "euckr", "euckr_bin", 1, 2, 0, mysqlnd_mbcharlen_euckr, check_mb_euckr},
+ { 86, "gb2312", "gb2312_bin", 1, 2, 0, mysqlnd_mbcharlen_gb2312, check_mb_gb2312},
+ { 87, "gbk", "gbk_bin", 1, 2, 0, mysqlnd_mbcharlen_gbk, check_mb_gbk},
+ { 88, "sjis", "sjis_bin", 1, 2, 0, mysqlnd_mbcharlen_sjis, check_mb_sjis},
+ { 89, "tis620", "tis620_bin", 1, 1, 0, NULL, NULL},
+ { 90, "ucs2", "ucs2_bin", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 91, "ujis", "ujis_bin", 1, 3, 0, mysqlnd_mbcharlen_ujis, check_mb_ujis},
+ { 94, "latin1", "latin1_spanish_ci", 1, 1, 0, NULL, NULL},
+ { 96, "cp932", "cp932_bin", 1, 2, 1, mysqlnd_mbcharlen_cp932, check_mb_cp932},
+ { 99, "cp1250", "cp1250_polish_ci", 1, 1, 0, NULL, NULL},
+ { 98, "eucjpms", "eucjpms_bin", 1, 3, 0, mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 128, "ucs2", "ucs2_unicode_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 129, "ucs2", "ucs2_icelandic_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 130, "ucs2", "ucs2_latvian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 131, "ucs2", "ucs2_romanian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 132, "ucs2", "ucs2_slovenian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 133, "ucs2", "ucs2_polish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 134, "ucs2", "ucs2_estonian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 135, "ucs2", "ucs2_spanish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 136, "ucs2", "ucs2_swedish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 137, "ucs2", "ucs2_turkish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 138, "ucs2", "ucs2_czech_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 139, "ucs2", "ucs2_danish_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 140, "ucs2", "ucs2_lithunian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 141, "ucs2", "ucs2_slovak_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 142, "ucs2", "ucs2_spanish2_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 143, "ucs2", "ucs2_roman_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 144, "ucs2", "ucs2_persian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 145, "ucs2", "ucs2_esperanto_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 146, "ucs2", "ucs2_hungarian_ci", 2, 2, 0, mysqlnd_mbcharlen_ucs2, check_mb_ucs2},
+ { 192, "utf8", "utf8_general_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 193, "utf8", "utf8_icelandic_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 194, "utf8", "utf8_latvian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 195, "utf8", "utf8_romanian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 196, "utf8", "utf8_slovenian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 197, "utf8", "utf8_polish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 198, "utf8", "utf8_estonian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 119, "utf8", "utf8_spanish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 200, "utf8", "utf8_swedish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 201, "utf8", "utf8_turkish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 202, "utf8", "utf8_czech_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 203, "utf8", "utf8_danish_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid },
+ { 204, "utf8", "utf8_lithunian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid },
+ { 205, "utf8", "utf8_slovak_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 206, "utf8", "utf8_spanish2_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 207, "utf8", "utf8_roman_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 208, "utf8", "utf8_persian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 209, "utf8", "utf8_esperanto_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 210, "utf8", "utf8_hungarian_ci", 1, 3, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 254, "utf8", "utf8_general_cs", 1, 2, 0, mysqlnd_mbcharlen_utf8, check_mb_utf8_valid },
+ { 0, NULL, NULL, 0, 0, 0, NULL, NULL}
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_find_charset_nr */
+PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_nr(uint 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)
+{
+ 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, int 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++) {
+ uint 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, int 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");
+
+ for (;escapestr < end; escapestr++) {
+ char esc = '\0';
+ uint 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));
+}
+/* }}} */
+
+/*
+ * 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 0000000000..d324d1ff0b
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_charset.h
@@ -0,0 +1,35 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2007 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> |
+ +----------------------------------------------------------------------+
+*/
+
+PHPAPI ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char *newstr,
+ const char *escapestr, int escapestr_len TSRMLS_DC);
+
+PHPAPI ulong mysqlnd_cset_escape_slashes(const MYSQLND_CHARSET * const cset, char *newstr,
+ const char *escapestr, int escapestr_len TSRMLS_DC);
+
+
+/*
+ * 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 0000000000..3dc3f592a6
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_debug.c
@@ -0,0 +1,1345 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_palloc.h"
+#include "mysqlnd_statistics.h"
+#include "zend_builtin_functions.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
+
+#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
+
+static char * mysqlnd_emalloc_name = "_mysqlnd_emalloc";
+static char * mysqlnd_pemalloc_name = "_mysqlnd_pemalloc";
+static char * mysqlnd_ecalloc_name = "_mysqlnd_ecalloc";
+static char * mysqlnd_pecalloc_name = "_mysqlnd_pecalloc";
+static char * mysqlnd_erealloc_name = "_mysqlnd_erealloc";
+static char * mysqlnd_perealloc_name= "_mysqlnd_perealloc";
+static char * mysqlnd_efree_name = "_mysqlnd_efree";
+static char * mysqlnd_pefree_name = "_mysqlnd_pefree";
+static char * mysqlnd_malloc_name = "_mysqlnd_malloc";
+static char * mysqlnd_calloc_name = "_mysqlnd_calloc";
+static char * mysqlnd_realloc_name = "_mysqlnd_realloc";
+static char * mysqlnd_free_name = "_mysqlnd_free";
+
+/* {{{ 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;
+ uint 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) {
+ if (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 = spprintf(&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;
+ efree(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;
+ uint 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) {
+ if (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);
+ vspprintf(&buffer, 0, format, args);
+ va_end(args);
+
+ message_line_len = spprintf(&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);
+ efree(buffer);
+
+ ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
+ efree(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_res_meta::func_enter */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
+ unsigned int line, const char * const file,
+ char * func_name, uint func_name_len)
+{
+ if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
+ return FALSE;
+ }
+ if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
+ return FALSE;
+ }
+
+ if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 &&
+ (func_name == mysqlnd_emalloc_name || func_name == mysqlnd_pemalloc_name ||
+ func_name == mysqlnd_ecalloc_name || func_name == mysqlnd_pecalloc_name ||
+ func_name == mysqlnd_erealloc_name || func_name == mysqlnd_perealloc_name ||
+ func_name == mysqlnd_efree_name || func_name == mysqlnd_pefree_name ||
+ func_name == mysqlnd_efree_name || func_name == mysqlnd_pefree_name ||
+ func_name == mysqlnd_malloc_name || func_name == mysqlnd_calloc_name ||
+ func_name == mysqlnd_realloc_name || func_name == mysqlnd_free_name ||
+ func_name == mysqlnd_palloc_zval_ptr_dtor_name || func_name == mysqlnd_palloc_get_zval_name ||
+ func_name == mysqlnd_read_header_name || func_name == mysqlnd_read_body_name)) {
+ zend_stack_push(&self->call_stack, "", sizeof(""));
+ return FALSE;
+ }
+
+ zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
+
+ 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;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::func_leave */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line,
+ const char * const file)
+{
+ char *func_name;
+
+ if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
+ return PASS;
+ }
+ if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
+ return PASS;
+ }
+
+ zend_stack_top(&self->call_stack, (void **)&func_name);
+
+ 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))
+ {
+ self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
+ }
+
+ return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
+{
+ MYSQLND_ZTS(self);
+ if (self->stream) {
+ php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
+ self->stream = NULL;
+ }
+ 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_hash_destroy(&self->not_filtered_functions);
+ efree(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)
+{
+ uint mode_len = strlen(mode), i;
+ enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
+
+ 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;
+ 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 {
+ 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];
+ uint 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;
+ }
+ }
+ }
+ 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;
+ 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 */
+MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D)
+{
+ MYSQLND_DEBUG *ret = ecalloc(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_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
+
+ ret->m = & mysqlnd_mysqlnd_debug_methods;
+
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_debug */
+void _mysqlnd_debug(const char *mode TSRMLS_DC)
+{
+#ifdef PHP_DEBUG
+ MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
+ if (!dbg) {
+ MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(TSRMLS_C);
+ 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);
+ }
+#endif
+}
+/* }}} */
+
+
+#if ZEND_DEBUG
+#else
+#define __zend_filename "/unknown/unknown"
+#define __zend_lineno 0
+#endif
+
+
+/* {{{ _mysqlnd_emalloc */
+void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_emalloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ ret = emalloc(size);
+ DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pemalloc */
+void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_pemalloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ if (persistent == FALSE) {
+ DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ ret = pemalloc(size, persistent);
+ DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent);
+
+ if (persistent == FALSE) {
+ DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ if (MYSQLND_G(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_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT;
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_ecalloc */
+void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_ecalloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
+
+ ret = ecalloc(nmemb, size);
+
+ DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pecalloc */
+void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_pecalloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ if (persistent == FALSE) {
+ DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ ret = pecalloc(nmemb, size, persistent);
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+
+ if (persistent == FALSE) {
+ DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ if (MYSQLND_G(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_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT;
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_erealloc */
+void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_erealloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
+
+ ret = erealloc(ptr, new_size);
+
+ DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_INF_FMT("new_ptr=%p", ret);
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_perealloc */
+void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_perealloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent);
+ if (persistent == FALSE) {
+ DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ ret = perealloc(ptr, new_size, persistent);
+
+ DBG_INF_FMT("new_ptr=%p", ret);
+
+ if (persistent == FALSE) {
+ DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_EREALLOC_COUNT:STAT_MEM_REALLOC_COUNT);
+ if (MYSQLND_G(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_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT;
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, new_size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_efree */
+void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
+{
+ DBG_ENTER(mysqlnd_efree_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p", ptr);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT);
+
+ efree(ptr);
+
+ DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_pefree */
+void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
+{
+ DBG_ENTER(mysqlnd_pefree_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent);
+ if (persistent == FALSE) {
+ DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+
+ pefree(ptr, persistent);
+
+ if (persistent == FALSE) {
+ DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
+ }
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT);
+ }
+ DBG_VOID_RETURN;
+}
+
+
+/* {{{ _mysqlnd_malloc */
+void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_malloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+
+ ret = malloc(size);
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_calloc */
+void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_calloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+
+ ret = calloc(nmemb, size);
+
+ DBG_INF_FMT("size=%lu ptr=%p", size, ret);
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_realloc */
+void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+{
+ void *ret;
+ DBG_ENTER(mysqlnd_realloc_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
+ DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
+
+ ret = realloc(ptr, new_size);
+
+ DBG_INF_FMT("new_ptr=%p", ret);
+
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_free */
+void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
+{
+ DBG_ENTER(mysqlnd_free_name);
+ DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
+ DBG_INF_FMT("ptr=%p", ptr);
+
+ free(ptr);
+
+ if (MYSQLND_G(collect_memory_statistics)) {
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
+
+/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
+
+#if PHP_MAJOR_VERSION >= 6
+/* {{{ 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, 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;
+ TSRMLS_FETCH();
+
+ /*
+ * 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;
+ TSRMLS_FETCH();
+
+ 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: {
+ zstr class_name;
+ zend_uint class_name_len;
+ int dup;
+ TSRMLS_FETCH();
+
+ TRACE_APPEND_STR("Object(");
+
+ dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
+
+ if (UG(unicode)) {
+ zval tmp;
+
+ 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);
+ } else {
+ TRACE_APPEND_STRL(class_name.s, class_name_len);
+ }
+ 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, 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;
+
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+ num = va_arg(args, int*);
+
+ 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), (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, 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;
+ TSRMLS_FETCH();
+
+ 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 dup;
+ TSRMLS_FETCH();
+
+ TRACE_APPEND_STR("Object(");
+
+ dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
+
+ TRACE_APPEND_STRL(class_name, class_name_len);
+ if(!dup) {
+ efree(class_name);
+ }
+
+ TRACE_APPEND_STR("), ");
+ break;
+ }
+ default:
+ break;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static int mysqlnd_build_trace_string(zval **frame, 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;
+
+ str = va_arg(args, char**);
+ len = va_arg(args, int*);
+ num = va_arg(args, int*);
+
+ 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), (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
+
+
+char * mysqlnd_get_backtrace(TSRMLS_D)
+{
+ zval *trace;
+ char *res = estrdup(""), **str = &res, *s_tmp;
+ int res_len = 0, *len = &res_len, num = 0;
+
+ MAKE_STD_ZVAL(trace);
+ zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC);
+
+ zend_hash_apply_with_arguments(Z_ARRVAL_P(trace), (apply_func_args_t)mysqlnd_build_trace_string, 3, str, len, &num);
+ zval_ptr_dtor(&trace);
+
+ 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';
+
+ 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_debug.h b/ext/mysqlnd/mysqlnd_debug.h
new file mode 100644
index 0000000000..120d74aaa5
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_debug.h
@@ -0,0 +1,145 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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 "zend_stack.h"
+
+#define MYSQLND_DEBUG_MEMORY 1
+
+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,
+ char * func_name, uint func_name_len);
+ enum_func_status (*func_leave)(MYSQLND_DEBUG *self, unsigned int line, const char * const file);
+ enum_func_status (*close)(MYSQLND_DEBUG *self);
+ enum_func_status (*free)(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;
+ HashTable not_filtered_functions;
+ struct st_mysqlnd_debug_methods *m;
+};
+
+
+MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D);
+
+#define MYSQLND_MEM_D TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC
+
+
+void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D);
+void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D);
+void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D);
+void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D);
+void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D);
+void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D);
+void _mysqlnd_efree(void *ptr MYSQLND_MEM_D);
+void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D);
+void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D);
+void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D);
+void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D);
+void _mysqlnd_free(void *ptr MYSQLND_MEM_D);
+
+char * mysqlnd_get_backtrace(TSRMLS_D);
+
+#if PHP_DEBUG && !defined(PHP_WIN32)
+#define DBG_INF(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", (msg)); } while (0)
+#define DBG_ERR(msg) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", (msg)); } while (0)
+#define DBG_INF_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0)
+#define DBG_ERR_FMT(...) do { if (dbg_skip_trace == FALSE) MYSQLND_G(dbg)->m->log_va(MYSQLND_G(dbg), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0)
+
+#define DBG_ENTER(func_name) zend_bool dbg_skip_trace = TRUE; if (MYSQLND_G(dbg)) dbg_skip_trace = !MYSQLND_G(dbg)->m->func_enter(MYSQLND_G(dbg), __LINE__, __FILE__, func_name, strlen(func_name));
+#define DBG_RETURN(value) do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return (value); } while (0)
+#define DBG_VOID_RETURN do { if (MYSQLND_G(dbg)) MYSQLND_G(dbg)->m->func_leave(MYSQLND_G(dbg), __LINE__, __FILE__); return; } while (0)
+
+
+
+#else
+static inline void DBG_INF(char *msg) {}
+static inline void DBG_ERR(char *msg) {}
+static inline void DBG_INF_FMT(char *format, ...) {}
+static inline void DBG_ERR_FMT(char *format, ...) {}
+static inline void DBG_ENTER(char *func_name) {}
+#define DBG_RETURN(value) return (value)
+#define DBG_VOID_RETURN return;
+#endif
+
+
+#if MYSQLND_DEBUG_MEMORY
+
+#define mnd_emalloc(size) _mysqlnd_emalloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_pemalloc(size, pers) _mysqlnd_pemalloc((size), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_ecalloc(nmemb, size) _mysqlnd_ecalloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_pecalloc(nmemb, size, p) _mysqlnd_pecalloc((nmemb), (size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_erealloc(ptr, new_size) _mysqlnd_erealloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_perealloc(ptr, new_size, p) _mysqlnd_perealloc((ptr), (new_size), (p) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_efree(ptr) _mysqlnd_efree((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_pefree(ptr, pers) _mysqlnd_pefree((ptr), (pers) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_malloc(size) _mysqlnd_malloc((size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_calloc(nmemb, size) _mysqlnd_calloc((nmemb), (size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_realloc(ptr, new_size) _mysqlnd_realloc((ptr), (new_size) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+#define mnd_free(ptr) _mysqlnd_free((ptr) TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
+
+#else
+
+#define mnd_emalloc(size) emalloc((size))
+#define mnd_pemalloc(size, pers) pemalloc((size), (pers))
+#define mnd_ecalloc(nmemb, size) ecalloc((nmemb), (size))
+#define mnd_pecalloc(nmemb, size, p) pecalloc((nmemb), (size), (p))
+#define mnd_erealloc(ptr, new_size) erealloc((ptr), (new_size))
+#define mnd_perealloc(ptr, new_size, p) perealloc((ptr), (new_size), (p))
+#define mnd_efree(ptr) efree((ptr))
+#define mnd_pefree(ptr, pers) pefree((ptr), (pers))
+#define mnd_malloc(size) malloc((size))
+#define mnd_calloc(nmemb, size) calloc((nmemb), (size))
+#define mnd_realloc(ptr, new_size) realloc((ptr), (new_size))
+#define mnd_free(ptr) free((ptr))
+
+#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_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
new file mode 100644
index 0000000000..f8a3a8b57d
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -0,0 +1,367 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_ENUM_N_DEF_H
+#define MYSQLND_ENUM_N_DEF_H
+
+
+#define MYSQLND_ERRMSG_SIZE 512
+#define MYSQLND_SQLSTATE_LENGTH 5
+#define MYSQLND_SQLSTATE_NULL "00000"
+
+#define MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED 16
+#define MYSQLND_SERVER_QUERY_NO_INDEX_USED 32
+
+#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 */
+
+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,
+#if PHP_MAJOR_VERSION >= 6
+ MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE = 200,
+#endif
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ MYSQLND_OPT_INT_AND_YEAR_AS_INT = 201,
+#endif
+ MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202,
+ MYSQLND_OPT_NET_READ_BUFFER_SIZE = 203,
+} enum_mysqlnd_option;
+
+
+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 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_RSET_QUERY,
+ STAT_NON_RSET_QUERY,
+ STAT_NO_INDEX_USED,
+ STAT_BAD_INDEX_USED,
+ 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_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_AMMOUNT,
+ STAT_MEM_ECALLOC_COUNT,
+ STAT_MEM_ECALLOC_AMMOUNT,
+ STAT_MEM_EREALLOC_COUNT,
+ STAT_MEM_EREALLOC_AMMOUNT,
+ STAT_MEM_EFREE_COUNT,
+ STAT_MEM_MALLOC_COUNT,
+ STAT_MEM_MALLOC_AMMOUNT,
+ STAT_MEM_CALLOC_COUNT,
+ STAT_MEM_CALLOC_AMMOUNT,
+ STAT_MEM_REALLOC_COUNT,
+ STAT_MEM_REALLOC_AMMOUNT,
+ STAT_MEM_FREE_COUNT,
+ STAT_LAST /* Should be always the last */
+} enum_mysqlnd_collected_stats;
+
+
+#define MYSQLND_DEFAULT_PREFETCH_ROWS (ulong) 1
+
+
+#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_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h
new file mode 100644
index 0000000000..8f8e0e69bc
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h
@@ -0,0 +1,121 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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
+#define MYSQL MYSQLND
+#define my_bool zend_bool
+#define my_ulonglong mynd_ulonglong
+
+#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
+
+#define SERVER_QUERY_NO_GOOD_INDEX_USED MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED
+#define SERVER_QUERY_NO_INDEX_USED MYSQLND_SERVER_QUERY_NO_INDEX_USED
+
+
+/* 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))
+#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_lengths(r) mysqlnd_fetch_lengths((r))
+#define mysql_fetch_row(r) mysqlnd_fetch_row((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_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_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_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_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_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)->data)
+
+#endif /* MYSQLND_LIBMYSQL_COMPAT_H */
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
new file mode 100644
index 0000000000..2e5466aa07
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -0,0 +1,263 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+
+enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn,
+ enum php_mysql_packet_type ok_packet,
+ zend_bool silent, enum php_mysqlnd_server_command command
+ TSRMLS_DC);
+
+
+#define ALLOC_CALLBACK_ARGS(a, b, c)\
+if (c) {\
+ a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\
+ for (i = b; i < c; i++) {\
+ a[i] = mnd_emalloc(sizeof(zval *));\
+ MAKE_STD_ZVAL(*a[i]);\
+ }\
+}
+
+#define FREE_CALLBACK_ARGS(a, b, c)\
+if (a) {\
+ for (i=b; i < c; i++) {\
+ zval_ptr_dtor(a[i]);\
+ mnd_efree(a[i]);\
+ }\
+ mnd_efree(a);\
+}
+
+/* {{{ 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");
+
+ *ptr= info= ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_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, char *buf, uint 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, 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, uint error_buf_len TSRMLS_DC)
+{
+ MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
+
+ DBG_ENTER("mysqlnd_local_infile_error");
+
+ if (info) {
+ strncpy(error_buf, info->error_msg, error_buf_len);
+ DBG_INF_FMT("have info, %d", info->error_no);
+ DBG_RETURN(info->error_no);
+ }
+
+ strncpy(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)
+{
+ 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 * 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, const char *filename, zend_bool *is_warning TSRMLS_DC)
+{
+ char *buf;
+ char empty_packet[MYSQLND_HEADER_SIZE];
+ enum_func_status result = FAIL;
+ uint buflen = 4096;
+ void *info = NULL;
+ int bufsize;
+ size_t ret;
+ MYSQLND_INFILE infile;
+
+ 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 = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC);
+ *is_warning = TRUE;
+ goto infile_error;
+ }
+
+ infile = conn->infile;
+ /* allocate buffer for reading data */
+ buf = (char *)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)) {
+ *is_warning = TRUE;
+ /* error occured */
+ strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
+ conn->error_info.error_no =
+ infile.local_infile_error(info, conn->error_info.error,
+ sizeof(conn->error_info.error) TSRMLS_CC);
+ /* write empty packet to server */
+ ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 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 = mysqlnd_stream_write_w_header(conn, buf, bufsize 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 = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC)) < 0) {
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ goto infile_error;
+ }
+
+ /* error during read occured */
+ if (bufsize < 0) {
+ *is_warning = TRUE;
+ DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
+ conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error,
+ sizeof(conn->error_info.error) TSRMLS_CC);
+ goto infile_error;
+ }
+
+ result = PASS;
+
+infile_error:
+ /* get response from server and update upsert values */
+ if (FAIL == mysqlnd_simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY TSRMLS_CC)) {
+ result = FAIL;
+ goto infile_error;
+ }
+
+ (*conn->infile.local_infile_end)(info TSRMLS_CC);
+ 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_palloc.c b/ext/mysqlnd/mysqlnd_palloc.c
new file mode 100644
index 0000000000..fba769038a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_palloc.c
@@ -0,0 +1,565 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_palloc.h"
+#include "mysqlnd_debug.h"
+
+/* Used in mysqlnd_debug.c */
+char * mysqlnd_palloc_zval_ptr_dtor_name = "mysqlnd_palloc_zval_ptr_dtor";
+char * mysqlnd_palloc_get_zval_name = "mysqlnd_palloc_get_zval";
+
+
+#ifdef ZTS
+#define LOCK_PCACHE(cache) tsrm_mutex_lock((cache)->LOCK_access)
+#define UNLOCK_PCACHE(cache) tsrm_mutex_unlock((cache)->LOCK_access)
+#else
+#define LOCK_PCACHE(cache)
+#define UNLOCK_PCACHE(cache)
+#endif
+
+
+#if PHP_MAJOR_VERSION < 6
+#define IS_UNICODE_DISABLED (1)
+#else
+#define IS_UNICODE_DISABLED (!UG(unicode))
+#endif
+
+
+/* {{{ _mysqlnd_palloc_init_cache */
+PHPAPI MYSQLND_ZVAL_PCACHE* _mysqlnd_palloc_init_cache(unsigned int cache_size TSRMLS_DC)
+{
+ MYSQLND_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_ZVAL_PCACHE));
+ unsigned int i;
+
+ DBG_ENTER("_mysqlnd_palloc_init_cache");
+ DBG_INF_FMT("cache=%p size=%u", ret, cache_size);
+
+#ifdef ZTS
+ ret->LOCK_access = tsrm_mutex_alloc();
+#endif
+
+ ret->max_items = cache_size;
+ ret->free_items = cache_size;
+ ret->references = 1;
+
+ /* 1. First initialize the free list part of the structure */
+ /* One more for empty position of last_added - always 0x0, bounds checking */
+ ret->free_list.ptr_line = calloc(ret->max_items + 1, sizeof(mysqlnd_zval *));
+ ret->free_list.last_added = ret->free_list.ptr_line + ret->max_items;
+
+ /* 3. Allocate and initialize our zvals and initialize the free list */
+ ret->block = calloc(ret->max_items, sizeof(mysqlnd_zval));
+ ret->last_in_block = &(ret->block[ret->max_items]);
+ for (i = 0; i < ret->max_items; i++) {
+ /* 1. Initialize */
+ INIT_PZVAL(&(ret->block[i].zv));
+ ZVAL_NULL(&(ret->block[i].zv));
+ /* Assure it will never be freed before MSHUTDOWN */
+ ZVAL_ADDREF(&(ret->block[i].zv));
+ /* 2. Add to the free list */
+ *(--ret->free_list.last_added) = &(ret->block[i]);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_get_cache_reference */
+MYSQLND_ZVAL_PCACHE* mysqlnd_palloc_get_cache_reference(MYSQLND_ZVAL_PCACHE * const cache)
+{
+ if (cache) {
+ LOCK_PCACHE(cache);
+ cache->references++;
+ UNLOCK_PCACHE(cache);
+ }
+ return cache;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_free_cache */
+/*
+ As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with
+ copy_ctor but scrap what they point to with zval_dtor() and then just free our
+ pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them
+ to the free list after usage. We ZVAL_NULL() them when we allocate them in the
+ constructor of the cache.
+*/
+void _mysqlnd_palloc_free_cache(MYSQLND_ZVAL_PCACHE *cache TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_palloc_free_cache");
+ DBG_INF_FMT("cache=%p", cache);
+
+#ifdef ZTS
+ tsrm_mutex_free(cache->LOCK_access);
+#endif
+
+ /* Data in pointed by 'block' was cleaned in RSHUTDOWN */
+ mnd_free(cache->block);
+ mnd_free(cache->free_list.ptr_line);
+ mnd_free(cache);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_palloc_init_thd_cache */
+PHPAPI MYSQLND_THD_ZVAL_PCACHE* _mysqlnd_palloc_init_thd_cache(MYSQLND_ZVAL_PCACHE * const cache TSRMLS_DC)
+{
+ MYSQLND_THD_ZVAL_PCACHE *ret = calloc(1, sizeof(MYSQLND_THD_ZVAL_PCACHE));
+ DBG_ENTER("_mysqlnd_palloc_init_thd_cache");
+ DBG_INF_FMT("ret = %p", ret);
+
+ ret->parent = mysqlnd_palloc_get_cache_reference(cache);
+
+#ifdef ZTS
+ ret->thread_id = tsrm_thread_id();
+#endif
+
+ ret->references = 1;
+
+ /* 1. Initialize the GC list */
+ ret->gc_list.ptr_line = calloc(cache->max_items, sizeof(mysqlnd_zval *));
+ /* Backward and forward looping is possible */
+ ret->gc_list.last_added = ret->gc_list.ptr_line;
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_get_thd_cache_reference */
+MYSQLND_THD_ZVAL_PCACHE* mysqlnd_palloc_get_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE * const cache)
+{
+ if (cache) {
+ ++cache->references;
+ mysqlnd_palloc_get_cache_reference(cache->parent);
+ }
+ return cache;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_free_cache */
+/*
+ As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with
+ copy_ctor but scrap what they point to with zval_dtor() and then just free our
+ pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them
+ to the free list after usage. We ZVAL_NULL() them when we allocate them in the
+ constructor of the cache.
+*/
+static
+void mysqlnd_palloc_free_thd_cache(MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_palloc_free_thd_cache");
+ DBG_INF_FMT("cache=%p", cache);
+
+ mnd_free(cache->gc_list.ptr_line);
+ mnd_free(cache);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_palloc_free_thd_cache_reference */
+PHPAPI void _mysqlnd_palloc_free_thd_cache_reference(MYSQLND_THD_ZVAL_PCACHE **cache TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_palloc_free_thd_cache_reference");
+ if (*cache) {
+ DBG_INF_FMT("cache=%p refs=%d", *cache, (*cache)->references);
+ --(*cache)->parent->references;
+
+ if (--(*cache)->references == 0) {
+ mysqlnd_palloc_free_thd_cache(*cache TSRMLS_CC);
+ }
+ *cache = NULL;
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ The cache line is a big contiguous array of zval pointers.
+ Because the CPU cache will cache starting from an address, and not
+ before it, then we have to organize our structure according to this.
+ Thus, if 'last_added' is valid pointer (not NULL) then last_added is
+ increased. When zval is cached, if there is room, last_added is decreased
+ and then the zval pointer will be assigned to it. This means that some
+ positions may become hot points and stay in the cache.
+ Imagine we have 5 pointers in a line
+ 1. last_added = list_item->ptr_line + cache->max_items;
+ 2. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 3. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 4. get_zval -> *last_added = NULL. Use MAKE_STD_ZVAL
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ empty_position, always 0x0 <-- last_added
+
+ 5. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ (memory addresses increase downwards)
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+ 6. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 7. free_zval -> if (free_items++ != max_items) {// we can add more
+ *(--last_added) = zval_ptr;
+ }
+ 0x0
+ 0x0
+ 0xC <-- last_added
+ 0xB
+ 0xA
+ ---
+ 0x0
+
+ 8. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0xB <-- last_added
+ 0xA
+ ---
+ 0x0
+
+ 9. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0xA <-- last_added
+ ---
+ 0x0
+
+ 10. get_zval -> *last_added != NULL. -> p = *last_added; *last_added++ = NULL;
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ ---
+ 0x0 <-- last_added
+
+*/
+
+
+/* {{{ mysqlnd_palloc_get_zval */
+void *mysqlnd_palloc_get_zval(MYSQLND_THD_ZVAL_PCACHE * const thd_cache, zend_bool *allocated TSRMLS_DC)
+{
+ void *ret = NULL;
+
+ DBG_ENTER("mysqlnd_palloc_get_zval");
+ DBG_INF_FMT("cache=%p *last_added=%p free_items=%d",
+ thd_cache, thd_cache? thd_cache->parent->free_list.last_added:NULL,
+ thd_cache->parent->free_items);
+
+ if (thd_cache) {
+ MYSQLND_ZVAL_PCACHE *cache = thd_cache->parent;
+ LOCK_PCACHE(cache);
+
+ if ((ret = *cache->free_list.last_added)) {
+ *cache->free_list.last_added++ = NULL;
+ *allocated = FALSE;
+#ifdef ZTS
+ ((mysqlnd_zval *) ret)->thread_id = thd_cache->thread_id;
+#endif
+ --cache->free_items;
+ ++cache->get_hits;
+ } else {
+ ++cache->get_misses;
+ }
+ UNLOCK_PCACHE(cache);
+ }
+ if (!ret) {
+ /*
+ We allocate a bit more. The user of this function will use it, but at
+ end it will use only the zval part. Because the zval part is first then
+ when freeing the zval part the whole allocated block will be cleaned, not
+ only the zval part (by the Engine when destructing the zval).
+ */
+ ALLOC_ZVAL(ret);
+ INIT_PZVAL((zval *) ret);
+ *allocated = TRUE;
+ } else {
+ /* This will set the refcount to 1, increase it, to keep the variable */
+ INIT_PZVAL(&((mysqlnd_zval *) ret)->zv);
+ ZVAL_ADDREF(&(((mysqlnd_zval *)ret)->zv));
+ }
+
+ DBG_INF_FMT("allocated=%d ret=%p", *allocated, ret);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_zval_ptr_dtor */
+void mysqlnd_palloc_zval_ptr_dtor(zval **zv, MYSQLND_THD_ZVAL_PCACHE * const thd_cache,
+ enum_mysqlnd_res_type type, zend_bool *copy_ctor_called TSRMLS_DC)
+{
+ MYSQLND_ZVAL_PCACHE *cache;
+ DBG_ENTER("mysqlnd_palloc_zval_ptr_dtor");
+ DBG_INF_FMT("cache=%p parent_block=%p last_in_block=%p *zv=%p refc=%d type=%d ",
+ thd_cache,
+ thd_cache->parent? thd_cache->parent->block:NULL,
+ thd_cache->parent? thd_cache->parent->last_in_block:NULL,
+ *zv, ZVAL_REFCOUNT(*zv), type);
+ *copy_ctor_called = FALSE;
+ /* Check whether cache is used and the zval is from the cache */
+ if (!thd_cache || !(cache = thd_cache->parent) || ((char *)*zv < (char *)thd_cache->parent->block ||
+ (char *)*zv > (char *)thd_cache->parent->last_in_block)) {
+ /*
+ 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) {
+ ; /* do nothing, zval_ptr_dtor will do the job*/
+ } else if (ZVAL_REFCOUNT(*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 {
+ if (Z_TYPE_PP(zv) == IS_STRING) {
+ ZVAL_NULL(*zv);
+ }
+ }
+ zval_ptr_dtor(zv);
+ DBG_VOID_RETURN;
+ }
+
+ /* The zval is from our cache */
+ /* refcount is always > 1, because we call ZVAL_ADDREF(). Thus test refcount > 2 */
+ if (ZVAL_REFCOUNT(*zv) > 2) {
+ /*
+ Because the zval is first element in mysqlnd_zval structure, then we can
+ do upcasting from zval to mysqlnd_zval here. Because we know that this
+ zval is part of our pre-allocated block.
+
+ Now check whether this zval points to ZE allocated memory or to our
+ buffers. If it points to the internal buffers, call copy_ctor()
+ which will do estrndup for strings. And nothing for null, int, double.
+
+ This branch will be skipped for PS, because there is no need to copy
+ what is pointed by them, as they don't point to the internal buffers.
+ */
+ if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_INT_BUFFER) {
+ zval_copy_ctor(*zv);
+ *copy_ctor_called = TRUE;
+ ((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_EXT_BUFFER;
+ }
+ /*
+ This will decrease the counter of the user-level (mysqlnd). When the engine
+ layer (the script) has finished working this this zval, when the variable is
+ no more used, out of scope whatever, then it will try zval_ptr_dtor() but
+ and the refcount will reach 1 and the engine won't try to destruct the
+ memory allocated by us.
+ */
+ zval_ptr_dtor(zv);
+
+ /*
+ Unfortunately, we can't return this variable to the free_list
+ because it's still used. And this cleaning up will happen at request
+ shutdown :(.
+ */
+ LOCK_PCACHE(cache);
+ ++cache->put_misses;
+ *(thd_cache->gc_list.last_added++) = (mysqlnd_zval *)*zv;
+ UNLOCK_PCACHE(cache);
+ } else {
+ /* No user reference */
+ if (((mysqlnd_zval *)*zv)->point_type == MYSQLND_POINTS_EXT_BUFFER) {
+ /* PS are here and also in Unicode mode, for non-binary */
+ zval_dtor(*zv);
+ }
+ LOCK_PCACHE(cache);
+ ++cache->put_hits;
+ ++cache->free_items;
+ ((mysqlnd_zval *)*zv)->point_type = MYSQLND_POINTS_FREE;
+ ZVAL_DELREF(*zv); /* Make it 1 */
+ ZVAL_NULL(*zv);
+#ifdef ZTS
+ memset(&((mysqlnd_zval *)*zv)->thread_id, 0, sizeof(THREAD_T));
+#endif
+ *(--cache->free_list.last_added) = (mysqlnd_zval *)*zv;
+
+ UNLOCK_PCACHE(cache);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_palloc_rinit */
+PHPAPI MYSQLND_THD_ZVAL_PCACHE * _mysqlnd_palloc_rinit(MYSQLND_ZVAL_PCACHE * cache TSRMLS_DC)
+{
+ return mysqlnd_palloc_init_thd_cache(cache);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_palloc_rshutdown */
+PHPAPI void _mysqlnd_palloc_rshutdown(MYSQLND_THD_ZVAL_PCACHE * thd_cache TSRMLS_DC)
+{
+ MYSQLND_ZVAL_PCACHE *cache;
+ mysqlnd_zval **p;
+
+ DBG_ENTER("_mysqlnd_palloc_rshutdown");
+ DBG_INF_FMT("cache=%p", thd_cache);
+
+ if (!thd_cache || !(cache = thd_cache->parent)) {
+ return;
+ }
+
+ /*
+ Keep in mind that for pthreads pthread_equal() should be used to be
+ fully standard compliant. However, the PHP code all-around, incl. the
+ the Zend MM uses direct comparison.
+ */
+ p = thd_cache->gc_list.ptr_line;
+ while (p < thd_cache->gc_list.last_added) {
+ zval_dtor(&(*p)->zv);
+ p++;
+ }
+
+ p = thd_cache->gc_list.ptr_line;
+ LOCK_PCACHE(cache);
+ while (p < thd_cache->gc_list.last_added) {
+ (*p)->point_type = MYSQLND_POINTS_FREE;
+ *(--cache->free_list.last_added) = *p;
+ ++cache->free_items;
+#ifdef ZTS
+ memset(&((*p)->thread_id), 0, sizeof(THREAD_T));
+#endif
+ p++;
+ }
+ UNLOCK_PCACHE(cache);
+
+ mysqlnd_palloc_free_thd_cache_reference(&thd_cache);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_palloc_rshutdown */
+PHPAPI void mysqlnd_palloc_stats(const MYSQLND_ZVAL_PCACHE * const cache, zval *return_value)
+{
+ if (cache) {
+#if PHP_MAJOR_VERSION >= 6
+ TSRMLS_FETCH();
+#endif
+
+ LOCK_PCACHE(cache);
+ array_init(return_value);
+#if PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ UChar *ustr;
+ int ulen;
+
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_hits", sizeof("put_hits") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "put_misses", sizeof("put_misses") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_hits", sizeof("get_hits") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "get_misses", sizeof("get_misses") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "size", sizeof("size") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "free_items", sizeof("free_items") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, "references", sizeof("references") TSRMLS_CC);
+ add_u_assoc_long_ex(return_value, IS_UNICODE, ZSTR(ustr), ulen + 1, cache->put_hits);
+ efree(ustr);
+ } else
+#endif
+ {
+ add_assoc_long_ex(return_value, "put_hits", sizeof("put_hits"), cache->put_hits);
+ add_assoc_long_ex(return_value, "put_misses", sizeof("put_misses"), cache->put_misses);
+ add_assoc_long_ex(return_value, "get_hits", sizeof("get_hits"), cache->get_hits);
+ add_assoc_long_ex(return_value, "get_misses", sizeof("get_misses"), cache->get_misses);
+ add_assoc_long_ex(return_value, "size", sizeof("size"), cache->max_items);
+ add_assoc_long_ex(return_value, "free_items", sizeof("free_items"), cache->free_items);
+ add_assoc_long_ex(return_value, "references", sizeof("references"), cache->references);
+ }
+ UNLOCK_PCACHE(cache);
+ } else {
+ ZVAL_NULL(return_value);
+ }
+}
+/* }}} */
+
+/*
+ * 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_palloc.h b/ext/mysqlnd/mysqlnd_palloc.h
new file mode 100644
index 0000000000..8ef0eaab87
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_palloc.h
@@ -0,0 +1,114 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_PALLOC_H
+#define MYSQLND_PALLOC_H
+
+/* Used in mysqlnd_debug.c */
+extern char * mysqlnd_palloc_zval_ptr_dtor_name;
+extern char * mysqlnd_palloc_get_zval_name;
+
+
+/* Session caching allocator */
+struct st_mysqlnd_zval_list {
+ zval **ptr_line;
+ zval **last_added;
+};
+
+typedef struct st_mysqlnd_zval_cache MYSQLND_ZVAL_CACHE;
+
+struct st_mysqlnd_zval_cache {
+ struct st_mysqlnd_zval_list *free_list;
+ unsigned int free_items;
+ unsigned int max_items;
+ unsigned int references;
+ unsigned long get_hits;
+ unsigned long get_misses;
+ unsigned long put_hits;
+ unsigned long put_full_misses;
+ unsigned long put_refcount_misses;
+};
+
+
+enum mysqlnd_zval_ptr_type
+{
+ MYSQLND_POINTS_INT_BUFFER,
+ MYSQLND_POINTS_EXT_BUFFER,
+ MYSQLND_POINTS_FREE
+};
+
+/* Persistent caching allocator */
+typedef struct st_mysqlnd_zval {
+ /* Should be first */
+ zval zv;
+ enum mysqlnd_zval_ptr_type point_type;
+#ifdef ZTS
+ THREAD_T thread_id;
+#endif
+} mysqlnd_zval;
+
+
+typedef struct st_mysqlnd_ndzval_list {
+ mysqlnd_zval **ptr_line; /* we allocate this, all are pointers to the block */
+ mysqlnd_zval **last_added; /* this points to the ptr_line, and moves left-right. It's our stack */
+} mysqlnd_ndzval_list;
+
+
+struct st_mysqlnd_zval_pcache {
+ mysqlnd_zval *block;
+ mysqlnd_zval *last_in_block;
+ mysqlnd_ndzval_list free_list; /* Fetch from here */
+
+#ifdef ZTS
+ MUTEX_T LOCK_access;
+#endif
+ unsigned int references;
+
+ /* These are just for statistics and not used for operational purposes */
+ unsigned int free_items;
+ unsigned int max_items;
+
+ unsigned long get_hits;
+ unsigned long get_misses;
+ unsigned long put_hits;
+ unsigned long put_misses;
+};
+
+struct st_mysqlnd_thread_zval_pcache {
+ struct st_mysqlnd_zval_pcache *parent;
+
+ unsigned int references;
+#ifdef ZTS
+ THREAD_T thread_id;
+#endif
+ mysqlnd_ndzval_list gc_list; /* GC these from time to time */
+};
+
+#endif /* MYSQLND_PALLOC_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_portability.h b/ext/mysqlnd/mysqlnd_portability.h
new file mode 100644
index 0000000000..37aeb67e21
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_portability.h
@@ -0,0 +1,515 @@
+/* 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.
+*/
+
+/* 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... */
+
+#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
+#ifndef HAVE_INT8_T
+typedef signed char int8; /* Signed integer >= 8 bits */
+#else
+typedef int8_t int8; /* Signed integer >= 8 bits */
+#endif
+#endif
+
+#ifndef HAVE_UINT8
+#ifndef HAVE_UINT8_T
+typedef unsigned char uint8; /* Unsigned integer >= 8 bits */
+#else
+typedef uint8_t uint8; /* Signed integer >= 8 bits */
+#endif
+#endif
+
+#ifndef HAVE_INT16
+#ifndef HAVE_INT16_T
+typedef signed short int16; /* Signed integer >= 16 bits */
+#else
+typedef int16_t int16; /* Signed integer >= 16 bits */
+#endif
+#endif
+
+#ifndef HAVE_UINT16
+#ifndef HAVE_UINT16_T
+typedef unsigned short uint16; /* Signed integer >= 16 bits */
+#else
+typedef uint16_t uint16; /* Signed integer >= 16 bits */
+#endif
+#endif
+
+#ifndef HAVE_UCHAR
+typedef unsigned char uchar; /* Short for unsigned char */
+#endif
+
+
+#if defined(HAVE_INT32_T) && defined(HAVE_UINT32_T)
+typedef int32_t int32;
+typedef uint32_t uint32;
+
+#elif SIZEOF_INT == 4
+
+#ifndef HAVE_INT32
+typedef signed int int32;
+#endif
+#ifndef HAVE_UINT32
+typedef unsigned int uint32;
+#endif
+
+#elif SIZEOF_LONG == 4
+
+#ifndef HAVE_INT32
+typedef signed long int32;
+#endif
+#ifndef HAVE_UINT32
+typedef unsigned long uint32;
+#endif
+
+#else
+error "Neither int or long is of 4 bytes width"
+#endif
+
+#if !defined(HAVE_ULONG) && !defined(__USE_MISC) && !defined(ulong)
+typedef unsigned long ulong; /* Short for unsigned long */
+#endif
+#ifndef longlong_defined
+#if defined(HAVE_LONG_LONG) && SIZEOF_LONG != 8
+typedef unsigned long long int ulonglong; /* ulong or unsigned long long */
+typedef long long int longlong;
+#else
+typedef unsigned long ulonglong; /* ulong or unsigned long long */
+typedef long longlong;
+#endif
+#endif
+
+
+#define int1store(T,A) do { *((zend_uchar*) (T)) = (A); } while(0)
+#define uint1korr(A) (*(((uint8*)(A))))
+
+/* Bit values are sent in reverted order of bytes, compared to normal !!! */
+#define bit_uint2korr(A) ((uint16) (((uint16) (((uchar*) (A))[1])) +\
+ ((uint16) (((uchar*) (A))[0]) << 8)))
+#define bit_uint3korr(A) ((uint32) (((uint32) (((uchar*) (A))[2])) +\
+ (((uint32) (((uchar*) (A))[1])) << 8) +\
+ (((uint32) (((uchar*) (A))[0])) << 16)))
+
+#define bit_uint4korr(A) ((uint32) (((uint32) (((uchar*) (A))[3])) +\
+ (((uint32) (((uchar*) (A))[2])) << 8) +\
+ (((uint32) (((uchar*) (A))[1])) << 16) +\
+ (((uint32) (((uchar*) (A))[0])) << 24)))
+
+#define bit_uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[4])) +\
+ (((uint32) ((uchar) (A)[3])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[1])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[0])) << 32))
+
+#define bit_uint6korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[5])) +\
+ (((uint32) (((uchar*) (A))[4])) << 8) +\
+ (((uint32) (((uchar*) (A))[3])) << 16) +\
+ (((uint32) (((uchar*) (A))[2])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[1])) +\
+ (((uint32) (((uchar*) (A))[0]) << 8)))) << 32))
+
+#define bit_uint7korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[6])) +\
+ (((uint32) (((uchar*) (A))[5])) << 8) +\
+ (((uint32) (((uchar*) (A))[4])) << 16) +\
+ (((uint32) (((uchar*) (A))[3])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[2])) +\
+ (((uint32) (((uchar*) (A))[1])) << 8) +\
+ (((uint32) (((uchar*) (A))[0])) << 16))) << 32))
+
+
+#define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\
+ (((uint32) (((uchar*) (A))[6])) << 8) +\
+ (((uint32) (((uchar*) (A))[5])) << 16) +\
+ (((uint32) (((uchar*) (A))[4])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[3])) +\
+ (((uint32) (((uchar*) (A))[2])) << 8) +\
+ (((uint32) (((uchar*) (A))[1])) << 16) +\
+ (((uint32) (((uchar*) (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 */
+#if defined(__i386__) && !defined(_WIN64)
+#define sint2korr(A) (*((int16 *) (A)))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (*((long *) (A)))
+
+#define uint2korr(A) (*((uint16 *) (A)))
+#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16))
+#define uint4korr(A) (*((unsigned long *) (A)))
+
+
+
+#define uint8korr(A) (*((ulonglong *) (A)))
+#define sint8korr(A) (*((longlong *) (A)))
+#define int2store(T,A) *((uint16*) (T))= (uint16) (A)
+#define int3store(T,A) { \
+ *(T)= (uchar) ((A));\
+ *(T+1)=(uchar) (((uint) (A) >> 8));\
+ *(T+2)=(uchar) (((A) >> 16)); }
+#define int4store(T,A) *((long *) (T))= (long) (A)
+#define int5store(T,A) { \
+ *((uchar *)(T))= (uchar)((A));\
+ *(((uchar *)(T))+1)=(uchar) (((A) >> 8));\
+ *(((uchar *)(T))+2)=(uchar) (((A) >> 16));\
+ *(((uchar *)(T))+3)=(uchar) (((A) >> 24)); \
+ *(((uchar *)(T))+4)=(uchar) (((A) >> 32)); }
+
+/* From Andrey Hristov, based on int5store() */
+#define int6store(T,A) { \
+ *(((uchar *)(T)))= (uchar)((A));\
+ *(((uchar *)(T))+1))=(uchar) (((A) >> 8));\
+ *(((uchar *)(T))+2))=(uchar) (((A) >> 16));\
+ *(((uchar *)(T))+3))=(uchar) (((A) >> 24)); \
+ *(((uchar *)(T))+4))=(uchar) (((A) >> 32)); \
+ *(((uchar *)(T))+5))=(uchar) (((A) >> 40)); }
+
+#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A)
+
+typedef union {
+ double v;
+ long m[2];
+} doubleget_union;
+#define doubleget(V,M) { ((doubleget_union *)&(V))->m[0] = *((long*) (M)); \
+ ((doubleget_union *)&(V))->m[1] = *(((long*) (M))+1); }
+#define doublestore(T,V) { *((long *) (T)) = ((doubleget_union *)&(V))->m[0]; \
+ *(((long *) (T))+1) = ((doubleget_union *)&(V))->m[1]; }
+#define float4get(V,M) { *((float *) &(V)) = *((float*) (M)); }
+#define float8get(V,M) doubleget((V),(M))
+/* From Andrey Hristov based on doubleget */
+#define floatget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(float))
+#define floatstore float4store
+#define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float))
+#define float8store(V,M) doublestore((V),(M))
+#endif /* __i386__ */
+
+#ifndef sint2korr
+#define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\
+ ((int16) ((int16) (A)[1]) << 8))
+#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \
+ (((uint32) 255L << 24) | \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])) : \
+ (((uint32) (uchar) (A)[2]) << 16) |\
+ (((uint32) (uchar) (A)[1]) << 8) | \
+ ((uint32) (uchar) (A)[0])))
+#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\
+ (((int32) ((uchar) (A)[1]) << 8)) +\
+ (((int32) ((uchar) (A)[2]) << 16)) +\
+ (((int32) ((int16) (A)[3]) << 24)))
+
+#define sint8korr(A) (longlong) uint8korr(A)
+#define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\
+ ((uint16) ((uchar) (A)[1]) << 8))
+#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16))
+#define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24))
+#define bit_uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) ((uchar) (A)[4])) << 32))
+/* From Andrey Hristov, based on uint5korr */
+#define bit_uint6korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[5])) +\
+ (((uint32) (((uchar*) (A))[4])) << 8) +\
+ (((uint32) (((uchar*) (A))[3])) << 16) +\
+ (((uint32) (((uchar*) (A))[2])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[1])) +\
+ (((uint32) (((uchar*) (A))[0]) << 8)))) << 32))
+
+#define bit_uint7korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[6])) +\
+ (((uint32) (((uchar*) (A))[5])) << 8) +\
+ (((uint32) (((uchar*) (A))[4])) << 16) +\
+ (((uint32) (((uchar*) (A))[3])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[2])) +\
+ (((uint32) (((uchar*) (A))[1])) << 8) +\
+ (((uint32) (((uchar*) (A))[0])) << 16))) << 32))
+
+
+#define bit_uint8korr(A) ((ulonglong)(((uint32) (((uchar*) (A))[7])) +\
+ (((uint32) (((uchar*) (A))[6])) << 8) +\
+ (((uint32) (((uchar*) (A))[5])) << 16) +\
+ (((uint32) (((uchar*) (A))[4])) << 24)) +\
+ (((ulonglong) (((uint32) (((uchar*) (A))[3])) +\
+ (((uint32) (((uchar*) (A))[2])) << 8) +\
+ (((uint32) (((uchar*) (A))[1])) << 16) +\
+ (((uint32) (((uchar*) (A))[0])) << 24))) << 32))
+
+#define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\
+ (((uint32) ((uchar) (A)[1])) << 8) +\
+ (((uint32) ((uchar) (A)[2])) << 16) +\
+ (((uint32) ((uchar) (A)[3])) << 24)) +\
+ (((ulonglong) (((uint32) ((uchar) (A)[4])) +\
+ (((uint32) ((uchar) (A)[5])) << 8) +\
+ (((uint32) ((uchar) (A)[6])) << 16) +\
+ (((uint32) ((uchar) (A)[7])) << 24))) << 32))
+
+
+#define int2store(T,A) do { uint def_temp= (uint) (A) ;\
+ *((uchar*) (T)) = (uchar)(def_temp); \
+ *((uchar*) (T+1)) = (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) { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \
+ int4store((T),def_temp); \
+ int4store((T+4),def_temp2); \
+ }
+#ifdef WORDS_BIGENDIAN
+#define float4store(T,A) do { \
+ *(((char *)(T))) = (char) ((char *) &A)[3];\
+ *(((char *)(T))+1) = (char) ((char *) &A)[2];\
+ *(((char *)(T))+2) = (char) ((char *) &A)[1];\
+ *(((char *)(T))+3) = (char) ((char *) &A)[0]; } while (0)
+
+#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))
+#define float4store(V,M) memcpy((char*) (V),(char*) (&M),sizeof(float))
+
+#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
+#define doublestore(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 doubleget(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 */
+
+#define float8get(V,M) doubleget((V),(M))
+#define float8store(V,M) doublestore((V),(M))
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* sint2korr */
+
+/* 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 */
+
+#ifdef WORDS_BIGENDIAN
+
+#define ushortget(V,M) { V = (uint16) (((uint16) ((uchar) (M)[1]))+\
+ ((uint16) ((uint16) (M)[0]) << 8)); }
+#define shortget(V,M) { V = (short) (((short) ((uchar) (M)[1]))+\
+ ((short) ((short) (M)[0]) << 8)); }
+#define longget(V,M) do { int32 def_temp;\
+ ((char*) &def_temp)[0]=(M)[0];\
+ ((char*) &def_temp)[1]=(M)[1];\
+ ((char*) &def_temp)[2]=(M)[2];\
+ ((char*) &def_temp)[3]=(M)[3];\
+ (V)=def_temp; } while (0)
+#define ulongget(V,M) do { uint32 def_temp;\
+ ((char*) &def_temp)[0]=(M)[0];\
+ ((char*) &def_temp)[1]=(M)[1];\
+ ((char*) &def_temp)[2]=(M)[2];\
+ ((char*) &def_temp)[3]=(M)[3];\
+ (V)=def_temp; } while (0)
+#define shortstore(T,A) do { \
+ uint def_temp=(uint) (A) ;\
+ *(((char *)(T))+1)=(char)(def_temp); \
+ *(((char *)(T))+0)=(char)(def_temp >> 8); } while (0)
+#define longstore(T,A) do { \
+ *(((char *)(T))+3)=(char)((A));\
+ *(((char *)(T))+2)=(char)(((A) >> 8));\
+ *(((char *)(T))+1)=(char)(((A) >> 16));\
+ *(((char *)(T))+0)=(char)(((A) >> 24)); } while (0)
+
+#define doubleget(V,M) memcpy((char*) &(V),(char*) (M), sizeof(double))
+#define doublestore(T,V) memcpy((char*) (T),(char*) &(V), sizeof(double))
+#define longlongget(V,M) memcpy((char*) &(V),(char*) (M), sizeof(ulonglong))
+#define longlongstore(T,V) memcpy((char*) (T),(char*) &(V), sizeof(ulonglong))
+
+#else
+
+#define ushortget(V,M) { V = uint2korr((M)); }
+#define shortget(V,M) { V = sint2korr((M)); }
+#define longget(V,M) { V = sint4korr((M)); }
+#define ulongget(V,M) { V = uint4korr((M)); }
+#define shortstore(T,V) int2store((T),(V))
+#define longstore(T,V) int4store((T),(V))
+#ifndef doubleget
+#define doubleget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(double))
+#define doublestore(T,V) memcpy((char*) (T),(char*) &(V),sizeof(double))
+#endif /* doubleget */
+#define longlongget(V,M) memcpy((char*) &(V),(char*) (M),sizeof(ulonglong))
+#define longlongstore(T,V) memcpy((char*) (T),(char*) &(V),sizeof(ulonglong))
+
+#endif /* WORDS_BIGENDIAN */
+
+
+#ifdef PHP_WIN32
+#define MYSQLND_LLU_SPEC "%I64u"
+#define MYSQLND_LL_SPEC "%I64d"
+#ifndef L64
+#define L64(x) x##i64
+#endif
+typedef unsigned __int64 my_uint64;
+typedef __int64 my_int64;
+typedef unsigned __int64 mynd_ulonglong;
+typedef __int64 mynd_longlong;
+#else
+#define MYSQLND_LLU_SPEC "%llu"
+#define MYSQLND_LL_SPEC "%lld"
+#ifndef L64
+#define L64(x) x##LL
+#endif
+#ifndef HAVE_UINT64_T
+typedef unsigned long long my_uint64;
+typedef unsigned long long mynd_ulonglong;
+#else
+typedef uint64_t my_uint64;
+typedef uint64_t mynd_ulonglong;
+#endif
+#ifndef HAVE_INT64_T
+typedef long long my_int64;
+typedef long long mynd_longlong;
+#else
+typedef int64_t my_int64;
+typedef int64_t mynd_longlong;
+#endif
+#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_priv.h b/ext/mysqlnd/mysqlnd_priv.h
new file mode 100644
index 0000000000..83bc0665cb
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -0,0 +1,191 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_PRIV_H
+#define MYSQLND_PRIV_H
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef pestrndup
+#define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length)))
+#endif
+
+
+#define MYSQLND_CLASS_METHODS_START(class) static \
+ struct st_##class##_methods mysqlnd_##class##_methods = {
+#define MYSQLND_CLASS_METHODS_END }
+#define MYSQLND_METHOD(class, method) php_##class##_##method##_pub
+#define MYSQLND_METHOD_PRIVATE(class, method) php_##class##_##method##_priv
+
+#if PHP_MAJOR_VERSION < 6
+#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;\
+}
+#else
+#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;\
+}
+#endif
+
+
+
+#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 */
+/*
+ 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
+
+
+
+/* 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 = (mynd_ulonglong) ~0
+
+/* Error handling */
+#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
+ {\
+ if ((buf)) { \
+ pefree((buf), (persistent)); \
+ } \
+ (buf) = (message); \
+ (buf_len) = (len); \
+ /* Transfer ownership*/ \
+ (message) = NULL; \
+ }
+
+#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
+ {\
+ if ((buf)) { \
+ pefree((buf), (persistent)); \
+ (buf) = NULL; \
+ } \
+ (buf_len) = 0; \
+ }
+
+
+#define SET_EMPTY_ERROR(error_info) \
+ { \
+ error_info.error_no = 0; \
+ error_info.error[0] = '\0'; \
+ strncpy(error_info.sqlstate, "00000", sizeof("00000") - 1); \
+ }
+
+#define SET_CLIENT_ERROR(error_info, a, b, c) \
+ { \
+ error_info.error_no = a; \
+ strncpy(error_info.sqlstate, b, sizeof(error_info.sqlstate)); \
+ strncpy(error_info.error, c, sizeof(error_info.error)); \
+ }
+
+#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(stmt->error_info, a, b, c)
+
+
+
+/* PS stuff */
+typedef void (*ps_field_fetch_func)(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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];
+
+extern const char * mysqlnd_out_of_sync;
+extern const char * mysqlnd_server_gone;
+
+
+enum_func_status mysqlnd_handle_local_infile(MYSQLND *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 ps_fetch_from_1_to_8_bytes(zval *zv, const MYSQLND_FIELD * const field,
+ uint pack_len, zend_uchar **row, zend_bool as_unicode,
+ unsigned int byte_count TSRMLS_DC);
+
+
+
+int mysqlnd_set_sock_no_delay(php_stream *stream);
+#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 0000000000..00dc46761f
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -0,0 +1,1740 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_result_meta.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.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.c */
+enum_func_status mysqlnd_simple_command(MYSQLND *conn, enum php_mysqlnd_server_command command,
+ const char * const arg, size_t arg_len,
+ enum php_mysql_packet_type ok_packet,
+ zend_bool silent TSRMLS_DC);
+
+/* Exported by mysqlnd_ps_codec.c */
+zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len,
+ zend_bool *free_buffer TSRMLS_DC);
+
+
+MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC);
+
+enum_func_status mysqlnd_fetch_stmt_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);
+
+void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
+
+
+/* {{{ mysqlnd_stmt::store_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND *conn = stmt->conn;
+ MYSQLND_RES *result;
+ zend_bool to_cache = FALSE;
+
+ DBG_ENTER("mysqlnd_stmt::store_result");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ /* 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 */
+ MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC);
+ DBG_RETURN(res);
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (conn->state != 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 = stmt->m->store_result;
+
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->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_fetch_stmt_row_buffered;
+ result->m.fetch_lengths = NULL;/* makes no sense */
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+
+ /* Create room for 'next_extend' rows */
+
+ /* Not set for SHOW statements at PREPARE stage */
+ if (result->conn) {
+ result->conn->m->free_reference(result->conn TSRMLS_CC);
+ result->conn = NULL; /* store result does not reference the connection */
+ }
+
+ ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
+ TRUE, stmt->update_max_length,
+ to_cache TSRMLS_CC);
+
+ if (PASS == ret) {
+ /* libmysql API docs say it should be so for SELECT statements */
+ stmt->upsert_status.affected_rows = stmt->result->data->row_count;
+
+ stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
+ } else {
+ conn->error_info = result->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 stmt TSRMLS_DC)
+{
+ MYSQLND *conn = stmt->conn;
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_stmt::get_result");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ /* 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 */
+ MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC);
+ DBG_RETURN(res);
+ }
+
+ /* Nothing to store for UPSERT/LOAD DATA*/
+ if (conn->state != 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(stmt->conn->error_info);
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_BUFFERED_SETS);
+
+ result = mysqlnd_result_init(stmt->result->field_count,
+ mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache) TSRMLS_CC);
+
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
+
+ /* Not set for SHOW statements at PREPARE stage */
+ if (stmt->result->conn) {
+ stmt->result->conn->m->free_reference(stmt->result->conn TSRMLS_CC);
+ stmt->result->conn = NULL; /* store result does not reference the connection */
+ }
+
+ if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
+ stmt->upsert_status.affected_rows = result->data->row_count;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ result->type = MYSQLND_RES_PS_BUF;
+ } else {
+ stmt->error_info = conn->error_info;
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_skip_metadata */
+static enum_func_status
+mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ /* Follows parameter metadata, we have just to skip it, as libmysql does */
+ unsigned int i = 0;
+ php_mysql_packet_res_field field_packet;
+
+ DBG_ENTER("mysqlnd_stmt_skip_metadata");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);
+ field_packet.skip_parsing = TRUE;
+ for (;i < stmt->param_count; i++) {
+ if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) {
+ PACKET_FREE_ALLOCA(field_packet);
+ DBG_RETURN(FAIL);
+ }
+ }
+ PACKET_FREE_ALLOCA(field_packet);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_read_prepare_response */
+static enum_func_status
+mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ php_mysql_packet_prepare_response prepare_resp;
+
+ DBG_ENTER("mysqlnd_stmt_read_prepare_response");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET);
+ if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) {
+ PACKET_FREE_ALLOCA(prepare_resp);
+ return FAIL;
+ }
+
+ if (0xFF == prepare_resp.error_code) {
+ stmt->error_info = stmt->conn->error_info = prepare_resp.error_info;
+ return FAIL;
+ }
+
+ 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;
+ PACKET_FREE_ALLOCA(prepare_resp);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_prepare_read_eof */
+static enum_func_status
+mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ php_mysql_packet_eof fields_eof;
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(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));
+ 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_ALLOCA(fields_eof);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::prepare */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * const query,
+ unsigned int query_len TSRMLS_DC)
+{
+ MYSQLND_STMT *stmt_to_prepare = stmt;
+
+ DBG_ENTER("mysqlnd_stmt::prepare");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ 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 = stmt->m->use_result;
+ stmt->default_rset_handler(stmt TSRMLS_CC);
+ }
+ /* No 'else' here please :) */
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ 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.
+ */
+ stmt_to_prepare = mysqlnd_stmt_init(stmt->conn);
+ }
+
+ if (FAIL == mysqlnd_simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query,
+ query_len, PROT_LAST, FALSE TSRMLS_CC) ||
+ FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) {
+ goto fail;
+ }
+
+ if (stmt_to_prepare->param_count) {
+ if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) ||
+ FAIL == mysqlnd_stmt_prepare_read_eof(stmt_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 = mysqlnd_result_init(stmt_to_prepare->field_count, NULL TSRMLS_CC);
+ /* 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);
+
+ 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(stmt_to_prepare TSRMLS_CC)) {
+ goto fail;
+ }
+ }
+
+ if (stmt_to_prepare != stmt) {
+ /* Free old buffers, binding and resources on server */
+ stmt->m->close(stmt, TRUE TSRMLS_CC);
+
+ memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT));
+
+ /* Now we will have a clean new statement object */
+ mnd_efree(stmt_to_prepare);
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+
+fail:
+ if (stmt_to_prepare != stmt) {
+ stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC);
+ }
+ stmt->state = MYSQLND_STMT_INITTED;
+
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::execute */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND *conn = stmt->conn;
+ zend_uchar *request;
+ size_t request_len;
+ zend_bool free_request;
+
+ DBG_ENTER("mysqlnd_stmt::execute");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ SET_ERROR_AFF_ROWS(stmt);
+ SET_ERROR_AFF_ROWS(stmt->conn);
+
+ if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) {
+ 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);
+ }
+ }
+ }
+
+ /*
+ 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);
+ } 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 && !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);
+ }
+
+ request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request TSRMLS_CC);
+
+ /* support for buffer types should be added here ! */
+
+ ret = mysqlnd_simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len,
+ PROT_LAST /* we will handle the response packet*/,
+ FALSE TSRMLS_CC);
+
+ if (free_request) {
+ mnd_efree(request);
+ }
+
+ if (ret == FAIL) {
+ stmt->error_info = conn->error_info;
+ DBG_INF("FAIL");
+ DBG_RETURN(FAIL);
+ }
+ stmt->execute_count++;
+
+ ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);
+ if (ret == FAIL) {
+ stmt->error_info = conn->error_info;
+ stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
+ if (conn->state == CONN_QUIT_SENT) {
+ /* close the statement here, the connection has been closed */
+ }
+ } else {
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+ stmt->send_types_to_server = 0;
+ stmt->upsert_status = conn->upsert_status;
+ stmt->state = MYSQLND_STMT_EXECUTED;
+ if (conn->last_query_type == QUERY_UPSERT) {
+ stmt->upsert_status = conn->upsert_status;
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+ } else if (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);
+ }
+
+ /* 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=%d cursor=%d\n", stmt->upsert_status.server_status,
+ stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ stmt->cursor_exists = TRUE;
+ conn->state = CONN_READY;
+ /* Only cursor read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
+ /*
+ 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 = stmt->m->store_result;
+ DBG_INF("store_result");
+ } else {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ }
+ }
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_fetch_stmt_row_buffered */
+enum_func_status
+mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags,
+ zend_bool *fetched_anything TSRMLS_DC)
+{
+ unsigned int i;
+ MYSQLND_STMT *stmt = (MYSQLND_STMT *) param;
+
+ DBG_ENTER("mysqlnd_fetch_stmt_row_buffered");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ /* If we haven't read everything */
+ if (result->data->data_cursor &&
+ (result->data->data_cursor - result->data->data) < result->data->row_count)
+ {
+ /* The user could have skipped binding - don't crash*/
+ if (stmt->result_bind) {
+ zval **current_row = *result->data->data_cursor;
+ 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
+ zval_dtor(stmt->result_bind[i].zv);
+#endif
+ /* copy the type */
+ if (stmt->result_bind[i].bound == TRUE) {
+ DBG_INF_FMT("i=%d type=%d", 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);
+ }
+ }
+ }
+ }
+ result->data->data_cursor++;
+ *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 {
+ result->data->data_cursor = NULL;
+ *fetched_anything = FALSE;
+ 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 *stmt = (MYSQLND_STMT *) param;
+ unsigned int i, field_count = result->field_count;
+ php_mysql_packet_row *row_packet = result->row_packet;
+
+ DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ *fetched_anything = FALSE;
+ DBG_INF("eof reached");
+ DBG_RETURN(PASS);
+ }
+ if (result->conn->state != 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);
+ }
+ /* 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
+ mysqlnd_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++;
+ *fetched_anything = TRUE;
+
+ if (!row_packet->skip_extraction) {
+ mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+
+ DBG_INF("extracting data");
+ 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;
+
+ 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 mysqlnd_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)) ) {
+ stmt->result_bind[i].zv->value = data->value;
+#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[i].zv);
+#endif
+ if (
+ (Z_TYPE_P(data) == IS_STRING
+#if PHP_MAJOR_VERSION >= 6
+ || Z_TYPE_P(data) == IS_UNICODE
+#endif
+ )
+ && (result->meta->fields[i].max_length < Z_STRLEN_P(data)))
+ {
+ result->meta->fields[i].max_length = Z_STRLEN_P(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 mysqlnd_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.
+ */
+ mnd_efree(row_packet->row_buffer);
+ row_packet->row_buffer = NULL;
+ }
+ } else if (ret == FAIL) {
+ if (row_packet->error_info.error_no) {
+ stmt->conn->error_info = row_packet->error_info;
+ stmt->error_info = row_packet->error_info;
+ }
+ *fetched_anything = FALSE;
+ result->conn->state = 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) {
+ result->conn->state = CONN_NEXT_RESULT_PENDING;
+ } else {
+ result->conn->state = CONN_READY;
+ }
+ *fetched_anything = FALSE;
+ }
+
+ DBG_INF_FMT("ret=%s fetched_anything=%d", ret == PASS? "PASS":"FAIL", *fetched_anything);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::use_result */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+ MYSQLND *conn = stmt->conn;
+
+ DBG_ENTER("mysqlnd_stmt::use_result");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (!stmt->field_count ||
+ (!stmt->cursor_exists && conn->state != CONN_FETCHING_DATA) ||
+ (stmt->cursor_exists && conn->state != 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);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
+ MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
+
+ result = stmt->result;
+ result->type = MYSQLND_RES_PS_UNBUF;
+ result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
+ mysqlnd_stmt_fetch_row_unbuffered;
+ result->m.fetch_lengths = NULL; /* makes no sense */
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+
+ DBG_INF_FMT("cursor=%d zval_cache=%p", stmt->cursor_exists, result->zval_cache);
+ /*
+ 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.
+ */
+ PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
+ result->row_packet->field_count = result->field_count;
+ result->row_packet->binary_protocol = TRUE;
+ result->row_packet->fields_metadata = stmt->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;
+ result->lengths = NULL;
+
+ stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
+
+ /* No multithreading issues as we don't share the connection :) */
+
+ 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 *stmt = (MYSQLND_STMT *) param;
+ zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
+ php_mysql_packet_row *row_packet = result->row_packet;
+
+ DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
+ DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
+
+ if (!stmt) {
+ DBG_ERR("no statement");
+ DBG_RETURN(FAIL);
+ }
+
+ 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);
+ }
+
+ 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 == mysqlnd_simple_command(stmt->conn, COM_STMT_FETCH, (char *)buf, sizeof(buf),
+ PROT_LAST /* we will handle the response packet*/,
+ FALSE TSRMLS_CC)) {
+ 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;
+ mysqlnd_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 (!row_packet->skip_extraction) {
+ /* 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 mysqlnd_unbuffered_free_last_data()
+ */
+ DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(stmt->result_bind[i].zv));
+ if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
+ stmt->result_bind[i].zv->value = data->value;
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[i].zv);
+#endif
+ if ((Z_TYPE_P(data) == IS_STRING
+#if PHP_MAJOR_VERSION >= 6
+ || Z_TYPE_P(data) == IS_UNICODE
+#endif
+ )
+ && (result->meta->fields[i].max_length < Z_STRLEN_P(data)))
+ {
+ result->meta->fields[i].max_length = Z_STRLEN_P(data);
+ }
+ }
+ }
+ }
+ }
+ result->unbuf->row_count++;
+ *fetched_anything = TRUE;
+ /* 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) {
+ mnd_efree(row_packet->row_buffer);
+ row_packet->row_buffer = NULL;
+ }
+ MYSQLND_INC_CONN_STATISTIC(&stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
+ } 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=%d s_status=%d warns=%d eof=%d",
+ ret == PASS? "PASS":"FAIL", *fetched_anything,
+ row_packet->server_status, row_packet->warning_count,
+ result->unbuf->eof_reached);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::fetch */
+PHPAPI enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt,
+ zend_bool * const fetched_anything TSRMLS_DC)
+{
+ enum_func_status ret;
+ DBG_ENTER("mysqlnd_stmt::fetch");
+ 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(stmt 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=%d", 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*)stmt, 0, fetched_anything TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::reset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ enum_func_status ret = PASS;
+ MYSQLND * conn = stmt->conn;
+ zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+
+ DBG_ENTER("mysqlnd_stmt::reset");
+ 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) {
+ 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;
+ }
+ }
+ }
+
+ /*
+ If the user decided to close the statement 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");
+ stmt->default_rset_handler(stmt TSRMLS_CC);
+ stmt->state = MYSQLND_STMT_USER_FETCHING;
+ }
+
+ if (stmt->result) {
+ DBG_INF("skipping result");
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+ /* Now the line should be free, if it wasn't */
+
+ int4store(cmd_buf, stmt->stmt_id);
+ if (conn->state == CONN_READY &&
+ FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
+ sizeof(cmd_buf), PROT_OK_PACKET,
+ FALSE TSRMLS_CC))) {
+ stmt->error_info = conn->error_info;
+ }
+ stmt->upsert_status = conn->upsert_status;
+
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
+ 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 stmt, unsigned int param_no,
+ const char * const data, unsigned long length TSRMLS_DC)
+{
+ enum_func_status ret = FAIL;
+ MYSQLND * conn = stmt->conn;
+ zend_uchar *cmd_buf;
+ size_t packet_len;
+ enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
+
+ DBG_ENTER("mysqlnd_stmt::send_long_data");
+ DBG_INF_FMT("stmt=%lu param_no=%d data_len=%lu", stmt->stmt_id, param_no, length);
+
+ 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
+ mysqlnd_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->state == CONN_READY) {
+ stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
+ cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
+
+ 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 = mysqlnd_simple_command(conn, cmd, (char *)cmd_buf, packet_len,
+ PROT_LAST , FALSE TSRMLS_CC);
+ mnd_efree(cmd_buf);
+ if (FAIL == ret) {
+ stmt->error_info = 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 = php_mysqlnd_consume_uneaten_data(conn, 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 %u 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_param */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt,
+ MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
+{
+ unsigned int i = 0;
+
+ DBG_ENTER("mysqlnd_stmt::bind_param");
+ 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) {
+ 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++) {
+ /* For BLOBS zv is NULL */
+ if (stmt->param_bind[i].zv) {
+ /*
+ We may have the last reference, then call zval_ptr_dtor()
+ or we may leak memory.
+ */
+ zval_ptr_dtor(&stmt->param_bind[i].zv);
+ stmt->param_bind[i].zv = NULL;
+ }
+ }
+ mnd_efree(stmt->param_bind);
+ }
+
+ stmt->param_bind = param_bind;
+ for (i = 0; i < stmt->param_count; i++) {
+ /* The client will use stmt_send_long_data */
+ DBG_INF_FMT("%d is of type %d", i, stmt->param_bind[i].type);
+ if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) {
+ /* Prevent from freeing */
+ ZVAL_ADDREF(stmt->param_bind[i].zv);
+ /* Don't update is_ref, or we will leak during conversion */
+ } else {
+ stmt->param_bind[i].zv = NULL;
+ 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_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
+ MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
+{
+ uint i = 0;
+
+ DBG_ENTER("mysqlnd_stmt::bind_result");
+ DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
+
+ 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);
+ if (result_bind) {
+ mnd_efree(result_bind);
+ }
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ if (stmt->field_count) {
+ if (!result_bind) {
+ DBG_ERR("no result bind passed");
+ DBG_RETURN(FAIL);
+ }
+
+ mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC);
+
+ stmt->result_bind = result_bind;
+ for (i = 0; i < stmt->field_count; i++) {
+ /* Prevent from freeing */
+ ZVAL_ADDREF(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) {
+ mnd_efree(result_bind);
+ }
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::insert_id */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->upsert_status.last_insert_id;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::affected_rows */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->upsert_status.affected_rows;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::num_rows */
+static mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const stmt)
+{
+ return 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 stmt)
+{
+ return stmt->upsert_status.warning_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::field_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::param_count */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->param_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::errno */
+static unsigned int
+MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->error_info.error_no;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::error */
+static const char *
+MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const stmt)
+{
+ return stmt->error_info.error;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::sqlstate */
+static const char *
+MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt)
+{
+ return 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 stmt, mynd_ulonglong row TSRMLS_DC)
+{
+ return 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 stmt)
+{
+ if (!stmt->param_count) {
+ return NULL;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::result_metadata */
+static MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ MYSQLND_RES *result;
+
+ DBG_ENTER("mysqlnd_stmt::result_metadata");
+ 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);
+ }
+
+ /*
+ 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.
+ */
+ result = mysqlnd_result_init(stmt->field_count, NULL TSRMLS_CC);
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ result->unbuf->eof_reached = TRUE;
+ result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
+
+ DBG_INF_FMT("result=%p", result);
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::attr_set */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt,
+ enum mysqlnd_stmt_attr attr_type,
+ const void * const value TSRMLS_DC)
+{
+ unsigned long val = *(unsigned long *) value;
+ DBG_ENTER("mysqlnd_stmt::attr_set");
+ DBG_INF_FMT("stmt=%lu attr_type=%u value=%lu", stmt->stmt_id, attr_type, val);
+
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ /*
+ 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 = val? TRUE:FALSE;
+ break;
+ case STMT_ATTR_CURSOR_TYPE: {
+ if (val > (unsigned long) CURSOR_TYPE_READ_ONLY) {
+ SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ return FAIL;
+ }
+ stmt->flags = val;
+ break;
+ }
+ case STMT_ATTR_PREFETCH_ROWS: {
+ if (val == 0) {
+ val = MYSQLND_DEFAULT_PREFETCH_ROWS;
+ } else if (val > 1) {
+ SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ return FAIL;
+ }
+ stmt->prefetch_rows = val;
+ 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)(MYSQLND_STMT * const stmt,
+ enum mysqlnd_stmt_attr attr_type,
+ void * const value TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::attr_set");
+ 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);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::free_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::free_result");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ if (!stmt->result) {
+ DBG_INF("no result");
+ DBG_RETURN(PASS);
+ }
+
+ 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 = stmt->m->use_result;
+ stmt->default_rset_handler(stmt 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(stmt TSRMLS_CC);
+
+ /* Now we can destroy the result set */
+ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
+ }
+
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+
+ /* Line is free! */
+ stmt->conn->state = CONN_READY;
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_separate_result_bind */
+void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ unsigned int i;
+
+ DBG_ENTER("mysqlnd_stmt_separate_result_bind");
+ 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("%d has refcount=%u", i, ZVAL_REFCOUNT(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 (ZVAL_REFCOUNT(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);
+ }
+ }
+ }
+ mnd_efree(stmt->result_bind);
+ stmt->result_bind = NULL;
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_internal_free_stmt_content */
+static
+void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_internal_free_stmt_content");
+ 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) {
+ 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++) {
+ /* For BLOBs zv is NULL */
+ if (stmt->param_bind[i].zv) {
+ zval_ptr_dtor(&stmt->param_bind[i].zv);
+ }
+ }
+
+ mnd_efree(stmt->param_bind);
+ stmt->param_bind = NULL;
+ }
+
+ /*
+ First separate the bound variables, which point to the result set, then
+ destroy the set.
+ */
+ mysqlnd_stmt_separate_result_bind(stmt 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->cmd_buffer.buffer) {
+ mnd_efree(stmt->cmd_buffer.buffer);
+ stmt->cmd_buffer.buffer = NULL;
+ }
+
+ if (stmt->conn) {
+ stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
+ stmt->conn = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::close */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+{
+ MYSQLND * conn = stmt->conn;
+ zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ enum_mysqlnd_collected_stats stat = STAT_LAST;
+
+ DBG_ENTER("mysqlnd_stmt::close");
+ DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
+
+ 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.
+ */
+ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("fetching result set header");
+ stmt->default_rset_handler(stmt 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);
+ }
+ /*
+ 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->state == CONN_READY &&
+ FAIL == mysqlnd_simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf),
+ PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
+ FALSE TSRMLS_CC)) {
+ stmt->error_info = conn->error_info;
+ DBG_RETURN(FAIL);
+ }
+ }
+ switch (stmt->execute_count) {
+ case 0:
+ stat = STAT_PS_PREPARED_NEVER_EXECUTED;
+ break;
+ case 1:
+ stat = STAT_PS_PREPARED_ONCE_USED;
+ break;
+ default:
+ break;
+ }
+ if (stat != STAT_LAST) {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
+ }
+
+ mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::dtor */
+static enum_func_status
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+{
+ enum_func_status ret;
+
+ DBG_ENTER("mysqlnd_stmt::close");
+ DBG_INF_FMT("stmt=%p", stmt);
+
+ MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
+ STAT_STMT_CLOSE_EXPLICIT);
+
+ if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) {
+ mnd_efree(stmt);
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+static
+struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
+ 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, free_result),
+ MYSQLND_METHOD(mysqlnd_stmt, data_seek),
+ MYSQLND_METHOD(mysqlnd_stmt, reset),
+ MYSQLND_METHOD(mysqlnd_stmt, close),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor),
+
+ MYSQLND_METHOD(mysqlnd_stmt, fetch),
+
+ MYSQLND_METHOD(mysqlnd_stmt, bind_param),
+ MYSQLND_METHOD(mysqlnd_stmt, bind_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_stmt_init */
+MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
+{
+ MYSQLND_STMT *stmt = mnd_ecalloc(1, sizeof(MYSQLND_STMT));
+
+ DBG_ENTER("_mysqlnd_stmt_init");
+ DBG_INF_FMT("stmt=%p", stmt);
+
+ stmt->m = &mysqlnd_stmt_methods;
+ stmt->state = MYSQLND_STMT_INITTED;
+ stmt->cmd_buffer.length = 4096;
+ stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length);
+
+ 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);
+
+ DBG_RETURN(stmt);
+}
+/* }}} */
+
+/*
+ * 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 0000000000..ea04cfb5fa
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -0,0 +1,854 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_debug.h"
+
+#define MYSQLND_SILENT
+
+
+
+typedef int8 my_int8;
+typedef uint8 my_uint8;
+
+typedef int16 my_int16;
+typedef uint16 my_uint16;
+
+typedef int32 my_int32;
+typedef uint32 my_uint32;
+
+
+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,
+ uint 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;
+ if (field->flags & UNSIGNED_FLAG) {
+ my_uint64 uval = 0;
+
+ switch (byte_count) {
+ case 8:uval = is_bit? (my_uint64) bit_uint8korr(*row):(my_uint64) 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? (my_uint64) bit_uint4korr(*row):(my_uint64) uint4korr(*row);break;
+ case 3:uval = is_bit? (my_uint64) bit_uint3korr(*row):(my_uint64) uint3korr(*row);break;
+ case 2:uval = is_bit? (my_uint64) bit_uint2korr(*row):(my_uint64) uint2korr(*row);break;
+ case 1:uval = (my_uint64) uint1korr(*row);break;
+ }
+
+#if SIZEOF_LONG==4
+ if (uval > INT_MAX) {
+ 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, uval);
+ } else {
+ tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
+ }
+ }
+ } else {
+ /* SIGNED */
+ my_int64 lval = 0;
+ switch (byte_count) {
+ case 8:lval = (my_int64) sint8korr(*row);break;
+ /*
+ 7, 6 and 5 are not possible.
+ BIT is only unsigned, thus only uint5|6|7 macroses exist
+ */
+ case 4:lval = (my_int64) sint4korr(*row);break;
+ case 3:lval = (my_int64) sint3korr(*row);break;
+ case 2:lval = (my_int64) sint2korr(*row);break;
+ case 1:lval = (my_int64) *(my_int8*)*row;break;
+ }
+
+#if SIZEOF_LONG==4
+ if ((L64(2147483647) < (my_int64) lval) || (L64(-2147483648) > (my_int64) lval)) {
+ tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval);
+ } else
+#endif /* SIZEOF */
+ {
+ ZVAL_LONG(zv, lval);
+ }
+ }
+
+ if (tmp_len) {
+#if PHP_MAJOR_VERSION >= 6
+ if (as_unicode) {
+ ZVAL_UTF8_STRINGL(zv, tmp, tmp_len, ZSTR_DUPLICATE);
+ } else
+#endif
+ {
+ ZVAL_STRINGL(zv, tmp, tmp_len, 1);
+ }
+ }
+ (*row)+= byte_count;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_null */
+static
+void ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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,
+ uint 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);
+#if 0
+ if (field->flags & UNSIGNED_FLAG) {
+ ZVAL_LONG(zv, *(my_uint8*)*row);
+ } else {
+ ZVAL_LONG(zv, *(my_int8*)*row);
+ }
+ (*row)++;
+#endif
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int16 */
+static
+void ps_fetch_int16(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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);
+#if 0
+ if (field->flags & UNSIGNED_FLAG) {
+ ZVAL_LONG(zv, (my_uint16) sint2korr(*row));
+ } else {
+ ZVAL_LONG(zv, (my_int16) sint2korr(*row));
+ }
+ (*row)+= 2;
+#endif
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int32 */
+static
+void ps_fetch_int32(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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);
+#if 0
+
+ if (field->flags & UNSIGNED_FLAG) {
+ my_uint32 uval;
+
+ /* unsigned int (11) */
+ uval= (my_uint32) sint4korr(*row);
+#if SIZEOF_LONG==4
+ if (uval > INT_MAX) {
+ char *tmp, *p;
+ int j=10;
+ tmp= mnd_emalloc(11);
+ p= &tmp[9];
+ do {
+ *p-- = (uval % 10) + 48;
+ uval = uval / 10;
+ } while (--j > 0);
+ tmp[10]= '\0';
+ /* unsigned int > INT_MAX is 10 digits - ALWAYS */
+#if PHP_MAJOR_VERSION >= 6
+ if (!as_unicode) {
+#endif
+ ZVAL_STRING(zv, tmp, 0);
+#if PHP_MAJOR_VERSION >= 6
+ } else {
+ ZVAL_UTF8_STRINGL(zv, tmp, 10, ZSTR_AUTOFREE);
+ }
+#endif /* PHP_MAJOR_VERSION >= 6 */
+ } else
+#endif /* #if SIZEOF_LONG==4 */
+ {
+ ZVAL_LONG(zv, uval);
+ }
+ } else {
+ ZVAL_LONG(zv, (my_int32) sint4korr(*row));
+ }
+ (*row)+= 4;
+#endif /* 0 */
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int64 */
+static
+void ps_fetch_int64(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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);
+#if 0
+
+ my_uint64 llval = (my_uint64) sint8korr(*row);
+ zend_bool uns = field->flags & UNSIGNED_FLAG? TRUE:FALSE;
+
+#if SIZEOF_LONG==8
+ if (uns == TRUE && llval > 9223372036854775807L) {
+#elif SIZEOF_LONG==4
+ if ((uns == TRUE && llval > L64(2147483647)) ||
+ (uns == FALSE && ((L64( 2147483647) < (my_int64) llval) ||
+ (L64(-2147483648) > (my_int64) llval))))
+ {
+#endif
+ char tmp[22];
+ /* even though lval is declared as unsigned, the value
+ * may be negative. Therefor we cannot use MYSQLND_LLU_SPEC and must
+ * use MYSQLND_LL_SPEC.
+ */
+ sprintf((char *)&tmp, uns == TRUE? MYSQLND_LLU_SPEC : MYSQLND_LL_SPEC, llval);
+#if PHP_MAJOR_VERSION >= 6
+ if (!as_unicode) {
+#endif
+ ZVAL_STRING(zv, tmp, 1);
+#if PHP_MAJOR_VERSION >= 6
+ } else {
+ ZVAL_UTF8_STRING(zv, tmp, ZSTR_DUPLICATE);
+ }
+#endif
+ } else {
+ /* This cast is safe, as we have checked the values above */
+ ZVAL_LONG(zv, (long) llval);
+ }
+ (*row)+= 8;
+#endif /* 0 */
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_float */
+static
+void ps_fetch_float(zval *zv, const MYSQLND_FIELD * const field,
+ uint pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ float value;
+ float4get(value, *row);
+ ZVAL_DOUBLE(zv, value);
+ (*row)+= 4;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_double */
+static
+void ps_fetch_double(zval *zv, const MYSQLND_FIELD * const field,
+ uint pack_len, zend_uchar **row,
+ zend_bool as_unicode TSRMLS_DC)
+{
+ double value;
+ float8get(value, *row);
+ ZVAL_DOUBLE(zv, value);
+ (*row)+= 8;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_time */
+static
+void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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 *to;
+
+ 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;
+ }
+
+ /*
+ QQ : How to make this unicode without copying two times the buffer -
+ Unicode equivalent of spprintf?
+ */
+ length = spprintf(&to, 0, "%s%02u:%02u:%02u",
+ (t.neg ? "-" : ""), t.hour, t.minute, t.second);
+
+#if PHP_MAJOR_VERSION >= 6
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, to, length, 1);
+ mnd_efree(to);
+#if PHP_MAJOR_VERSION >= 6
+ } else {
+ ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);
+ }
+#endif
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_date */
+static
+void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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 *to;
+
+ 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;
+ }
+
+ /*
+ QQ : How to make this unicode without copying two times the buffer -
+ Unicode equivalent of spprintf?
+ */
+ length = spprintf(&to, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
+
+#if PHP_MAJOR_VERSION >= 6
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, to, length, 1);
+ mnd_efree(to);
+#if PHP_MAJOR_VERSION >= 6
+ } else {
+ ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);
+ }
+#endif
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_datetime */
+static
+void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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 *to;
+
+ 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;
+ }
+
+ /*
+ QQ : How to make this unicode without copying two times the buffer -
+ Unicode equivalent of spprintf?
+ */
+ length = spprintf(&to, 0, "%04u-%02u-%02u %02u:%02u:%02u",
+ t.year, t.month, t.day, t.hour, t.minute, t.second);
+
+#if PHP_MAJOR_VERSION >= 6
+ if (!as_unicode) {
+#endif
+ ZVAL_STRINGL(zv, to, length, 1);
+ mnd_efree(to);
+#if PHP_MAJOR_VERSION >= 6
+ } else {
+ ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);
+ }
+#endif
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_string */
+static
+void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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);
+
+#if PHP_MAJOR_VERSION < 6
+ ZVAL_STRINGL(zv, (char *)*row, length, 1);
+#else
+ if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) {
+ ZVAL_STRINGL(zv, (char *)*row, length, 1);
+ } else {
+ ZVAL_UTF8_STRINGL(zv, (char*)*row, length, ZSTR_DUPLICATE);
+ }
+#endif
+
+ (*row) += length;
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_bit */
+static
+void ps_fetch_bit(zval *zv, const MYSQLND_FIELD * const field,
+ uint 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_subsystem */
+void _mysqlnd_init_ps_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_date;
+ 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_execute_store_params */
+void
+mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p,
+ size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC)
+{
+ unsigned int i = 0;
+ unsigned left = (*buf_len - (*p - *buf));
+ unsigned int data_size = 0;
+
+/* 1. Store type information */
+ 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);
+ memcpy(tmp_buf, *buf, offset);
+ *buf = tmp_buf;
+
+ /* Update our pos pointer */
+ *p = *buf + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++) {
+ /* our types are not unsigned */
+#if SIZEOF_LONG==8
+ if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) {
+ stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG;
+ }
+#endif
+ int2store(*p, stmt->param_bind[i].type);
+ *p+= 2;
+ }
+ }
+
+/* 2. Store data */
+ /* 2.1 Calculate how much space we need */
+ for (i = 0; i < stmt->param_count; i++) {
+ if (stmt->param_bind[i].zv &&
+ Z_TYPE_P(stmt->param_bind[i].zv) == IS_NULL) {
+ continue;
+ }
+
+ switch (stmt->param_bind[i].type) {
+ case MYSQL_TYPE_DOUBLE:
+ data_size += 8;
+ break;
+#if SIZEOF_LONG==8
+ case MYSQL_TYPE_LONGLONG:
+ data_size += 8;
+ break;
+#elif SIZEOF_LONG==4
+ case MYSQL_TYPE_LONG:
+ data_size += 4;
+ break;
+#else
+#error "Should not happen"
+#endif
+ 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 follow after it.
+ */
+ data_size++;
+ }
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ data_size += 8; /* max 8 bytes for size */
+ convert_to_string_ex(&stmt->param_bind[i].zv);
+ data_size += Z_STRLEN_P(stmt->param_bind[i].zv);
+ 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);
+ memcpy(tmp_buf, *buf, offset);
+ *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 = 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;
+#if SIZEOF_LONG==8
+ case MYSQL_TYPE_LONGLONG:
+ convert_to_long_ex(&data);
+ int8store(*p, Z_LVAL_P(data));
+ (*p) += 8;
+ break;
+#elif SIZEOF_LONG==4
+ case MYSQL_TYPE_LONG:
+ convert_to_long_ex(&data);
+ int4store(*p, Z_LVAL_P(data));
+ (*p) += 4;
+ break;
+#else
+#error "Should not happen"
+#endif
+ 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:
+ /*
+ If the user uses refs, it could be that the type has
+ has changed and we need to convert, again. Which is noop,
+ if the type hasn't changed.
+ */
+ convert_to_string_ex(&stmt->param_bind[i].zv);
+ {
+ 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;
+ }
+ }
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_execute_generate_request */
+zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len,
+ zend_bool *free_buffer TSRMLS_DC)
+{
+ zend_uchar *p = stmt->cmd_buffer.buffer,
+ *cmd_buffer = stmt->cmd_buffer.buffer;
+ size_t cmd_buffer_length = stmt->cmd_buffer.length;
+ unsigned int null_byte_offset,
+ null_count= (stmt->param_count + 7) / 8;
+
+ 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;
+
+
+ null_byte_offset = p - cmd_buffer;
+ memset(p, 0, null_count);
+ p += null_count;
+
+
+ int1store(p, stmt->send_types_to_server);
+ p++;
+
+ mysqlnd_stmt_execute_store_params(stmt, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC);
+
+ *free_buffer = (cmd_buffer != stmt->cmd_buffer.buffer);
+ *request_len = (p - cmd_buffer);
+ return cmd_buffer;
+}
+/* }}} */
+
+/*
+ * 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_qcache.c b/ext/mysqlnd/mysqlnd_qcache.c
new file mode 100644
index 0000000000..a08c7d488d
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_qcache.c
@@ -0,0 +1,141 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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"
+
+#define MYSQLND_SILENT
+
+#ifdef ZTS
+#define LOCK_QCACHE(cache) tsrm_mutex_lock((cache)->LOCK_access)
+#define UNLOCK_QCACHE(cache) tsrm_mutex_unlock((cache)->LOCK_access)
+#else
+#define LOCK_QCACHE(cache)
+#define UNLOCK_QCACHE(cache)
+#endif
+
+
+/* {{{ mysqlnd_qcache_init_cache */
+PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_init_cache()
+{
+ MYSQLND_QCACHE *cache = calloc(1, sizeof(MYSQLND_QCACHE));
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_qcache_init_cache %p]\n", cache);
+#endif
+
+ cache->references = 1;
+#ifdef ZTS
+ cache->LOCK_access = tsrm_mutex_alloc();
+#endif
+ cache->ht = malloc(sizeof(HashTable));
+ zend_hash_init(cache->ht, 10 /* init_elements */, NULL, NULL, TRUE /*pers*/);
+
+ return cache;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_qcache_get_cache_reference */
+PHPAPI MYSQLND_QCACHE * mysqlnd_qcache_get_cache_reference(MYSQLND_QCACHE * const cache)
+{
+ if (cache) {
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_qcache_get_cache_reference %p will become %d]\n", cache, cache->references+1);
+#endif
+ LOCK_QCACHE(cache);
+ cache->references++;
+ UNLOCK_QCACHE(cache);
+ }
+ return cache;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_qcache_free_cache */
+/*
+ As this call will happen on MSHUTDOWN(), then we don't need to copy the zvals with
+ copy_ctor but scrap what they point to with zval_dtor() and then just free our
+ pre-allocated block. Precondition is that we ZVAL_NULL() the zvals when we put them
+ to the free list after usage. We ZVAL_NULL() them when we allocate them in the
+ constructor of the cache.
+*/
+static
+void mysqlnd_qcache_free_cache(MYSQLND_QCACHE *cache)
+{
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_qcache_free_cache %p]\n", cache);
+#endif
+
+#ifdef ZTS
+ tsrm_mutex_free(cache->LOCK_access);
+#endif
+ zend_hash_destroy(cache->ht);
+ free(cache->ht);
+ free(cache);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_qcache_free_cache_reference */
+PHPAPI void mysqlnd_qcache_free_cache_reference(MYSQLND_QCACHE **cache)
+{
+ if (*cache) {
+ zend_bool to_free;
+#ifndef MYSQLND_SILENT
+ php_printf("[mysqlnd_qcache_free_cache_reference %p] refs=%d\n", *cache, (*cache)->references);
+#endif
+ LOCK_QCACHE(*cache);
+ to_free = --(*cache)->references == 0;
+ /* Unlock before destroying */
+ UNLOCK_QCACHE(*cache);
+ if (to_free) {
+ mysqlnd_qcache_free_cache(*cache);
+ }
+ *cache = NULL;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_qcache_free_cache_reference */
+PHPAPI void mysqlnd_qcache_stats(const MYSQLND_QCACHE * const cache, zval *return_value)
+{
+ if (cache) {
+ LOCK_QCACHE(cache);
+ array_init(return_value);
+ add_assoc_long_ex(return_value, "references", sizeof("references"), cache->references);
+ UNLOCK_QCACHE(cache);
+ } else {
+ ZVAL_NULL(return_value);
+ }
+}
+/* }}} */
+
+/*
+ * 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 0000000000..2d42d65469
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -0,0 +1,1194 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_result.h"
+#include "mysqlnd_result_meta.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_charset.h"
+#include "mysqlnd_debug.h"
+#include "ext/standard/basic_functions.h"
+
+#define MYSQLND_SILENT
+
+
+/* {{{ mysqlnd_unbuffered_free_last_data */
+void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC)
+{
+ MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
+
+ DBG_ENTER("mysqlnd_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_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]),
+ result->zval_cache, result->type,
+ &copy_ctor_called TSRMLS_CC);
+ if (copy_ctor_called) {
+ ctor_called_count++;
+ }
+ }
+ /* By using value3 macros we hold a mutex only once, there is no value2 */
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(global_stats,
+ STAT_COPY_ON_WRITE_PERFORMED,
+ ctor_called_count,
+ STAT_COPY_ON_WRITE_SAVED,
+ result->field_count - ctor_called_count,
+ STAT_COPY_ON_WRITE_PERFORMED, 0);
+
+ /* Free last row's zvals */
+ efree(unbuf->last_row_data);
+ unbuf->last_row_data = NULL;
+ }
+ if (unbuf->last_row_buffer) {
+ /* Nothing points to this buffer now, free it */
+ efree(unbuf->last_row_buffer);
+ unbuf->last_row_buffer = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+/* {{{ mysqlnd_free_buffered_data */
+void mysqlnd_free_buffered_data(MYSQLND_RES *result TSRMLS_DC)
+{
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache = result->zval_cache;
+ MYSQLND_RES_BUFFERED *set = result->data;
+ unsigned int field_count = result->field_count;
+ unsigned int row;
+
+ DBG_ENTER("mysqlnd_free_buffered_data");
+ DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", result->data->row_count);
+
+ DBG_INF_FMT("before: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC));
+ for (row = 0; row < result->data->row_count; row++) {
+ unsigned int col;
+ zval **current_row = current_row = set->data[row];
+ zend_uchar *current_buffer = set->row_buffers[row];
+
+ for (col = 0; col < field_count; col++) {
+ zend_bool copy_ctor_called;
+ mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), zval_cache,
+ result->type, &copy_ctor_called TSRMLS_CC);
+#if MYSQLND_DEBUG_MEMORY
+ DBG_INF_FMT("Copy_ctor_called=%d", copy_ctor_called);
+#endif
+ MYSQLND_INC_GLOBAL_STATISTIC(copy_ctor_called? STAT_COPY_ON_WRITE_PERFORMED:
+ STAT_COPY_ON_WRITE_SAVED);
+ }
+#if MYSQLND_DEBUG_MEMORY
+ DBG_INF("Freeing current_row & current_buffer");
+#endif
+ pefree(current_row, set->persistent);
+ pefree(current_buffer, set->persistent);
+ }
+ DBG_INF("Freeing data & row_buffer");
+ pefree(set->data, set->persistent);
+ pefree(set->row_buffers, set->persistent);
+ set->data = NULL;
+ set->row_buffers = NULL;
+ set->data_cursor = NULL;
+ set->row_count = 0;
+ if (set->qcache) {
+ mysqlnd_qcache_free_cache_reference(&set->qcache);
+ }
+ DBG_INF("Freeing set");
+ pefree(set, set->persistent);
+
+ DBG_INF_FMT("after: real_usage=%lu usage=%lu", zend_memory_usage(TRUE TSRMLS_CC), zend_memory_usage(FALSE TSRMLS_CC));
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::free_result_buffers */
+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->data? "buffered":"unknown"));
+
+ if (result->unbuf) {
+ mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+ efree(result->unbuf);
+ result->unbuf = NULL;
+ } else if (result->data) {
+ mysqlnd_free_buffered_data(result TSRMLS_CC);
+ result->data = NULL;
+ }
+
+ if (result->lengths) {
+ efree(result->lengths);
+ result->lengths = 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->row_packet) {
+ DBG_INF("Freeing packet");
+ PACKET_FREE(result->row_packet);
+ result->row_packet = NULL;
+ }
+
+ result->conn = NULL;
+
+ if (result->meta) {
+ result->meta->m->free_metadata(result->meta, FALSE TSRMLS_CC);
+ result->meta = NULL;
+ }
+
+ if (result->zval_cache) {
+ DBG_INF("Freeing zval cache reference");
+ mysqlnd_palloc_free_thd_cache_reference(&result->zval_cache);
+ result->zval_cache = 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->conn is an address if this is an unbuffered query.
+ In this case, decrement the reference counter in the connection
+ object and if needed free the latter.
+ */
+ if (result->conn) {
+ result->conn->m->free_reference(result->conn TSRMLS_CC);
+ result->conn = NULL;
+ }
+
+ result->m.free_result_contents(result TSRMLS_CC);
+ efree(result);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::read_result_metadata */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES *result, MYSQLND *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, FALSE TSRMLS_CC);
+ result->meta = NULL;
+ }
+
+ result->meta = mysqlnd_result_meta_init(result->field_count TSRMLS_CC);
+
+ /* 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);
+ }
+
+ /*
+ 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, MYSQLND_STMT *stmt TSRMLS_DC)
+{
+ enum_func_status ret;
+ php_mysql_packet_rset_header rset_header;
+
+ DBG_ENTER("mysqlnd_query_read_result_set_header");
+ DBG_INF_FMT("stmt=%d", stmt? stmt->stmt_id:0);
+
+ ret = FAIL;
+ PACKET_INIT_ALLOCA(rset_header, PROT_RSET_HEADER_PACKET);
+ do {
+ if (FAIL == (ret = PACKET_READ_ALLOCA(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;
+ conn->upsert_status.affected_rows = -1;
+ /*
+ This will copy the error code and the messages, as they
+ are buffers in the struct
+ */
+ conn->error_info = rset_header.error_info;
+ ret = FAIL;
+ 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->state = CONN_SENDING_LOAD_DATA;
+ ret = mysqlnd_handle_local_infile(conn, rset_header.info_or_local_file, &is_warning TSRMLS_CC);
+ conn->state = (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->state = CONN_NEXT_RESULT_PENDING;
+ } else {
+ conn->state = CONN_READY;
+ }
+ ret = PASS;
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_NON_RSET_QUERY);
+ break;
+ default:{ /* Result set */
+ php_mysql_packet_eof fields_eof;
+ MYSQLND_RES *result;
+ enum_mysqlnd_collected_stats stat = 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));
+ conn->last_query_type = QUERY_SELECT;
+ conn->state = CONN_FETCHING_DATA;
+ /* PS has already allocated it */
+ if (!stmt) {
+ conn->field_count = rset_header.field_count;
+ result =
+ conn->current_result=
+ mysqlnd_result_init(rset_header.field_count,
+ mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)
+ 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.
+ */
+ conn->field_count = rset_header.field_count;
+ result =
+ stmt->result =
+ mysqlnd_result_init(rset_header.field_count,
+ mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache)
+ 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 (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) {
+ /* For PS, we leave them in Prepared state */
+ if (!stmt) {
+ efree(conn->current_result);
+ conn->current_result = NULL;
+ }
+ DBG_ERR("Error ocurred while reading metadata");
+ break;
+ }
+
+ /* Check for SERVER_STATUS_MORE_RESULTS if needed */
+ PACKET_INIT_ALLOCA(fields_eof, PROT_EOF_PACKET);
+ if (FAIL == (ret = PACKET_READ_ALLOCA(fields_eof, conn))) {
+ DBG_ERR("Error ocurred while reading the EOF packet");
+ result->m.free_result_contents(result TSRMLS_CC);
+ efree(result);
+ if (!stmt) {
+ conn->current_result = NULL;
+ } else {
+ stmt->result = NULL;
+ memset(stmt, 0, sizeof(MYSQLND_STMT));
+ stmt->state = MYSQLND_STMT_INITTED;
+ }
+ } else {
+ DBG_INF_FMT("warns=%u status=%u", fields_eof.warning_count, fields_eof.server_status);
+ conn->upsert_status.warning_count = fields_eof.warning_count;
+ conn->upsert_status.server_status = fields_eof.server_status;
+ if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_GOOD_INDEX_USED) {
+ stat = STAT_BAD_INDEX_USED;
+ } else if (fields_eof.server_status & MYSQLND_SERVER_QUERY_NO_INDEX_USED) {
+ stat = STAT_NO_INDEX_USED;
+ }
+ if (stat != STAT_LAST) {
+ char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
+#if A0
+ php_log_err(backtrace TSRMLS_CC);
+#endif
+ efree(backtrace);
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
+ }
+ }
+
+ PACKET_FREE_ALLOCA(fields_eof);
+
+ break;
+ }
+ }
+ } while (0);
+ PACKET_FREE_ALLOCA(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)
+{
+ int i;
+ zval **previous_row;
+
+ /*
+ If:
+ - unbuffered result
+ - first row has not been read
+ - last_row has been read
+ */
+ if (result->data->data_cursor == NULL ||
+ result->data->data_cursor == result->data->data ||
+ ((result->data->data_cursor - result->data->data) > result->data->row_count))
+ {
+ return NULL;/* No rows or no more rows */
+ }
+
+ previous_row = *(result->data->data_cursor - 1);
+ for (i = 0; i < result->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)
+{
+ return result->lengths;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_lengths */
+PHPAPI unsigned long * mysqlnd_fetch_lengths(MYSQLND_RES * const result)
+{
+ return result->m.fetch_lengths? result->m.fetch_lengths(result):NULL;
+}
+/* }}} */
+
+
+/* {{{ 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;
+ unsigned int i,
+ field_count = result->field_count;
+ php_mysql_packet_row *row_packet = result->row_packet;
+ unsigned long *lengths = result->lengths;
+
+ DBG_ENTER("mysqlnd_fetch_row_unbuffered");
+ DBG_INF_FMT("flags=%d", flags);
+
+ if (result->unbuf->eof_reached) {
+ /* No more rows obviously */
+ *fetched_anything = FALSE;
+ DBG_RETURN(PASS);
+ }
+ if (result->conn->state != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
+ UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ 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
+ mysqlnd_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++;
+ *fetched_anything = TRUE;
+
+ mysqlnd_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);
+
+ for (i = 0; i < field_count; i++) {
+ zval *data = result->unbuf->last_row_data[i];
+ int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+ MYSQLND_RES_METADATA *meta = result->meta;
+
+ if (lengths) {
+ lengths[i] = len;
+ }
+
+ /* Forbid ZE to free it, we will clean it */
+ ZVAL_ADDREF(data);
+
+ if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
+ ZVAL_ADDREF(data);
+ }
+ if (flags & MYSQLND_FETCH_NUM) {
+ 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.
+ */
+ if (meta->zend_hash_keys[i].is_numeric == FALSE) {
+#if PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
+ meta->zend_hash_keys[i].ustr,
+ meta->zend_hash_keys[i].ulen + 1,
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ } else
+#endif
+ {
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ meta->fields[i].name,
+ meta->fields[i].name_length + 1,
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ }
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
+ }
+ }
+ }
+ } else if (ret == FAIL) {
+ if (row_packet->error_info.error_no) {
+ result->conn->error_info = row_packet->error_info;
+ DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
+ }
+ *fetched_anything = FALSE;
+ result->conn->state = 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("warns=%u 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) {
+ result->conn->state = CONN_NEXT_RESULT_PENDING;
+ } else {
+ result->conn->state = CONN_READY;
+ }
+ mysqlnd_unbuffered_free_last_data(result TSRMLS_CC);
+ *fetched_anything = FALSE;
+ }
+
+ DBG_INF_FMT("ret=%s fetched=%d", ret == PASS? "PASS":"FAIL", *fetched_anything);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::use_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::use_result");
+ DBG_INF_FMT("ps=%d", ps);
+
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
+ result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered;
+ result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+
+ /*
+ 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.
+ */
+ PACKET_INIT(result->row_packet, PROT_ROW_PACKET, php_mysql_packet_row *);
+ result->row_packet->field_count = result->field_count;
+ result->row_packet->binary_protocol = FALSE;
+ 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;
+ result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+
+ /* No multithreading issues as we don't share the connection :) */
+
+ DBG_RETURN(result);
+}
+/* }}} */
+
+
+/* {{{ 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;
+
+ DBG_ENTER("mysqlnd_fetch_row_buffered");
+ DBG_INF_FMT("flags=%u row=%p", flags, row);
+
+ /* If we haven't read everything */
+ if (result->data->data_cursor &&
+ (result->data->data_cursor - result->data->data) < result->data->row_count)
+ {
+ zval **current_row = *result->data->data_cursor;
+ for (i = 0; i < result->field_count; i++) {
+ zval *data = current_row[i];
+
+ /*
+ Let us later know what to do with this zval. If ref_count > 1, we will just
+ decrease it, otherwise free it. zval_ptr_dtor() make this very easy job.
+ */
+ ZVAL_ADDREF(data);
+
+ if ((flags & MYSQLND_FETCH_BOTH) == MYSQLND_FETCH_BOTH) {
+ ZVAL_ADDREF(data);
+ }
+ if (flags & MYSQLND_FETCH_NUM) {
+ 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.
+ */
+ if (result->meta->zend_hash_keys[i].is_numeric == FALSE) {
+#if PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ zend_u_hash_quick_update(Z_ARRVAL_P(row), IS_UNICODE,
+ result->meta->zend_hash_keys[i].ustr,
+ result->meta->zend_hash_keys[i].ulen + 1,
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ } else
+#endif
+ {
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ result->meta->fields[i].name,
+ result->meta->fields[i].name_length + 1,
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ result->meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ }
+ }
+ }
+ result->data->data_cursor++;
+ *fetched_anything = TRUE;
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ } else {
+ result->data->data_cursor = NULL;
+ *fetched_anything = FALSE;
+ DBG_INF("EOF reached");
+ }
+ DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+#define STORE_RESULT_PREALLOCATED_SET 32
+
+/* {{{ mysqlnd_store_result_fetch_data */
+enum_func_status
+mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,
+ MYSQLND_RES_METADATA *meta,
+ zend_bool binary_protocol,
+ zend_bool update_max_length,
+ zend_bool to_cache TSRMLS_DC)
+{
+ enum_func_status ret;
+ php_mysql_packet_row row_packet;
+ unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET, free_rows;
+ MYSQLND_RES_BUFFERED *set;
+
+ DBG_ENTER("mysqlnd_store_result_fetch_data");
+ DBG_INF_FMT("conn=%llu binary_proto=%d update_max_len=%d to_cache=%d",
+ conn->thread_id, binary_protocol, update_max_length, to_cache);
+
+ free_rows = next_extend;
+
+ result->data = set = mnd_pecalloc(1, sizeof(MYSQLND_RES_BUFFERED), to_cache);
+ set->data = mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zval **), to_cache);
+ set->row_buffers= mnd_pemalloc(STORE_RESULT_PREALLOCATED_SET * sizeof(zend_uchar *), to_cache);
+ set->persistent = to_cache;
+ set->qcache = to_cache? mysqlnd_qcache_get_cache_reference(conn->qcache):NULL;
+ set->references = 1;
+
+ PACKET_INIT_ALLOCA(row_packet, PROT_ROW_PACKET);
+ 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;
+ /* Let the row packet fill our buffer and skip additional malloc + memcpy */
+ while (FAIL != (ret = PACKET_READ_ALLOCA(row_packet, conn)) && !row_packet.eof) {
+ int i;
+ zval **current_row;
+
+ if (!free_rows) {
+ mynd_ulonglong total_rows = free_rows = next_extend = next_extend * 5 / 3; /* extend with 33% */
+ total_rows += set->row_count;
+ set->data = mnd_perealloc(set->data, total_rows * sizeof(zval **), set->persistent);
+
+ set->row_buffers = mnd_perealloc(set->row_buffers,
+ total_rows * sizeof(zend_uchar *), set->persistent);
+ }
+ free_rows--;
+ current_row = set->data[set->row_count] = row_packet.fields;
+ 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;
+
+
+ if (update_max_length == TRUE) {
+ for (i = 0; i < row_packet.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;
+ }
+ }
+ }
+ }
+ /*
+ 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.
+ */
+ }
+ 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) {
+ set->data = mnd_perealloc(set->data,
+ (size_t) set->row_count * sizeof(zval **),
+ set->persistent);
+ set->row_buffers = mnd_perealloc(set->row_buffers,
+ (size_t) set->row_count * sizeof(zend_uchar *),
+ set->persistent);
+ }
+
+ if (conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
+ conn->state = CONN_NEXT_RESULT_PENDING;
+ } else {
+ conn->state = CONN_READY;
+ }
+
+ if (ret == FAIL) {
+ 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 = result->data->row_count;
+ }
+ PACKET_FREE_ALLOCA(row_packet);
+
+ DBG_INF_FMT("ret=%s row_count=%u warns=%u status=%u", ret == PASS? "PASS":"FAIL",
+ set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::store_result */
+MYSQLND_RES *
+MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
+ MYSQLND * const conn,
+ zend_bool ps_protocol TSRMLS_DC)
+{
+ enum_func_status ret;
+ zend_bool to_cache = FALSE;
+
+ DBG_ENTER("mysqlnd_res::store_result");
+ DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol);
+
+ result->conn = NULL; /* store result does not reference the connection */
+ result->type = MYSQLND_RES_NORMAL;
+ result->m.fetch_row = result->m.fetch_row_normal_buffered;
+ result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
+
+ conn->state = CONN_FETCHING_DATA;
+
+ result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+
+ ret = mysqlnd_store_result_fetch_data(conn, result, result->meta,
+ ps_protocol, TRUE, to_cache TSRMLS_CC);
+ if (PASS == ret) {
+ /* libmysql's documentation says it should be so for SELECT statements */
+ conn->upsert_status.affected_rows = result->data->row_count;
+ } else {
+ conn->error_info = result->data->error_info;
+ result->m.free_result_internal(result TSRMLS_CC);
+ result = NULL;
+ }
+
+ 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->data && result->conn && 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");
+ DBG_INF_FMT("implicit=%d", implicit);
+
+ 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, mynd_ulonglong row TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::data_seek");
+ DBG_INF_FMT("row=%lu", row);
+
+ if (!result->data) {
+ return FAIL;
+ }
+
+ /* libmysql just moves to the end, it does traversing of a linked list */
+ if (row >= result->data->row_count) {
+ result->data->data_cursor = NULL;
+ } else {
+ result->data->data_cursor = result->data->data + row;
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::num_fields */
+mynd_ulonglong
+MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const res)
+{
+ /* Be compatible with libmysql. We count row_count, but will return 0 */
+ return res->data? res->data->row_count:0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::num_fields */
+unsigned int
+MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const res)
+{
+ return res->field_count;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field */
+static MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::fetch_field");
+ DBG_RETURN(result->meta? result->meta->m->fetch_field(result->meta TSRMLS_CC):NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_field_direct */
+static MYSQLND_FIELD *
+MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(const MYSQLND_RES * const result,
+ MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_res::fetch_field_direct");
+ DBG_RETURN(result->meta? result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC):NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::field_seek */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result,
+ MYSQLND_FIELD_OFFSET field_offset)
+{
+ 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)
+{
+ return result->meta? result->meta->m->field_tell(result->meta):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");
+ DBG_INF_FMT("flags=%u mysqlnd_extension=%d", flags, extension);
+
+ 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_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;
+
+ DBG_ENTER("mysqlnd_res::fetch_all");
+ DBG_INF_FMT("flags=%u", flags);
+
+ /* mysqlnd_res::fetch_all works with buffered resultsets only */
+ if (result->conn || !result->data ||
+ !result->data->row_count || !result->data->data_cursor ||
+ result->data->data_cursor >= result->data->data + result->data->row_count)
+ {
+ RETVAL_NULL();
+ DBG_VOID_RETURN;
+ }
+
+ mysqlnd_array_init(return_value, (uint) result->data->row_count);
+
+ while (result->data->data_cursor &&
+ (result->data->data_cursor - result->data->data) < result->data->row_count)
+ {
+ MAKE_STD_ZVAL(row);
+ mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
+ add_index_zval(return_value, i++, row);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res::fetch_into */
+static void
+MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES *result, unsigned int offset,
+ zval *return_value TSRMLS_DC)
+{
+ zval row;
+ zval **entry;
+ uint 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);
+ ZVAL_REFCOUNT(return_value) = 1;
+ zval_dtor(&row);
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_init */
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC)
+{
+ MYSQLND_RES *ret = mnd_ecalloc(1, sizeof(MYSQLND_RES));
+
+ DBG_ENTER("mysqlnd_result_init");
+ DBG_INF_FMT("field_count=%u cache=%p", field_count, cache);
+
+ ret->field_count = field_count;
+ ret->zval_cache = cache;
+
+ ret->m.use_result = MYSQLND_METHOD(mysqlnd_res, use_result);
+ ret->m.store_result = MYSQLND_METHOD(mysqlnd_res, store_result);
+ ret->m.free_result = MYSQLND_METHOD(mysqlnd_res, free_result);
+ ret->m.seek_data = MYSQLND_METHOD(mysqlnd_res, data_seek);
+ ret->m.num_rows = MYSQLND_METHOD(mysqlnd_res, num_rows);
+ ret->m.num_fields = MYSQLND_METHOD(mysqlnd_res, num_fields);
+ ret->m.fetch_into = MYSQLND_METHOD(mysqlnd_res, fetch_into);
+ ret->m.fetch_all = MYSQLND_METHOD(mysqlnd_res, fetch_all);
+ ret->m.fetch_field_data = MYSQLND_METHOD(mysqlnd_res, fetch_field_data);
+ ret->m.seek_field = MYSQLND_METHOD(mysqlnd_res, field_seek);
+ ret->m.field_tell = MYSQLND_METHOD(mysqlnd_res, field_tell);
+ ret->m.fetch_field = MYSQLND_METHOD(mysqlnd_res, fetch_field);
+ ret->m.fetch_field_direct = MYSQLND_METHOD(mysqlnd_res, fetch_field_direct);
+
+ ret->m.skip_result = MYSQLND_METHOD(mysqlnd_res, skip_result);
+ ret->m.free_result_buffers = MYSQLND_METHOD(mysqlnd_res, free_result_buffers);
+ ret->m.free_result_internal = mysqlnd_internal_free_result;
+ ret->m.free_result_contents = mysqlnd_internal_free_result_contents;
+
+ ret->m.read_result_metadata = MYSQLND_METHOD(mysqlnd_res, read_result_metadata);
+ ret->m.fetch_row_normal_buffered = mysqlnd_fetch_row_buffered;
+ ret->m.fetch_row_normal_unbuffered = mysqlnd_fetch_row_unbuffered;
+
+ 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 0000000000..eec92becff
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_RESULT_H
+#define MYSQLND_RESULT_H
+
+MYSQLND_RES *mysqlnd_result_init(unsigned int field_count, MYSQLND_THD_ZVAL_PCACHE *cache TSRMLS_DC);
+
+void mysqlnd_unbuffered_free_last_data(MYSQLND_RES *result TSRMLS_DC);
+
+enum_func_status
+mysqlnd_store_result_fetch_data(MYSQLND * const conn, MYSQLND_RES *result,
+ MYSQLND_RES_METADATA *meta,
+ zend_bool binary_protocol,
+ zend_bool update_max_length,
+ zend_bool to_cache TSRMLS_DC);
+
+enum_func_status mysqlnd_query_read_result_set_header(MYSQLND *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 0000000000..bd80ae24b2
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -0,0 +1,440 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_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(char *key, size_t length, long *idx)
+{
+ register char *tmp=key;
+
+ if (*tmp=='-') {
+ tmp++;
+ }
+ if ((*tmp>='0' && *tmp<='9')) {
+ do { /* possibly a numeric index */
+ 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 PHP_MAJOR_VERSION >= 6
+/* {{{ 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 TSRMLS_DC)
+{
+ int i = 0;
+ php_mysql_packet_res_field field_packet;
+
+ DBG_ENTER("mysqlnd_res_meta::read_metadata");
+
+ PACKET_INIT_ALLOCA(field_packet, PROT_RSET_FLD_PACKET);
+ for (;i < meta->field_count; i++) {
+ long idx;
+
+ if (meta->fields[i].root) {
+ /* We re-read metadata for PS */
+ mnd_efree(meta->fields[i].root);
+ meta->fields[i].root = NULL;
+ }
+
+ field_packet.metadata = &(meta->fields[i]);
+ if (FAIL == PACKET_READ_ALLOCA(field_packet, conn)) {
+ PACKET_FREE_ALLOCA(field_packet);
+ DBG_RETURN(FAIL);
+ }
+ if (field_packet.stupid_list_fields_eof == TRUE) {
+ break;
+ }
+
+ if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
+ DBG_ERR_FMT("Unknown type %d 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 %d sent by the server. "
+ "Please send a report to the developers",
+ meta->fields[i].type);
+ PACKET_FREE_ALLOCA(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 PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ UChar *ustr;
+ int ulen;
+ 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
+#endif
+ {
+ /* 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);
+ }
+ }
+ }
+ PACKET_FREE_ALLOCA(field_packet);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::free */
+static void
+MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC)
+{
+ int i;
+ MYSQLND_FIELD *fields;
+
+ DBG_ENTER("mysqlnd_res_meta::free");
+ DBG_INF_FMT("persistent=%d", persistent);
+
+ if ((fields = meta->fields)) {
+ DBG_INF("Freeing fields metadata");
+ i = meta->field_count;
+ while (i--) {
+ php_mysqlnd_free_field_metadata(fields++, persistent TSRMLS_CC);
+ }
+ mnd_pefree(meta->fields, persistent);
+ meta->fields = NULL;
+ }
+
+ if (meta->zend_hash_keys) {
+ DBG_INF("Freeing zend_hash_keys");
+#if PHP_MAJOR_VERSION >= 6
+ 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, persistent);
+ }
+ }
+ }
+#endif
+ mnd_pefree(meta->zend_hash_keys, persistent);
+ meta->zend_hash_keys = NULL;
+ }
+ DBG_INF("Freeing metadata structure");
+ mnd_pefree(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 = mnd_pemalloc(sizeof(MYSQLND_RES_METADATA), persistent);
+ MYSQLND_FIELD *new_fields = mnd_pecalloc(meta->field_count + 1, sizeof(MYSQLND_FIELD), persistent);
+ 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=%d", persistent);
+
+ new_meta->zend_hash_keys = mnd_pemalloc(len, persistent);
+ memcpy(new_meta->zend_hash_keys, meta->zend_hash_keys, len);
+ new_meta->m = meta->m;
+
+ /*
+ 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);
+ 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);
+ /* copy the trailing \0 too */
+ memcpy(new_fields[i].def, orig_fields[i].def, orig_fields[i].def_length + 1);
+ }
+#if PHP_MAJOR_VERSION >= 6
+ 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);
+ }
+#endif
+ }
+ new_meta->current_field = 0;
+ new_meta->field_count = meta->field_count;
+
+ new_meta->fields = new_fields;
+
+ DBG_RETURN(new_meta);
+}
+/* }}} */
+
+/* {{{ mysqlnd_res_meta::fetch_field */
+static 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_RETURN(NULL);
+ }
+ DBG_RETURN(&meta->fields[meta->current_field++]);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::fetch_field_direct */
+static 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=%d", fieldnr);
+ DBG_RETURN(&meta->fields[fieldnr]);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_res_meta::field_tell */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const meta)
+{
+ return meta->current_field;
+}
+/* }}} */
+
+
+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, 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 */
+MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count TSRMLS_DC)
+{
+ MYSQLND_RES_METADATA *ret;
+ DBG_ENTER("mysqlnd_result_meta_init");
+
+ /* +1 is to have empty marker at the end */
+ ret = mnd_ecalloc(1, sizeof(MYSQLND_RES_METADATA));
+ ret->field_count = field_count;
+ ret->fields = ecalloc(field_count + 1, sizeof(MYSQLND_FIELD));
+ ret->zend_hash_keys = ecalloc(field_count, sizeof(struct mysqlnd_field_hash_key));
+
+ ret->m = & mysqlnd_mysqlnd_res_meta_methods;
+ DBG_INF_FMT("meta=%p", ret);
+ 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_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h
new file mode 100644
index 0000000000..f1bfaee681
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_result_meta.h
@@ -0,0 +1,40 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_RESULT_META_H
+#define MYSQLND_RESULT_META_H
+
+
+MYSQLND_RES_METADATA *mysqlnd_result_meta_init(unsigned int field_count 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_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c
new file mode 100644
index 0000000000..66d9bacd44
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_statistics.c
@@ -0,0 +1,155 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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"
+
+
+#define STR_W_LEN(str) str, (sizeof(str) - 1)
+
+/* {{{ mysqlnd_stats_values_names
+ */
+const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
+{
+ { STR_W_LEN("bytes_sent") },
+ { STR_W_LEN("bytes_received") },
+ { STR_W_LEN("packets_sent") },
+ { STR_W_LEN("packets_received") },
+ { STR_W_LEN("protocol_overhead_in") },
+ { STR_W_LEN("protocol_overhead_out") },
+ { STR_W_LEN("result_set_queries") },
+ { STR_W_LEN("non_result_set_queries") },
+ { STR_W_LEN("no_index_used") },
+ { STR_W_LEN("bad_index_used") },
+ { STR_W_LEN("buffered_sets") },
+ { STR_W_LEN("unbuffered_sets") },
+ { STR_W_LEN("ps_buffered_sets") },
+ { STR_W_LEN("ps_unbuffered_sets") },
+ { STR_W_LEN("flushed_normal_sets") },
+ { STR_W_LEN("flushed_ps_sets") },
+ { STR_W_LEN("ps_prepared_never_executed") },
+ { STR_W_LEN("ps_prepared_once_executed") },
+ { STR_W_LEN("rows_fetched_from_server_normal") },
+ { STR_W_LEN("rows_fetched_from_server_ps") },
+ { STR_W_LEN("rows_buffered_from_client_normal") },
+ { STR_W_LEN("rows_buffered_from_client_ps") },
+ { STR_W_LEN("rows_fetched_from_client_normal_buffered") },
+ { STR_W_LEN("rows_fetched_from_client_normal_unbuffered") },
+ { STR_W_LEN("rows_fetched_from_client_ps_buffered") },
+ { STR_W_LEN("rows_fetched_from_client_ps_unbuffered") },
+ { STR_W_LEN("rows_fetched_from_client_ps_cursor") },
+ { STR_W_LEN("rows_skipped_normal") },
+ { STR_W_LEN("rows_skipped_ps") },
+ { STR_W_LEN("copy_on_write_saved") },
+ { STR_W_LEN("copy_on_write_performed") },
+ { STR_W_LEN("command_buffer_too_small") },
+ { STR_W_LEN("connect_success") },
+ { STR_W_LEN("connect_failure") },
+ { STR_W_LEN("connection_reused") },
+ { STR_W_LEN("reconnect") },
+ { STR_W_LEN("pconnect_success") },
+ { STR_W_LEN("active_connections") },
+ { STR_W_LEN("active_persistent_connections") },
+ { STR_W_LEN("explicit_close") },
+ { STR_W_LEN("implicit_close") },
+ { STR_W_LEN("disconnect_close") },
+ { STR_W_LEN("in_middle_of_command_close") },
+ { STR_W_LEN("explicit_free_result") },
+ { STR_W_LEN("implicit_free_result") },
+ { STR_W_LEN("explicit_stmt_close") },
+ { STR_W_LEN("implicit_stmt_close") },
+ { STR_W_LEN("mem_emalloc_count") },
+ { STR_W_LEN("mem_emalloc_ammount") },
+ { STR_W_LEN("mem_ecalloc_count") },
+ { STR_W_LEN("mem_ecalloc_ammount") },
+ { STR_W_LEN("mem_erealloc_count") },
+ { STR_W_LEN("mem_erealloc_ammount") },
+ { STR_W_LEN("mem_efree_count") },
+ { STR_W_LEN("mem_malloc_count") },
+ { STR_W_LEN("mem_malloc_ammount") },
+ { STR_W_LEN("mem_calloc_count") },
+ { STR_W_LEN("mem_calloc_ammount") },
+ { STR_W_LEN("mem_realloc_calloc") },
+ { STR_W_LEN("mem_realloc_ammount") },
+ { STR_W_LEN("mem_free_count") }
+};
+/* }}} */
+
+
+/* {{{ mysqlnd_fill_stats_hash */
+void
+mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+{
+ unsigned int i;
+
+ mysqlnd_array_init(return_value, STAT_LAST);
+ for (i = 0; i < STAT_LAST; i++) {
+ char tmp[22];
+
+ sprintf((char *)&tmp, MYSQLND_LLU_SPEC, stats->values[i]);
+#if PHP_MAJOR_VERSION >= 6
+ if (UG(unicode)) {
+ UChar *ustr, *tstr;
+ int ulen, tlen;
+
+ zend_string_to_unicode(UG(utf8_conv), &ustr, &ulen, mysqlnd_stats_values_names[i].s,
+ mysqlnd_stats_values_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
+#endif
+ {
+ add_assoc_string_ex(return_value, mysqlnd_stats_values_names[i].s,
+ mysqlnd_stats_values_names[i].l + 1, tmp, 1);
+ }
+ }
+}
+/* }}} */
+
+
+/* {{{ _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, return_value TSRMLS_CC ZEND_FILE_LINE_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_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h
new file mode 100644
index 0000000000..2122ac2411
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_statistics.h
@@ -0,0 +1,209 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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
+
+
+extern MYSQLND_STATS *mysqlnd_global_stats;
+
+typedef struct st_mysqlnd_string
+{
+ char *s;
+ size_t l;
+} MYSQLND_STRING;
+
+extern const MYSQLND_STRING mysqlnd_stats_values_names[];
+
+#ifdef ZTS
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic)]++; \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ }\
+ }
+
+#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global stats increase w value [%s] [%s]", mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic1)] += (value1); \
+ mysqlnd_global_stats->values[(statistic2)] += (value2); \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ }\
+ }
+
+#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global&conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic)]--; \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \
+ } \
+ }\
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic)]++; \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \
+ } \
+ }\
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ my_uint64 v = (my_uint64) (value); \
+ DBG_INF_FMT("Global&Conn stat increase w value [%s]", mysqlnd_stats_values_names[statistic]); \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic)] += v; \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)]+= v; \
+ } \
+ }\
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ my_uint64 v1 = (my_uint64) (value1); \
+ my_uint64 v2 = (my_uint64) (value2); \
+ my_uint64 v3 = (my_uint64) (value3); \
+ \
+ tsrm_mutex_lock(mysqlnd_global_stats->LOCK_access); \
+ mysqlnd_global_stats->values[(statistic1)]+= v1; \
+ mysqlnd_global_stats->values[(statistic2)]+= v2; \
+ mysqlnd_global_stats->values[(statistic3)]+= v3; \
+ tsrm_mutex_unlock(mysqlnd_global_stats->LOCK_access); \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \
+ } \
+ } \
+ }
+
+
+#else /* NON-ZTS */
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global stat increase [%s]", mysqlnd_stats_values_names[statistic]); \
+ mysqlnd_global_stats->values[(statistic)]++; \
+ } \
+ }
+
+#define MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(statistic1, value1, statistic2, value2) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global stats increase w value [%s] [%s]", \
+ mysqlnd_stats_values_names[statistic1], mysqlnd_stats_values_names[statistic2]); \
+ mysqlnd_global_stats->values[(statistic1)] += (value1); \
+ mysqlnd_global_stats->values[(statistic2)] += (value2); \
+ }\
+ }
+
+
+#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global&Conn stat decrease [%s]", mysqlnd_stats_values_names[statistic]); \
+ mysqlnd_global_stats->values[(statistic)]--; \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)]--; \
+ } \
+ } \
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ DBG_INF_FMT("Global&Conn stat increase [%s]", mysqlnd_stats_values_names[statistic]); \
+ mysqlnd_global_stats->values[(statistic)]++; \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)]++; \
+ } \
+ } \
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
+ { \
+ my_uint64 v = (my_uint64) (value); \
+ DBG_INF_FMT("Global&Conn stats increase w value [%s]", mysqlnd_stats_values_names[statistic]); \
+ if (MYSQLND_G(collect_statistics)) { \
+ mysqlnd_global_stats->values[(statistic)] += v; \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic)] += v; \
+ } \
+ } \
+ }
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ { \
+ if (MYSQLND_G(collect_statistics)) { \
+ my_uint64 v1 = (my_uint64) (value1); \
+ my_uint64 v2 = (my_uint64) (value2); \
+ my_uint64 v3 = (my_uint64) (value3); \
+ \
+ mysqlnd_global_stats->values[(statistic1)]+= v1; \
+ mysqlnd_global_stats->values[(statistic2)]+= v2; \
+ mysqlnd_global_stats->values[(statistic3)]+= v3; \
+ if ((conn_stats)) { \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic1)]+= v1; \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic2)]+= v2; \
+ ((MYSQLND_STATS *) conn_stats)->values[(statistic3)]+= v3; \
+ } \
+ } \
+ }
+
+#endif
+
+void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, zval *return_value
+ TSRMLS_DC ZEND_FILE_LINE_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 0000000000..31367d53c9
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -0,0 +1,540 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_STRUCTS_H
+#define MYSQLND_STRUCTS_H
+
+typedef struct st_mysqlnd_cmd_buffer
+{
+ zend_uchar *buffer;
+ size_t length;
+} MYSQLND_CMD_BUFFER;
+
+
+typedef struct st_mysqlnd_field
+{
+ char *name; /* Name of column */
+ char *org_name; /* Original column name, if an alias */
+ char *table; /* Table of column if column was a field */
+ char *org_table; /* Org table name, if table was an alias */
+ char *db; /* Database for table */
+ 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;
+ mynd_ulonglong affected_rows;
+ mynd_ulonglong 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;
+} mysqlnd_error_info;
+
+
+typedef struct st_mysqlnd_zval_pcache MYSQLND_ZVAL_PCACHE;
+typedef struct st_mysqlnd_thread_zval_pcache MYSQLND_THD_ZVAL_PCACHE;
+typedef struct st_mysqlnd_qcache MYSQLND_QCACHE;
+
+
+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
+{
+ uint nr;
+ char *name;
+ char *collation;
+ uint char_minlen;
+ uint char_maxlen;
+ uint dangerous_for_escape_backslash;
+ uint (*mb_charlen)(uint c);
+ uint (*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, char *buf, uint buf_len TSRMLS_DC);
+ int (*local_infile_error)(void *ptr, char *error_msg, uint error_msg_len TSRMLS_DC);
+ void (*local_infile_end)(void *ptr TSRMLS_DC);
+ zval *callback;
+ void *userdata;
+} MYSQLND_INFILE;
+
+typedef struct st_mysqlnd_option
+{
+ /* timeouts */
+ uint timeout_connect;
+ uint timeout_read;
+ uint timeout_write;
+
+ ulong flags;
+
+ /* init commands - we need to send them to server directly after connect */
+ uint num_commands;
+ char **init_commands;
+
+ /* configuration file information */
+ char *cfg_file;
+ char *cfg_section;
+
+ /* SSL information */
+ char *ssl_key;
+ char *ssl_cert;
+ char *ssl_ca;
+ char *ssl_capath;
+ char *ssl_cipher;
+ zend_bool use_ssl;
+
+ 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_year_as_int;
+#endif
+ unsigned int net_read_buffer_size;
+} MYSQLND_OPTION;
+
+
+typedef struct st_mysqlnd_connection MYSQLND;
+typedef struct st_mysqlnd_res MYSQLND_RES;
+typedef char** MYSQLND_ROW; /* return data as array of strings */
+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
+{
+ my_uint64 values[STAT_LAST];
+#ifdef ZTS
+ MUTEX_T LOCK_access;
+#endif
+} MYSQLND_STATS;
+
+
+typedef struct st_mysqlnd_net
+{
+ php_stream *stream;
+ /* sequence for simple checking of correct packets */
+ zend_uchar packet_no;
+
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ zend_uchar last_command;
+#endif
+
+ /* cmd buffer */
+ MYSQLND_CMD_BUFFER cmd_buffer;
+} MYSQLND_NET;
+
+
+struct st_mysqlnd_conn_methods
+{
+ ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, int escapestr_len TSRMLS_DC);
+ enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC);
+ enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC);
+ MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC);
+ MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC);
+ enum_func_status (*next_result)(MYSQLND * const conn TSRMLS_DC);
+ zend_bool (*more_results)(const MYSQLND * const conn);
+
+ MYSQLND_STMT * (*stmt_init)(MYSQLND * const conn TSRMLS_DC);
+
+ enum_func_status (*shutdown_server)(MYSQLND * const conn, unsigned long level TSRMLS_DC);
+ enum_func_status (*refresh_server)(MYSQLND * const conn, unsigned long options TSRMLS_DC);
+
+ enum_func_status (*ping)(MYSQLND * const conn TSRMLS_DC);
+ enum_func_status (*kill_connection)(MYSQLND *conn, unsigned int pid TSRMLS_DC);
+ enum_func_status (*select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC);
+ enum_func_status (*server_dump_debug_information)(MYSQLND * const conn TSRMLS_DC);
+ enum_func_status (*change_user)(MYSQLND * const conn, const char * user, const char * passwd, const char * db TSRMLS_DC);
+
+ unsigned int (*get_error_no)(const MYSQLND * const conn);
+ const char * (*get_error_str)(const MYSQLND * const conn);
+ const char * (*get_sqlstate)(const MYSQLND * const conn);
+ mynd_ulonglong (*get_thread_id)(const MYSQLND * const conn);
+ void (*get_statistics)(const MYSQLND * const conn, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+
+ unsigned long (*get_server_version)(const MYSQLND * const conn);
+ const char * (*get_server_information)(const MYSQLND * const conn);
+ enum_func_status (*get_server_statistics)(MYSQLND *conn, char **message, unsigned int * message_len TSRMLS_DC);
+ const char * (*get_host_information)(const MYSQLND * const conn);
+ unsigned int (*get_protocol_information)(const MYSQLND * const conn);
+ const char * (*get_last_message)(const MYSQLND * const conn);
+ const char * (*charset_name)(const MYSQLND * const conn);
+ MYSQLND_RES * (*list_fields)(MYSQLND *conn, const char *table, const char *achtung_wild TSRMLS_DC);
+ MYSQLND_RES * (*list_method)(MYSQLND *conn, const char *query, const char *achtung_wild, char *par1 TSRMLS_DC);
+
+ mynd_ulonglong (*get_last_insert_id)(const MYSQLND * const conn);
+ mynd_ulonglong (*get_affected_rows)(const MYSQLND * const conn);
+ unsigned int (*get_warning_count)(const MYSQLND * const conn);
+
+ unsigned int (*get_field_count)(const MYSQLND * const conn);
+
+ enum_func_status (*set_server_option)(MYSQLND * const conn, enum_mysqlnd_server_option option TSRMLS_DC);
+ enum_func_status (*set_client_option)(MYSQLND * const conn, enum_mysqlnd_option option, const char * const value TSRMLS_DC);
+ void (*free_contents)(MYSQLND *conn TSRMLS_DC); /* private */
+ enum_func_status (*close)(MYSQLND *conn, enum_connection_close_type close_type TSRMLS_DC);
+ void (*dtor)(MYSQLND *conn TSRMLS_DC); /* private */
+
+ MYSQLND * (*get_reference)(MYSQLND * const conn);
+ enum_func_status (*free_reference)(MYSQLND * const conn 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 */
+
+ MYSQLND_RES * (*use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC);
+ MYSQLND_RES * (*store_result)(MYSQLND_RES * result, MYSQLND * const conn, zend_bool ps TSRMLS_DC);
+ void (*fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC);
+ void (*fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+ void (*fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC);
+ mynd_ulonglong (*num_rows)(const MYSQLND_RES * const result);
+ unsigned int (*num_fields)(const MYSQLND_RES * const result);
+ enum_func_status (*skip_result)(MYSQLND_RES * const result TSRMLS_DC);
+ enum_func_status (*seek_data)(MYSQLND_RES * result, mynd_ulonglong row TSRMLS_DC);
+ MYSQLND_FIELD_OFFSET (*seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset);
+ MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES * const result);
+ MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES * const result TSRMLS_DC);
+ MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+
+ enum_func_status (*read_result_metadata)(MYSQLND_RES *result, MYSQLND *conn TSRMLS_DC);
+ unsigned long * (*fetch_lengths)(MYSQLND_RES * const result);
+ void (*free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */
+ enum_func_status (*free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC);
+ void (*free_result_internal)(MYSQLND_RES *result TSRMLS_DC);
+ void (*free_result_contents)(MYSQLND_RES *result TSRMLS_DC);
+};
+
+
+struct st_mysqlnd_res_meta_methods
+{
+ MYSQLND_FIELD * (*fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC);
+ MYSQLND_FIELD * (*fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+ MYSQLND_FIELD_OFFSET (*field_tell)(const MYSQLND_RES_METADATA * const meta);
+ enum_func_status (*read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND *conn TSRMLS_DC);
+ MYSQLND_RES_METADATA * (*clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC);
+ void (*free_metadata)(MYSQLND_RES_METADATA *meta, zend_bool persistent TSRMLS_DC);
+};
+
+
+struct st_mysqlnd_stmt_methods
+{
+ enum_func_status (*prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC);
+ enum_func_status (*execute)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ MYSQLND_RES * (*use_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ MYSQLND_RES * (*store_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ MYSQLND_RES * (*get_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ enum_func_status (*free_result)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ enum_func_status (*seek_data)(const MYSQLND_STMT * const stmt, mynd_ulonglong row TSRMLS_DC);
+ enum_func_status (*reset)(MYSQLND_STMT * const stmt TSRMLS_DC);
+ enum_func_status (*close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* private */
+ enum_func_status (*dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC); /* use this for mysqlnd_stmt_close */
+
+ enum_func_status (*fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC);
+
+ enum_func_status (*bind_param)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC);
+ enum_func_status (*bind_result)(MYSQLND_STMT * const stmt, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC);
+ enum_func_status (*send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_num,
+ const char * const data, unsigned long length TSRMLS_DC);
+ MYSQLND_RES * (*get_parameter_metadata)(MYSQLND_STMT * const stmt);
+ MYSQLND_RES * (*get_result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC);
+
+ mynd_ulonglong (*get_last_insert_id)(const MYSQLND_STMT * const stmt);
+ mynd_ulonglong (*get_affected_rows)(const MYSQLND_STMT * const stmt);
+ mynd_ulonglong (*get_num_rows)(const MYSQLND_STMT * const stmt);
+
+ unsigned int (*get_param_count)(const MYSQLND_STMT * const stmt);
+ unsigned int (*get_field_count)(const MYSQLND_STMT * const stmt);
+ unsigned int (*get_warning_count)(const MYSQLND_STMT * const stmt);
+
+ unsigned int (*get_error_no)(const MYSQLND_STMT * const stmt);
+ const char * (*get_error_str)(const MYSQLND_STMT * const stmt);
+ const char * (*get_sqlstate)(const MYSQLND_STMT * const stmt);
+
+ enum_func_status (*get_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC);
+ enum_func_status (*set_attribute)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC);
+};
+
+
+struct st_mysqlnd_connection
+{
+/* Operation related */
+ MYSQLND_NET net;
+
+/* Information related */
+ char *host;
+ char *unix_socket;
+ char *user;
+ char *passwd;
+ unsigned int *passwd_len;
+ char *scheme;
+ mynd_ulonglong thread_id;
+ char *server_version;
+ char *host_info;
+ unsigned char *scramble;
+ const MYSQLND_CHARSET *charset;
+ const MYSQLND_CHARSET *greet_charset;
+ MYSQLND_INFILE infile;
+ unsigned int protocol_version;
+ unsigned long max_packet_size;
+ unsigned int port;
+ unsigned long client_flag;
+ unsigned long server_capabilities;
+
+ int tmp_int;
+
+
+ /* For UPSERT queries */
+ mysqlnd_upsert_status upsert_status;
+ char *last_message;
+ unsigned int last_message_len;
+
+ /* If error packet, we use these */
+ mysqlnd_error_info error_info;
+
+ /*
+ 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_OPTION options;
+
+ /* zval cache */
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache;
+
+ /* qcache */
+ MYSQLND_QCACHE *qcache;
+
+ /* stats */
+ MYSQLND_STATS stats;
+
+ struct st_mysqlnd_conn_methods *m;
+};
+
+typedef struct st_php_mysql_packet_row php_mysql_packet_row;
+
+
+struct mysqlnd_field_hash_key
+{
+ zend_bool is_numeric;
+ unsigned long key;
+#if PHP_MAJOR_VERSION >= 6
+ 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 */
+
+ struct st_mysqlnd_res_meta_methods *m;
+};
+
+
+struct st_mysqlnd_buffered_result
+{
+ zval ***data;
+ zval ***data_cursor;
+ zend_uchar **row_buffers;
+ mynd_ulonglong row_count;
+ zend_bool persistent;
+
+ MYSQLND_QCACHE *qcache;
+ unsigned int references;
+
+ zend_bool async_invalid;
+ mysqlnd_error_info error_info;
+};
+
+
+struct st_mysqlnd_unbuffered_result
+{
+ /* For unbuffered (both normal and PS) */
+ zval **last_row_data;
+ zend_uchar *last_row_buffer;
+
+ mynd_ulonglong row_count;
+ zend_bool eof_reached;
+};
+
+
+struct st_mysqlnd_res
+{
+ struct st_mysqlnd_res_methods m;
+
+ MYSQLND *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 *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;
+
+ php_mysql_packet_row *row_packet; /* Unused for PS */
+
+ /* zval cache */
+ MYSQLND_THD_ZVAL_PCACHE *zval_cache;
+};
+
+
+struct st_mysqlnd_param_bind
+{
+ zval *zv;
+ zend_uchar type;
+ enum_param_bind_flags flags;
+};
+
+struct st_mysqlnd_result_bind
+{
+ zval *zv;
+ zend_uchar original_type;
+ zend_bool bound;
+};
+
+
+struct st_mysqlnd_stmt
+{
+ MYSQLND *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;
+
+ mysqlnd_upsert_status upsert_status;
+
+ mysqlnd_error_info error_info;
+
+ 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 cmd_buffer;
+ unsigned int execute_count;/* count how many times the stmt was executed */
+
+ struct st_mysqlnd_stmt_methods *m;
+};
+
+#endif /* MYSQLND_STRUCTS_H */
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
new file mode 100644
index 0000000000..73179281fa
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -0,0 +1,1956 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_palloc.h"
+#include "mysqlnd_debug.h"
+#include "ext/standard/sha1.h"
+#include "php_network.h"
+#include "zend_ini.h"
+
+#ifndef PHP_WIN32
+#include <netinet/tcp.h>
+#else
+#include <winsock.h>
+#endif
+
+
+#define USE_CORK 0
+
+#define MYSQLND_SILENT 1
+
+#define MYSQLND_DUMP_HEADER_N_BODY2
+#define MYSQLND_DUMP_HEADER_N_BODY_FULL2
+
+#define MYSQLND_MAX_PACKET_SIZE (256L*256L*256L-1)
+
+#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type) \
+ { \
+ if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
+ conn->state = 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)); \
+ DBG_RETURN(FAIL);\
+ }\
+ if ((buf_size) < (packet)->header.size) { \
+ DBG_ERR_FMT("Packet buffer wasn't big enough %u bytes will be unread", \
+ (packet)->header.size - (buf_size)); \
+ }\
+ if (!mysqlnd_read_body((conn), (buf), \
+ MIN((buf_size), (packet)->header.size) TSRMLS_CC)) { \
+ conn->state = 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)); \
+ DBG_RETURN(FAIL);\
+ } \
+ }
+
+
+extern mysqlnd_packet_methods packet_methods[];
+
+static const char *unknown_sqlstate= "HY000";
+
+char * const mysqlnd_empty_string = "";
+
+/* Used in mysqlnd_debug.c */
+char * mysqlnd_read_header_name = "mysqlnd_read_header";
+char * mysqlnd_read_body_name = "mysqlnd_read_body";
+
+
+/* {{{ 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"
+};
+/* }}} */
+
+
+/* {{{ 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 */
+mynd_ulonglong php_mysqlnd_net_field_length_ll(zend_uchar **packet)
+{
+ register zend_uchar *p= (zend_uchar *)*packet;
+
+ if (*p < 251) {
+ (*packet)++;
+ return (mynd_ulonglong) *p;
+ }
+
+ switch (*p) {
+ case 251:
+ (*packet)++;
+ return (mynd_ulonglong) MYSQLND_NULL_LENGTH;
+ case 252:
+ (*packet) += 3;
+ return (mynd_ulonglong) uint2korr(p + 1);
+ case 253:
+ (*packet) += 4;
+ return (mynd_ulonglong) uint3korr(p + 1);
+ default:
+ (*packet) += 9;
+ return (mynd_ulonglong) uint8korr(p + 1);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_net_store_length */
+zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length)
+{
+ if (length < (mynd_ulonglong) L64(251)) {
+ *packet = (zend_uchar) length;
+ return packet + 1;
+ }
+
+ if (length < (mynd_ulonglong) L64(65536)) {
+ *packet++ = 252;
+ int2store(packet,(uint) length);
+ return packet + 2;
+ }
+
+ if (length < (mynd_ulonglong) L64(16777216)) {
+ *packet++ = 253;
+ int3store(packet,(ulong) length);
+ return packet + 3;
+ }
+ *packet++ = 254;
+ int8store(packet, length);
+ return packet + 8;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_consume_uneaten_data */
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC)
+{
+
+ /*
+ 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];
+ MYSQLND_NET *net = &conn->net;
+ 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("php_mysqlnd_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. PID=%d",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command], getpid());
+ }
+ }
+ net->last_command = cmd;
+
+ DBG_RETURN(skipped_bytes);
+}
+#endif
+/* }}} */
+
+
+/* {{{ 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");
+
+ if (buf_len > 2) {
+ *error_no = uint2korr(p);
+ p+= 2;
+ /* sqlstate is following */
+ if (*p == '#') {
+ memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH);
+ p+= MYSQLND_SQLSTATE_LENGTH;
+ }
+ error_msg_len = buf_len - (p - buf);
+ error_msg_len = MIN(error_msg_len, error_buf_len - 1);
+ memcpy(error, p, error_msg_len);
+ } else {
+ *error_no = CR_UNKNOWN_ERROR;
+ memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
+ }
+ sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
+ error[error_msg_len]= '\0';
+
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_set_sock_no_delay */
+int mysqlnd_set_sock_no_delay(php_stream *stream)
+{
+
+ 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));
+ TSRMLS_FETCH();
+
+ DBG_ENTER("mysqlnd_set_sock_no_delay");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
+#define STORE_HEADER_SIZE(safe_storage, buffer) int4store((safe_storage), (*(uint32 *)(buffer)))
+#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
+
+/* {{{ mysqlnd_stream_write_w_header */
+/*
+ IMPORTANT : It's expected that buf 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(buf) (not the pointer but the actual buffer)
+*/
+size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC)
+{
+ zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
+ zend_uchar *safe_storage = safe_buf;
+ MYSQLND_NET *net = &conn->net;
+ size_t old_chunk_size = net->stream->chunk_size;
+ size_t ret, left = count, packets_sent = 1;
+ zend_uchar *p = (zend_uchar *) buf;
+
+ DBG_ENTER("mysqlnd_stream_write_w_header");
+ DBG_INF_FMT("conn=%llu count=%lu", conn->thread_id, count);
+
+ net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
+
+ while (left > MYSQLND_MAX_PACKET_SIZE) {
+ STORE_HEADER_SIZE(safe_storage, p);
+ int3store(p, MYSQLND_MAX_PACKET_SIZE);
+ int1store(p + 3, net->packet_no);
+ net->packet_no++;
+ ret = php_stream_write(net->stream, (char *)p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
+ RESTORE_HEADER_SIZE(p, safe_storage);
+
+ p += MYSQLND_MAX_PACKET_SIZE;
+ left -= MYSQLND_MAX_PACKET_SIZE;
+
+ packets_sent++;
+ }
+ /* Even for zero size payload we have to send a packet */
+ STORE_HEADER_SIZE(safe_storage, p);
+ int3store(p, left);
+ int1store(p + 3, net->packet_no);
+ net->packet_no++;
+ ret = php_stream_write(net->stream, (char *)p, left + MYSQLND_HEADER_SIZE);
+ RESTORE_HEADER_SIZE(p, safe_storage);
+
+ if (!ret) {
+ DBG_ERR_FMT("Can't %u send bytes", count);
+ conn->state = CONN_QUIT_SENT;
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ }
+
+ 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);
+
+ net->stream->chunk_size = old_chunk_size;
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stream_write_w_command */
+#if USE_CORK && defined(TCP_CORK)
+static
+size_t mysqlnd_stream_write_w_command(MYSQLND * const conn, enum php_mysqlnd_server_command command,
+ const char * const buf, size_t count TSRMLS_DC)
+{
+ zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
+ zend_uchar *safe_storage = (char *) &safe_buf;
+ MYSQLND_NET *net = &conn->net;
+ size_t old_chunk_size = net->stream->chunk_size;
+ size_t ret, left = count, header_len = MYSQLND_HEADER_SIZE + 1, packets_sent = 1;
+ const zend_uchar *p = (zend_uchar *) buf;
+ zend_bool command_sent = FALSE;
+ int corked = 1;
+
+ DBG_ENTER("mysqlnd_stream_write_w_command");
+
+ net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
+
+ setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket,
+ IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
+
+ int1store(safe_storage + MYSQLND_HEADER_SIZE, command);
+ while (left > MYSQLND_MAX_PACKET_SIZE) {
+ size_t body_size = MYSQLND_MAX_PACKET_SIZE;
+
+ int3store(safe_storage, MYSQLND_MAX_PACKET_SIZE);
+ int1store(safe_storage + 3, net->packet_no);
+ net->packet_no++;
+
+ ret = php_stream_write(net->stream, (char *)safe_storage, header_len);
+ if (command_sent == FALSE) {
+ --header_len;
+ /* Sent one byte less*/
+ --body_size;
+ command_sent = TRUE;
+ }
+
+ ret = php_stream_write(net->stream, (char *)p, body_size);
+
+ p += body_size;
+ left -= body_size;
+
+ packets_sent++;
+ }
+ /* Even for zero size payload we have to send a packet */
+ int3store(safe_storage, header_len == MYSQLND_HEADER_SIZE? left:left+1);
+ int1store(safe_storage + 3, net->packet_no);
+ net->packet_no++;
+
+ ret = php_stream_write(net->stream, (char *)safe_storage, header_len);
+
+ if (left) {
+ ret = php_stream_write(net->stream, (char *)p, left);
+ }
+ corked = 0;
+ setsockopt(((php_netstream_data_t*)net->stream->abstract)->socket,
+ IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
+
+ 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);
+
+ net->stream->chunk_size = old_chunk_size;
+
+ DBG_RETURN(ret);
+}
+#endif
+/* }}} */
+
+
+/* {{{ mysqlnd_read_header */
+static enum_func_status
+mysqlnd_read_header(MYSQLND *conn, mysqlnd_packet_header *header TSRMLS_DC)
+{
+ MYSQLND_NET *net = &conn->net;
+ char buffer[MYSQLND_HEADER_SIZE];
+ char *p = buffer;
+ int to_read = MYSQLND_HEADER_SIZE, ret;
+
+ DBG_ENTER(mysqlnd_read_header_name);
+
+ do {
+ if (!(ret= php_stream_read(net->stream, p, to_read))) {
+ DBG_ERR_FMT("Error while reading header from socket");
+ return FAIL;
+ }
+ p += ret;
+ to_read -= ret;
+ } while (to_read);
+
+ header->size = uint3korr(buffer);
+ header->packet_no = uint1korr(buffer + 3);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
+ STAT_BYTES_RECEIVED, MYSQLND_HEADER_SIZE,
+ STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
+ STAT_PACKETS_RECEIVED, 1);
+
+ if (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++;
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ DBG_ERR_FMT("HEADER: packet_no=%d size=%3d", header->packet_no, header->size);
+#endif
+ DBG_RETURN(PASS);
+ }
+
+#if !MYSQLND_SILENT
+ DBG_ERR_FMT("Packets out of order. Expected %d received %d. Packet size=%d",
+ net->packet_no, header->packet_no, header->size);
+#endif
+ php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size=%d. PID=%d",
+ net->packet_no, header->packet_no, header->size, getpid());
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_body */
+static
+size_t mysqlnd_read_body(MYSQLND *conn, zend_uchar *buf, size_t size TSRMLS_DC)
+{
+ size_t ret;
+ char *p = (char *)buf;
+ int iter = 0;
+ MYSQLND_NET *net = &conn->net;
+ size_t old_chunk_size = net->stream->chunk_size;
+
+ DBG_ENTER(mysqlnd_read_body_name);
+ DBG_INF_FMT("chunk_size=%d", net->stream->chunk_size);
+
+ net->stream->chunk_size = MIN(size, conn->options.net_read_buffer_size);
+ do {
+ size -= (ret = php_stream_read(net->stream, p, size));
+ if (size || iter++) {
+ DBG_INF_FMT("read=%d buf=%p p=%p chunk_size=%d left=%d",
+ ret, buf, p , net->stream->chunk_size, size);
+ }
+ p += ret;
+ } while (size > 0);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, p - (char*)buf);
+ net->stream->chunk_size = old_chunk_size;
+
+#ifdef MYSQLND_DUMP_HEADER_N_BODY_FULL
+ {
+ int i;
+ DBG_INF_FMT("BODY: requested=%d last_read=%3d", p - (char*)buf, ret);
+ for (i = 0 ; i < p - (char*)buf; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("[%c] ", *(char *)(&(buf[i])));
+ }
+ for (i = 0 ; i < p - (char*)buf; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("%.2X ", (int)*((char*)&(buf[i])));
+ }
+ php_printf("\n\t\t\t-=-=-=-=-\n");
+ }
+#endif
+
+ DBG_RETURN(p - (char*)buf);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_greet_read */
+static enum_func_status
+php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ zend_uchar buf[512];
+ zend_uchar *p= buf;
+ zend_uchar *begin = buf;
+ php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet;
+
+ DBG_ENTER("php_mysqlnd_greet_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting");
+
+ packet->protocol_version = uint1korr(p);
+ p++;
+
+ if (packet->protocol_version == 0xFF) {
+ 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 = pestrdup((char *)p, conn->persistent);
+ p+= strlen(packet->server_version) + 1; /* eat the '\0' */
+
+ packet->thread_id = uint4korr(p);
+ p+=4;
+
+ memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
+ p+= 8;
+
+ /* pad1 */
+ p++;
+
+ packet->server_capabilities = uint2korr(p);
+ p+= 2;
+
+ packet->charset_no = uint1korr(p);
+ p++;
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+
+ /* pad2 */
+ p+= 13;
+
+ if (p - buf < packet->header.size) {
+ /* scramble_buf is split into two parts */
+ memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
+ p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ } else {
+ packet->pre41 = TRUE;
+ }
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_greet_free_mem */
+static
+void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet;
+ if (p->server_version) {
+ mnd_efree(p->server_version);
+ p->server_version = NULL;
+ }
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
+ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
+ CLIENT_MULTI_RESULTS)
+
+
+/* {{{ php_mysqlnd_crypt */
+static
+void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
+{
+ const unsigned char *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)
+{
+ PHP_SHA1_CTX context;
+ unsigned char sha1[SHA1_MAX_LENGTH];
+ unsigned char sha2[SHA1_MAX_LENGTH];
+
+
+ /* Phase 1: hash password */
+ PHP_SHA1Init(&context);
+ PHP_SHA1Update(&context, password, strlen((char *)password));
+ PHP_SHA1Final(sha1, &context);
+
+ /* Phase 2: hash sha1 */
+ PHP_SHA1Init(&context);
+ PHP_SHA1Update(&context, (unsigned char*)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, (unsigned char*)sha2, SHA1_MAX_LENGTH);
+ PHP_SHA1Final(buffer, &context);
+
+ /* let's crypt buffer now */
+ php_mysqlnd_crypt(buffer, (const unsigned char *)buffer, (const unsigned char *)sha1, SHA1_MAX_LENGTH);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_auth_write */
+static
+size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ char buffer[1024];
+ register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
+ int len;
+ register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet;
+
+ DBG_ENTER("php_mysqlnd_auth_write");
+
+ packet->client_flags |= MYSQLND_CAPABILITIES;
+
+ if (packet->db) {
+ packet->client_flags |= CLIENT_CONNECT_WITH_DB;
+ }
+
+ if (PG(open_basedir) && strlen(PG(open_basedir))) {
+ packet->client_flags ^= CLIENT_LOCAL_FILES;
+ }
+
+ /* don't allow multi_queries via connect parameter */
+ packet->client_flags ^= CLIENT_MULTI_STATEMENTS;
+ 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;
+
+ len= strlen(packet->user);
+ strncpy(p, packet->user, len);
+ p+= len;
+ *p++ = '\0';
+
+ /* copy scrambled pass*/
+ if (packet->password && packet->password[0]) {
+ /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
+ int1store(p, 20);
+ p++;
+ php_mysqlnd_scramble((unsigned char*)p, packet->server_scramble_buf,
+ (unsigned char *)packet->password);
+ p+= 20;
+ } else {
+ /* Zero length */
+ int1store(p, 0);
+ p++;
+ }
+
+ if (packet->db) {
+ memcpy(p, packet->db, packet->db_len);
+ p+= packet->db_len;
+ *p++= '\0';
+ }
+ /* Handle CLIENT_CONNECT_WITH_DB */
+ /* no \0 for no DB */
+
+ DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
+}
+/* }}} */
+
+/* {{{ php_mysqlnd_auth_free_mem */
+static
+void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ if (!alloca) {
+ mnd_efree((php_mysql_packet_auth *) _packet);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_ok_read */
+static enum_func_status
+php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ zend_uchar buf[1024];
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ int i;
+ register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet;
+
+ DBG_ENTER("php_mysqlnd_ok_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "OK");
+
+ /* Should be always 0x0 or 0xFF for error */
+ packet->field_count= uint1korr(p);
+ p++;
+
+ if (0xFF == 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);
+ packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
+
+ packet->server_status = uint2korr(p);
+ p+= 2;
+
+ packet->warning_count = uint2korr(p);
+ p+= 2;
+
+ /* There is a message */
+ if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) {
+ packet->message = pestrndup((char *)p, MIN(i, sizeof(buf) - (p - buf)), conn->persistent);
+ packet->message_len = i;
+ } else {
+ packet->message = NULL;
+ }
+
+ DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d",
+ packet->affected_rows, packet->last_insert_id, packet->server_status,
+ packet->warning_count);
+
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_ok_free_mem */
+static
+void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet;
+ if (p->message) {
+ mnd_efree(p->message);
+ p->message = NULL;
+ }
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_eof_read */
+static enum_func_status
+php_mysqlnd_eof_read(void *_packet, MYSQLND *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
+ */
+ php_mysql_packet_eof *packet= (php_mysql_packet_eof *) _packet;
+ zend_uchar buf[5 + 10 + sizeof(packet->sqlstate) + sizeof(packet->error)];
+ zend_uchar *p= buf;
+ zend_uchar *begin = buf;
+
+ DBG_ENTER("php_mysqlnd_eof_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "EOF");
+
+ /* Should be always 0xFE */
+ packet->field_count= uint1korr(p);
+ p++;
+
+ if (0xFF == 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;
+ packet->server_status = uint2korr(p);
+ p+= 2;
+ } else {
+ packet->warning_count = 0;
+ packet->server_status = 0;
+ }
+
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_INF_FMT("EOF packet: status=%d warnings=%d", packet->server_status, packet->warning_count);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_eof_free_mem */
+static
+void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ if (!alloca) {
+ mnd_efree(_packet);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_cmd_write */
+size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
+ php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet;
+ MYSQLND_NET *net = &conn->net;
+ unsigned int error_reporting = EG(error_reporting);
+ size_t written;
+
+ 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;
+
+ if (error_reporting) {
+ EG(error_reporting) = 0;
+ }
+
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC);
+#endif
+
+ if (!packet->argument || !packet->arg_len) {
+ char buffer[MYSQLND_HEADER_SIZE + 1];
+
+ int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
+ written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC);
+ } else {
+#if USE_CORK && defined(TCP_CORK)
+ written = mysqlnd_stream_write_w_command(conn, packet->command, packet->argument,
+ packet->arg_len TSRMLS_CC));
+#else
+ size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
+ zend_uchar *tmp, *p;
+ tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
+ p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
+
+ int1store(p, packet->command);
+ p++;
+
+ memcpy(p, packet->argument, packet->arg_len);
+
+ ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
+ if (tmp != net->cmd_buffer.buffer) {
+ MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
+ mnd_efree(tmp);
+ }
+ written = ret;
+#endif
+ }
+ if (error_reporting) {
+ /* restore error reporting */
+ EG(error_reporting) = error_reporting;
+ }
+ DBG_RETURN(written);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_cmd_free_mem */
+static
+void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ if (!alloca) {
+ mnd_efree((php_mysql_packet_command *) _packet);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_header_read */
+static enum_func_status
+php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ zend_uchar buf[1024];
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ size_t len;
+ php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet;
+
+ DBG_ENTER("php_mysqlnd_rset_header_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "resultset header");
+
+ /*
+ Don't increment. First byte is 0xFF on error, but otherwise is starting byte
+ of encoded sequence for length.
+ */
+ if (*p == 0xFF) {
+ /* Error */
+ p++;
+ 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);
+ switch (packet->field_count) {
+ case MYSQLND_NULL_LENGTH:
+ /*
+ First byte in the packet is the field count.
+ Thus, the name is size - 1. And we add 1 for a trailing \0.
+ */
+ len = packet->header.size - 1;
+ packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent);
+ memcpy(packet->info_or_local_file, p, len);
+ packet->info_or_local_file[len] = '\0';
+ packet->info_or_local_file_len = len;
+ break;
+ case 0x00:
+ packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
+ packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p);
+ packet->server_status = uint2korr(p);
+ p+=2;
+ packet->warning_count = uint2korr(p);
+ p+=2;
+ /* Check for additional textual data */
+ if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
+ packet->info_or_local_file = mnd_pemalloc(len + 1, conn->persistent);
+ memcpy(packet->info_or_local_file, p, len);
+ packet->info_or_local_file[len] = '\0';
+ packet->info_or_local_file_len = len;
+ }
+ break;
+ default:
+ /* Result set */
+ break;
+ }
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_header_free_mem */
+static
+void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet;
+ if (p->info_or_local_file) {
+ mnd_efree(p->info_or_local_file);
+ p->info_or_local_file = NULL;
+ }
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+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 TSRMLS_DC)
+{
+ /* Should be enough for the metadata of a single row */
+ php_mysql_packet_res_field *packet= (php_mysql_packet_res_field *) _packet;
+ zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ char *root_ptr;
+ size_t buf_len = conn->net.cmd_buffer.length, len, total_len = 0;
+ 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");
+
+ if (packet->skip_parsing) {
+ DBG_RETURN(PASS);
+ }
+ if (*p == 0xFE && 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);
+ switch ((len)) {
+ case 0:
+ *(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_fake;
+ default:
+ *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p;
+ *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
+ p += len;
+ total_len += len + 1;
+ break;
+ }
+ }
+
+ /* 1 byte filler */
+ p++;
+
+ meta->charsetnr = uint2korr(p);
+ p += 2;
+
+ meta->length = uint4korr(p);
+ p += 4;
+
+ meta->type = uint1korr(p);
+ p += 1;
+
+ meta->flags = uint2korr(p);
+ p += 2;
+
+ meta->decimals = uint2korr(p);
+ p += 1;
+
+ /* 2 byte filler */
+ p +=2;
+
+ /* 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 > (p - buf) &&
+ (len = php_mysqlnd_net_field_length(&p)) &&
+ len != MYSQLND_NULL_LENGTH)
+ {
+ DBG_INF_FMT("Def found, length %lu", len);
+ meta->def = mnd_emalloc(len + 1);
+ memcpy(meta->def, p, len);
+ meta->def[len] = '\0';
+ meta->def_length = len;
+ p += len;
+ }
+
+ if (p - begin > packet->header.size) {
+ DBG_ERR_FMT("Result set field packet %d bytes shorter than expected", p - begin - packet->header.size);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet %d bytes "
+ "shorter than expected. PID=%d", p - begin - packet->header.size, getpid());
+ }
+
+ root_ptr = meta->root = mnd_emalloc(total_len);
+ 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';
+ }
+
+ 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_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);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rset_field_free_mem */
+static
+void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet;
+
+ /* p->metadata was passed to us as temporal buffer */
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+static enum_func_status
+php_mysqlnd_read_row_ex(MYSQLND *conn, zend_uchar **buf, int buf_size,
+ 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 *new_buf = NULL, *p = *buf;
+ 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 , &header TSRMLS_CC)) {
+ ret = FAIL;
+ break;
+ }
+
+ *data_size += header.size;
+
+ if (first_iteration && header.size > buf_size) {
+ 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.
+ */
+ p = new_buf = mnd_pemalloc(*data_size + 1, persistent_alloc);
+ } 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.
+ */
+ new_buf = mnd_perealloc(new_buf, *data_size + 1, persistent_alloc);
+ /* The position could have changed, recalculate */
+ p = new_buf + (*data_size - header.size);
+ }
+
+ if (!mysqlnd_read_body(conn, p, header.size TSRMLS_CC)) {
+ DBG_ERR("Empty row packet body");
+ php_error(E_WARNING, "Empty row packet body. PID=%d", getpid());
+ ret = FAIL;
+ break;
+ }
+
+ if (header.size < MYSQLND_MAX_PACKET_SIZE) {
+ break;
+ }
+ }
+ if (ret == PASS && new_buf) {
+ *buf = new_buf;
+ }
+ *data_size -= prealloc_more_bytes;
+ DBG_RETURN(ret);
+}
+
+
+/* {{{ php_mysqlnd_rowp_read_binary_protocol */
+static
+void php_mysqlnd_rowp_read_binary_protocol(php_mysql_packet_row *packet, MYSQLND *conn,
+ zend_uchar *p, size_t data_size TSRMLS_DC)
+{
+ int i;
+ zend_uchar *null_ptr, bit;
+ zval **current_field, **end_field, **start_field;
+ zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode;
+ zend_bool allocated;
+ void *obj;
+
+ DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
+
+ end_field = (current_field = start_field = packet->fields) + packet->field_count;
+
+
+ /* skip the first byte, not 0xFE -> 0x0, status */
+ p++;
+ null_ptr= p;
+ p += (packet->field_count + 9)/8; /* skip null bits */
+ bit = 4; /* first 2 bits are reserved */
+
+ for (i = 0; current_field < end_field; current_field++, i++) {
+#if 1
+ obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC);
+ if (allocated) {
+ *current_field = (zval *) obj;
+ } else {
+ /* It's from the cache, so we can upcast here */
+ *current_field = &((mysqlnd_zval *) obj)->zv;
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
+ }
+#else
+ MAKE_STD_ZVAL(*current_field);
+#endif
+ if (*null_ptr & bit) {
+ ZVAL_NULL(*current_field);
+ } else {
+ enum_mysqlnd_field_types type = packet->fields_metadata[i].type;
+ mysqlnd_ps_fetch_functions[type].func(*current_field, &packet->fields_metadata[i],
+ 0, &p, as_unicode TSRMLS_CC);
+ }
+ if (!((bit<<=1) & 255)) {
+ bit= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+ /* Normal queries: The buffer has one more byte at the end, because we need it */
+ packet->row_buffer[data_size] = '\0';
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_read_text_protocol */
+static
+void php_mysqlnd_rowp_read_text_protocol(php_mysql_packet_row *packet, MYSQLND *conn,
+ zend_uchar *p, size_t data_size TSRMLS_DC)
+{
+ int i;
+ zend_bool last_field_was_string;
+ zval **current_field, **end_field, **start_field;
+ zend_uchar *bit_area = packet->row_buffer + data_size + 1; /* we allocate from here */
+ zend_bool as_unicode = conn->options.numeric_and_datetime_as_unicode;
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ zend_bool as_int = conn->options.int_and_year_as_int;
+#endif
+
+ DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
+
+ end_field = (current_field = start_field = packet->fields) + packet->field_count;
+ for (i = 0; current_field < end_field; current_field++, i++) {
+ /* Don't reverse the order. It is significant!*/
+ void *obj;
+ zend_bool allocated;
+ 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);
+
+ obj = mysqlnd_palloc_get_zval(conn->zval_cache, &allocated TSRMLS_CC);
+ if (allocated) {
+ *current_field = (zval *) obj;
+ } else {
+ /* It's from the cache, so we can upcast here */
+ *current_field = &((mysqlnd_zval *) obj)->zv;
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_FREE;
+ }
+
+ 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 PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION)
+ struct st_mysqlnd_perm_bind perm_bind =
+ mysqlnd_ps_fetch_functions[packet->fields_metadata[i].type];
+#endif
+
+#ifdef MYSQLND_STRING_TO_INT_CONVERSION
+ if (as_int && perm_bind.php_type == IS_LONG &&
+ perm_bind.pack_len <= SIZEOF_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 */
+ my_int64 v = atoll((char *) p);
+ ZVAL_LONG(*current_field, v);
+ } else {
+ my_uint64 v = (my_uint64) atoll((char *) p);
+ zend_bool uns = packet->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) < (my_int64) v) ||
+ (L64(-2147483648) > (my_int64) v))))
+#endif /* SIZEOF */
+ {
+ ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ } else {
+ ZVAL_LONG(*current_field, (my_int64)v);
+ }
+ }
+ *(p + len) = save;
+ } else
+#endif
+ if (packet->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, &(packet->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, MYSQLND_LLU_SPEC,
+ (my_int64) Z_LVAL_PP(current_field));
+#if PHP_MAJOR_VERSION >= 6
+ 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);
+ }
+ if (allocated == FALSE) {
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
+ }
+ } 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 PHP_MAJOR_VERSION >= 6
+ 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);
+ }
+ if (allocated == FALSE) {
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
+ }
+ }
+ /*
+ IS_UNICODE should not be specially handled. In unicode mode
+ the buffers are not referenced - everything is copied.
+ */
+ } else
+#if PHP_MAJOR_VERSION < 6
+ {
+ ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ if (allocated == FALSE) {
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
+ }
+ }
+#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.
+
+ I suppose we can use UG(unicode) in mysqlnd.c/mysqlnd_palloc.c when
+ freeing a result set
+ to check if we need to call copy_ctor().
+
+ 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 &&
+ packet->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);
+ }
+ if (allocated == FALSE) {
+ /*
+ The zval cache will check and see that the type is IS_STRING.
+ In this case it will call copy_ctor(). This is valid when
+ allocated == TRUE . In this case we can't upcast. Thus for non-PS
+ point_type doesn't matter much, as the valuable information is
+ in the type of result set. Still good to set it.
+ */
+ if (Z_TYPE_P(*current_field) == IS_STRING) {
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
+ } else {
+ ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
+ }
+ }
+#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 */
+ packet->row_buffer[data_size] = '\0';
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ 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 TSRMLS_DC)
+{
+ MYSQLND_NET *net = &conn->net;
+ zend_uchar *p;
+ enum_func_status ret = PASS;
+ size_t data_size = 0;
+ size_t old_chunk_size = net->stream->chunk_size;
+ php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet;
+ size_t post_alloc_for_bit_fields = 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->row_buffer, 0, &data_size,
+ packet->persistent_alloc, post_alloc_for_bit_fields
+ TSRMLS_CC);
+ if (FAIL == ret) {
+ goto end;
+ }
+
+ /* packet->row_buffer is of size 'data_size + 1' */
+ packet->header.size = data_size;
+
+ if ((*(p = packet->row_buffer)) == 0xFF) {
+ /*
+ 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 (*p == 0xFE && 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 */
+ }
+ } 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_pemalloc(packet->field_count * sizeof(zval *),
+ packet->persistent_alloc);
+ }
+
+ if (packet->binary_protocol) {
+ php_mysqlnd_rowp_read_binary_protocol(packet, conn, p, data_size TSRMLS_CC);
+ } else {
+ php_mysqlnd_rowp_read_text_protocol(packet, conn, p, data_size TSRMLS_CC);
+ }
+ } 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 alloca TSRMLS_DC)
+{
+ php_mysql_packet_row *p= (php_mysql_packet_row *) _packet;
+ if (p->row_buffer) {
+ mnd_pefree(p->row_buffer, p->persistent_alloc);
+ p->row_buffer = NULL;
+ }
+ /*
+ 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 (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+
+/* {{{ php_mysqlnd_stats_read */
+static enum_func_status
+php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ zend_uchar buf[1024];
+ php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet;
+
+ DBG_ENTER("php_mysqlnd_stats_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "statistics");
+
+ packet->message = mnd_pemalloc(packet->header.size + 1, conn->persistent);
+ 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 alloca TSRMLS_DC)
+{
+ php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet;
+ if (p->message) {
+ mnd_efree(p->message);
+ p->message = NULL;
+ }
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+/* 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 TSRMLS_DC)
+{
+ /* In case of an error, we should have place to put it */
+ zend_uchar buf[1024];
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ unsigned int data_size;
+ php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet;
+
+ DBG_ENTER("php_mysqlnd_prepare_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "prepare");
+
+ data_size = packet->header.size;
+ packet->error_code = uint1korr(p);
+ p++;
+
+ if (0xFF == 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 %d", data_size);
+ php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d. PID=%d", data_size, getpid());
+ DBG_RETURN(FAIL);
+ }
+
+ packet->stmt_id = uint4korr(p);
+ p += 4;
+
+ /* Number of columns in result set */
+ packet->field_count = uint2korr(p);
+ p += 2;
+
+ packet->param_count = uint2korr(p);
+ p += 2;
+
+ if (data_size > 9) {
+ /* 0x0 filler sent by the server for 5.0+ clients */
+ p++;
+
+ packet->warning_count = uint2korr(p);
+ }
+
+ DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d",
+ packet->stmt_id, packet->field_count, packet->param_count);
+
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_prepare_free_mem */
+static
+void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet;
+ if (!alloca) {
+ mnd_efree(p);
+ }
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_chg_user_read */
+static enum_func_status
+php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
+{
+ /* There could be an error message */
+ zend_uchar buf[1024];
+ zend_uchar *p = buf;
+ zend_uchar *begin = buf;
+ php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet;
+
+ DBG_ENTER("php_mysqlnd_chg_user_read");
+
+ PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "change user response ");
+
+ /*
+ Don't increment. First byte is 0xFF on error, but otherwise is starting byte
+ of encoded sequence for length.
+ */
+
+ /* Should be always 0x0 or 0xFF for error */
+ packet->field_count= uint1korr(p);
+ p++;
+
+ if (packet->header.size == 1 && buf[0] == 0xFE &&
+ packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
+ /* We don't handle 3.23 authentication */
+ DBG_RETURN(FAIL);
+ }
+
+ if (0xFF == packet->field_count) {
+ 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);
+ }
+ if (p - begin > packet->header.size) {
+ 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 %d bytes shorter than expected. PID=%d",
+ p - begin - packet->header.size, getpid());
+ }
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_chg_user_free_mem */
+static
+void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
+{
+ if (!alloca) {
+ mnd_efree(_packet);
+ }
+}
+/* }}} */
+
+
+/* {{{ packet_methods
+ */
+mysqlnd_packet_methods packet_methods[PROT_LAST] =
+{
+ {
+ sizeof(php_mysql_packet_greet),
+ php_mysqlnd_greet_read,
+ NULL, /* write */
+ php_mysqlnd_greet_free_mem,
+ }, /* PROT_GREET_PACKET */
+ {
+ sizeof(php_mysql_packet_auth),
+ NULL, /* read */
+ php_mysqlnd_auth_write,
+ php_mysqlnd_auth_free_mem,
+ }, /* PROT_AUTH_PACKET */
+ {
+ sizeof(php_mysql_packet_ok),
+ php_mysqlnd_ok_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_ok_free_mem,
+ }, /* PROT_OK_PACKET */
+ {
+ sizeof(php_mysql_packet_eof),
+ php_mysqlnd_eof_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_eof_free_mem,
+ }, /* PROT_EOF_PACKET */
+ {
+ sizeof(php_mysql_packet_command),
+ NULL, /* read */
+ php_mysqlnd_cmd_write, /* write */
+ php_mysqlnd_cmd_free_mem,
+ }, /* PROT_CMD_PACKET */
+ {
+ sizeof(php_mysql_packet_rset_header),
+ php_mysqlnd_rset_header_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rset_header_free_mem,
+ }, /* PROT_RSET_HEADER_PACKET */
+ {
+ sizeof(php_mysql_packet_res_field),
+ php_mysqlnd_rset_field_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rset_field_free_mem,
+ }, /* PROT_RSET_FLD_PACKET */
+ {
+ sizeof(php_mysql_packet_row),
+ php_mysqlnd_rowp_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_rowp_free_mem,
+ }, /* PROT_ROW_PACKET */
+ {
+ sizeof(php_mysql_packet_stats),
+ php_mysqlnd_stats_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_stats_free_mem,
+ }, /* PROT_STATS_PACKET */
+ {
+ sizeof(php_mysql_packet_prepare_response),
+ php_mysqlnd_prepare_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_prepare_free_mem,
+ }, /* PROT_PREPARE_RESP_PACKET */
+ {
+ sizeof(php_mysql_packet_chg_user_resp),
+ php_mysqlnd_chg_user_read, /* read */
+ NULL, /* write */
+ php_mysqlnd_chg_user_free_mem,
+ } /* PROT_CHG_USER_PACKET */
+};
+/* }}} */
+
+
+/*
+ * 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 0000000000..96da8d16a8
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -0,0 +1,335 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2007 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_WIREPROTOCOL_H
+#define MYSQLND_WIREPROTOCOL_H
+
+#define MYSQLND_HEADER_SIZE 4
+
+#define MYSQLND_NULL_LENGTH (unsigned long) ~0
+
+typedef zend_uchar mysqlnd_1b;
+typedef zend_ushort mysqlnd_2b;
+typedef zend_uint mysqlnd_4b;
+
+/* Used in mysqlnd_debug.c */
+extern char * mysqlnd_read_header_name;
+extern char * mysqlnd_read_body_name;
+
+
+/* Packet handling */
+#define PACKET_INIT(packet, enum_type, c_type) \
+ { \
+ packet = (c_type) ecalloc(1, packet_methods[enum_type].struct_size); \
+ ((c_type) (packet))->header.m = &packet_methods[enum_type]; \
+ }
+#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 { \
+ ((packet)->header.m->free_mem((packet), FALSE TSRMLS_CC)); \
+ } while (0);
+
+#define PACKET_INIT_ALLOCA(packet, enum_type) \
+ { \
+ memset(&(packet), 0, packet_methods[enum_type].struct_size); \
+ (packet).header.m = &packet_methods[enum_type]; \
+ }
+#define PACKET_WRITE_ALLOCA(packet, conn) PACKET_WRITE(&(packet), (conn))
+#define PACKET_READ_ALLOCA(packet, conn) PACKET_READ(&(packet), (conn))
+#define PACKET_FREE_ALLOCA(packet) (packet.header.m->free_mem(&(packet), TRUE TSRMLS_CC))
+
+/* Enums */
+enum php_mysql_packet_type
+{
+ PROT_GREET_PACKET= 0,
+ PROT_AUTH_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_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
+};
+
+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 TSRMLS_DC);
+ size_t (*write_to_net)(void *packet, MYSQLND *conn TSRMLS_DC);
+ void (*free_mem)(void *packet, zend_bool alloca TSRMLS_DC);
+} mysqlnd_packet_methods;
+
+extern mysqlnd_packet_methods packet_methods[];
+
+
+typedef struct st_mysqlnd_packet_header {
+ size_t size;
+ zend_uchar packet_no;
+ mysqlnd_packet_methods *m;
+} mysqlnd_packet_header;
+
+/* Server greets the client */
+typedef struct st_php_mysql_packet_greet {
+ mysqlnd_packet_header header;
+ mysqlnd_1b protocol_version;
+ char *server_version;
+ mysqlnd_4b thread_id;
+ zend_uchar scramble_buf[SCRAMBLE_LENGTH];
+ /* 1 byte pad */
+ mysqlnd_2b server_capabilities;
+ mysqlnd_1b charset_no;
+ mysqlnd_2b server_status;
+ /* 13 byte pad*/
+ 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;
+} php_mysql_packet_greet;
+
+
+/* Client authenticates */
+typedef struct st_php_mysql_packet_auth {
+ mysqlnd_packet_header header;
+ mysqlnd_4b client_flags;
+ uint32 max_packet_size;
+ mysqlnd_1b charset_no;
+ /* 23 byte pad */
+ char *user;
+ /* 8 byte scramble */
+ char *db;
+ /* 12 byte scramble */
+
+ /* Here the packet ends. This is user supplied data */
+ char *password;
+ /* +1 for \0 because of scramble() */
+ unsigned char *server_scramble_buf;
+ size_t db_len;
+} php_mysql_packet_auth;
+
+/* OK packet */
+typedef struct st_php_mysql_packet_ok {
+ mysqlnd_packet_header header;
+ mysqlnd_1b field_count; /* always 0x0 */
+ mynd_ulonglong affected_rows;
+ mynd_ulonglong last_insert_id;
+ mysqlnd_2b server_status;
+ mysqlnd_2b 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;
+} php_mysql_packet_ok;
+
+
+/* Command packet */
+typedef struct st_php_mysql_packet_command {
+ mysqlnd_packet_header header;
+ enum php_mysqlnd_server_command command;
+ const char *argument;
+ size_t arg_len;
+} php_mysql_packet_command;
+
+
+/* EOF packet */
+typedef struct st_php_mysql_packet_eof {
+ mysqlnd_packet_header header;
+ mysqlnd_1b field_count; /* 0xFE */
+ mysqlnd_2b warning_count;
+ mysqlnd_2b server_status;
+ /* If error packet, we use these */
+ char error[MYSQLND_ERRMSG_SIZE+1];
+ char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
+ unsigned int error_no;
+} php_mysql_packet_eof;
+/* EOF packet */
+
+
+/* Result Set header*/
+typedef struct st_php_mysql_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.
+ */
+ mysqlnd_2b warning_count;
+ mysqlnd_2b server_status;
+ mynd_ulonglong affected_rows;
+ mynd_ulonglong 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;
+} php_mysql_packet_rset_header;
+
+
+/* Result set field packet */
+typedef struct st_php_mysql_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;
+} php_mysql_packet_res_field;
+
+
+/* Row packet */
+struct st_php_mysql_packet_row {
+ mysqlnd_packet_header header;
+ zval **fields;
+ mysqlnd_4b field_count;
+ zend_bool eof;
+ /*
+ These are, of course, only for SELECT in the EOF packet,
+ which is detected by this packet
+ */
+ mysqlnd_2b warning_count;
+ mysqlnd_2b server_status;
+
+ zend_uchar *row_buffer;
+
+ 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;
+};
+
+
+/* Statistics packet */
+typedef struct st_php_mysql_packet_stats {
+ mysqlnd_packet_header header;
+ char *message;
+ /* message_len is not part of the packet*/
+ size_t message_len;
+} php_mysql_packet_stats;
+
+
+/* COM_PREPARE response packet */
+typedef struct st_php_mysql_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;
+} php_mysql_packet_prepare_response;
+
+
+/* Statistics packet */
+typedef struct st_php_mysql_packet_chg_user_resp {
+ mysqlnd_packet_header header;
+ mysqlnd_4b field_count;
+
+ /* message_len is not part of the packet*/
+ mysqlnd_2b server_capabilities;
+ /* If error packet, we use these */
+ mysqlnd_error_info error_info;
+} php_mysql_packet_chg_user_resp;
+
+
+size_t mysqlnd_stream_write(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC);
+size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC);
+
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC);
+#endif
+
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass);
+
+unsigned long php_mysqlnd_net_field_length(zend_uchar **packet);
+zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, mynd_ulonglong length);
+
+extern char * const mysqlnd_empty_string;
+
+#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.h b/ext/mysqlnd/php_mysqlnd.h
new file mode 100644
index 0000000000..0958fcae1f
--- /dev/null
+++ b/ext/mysqlnd/php_mysqlnd.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 6 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2007 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@php.net> |
+ | Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+
+ $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 */